Ejemplo n.º 1
0
LRESULT CALLBACK Interpreter::CbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
	// Looking for HCBT_CREATEWND, just pass others on...
	if (code == HCBT_CREATEWND)
	{
		//ASSERT(lParam != NULL);
		//LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
		//ASSERT(lpcs != NULL);

		OTE* underConstruction = m_oteUnderConstruction;

		if (!underConstruction->isNil())
		{
			// Nil this out as soon as possible
			m_oteUnderConstruction = Pointers.Nil;
			underConstruction->countDown();

			ASSERT(wParam != NULL); // should be non-NULL HWND

			// set m_bDlgCreate to TRUE if it is a dialog box
			//  (this controls what kind of subclassing is done later)
			//pThreadState->m_bDlgCreate = (lpcs->lpszClass == WC_DIALOG);

			// Pass to Smalltalk for subclassing (catch unwind failures so not thrown out)
			subclassWindow(underConstruction, HWND(wParam));
		}
	}

	return ::CallNextHookEx(hHookOldCbtFilter, code, wParam, lParam);
}
Ejemplo n.º 2
0
// Free up a pool of objects maintained by the interpreter on request
void ObjectMemory::OTEPool::clear()
{
	#ifdef MEMSTATS
	{
		tracelock lock(TRACESTREAM);
		TRACESTREAM << "OTEPool(" << this << ") before clear, " << dec << m_nAllocated << " allocated, " 
			<< m_nFree << " free" << endl;
	}
	#endif

	while (m_pFreeList)
	{
		OTE* ote = m_pFreeList;
		ote->beAllocated();
		
		// All objects on the free list originated from pool space, so we need to
		// send them back there
		ote->m_flags.m_space = OTEFlags::PoolSpace;

		VariantObject* pObj = static_cast<VariantObject*>(ote->m_location);
		m_pFreeList = reinterpret_cast<OTE*>(pObj->m_fields[0]);
		ObjectMemory::deallocate(ote);
	}

	#ifdef MEMSTATS
	{
		ASSERT(m_nFree <= m_nAllocated);
		m_nAllocated -= m_nFree;
		m_nFree = 0;
		tracelock lock(TRACESTREAM);
		TRACESTREAM << "OTEPool(" << this << ") after clear, " << dec << m_nAllocated << " allocated" << endl;
	}
	#endif
}
Ejemplo n.º 3
0
template <MWORD ImageNullTerms> HRESULT ObjectMemory::LoadPointers(ibinstream& imageFile, const ImageHeader* pHeader, size_t& cbRead)
{
	ASSERT(pHeader->nGlobalPointers == NumPointers);

	::ZeroMemory(m_pConstObjs, CONSTSPACESIZE);

	size_t cbPerm = 0;
	BYTE* pNextConst = reinterpret_cast<BYTE*>(m_pConstObjs);
	int i;
	for (i = 0; i < NumPermanent; i++)
	{
		VariantObject* pConstObj = reinterpret_cast<VariantObject*>(pNextConst);

		OTE* ote = m_pOT + i;
		MWORD bytesToRead;
		MWORD allocSize;
		if (ote->isNullTerminated())
		{
			MWORD byteSize = ote->getSize();
			allocSize = byteSize + NULLTERMSIZE;
			bytesToRead = byteSize + ImageNullTerms;
		}
		else
		{
			allocSize = bytesToRead = ote->getSize();
		}

		if (bytesToRead > 0)
		{
			// Now load the rest of the object (size includes itself)
			if (!imageFile.read(&(pConstObj->m_fields), bytesToRead))
				return ImageReadError(imageFile);
		}
		else
		{
			if (allocSize == 0) pConstObj = NULL;
		}

		cbPerm += bytesToRead;
		pNextConst += _ROUND2(allocSize, 4);

		markObject(ote);
		Oop* oldLocation = reinterpret_cast<Oop*>(ote->m_location);
		ote->m_location = pConstObj;

		ote->beSticky();
		// Repair the object
		FixupObject(ote, oldLocation, pHeader);
	}

#ifdef _DEBUG
	TRACESTREAM << i<< L" permanent objects loaded totalling " << cbPerm<< L" bytes" << std::endl;
#endif

	memcpy(const_cast<VMPointers*>(&Pointers), &_Pointers, sizeof(Pointers));

	cbRead += cbPerm;
	return S_OK;
}
Ejemplo n.º 4
0
static BOOL AnswerNewInterfacePointer(BehaviorOTE* oteClass, IUnknown* punk)
{
	if (oteClass->isNil())
		return Interpreter::primitiveFailure(4);

	if (punk)
		punk->AddRef();

	OTE* poteUnk = ExternalStructure::NewPointer(oteClass, punk);
	poteUnk->beFinalizable();
	Interpreter::replaceStackTopWithNew(poteUnk);
	return primitiveSuccess();
}
Ejemplo n.º 5
0
// N.B. Like the other instantiate methods in ObjectMemory, this method for instantiating
// objects in virtual space (used for allocating Processes, for example), does not adjust
// the ref. count of the class, because this is often unecessary, and does not adjust the
// sizes to allow for fixed fields - callers must do this
VirtualOTE* ObjectMemory::newVirtualObject(BehaviorOTE* classPointer, MWORD initialSize, MWORD maxSize)
{
	#ifdef _DEBUG
	{
		ASSERT(isBehavior(Oop(classPointer)));
		Behavior& behavior = *classPointer->m_location;
		ASSERT(behavior.isIndexable());
	}
	#endif

	// Trim the sizes to acceptable bounds
	if (initialSize <= dwOopsPerPage)
		initialSize = dwOopsPerPage;
	else
		initialSize = _ROUND2(initialSize, dwOopsPerPage);

	if (maxSize < initialSize)
		maxSize = initialSize;
	else
		maxSize = _ROUND2(maxSize, dwOopsPerPage);

	// We have to allow for the virtual allocation overhead. The allocation function will add in
	// space for this. The maximum size should include this, the initial size should not
	initialSize -= sizeof(VirtualObjectHeader)/sizeof(MWORD);

	unsigned byteSize = initialSize*sizeof(MWORD);
	VariantObject* pLocation = reinterpret_cast<VariantObject*>(AllocateVirtualSpace(maxSize * sizeof(MWORD), byteSize));
	if (pLocation)
	{
		// No need to alter ref. count of process class, as it is sticky

		// Fill space with nils for initial values
		const Oop nil = Oop(Pointers.Nil);
		const unsigned loopEnd = initialSize;
		for (unsigned i = 0; i < loopEnd; i++)
			pLocation->m_fields[i] = nil;

		OTE* ote = ObjectMemory::allocateOop(static_cast<POBJECT>(pLocation));
		ote->setSize(byteSize);
		ote->m_oteClass = classPointer;
		classPointer->countUp();
		ote->m_flags = m_spaceOTEBits[OTEFlags::VirtualSpace];
		ASSERT(ote->isPointers());

		return reinterpret_cast<VirtualOTE*>(ote);
	}

	return nullptr;
}
Ejemplo n.º 6
0
// There are some fixups that we can only apply after all the objects are loaded, because
// they involve reference from one object to other objects which may not be available
// during the normal load process. These fixes are applied here
void ObjectMemory::PostLoadFix()
{
	// Special case handling for Contexts because we store
	// the sp's as integers in the image file, but at
	// run-time they are expected to be direct pointers
	const OTE* pEnd = m_pOT + m_nOTSize;	// Loop invariant
	for (OTE* ote = m_pOT; ote < pEnd; ote++)
	{
		if (!ote->isFree())
		{
			if (ote->isBytes())
			{
#ifdef _DEBUG
				{
					// Its a byte object, and may be null terminated
					const Behavior* behavior = ote->m_oteClass->m_location;
					const BytesOTE* oteBytes = reinterpret_cast<const BytesOTE*>(ote);
					const VariantByteObject* object = oteBytes->m_location;
					ASSERT(behavior->m_instanceSpec.m_nullTerminated == ote->isNullTerminated());
				}
#endif
			}
			else if (ote->m_oteClass == _Pointers.ClassProcess)
			{
				ASSERT(ote->heapSpace() == OTEFlags::VirtualSpace);
				ProcessOTE* oteProcess = reinterpret_cast<ProcessOTE*>(ote);
				Process* process = oteProcess->m_location;
				process->PostLoadFix(oteProcess);
			}
		}
	}

	ProtectConstSpace(PAGE_READONLY);

#if defined(_DEBUG) && 0
	{
		// Dump out the pointers
		TRACESTREAM << NumPointers<< L" VM Pointers..." << std::endl;
		for (int i = 0; i < NumPointers; i++)
		{
			VariantObject* obj = static_cast<VariantObject*>(m_pConstObjs);
			POTE pote = POTE(obj->m_fields[i]);
			TRACESTREAM << i<< L": " << pote << std::endl;
		}
	}
#endif
}
Ejemplo n.º 7
0
void ObjectMemory::EmptyZct()
{
	if (m_bIsReconcilingZct)
		__debugbreak();
#ifdef _DEBUG
	nDeleted = 0;

	if (!alwaysReconcileOnAdd || Interpreter::executionTrace)
		CHECKREFSNOFIX
	else
		checkStackRefs();
#endif

	// Bump the refs from the stack. Any objects remaining in the ZCT with zero counts
	// are truly garbage.
	Interpreter::IncStackRefs();

	OTE** pZct = m_pZct;
	// This tells us that we are in the process of reconcilation
	m_bIsReconcilingZct = true;
	const int nOldZctEntries = m_nZctEntries;
	m_nZctEntries = -1;

	for (int i=0;i<nOldZctEntries;i++)
	{
		OTE* ote = pZct[i];
		if (!ote->isFree() && ote->m_flags.m_count == 0)
		{
			// Note that deallocate cannot make new Zct entries
			// Because we have bumped the ref. counts of all stack ref'd objects, only true
			// garbage objects can ever have a ref. count of zero. Therefore if recursively
			// counting down throws up new zero ref. counts, these should not be added to 
			// the Zct, but deallocated. To achieve this we set a global flag to indicate
			// that we are reconciling, see AddToZct() above. 
#ifdef _DEBUG
			nDeleted++;
#endif
			recursiveFree(ote);
		}
	}

//	CHECKREFSNOFIX
}
Ejemplo n.º 8
0
BytesOTE* __fastcall ObjectMemory::shallowCopy(BytesOTE* ote)
{
	ASSERT(ote->isBytes());

	// Copying byte objects is simple and fast
	VariantByteObject& bytes = *ote->m_location;
	BehaviorOTE* classPointer = ote->m_oteClass;
	MWORD objectSize = ote->sizeOf();

	OTE* copyPointer;
	// Allocate an uninitialized object ...
	VariantByteObject* pLocation = static_cast<VariantByteObject*>(allocObject(objectSize, copyPointer));
	ASSERT((objectSize > MaxSizeOfPoolObject && copyPointer->heapSpace() == OTEFlags::NormalSpace)
		|| copyPointer->heapSpace() == OTEFlags::PoolSpace);

	ASSERT(copyPointer->getSize() == objectSize);
	// This set does not want to copy over the immutability bit - i.e. even if the original was immutable, the 
	// copy will never be.
	copyPointer->setSize(ote->getSize());
	copyPointer->m_dwFlags = (copyPointer->m_dwFlags & ~OTEFlags::WeakMask) | (ote->m_dwFlags & OTEFlags::WeakMask);
	ASSERT(copyPointer->isBytes());
	copyPointer->m_oteClass = classPointer;
	classPointer->countUp();

	// Copy the entire object over the other one, including any null terminator and object header
	memcpy(pLocation, &bytes, objectSize);

	return reinterpret_cast<BytesOTE*>(copyPointer);
}
Ejemplo n.º 9
0
PointersOTE* __fastcall ObjectMemory::newUninitializedPointerObject(BehaviorOTE* classPointer, MWORD oops)
{
	// Total size must fit in a DWORD bits
	ASSERT(oops < ((DWORD(1) << 30) - ObjectHeaderSize));

	// Don't worry, compiler will not really use multiply instruction here
	MWORD objectSize = SizeOfPointers(oops);
	OTE* ote;
	allocObject(objectSize, ote);
	ASSERT((objectSize > MaxSizeOfPoolObject && ote->heapSpace() == OTEFlags::NormalSpace)
			|| ote->heapSpace() == OTEFlags::PoolSpace);

	// These are stored in the object itself
	ASSERT(ote->getSize() == objectSize);
	classPointer->countUp();
	ote->m_oteClass = classPointer;

	// DO NOT Initialise the fields to nils

	ASSERT(ote->isPointers());
	
	return reinterpret_cast<PointersOTE*>(ote);
}
Ejemplo n.º 10
0
// Compact an object by updating all the Oops it contains using the
// forwarding pointers in the old OT.
void ObjectMemory::compactObject(OTE* ote)
{
	// We shouldn't come in here unless OTE already fixed for this object
	HARDASSERT(ote >= m_pOT && ote < m_pFreePointerList);

	// First fix up the class (remember that the new object pointer is stored in the
	// old one's object location slot
	compactOop(ote->m_oteClass);

	if (ote->isPointers())
	{
		VariantObject* varObj = static_cast<VariantObject*>(ote->m_location);
		const MWORD lastPointer = ote->pointersSize();
		for (MWORD i = 0; i < lastPointer; i++)
		{
			// This will get nicely optimised by the Compiler
			Oop fieldPointer = varObj->m_fields[i];

			// We don't need to visit SmallIntegers and objects we've already visited
			if (!isIntegerObject(fieldPointer))
			{
				OTE* fieldOTE = reinterpret_cast<OTE*>(fieldPointer);
				// If pointing at a free'd object ,then it has been moved
				if (fieldOTE->isFree())
				{
					// Should be one of the old OT entries, pointing outside the o
					Oop movedTo = reinterpret_cast<Oop>(fieldOTE->m_location);
					HARDASSERT(movedTo >= (Oop)m_pOT && movedTo < (Oop)m_pFreePointerList);
					// Get the new OTE from the old ...
					varObj->m_fields[i] = movedTo;
				}
			}
		}
	}
	// else, we don't even need to look at the body of byte objects any more
}
Ejemplo n.º 11
0
BOOL __fastcall Interpreter::primitiveHookWindowCreate()
{
	Oop argPointer = stackTop();
	OTE* underConstruction = m_oteUnderConstruction;
	OTE* receiverPointer = reinterpret_cast<OTE*>(stackValue(1));

	if (!underConstruction->isNil() && underConstruction != receiverPointer)
	{
		// Hooked by another window - fail the primitive
		return primitiveFailureWith(1, underConstruction);
	}


	if (argPointer == Oop(Pointers.True))
	{
		// Hooking

		if (underConstruction != receiverPointer)
		{
			ASSERT(underConstruction->isNil());
			m_oteUnderConstruction= receiverPointer;
			receiverPointer->countUp();
		}
	}
	else
	{
		if (argPointer == Oop(Pointers.False))
		{
			// Unhooking
			if (underConstruction == receiverPointer)
			{
				tracelock lock(TRACESTREAM);
				TRACESTREAM << "WARNING: Unhooking create for " << hex << underConstruction << " before HCBT_CREATEWND" << endl;
				ObjectMemory::nilOutPointer(m_oteUnderConstruction);
			}
			else
				ASSERT(underConstruction->isNil());
		}
		else
			return primitiveFailureWith(0, argPointer);	// Invalid argument
	}

	popStack();
	return TRUE;
}
Ejemplo n.º 12
0
OTE* ObjectMemory::CopyElements(OTE* oteObj, MWORD startingAt, MWORD count)
{
	// Note that startingAt is expected to be a zero-based index
	ASSERT(startingAt >= 0);
	OTE* oteSlice;

	if (oteObj->isBytes())
	{
		BytesOTE* oteBytes = reinterpret_cast<BytesOTE*>(oteObj);
		size_t elementSize = ObjectMemory::GetBytesElementSize(oteBytes);

		if (count == 0 || ((startingAt + count) * elementSize <= oteBytes->bytesSize()))
		{
			MWORD objectSize = elementSize * count;

			if (oteBytes->m_flags.m_weakOrZ)
			{
				// TODO: Allocate the correct number of null term bytes based on the encoding
				auto newBytes = static_cast<VariantByteObject*>(allocObject(objectSize + NULLTERMSIZE, oteSlice));
				// When copying strings, the slices has the same string class
				(oteSlice->m_oteClass = oteBytes->m_oteClass)->countUp();
				memcpy(newBytes->m_fields, oteBytes->m_location->m_fields + (startingAt * elementSize), objectSize);
				*reinterpret_cast<NULLTERMTYPE*>(&newBytes->m_fields[objectSize]) = 0;
				oteSlice->beNullTerminated();
				return oteSlice;
			}
			else
			{
				VariantByteObject* newBytes = static_cast<VariantByteObject*>(allocObject(objectSize, oteSlice));
				// When copying bytes, the slice is always a ByteArray
				oteSlice->m_oteClass = Pointers.ClassByteArray;
				oteSlice->beBytes();
				memcpy(newBytes->m_fields, oteBytes->m_location->m_fields + (startingAt * elementSize), objectSize);
				return oteSlice;
			}
		}
	}
	else
	{
		// Pointers
		PointersOTE* otePointers = reinterpret_cast<PointersOTE*>(oteObj);
		BehaviorOTE* oteClass = otePointers->m_oteClass;
		InstanceSpecification instSpec = oteClass->m_location->m_instanceSpec;
		if (instSpec.m_indexable)
		{
			startingAt += instSpec.m_fixedFields;

			if (count == 0 || (startingAt + count) <= otePointers->pointersSize())
			{
				MWORD objectSize = SizeOfPointers(count);
				auto pSlice = static_cast<VariantObject*>(allocObject(objectSize, oteSlice));
				// When copying pointers, the slice is always an Array
				oteSlice->m_oteClass = Pointers.ClassArray;
				VariantObject* pSrc = otePointers->m_location;
				for (MWORD i = 0; i < count; i++)
				{
					countUp(pSlice->m_fields[i] = pSrc->m_fields[startingAt + i]);
				}
				return oteSlice;
			}
		}
	}

	return nullptr;
}
Ejemplo n.º 13
0
// Uses object identity to locate the next occurrence of the argument in the receiver from
// the specified index to the specified index
Oop* __fastcall Interpreter::primitiveNextIndexOfFromTo()
{
	Oop integerPointer = stackTop();
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(0);				// to not an integer
	const SMALLINTEGER to = ObjectMemoryIntegerValueOf(integerPointer);

	integerPointer = stackValue(1);
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(1);				// from not an integer
	SMALLINTEGER from = ObjectMemoryIntegerValueOf(integerPointer);

	Oop valuePointer = stackValue(2);
	OTE* receiverPointer = reinterpret_cast<OTE*>(stackValue(3));

//	#ifdef _DEBUG
		if (ObjectMemoryIsIntegerObject(receiverPointer))
			return primitiveFailure(2);				// Not valid for SmallIntegers
//	#endif

	Oop answer = ZeroPointer;
	if (to >= from)
	{
		if (!receiverPointer->isPointers())
		{
			// Search a byte object
			BytesOTE* oteBytes = reinterpret_cast<BytesOTE*>(receiverPointer);

			if (ObjectMemoryIsIntegerObject(valuePointer))// Arg MUST be an Integer to be a member
			{
				const MWORD byteValue = ObjectMemoryIntegerValueOf(valuePointer);
				if (byteValue < 256)	// Only worth looking for 0..255
				{
					const SMALLINTEGER length = oteBytes->bytesSize();
					// We can only be in here if to>=from, so if to>=1, then => from >= 1
					// furthermore if to <= length then => from <= length
					if (from < 1 || to > length)
						return primitiveFailure(2);

					// Search is in bounds, lets do it
			
					VariantByteObject* bytes = oteBytes->m_location;

					from--;
					while (from < to)
						if (bytes->m_fields[from++] == byteValue)
						{
							answer = ObjectMemoryIntegerObjectOf(from);
							break;
						}
				}
			}
		}
		else
		{
			// Search a pointer object - but only the indexable vars
			
			PointersOTE* oteReceiver = reinterpret_cast<PointersOTE*>(receiverPointer);
			VariantObject* receiver = oteReceiver->m_location;
			Behavior* behavior = receiverPointer->m_oteClass->m_location;
			const MWORD length = oteReceiver->pointersSize();
			const MWORD fixedFields = behavior->m_instanceSpec.m_fixedFields;

			// Similar reasoning with to/from as for byte objects, but here we need to
			// take account of the fixed fields.
			if (from < 1 || (to + fixedFields > length))
				return primitiveFailure(2);	// Out of bounds

			Oop* indexedFields = receiver->m_fields + fixedFields;
			from--;
			while (from < to)
				if (indexedFields[from++] == valuePointer)
				{
					answer = ObjectMemoryIntegerObjectOf(from);
					break;
				}
		}

	}
	else
		answer = ZeroPointer; 		// Range is non-inclusive, cannot be there

	stackValue(3) = answer;
	return primitiveSuccess(3);
}
Ejemplo n.º 14
0
// Non-standard, but has very beneficial effect on performance
BOOL __fastcall Interpreter::primitiveNextPutAll()
{
	Oop* sp = m_registers.m_stackPointer;
	WriteStreamOTE* streamPointer = reinterpret_cast<WriteStreamOTE*>(*(sp-1));		// Access receiver under argument

	WriteStream* writeStream = streamPointer->m_location;
	
	// Ensure valid stream - checks from Blue Book
	if (!ObjectMemoryIsIntegerObject(writeStream->m_index) ||
		!ObjectMemoryIsIntegerObject(writeStream->m_writeLimit))
		return primitiveFailure(0);	// Fails invariant check

	SMALLINTEGER index = ObjectMemoryIntegerValueOf(writeStream->m_index);
	SMALLINTEGER limit = ObjectMemoryIntegerValueOf(writeStream->m_writeLimit);

	if (index < 0)
		return primitiveFailure(2);

	Oop value = *(sp);
	
	OTE* oteBuf = writeStream->m_array;
	BehaviorOTE* bufClass = oteBuf->m_oteClass;

	MWORD newIndex;

	if (bufClass == Pointers.ClassString)
	{
		BehaviorOTE* oteClass = ObjectMemory::fetchClassOf(value);
		if (oteClass != Pointers.ClassString && oteClass != Pointers.ClassSymbol)
			return primitiveFailure(4);	// Attempt to put non-string

		StringOTE* oteString = reinterpret_cast<StringOTE*>(value);
		String* str = oteString->m_location;
		
		MWORD valueSize = oteString->bytesSize();
		newIndex = MWORD(index)+valueSize;

		if (newIndex >= static_cast<MWORD>(limit))			// Beyond write limit
			return primitiveFailure(2);

		if (static_cast<int>(newIndex) >= oteBuf->bytesSizeForUpdate())
			return primitiveFailure(3);	// Attempt to write off end of buffer

		String* buf = static_cast<String*>(oteBuf->m_location);
		memcpy(buf->m_characters+index, str->m_characters, valueSize);
	}
	else if (bufClass == Pointers.ClassByteArray)
	{
		if (ObjectMemory::fetchClassOf(value) != bufClass)
			return primitiveFailure(4);	// Attempt to put non-ByteArray

		ByteArrayOTE* oteBytes = reinterpret_cast<ByteArrayOTE*>(value);
		ByteArray* bytes = oteBytes->m_location;
		MWORD valueSize = oteBytes->bytesSize();
		newIndex = MWORD(index)+valueSize;

		if (newIndex >= (MWORD)limit)			// Beyond write limit
			return primitiveFailure(2);

		if (static_cast<int>(newIndex) >= oteBuf->bytesSizeForUpdate())
			return primitiveFailure(3);	// Attempt to write off end of buffer

		ByteArray* buf = static_cast<ByteArray*>(oteBuf->m_location);
		memcpy(buf->m_elements+index, bytes->m_elements, valueSize);
	}
	else if (bufClass == Pointers.ClassArray)
	{
		if (ObjectMemory::fetchClassOf(value) != Pointers.ClassArray)
			return primitiveFailure(4);	// Attempt to put non-Array

		ArrayOTE* oteArray = reinterpret_cast<ArrayOTE*>(value);
		Array* array = oteArray->m_location;
		MWORD valueSize = oteArray->pointersSize();
		newIndex = MWORD(index) + valueSize;

		if (newIndex >= (MWORD)limit)			// Beyond write limit
			return primitiveFailure(2);

		if (static_cast<int>(newIndex) >= oteBuf->pointersSizeForUpdate())
			return primitiveFailure(3);	// Attempt to write off end of buffer

		Array* buf = static_cast<Array*>(oteBuf->m_location);

		for (MWORD i = 0; i < valueSize; i++)
		{
			ObjectMemory::storePointerWithValue(buf->m_elements[index + i], array->m_elements[i]);
		}
	}
	else
		return primitiveFailure(1);
	
	writeStream->m_index = Integer::NewUnsigned32WithRef(newIndex);		// Increment the stream index

	// As we no longer pop stack here, the receiver is still under the argument
	*(sp-1) = value;

	return sizeof(Oop);		// Pop 4 bytes
}
Ejemplo n.º 15
0
// Perform a compacting GC
size_t ObjectMemory::compact(Oop* const sp)
{
	TRACE("Compacting OT, size %d, free %d, ...\n", m_nOTSize, m_pOT + m_nOTSize - m_pFreePointerList);
	EmptyZct(sp);

	// First perform a normal GC
	reclaimInaccessibleObjects(GCNormal);

	Interpreter::freePools();

	// Walk the OT from the bottom to locate free entries, and from the top to locate candidates to move
	// 

	size_t moved = 0;
	OTE* last = m_pOT + m_nOTSize - 1;
	OTE* first = m_pOT;
#pragma warning(push)
#pragma warning(disable : 4127)
	while(true)
#pragma warning(pop)
	{
		// Look for a tail ender
		while (last > first && last->isFree())
			last--;
		// Look for a free slot
		while (first < last && !first->isFree())
			first++;
		if (first == last)
			break;	// Met in the middle, we're done
		
		HARDASSERT(first->isFree());
		HARDASSERT(!last->isFree());

		// Copy the tail ender over the free slot
		*first = *last;
		moved++;
		// Leave forwarding pointer in the old slot
		last->m_location = reinterpret_cast<POBJECT>(first);
		last->beFree();
		last->m_count = 0;
		// Advance last as we've moved this slot
		last--;
	}

	HARDASSERT(last == first);
	// At this point, last == first, and the first free slot will be that after last

	TRACE("%d OTEs compacted\n", moved);

	// Now we can update the objects using the forwarding pointers in the old slots

	// We must remove the const. spaces memory protect for the duration of the pointer update
	ProtectConstSpace(PAGE_READWRITE);

	// New head of free list is first OTE after the single contiguous block of used OTEs
	// Need to set this before compacting as 
	m_pFreePointerList = last+1;

	// Now run through the new OT and update the Oops in the objects
	OTE* pOTE = m_pOT;
	while (pOTE <= last)
	{
		compactObject(pOTE);
		pOTE++;
	}

	// Note that this copies VMPointers to cache area
	ProtectConstSpace(PAGE_READONLY);

	// We must inform the interpreter that it needs to update any cached Oops from the forward pointers
	// before we rebuild the free list (which will destroy those pointers to the new OTEs)
	Interpreter::OnCompact();

	// The last used slot will be the slot before the first entry in the free list
	// Using this, round up from the last used slot to the to commit granularity, then uncommit any later slots
	// 
	
	OTE* end = (OTE*)_ROUND2(reinterpret_cast<ULONG_PTR>(m_pFreePointerList + 1), dwAllocationGranularity);

#ifdef _DEBUG
	m_nFreeOTEs = end - m_pFreePointerList;
#endif

	SIZE_T bytesToDecommit = reinterpret_cast<ULONG_PTR>(m_pOT + m_nOTSize) - reinterpret_cast<ULONG_PTR>(end);
	::VirtualFree(end, bytesToDecommit, MEM_DECOMMIT);
	m_nOTSize = end - m_pOT;

	// Now fix up the free list
	OTE* cur = m_pFreePointerList;
	while (cur < end)
	{
		HARDASSERT(cur->isFree());
		cur->m_location = reinterpret_cast<POBJECT>(cur + 1);
		cur++;
	}

	// Could do this before or after check refs, since that can account for Zct state
	PopulateZct(sp);

	CHECKREFERENCES

	HeapCompact();

	TRACE("... OT compacted, size %d, free %d.\n", m_nOTSize, end - m_pFreePointerList);

	Interpreter::scheduleFinalization();

	return m_pFreePointerList - m_pOT;
}
Ejemplo n.º 16
0
template <MWORD ImageNullTerms> HRESULT ObjectMemory::LoadObjects(ibinstream & imageFile, const ImageHeader * pHeader, size_t & cbRead)
{
	// Other free OTEs will be threaded in front of the first OTE off the end
	// of the currently committed table space. We set the free list pointer
	// to that OTE rather than NULL to distinguish attemps to access off the
	// end of the current table, which then allows us to dynamically grow it
	// on demand
	OTE* pEnd = m_pOT + pHeader->nTableSize;
	m_pFreePointerList = reinterpret_cast<OTE*>(pEnd);

#ifdef _DEBUG
	unsigned numObjects = NumPermanent;	// Allow for VM registry, etc!
	m_nFreeOTEs = m_nOTSize - pHeader->nTableSize;
#endif

	size_t nDataSize = 0;
	for (OTE* ote = m_pOT + NumPermanent; ote < pEnd; ote++)
	{
		if (!ote->isFree())
		{
			MWORD byteSize = ote->getSize();

			MWORD* oldLocation = reinterpret_cast<MWORD*>(ote->m_location);

			Object* pBody;

			// Allocate space for the object, and copy into that space
			if (ote->heapSpace() == OTEFlags::VirtualSpace)
			{
				MWORD dwMaxAlloc;
				if (!imageFile.read(&dwMaxAlloc, sizeof(MWORD)))
					return ImageReadError(imageFile);
				cbRead += sizeof(MWORD);

				pBody = reinterpret_cast<Object*>(AllocateVirtualSpace(dwMaxAlloc, byteSize));
				ote->m_location = pBody;
			}
			else
			{
				if (ote->isNullTerminated())
				{
					ASSERT(!ote->isPointers());
					pBody = AllocObj(ote, byteSize + NULLTERMSIZE);
					if (NULLTERMSIZE > ImageNullTerms)
					{
						// Ensure we have a full null-terminator
						*reinterpret_cast<NULLTERMTYPE*>(static_cast<VariantByteObject*>(pBody)->m_fields+byteSize) = 0;
					}
					byteSize += ImageNullTerms;
				}
				else
				{
					pBody = AllocObj(ote, byteSize);
				}

			}

			markObject(ote);
			if (!imageFile.read(pBody, byteSize))
				return ImageReadError(imageFile);

			cbRead += byteSize;
			FixupObject(ote, oldLocation, pHeader);

#ifdef _DEBUG
			numObjects++;
#endif
		}
		else
		{
			// Thread onto the free list
			ote->m_location = (reinterpret_cast<POBJECT>(m_pFreePointerList));
			m_pFreePointerList = ote;
#ifdef _DEBUG
			m_nFreeOTEs++;
#endif
		}
	}

	// Note that we don't terminate the free list with a null, because
	// it must point off into space in order to get a GPF when it
	// needs to be expanded (at which point we commit more pages)

#ifdef _DEBUG
	ASSERT(numObjects + m_nFreeOTEs == m_nOTSize);
	ASSERT(m_nFreeOTEs = CountFreeOTEs());
	TRACESTREAM << std::dec << numObjects<< L", " << m_nFreeOTEs<< L" free" << std::endl;
#endif

	cbRead += nDataSize;
	return S_OK;
}
Ejemplo n.º 17
0
template <bool MaybeZ, bool Initialized> BytesOTE* ObjectMemory::newByteObject(BehaviorOTE* classPointer, MWORD elementCount)
{
	Behavior& byteClass = *classPointer->m_location;
	OTE* ote;

	if (!MaybeZ || !byteClass.m_instanceSpec.m_nullTerminated)
	{
		ASSERT(!classPointer->m_location->m_instanceSpec.m_nullTerminated);

		VariantByteObject* newBytes = static_cast<VariantByteObject*>(allocObject(elementCount + SizeOfPointers(0), ote));
		ASSERT((elementCount > MaxSizeOfPoolObject && ote->heapSpace() == OTEFlags::NormalSpace)
			|| ote->heapSpace() == OTEFlags::PoolSpace);

		ASSERT(ote->getSize() == elementCount + SizeOfPointers(0));

		if (Initialized)
		{
			// Byte objects are initialized to zeros (but not the header)
			// Note that we round up to initialize to the next DWORD
			// This can be useful when working on a 32-bit word machine
			ZeroMemory(newBytes->m_fields, _ROUND2(elementCount, sizeof(DWORD)));
			classPointer->countUp();
		}

		ote->m_oteClass = classPointer;
		ote->beBytes();
	}
	else
	{
		ASSERT(classPointer->m_location->m_instanceSpec.m_nullTerminated);

		MWORD objectSize;

		switch (reinterpret_cast<const StringClass&>(byteClass).Encoding)
		{
		case StringEncoding::Ansi:
		case StringEncoding::Utf8:
			objectSize = elementCount * sizeof(AnsiString::CU);
			break;
		case StringEncoding::Utf16:
			objectSize = elementCount * sizeof(Utf16String::CU);
			break;
		case StringEncoding::Utf32:
			objectSize = elementCount * sizeof(Utf32String::CU);
			break;
		default:
			__assume(false);
			break;
		}

		// TODO: Allocate the correct number of null term bytes based on the encoding
		objectSize += NULLTERMSIZE;

		VariantByteObject* newBytes = static_cast<VariantByteObject*>(allocObject(objectSize + SizeOfPointers(0), ote));
		ASSERT((objectSize > MaxSizeOfPoolObject && ote->heapSpace() == OTEFlags::NormalSpace)
			|| ote->heapSpace() == OTEFlags::PoolSpace);

		ASSERT(ote->getSize() == objectSize + SizeOfPointers(0));

		if (Initialized)
		{
			// Byte objects are initialized to zeros (but not the header)
			// Note that we round up to initialize to the next DWORD
			// This can be useful when working on a 32-bit word machine
			ZeroMemory(newBytes->m_fields, _ROUND2(objectSize, sizeof(DWORD)));
			classPointer->countUp();
		}
		else
		{
			// We still want to ensure the null terminator is set, even if not initializing the rest of the object
			*reinterpret_cast<NULLTERMTYPE*>(&newBytes->m_fields[objectSize - NULLTERMSIZE]) = 0;
		}

		ote->m_oteClass = classPointer;
		ote->beNullTerminated();
		HARDASSERT(ote->isBytes());
	}

	return reinterpret_cast<BytesOTE*>(ote);
}
Ejemplo n.º 18
0
	/* 
		Implements 
		
		String>>replaceFrom: start
    		to: stop
    		with: aString
    		startingAt: startAt

		But is also used for ByteArray

		Does not use successFlag, and nils out argument (if successful)
		to leave a clean stack
	*/
	BOOL __fastcall Interpreter::primitiveStringReplace()
	{
		Oop integerPointer = stackTop();
		if (!ObjectMemoryIsIntegerObject(integerPointer))
			return primitiveFailure(0);

		SMALLINTEGER startAt = ObjectMemoryIntegerValueOf(integerPointer);
		OTE* argPointer = reinterpret_cast<OTE*>(stackValue(1));
		integerPointer = stackValue(2);
		if (!ObjectMemoryIsIntegerObject(integerPointer))
			return primitiveFailure(1);

		SMALLINTEGER stop = ObjectMemoryIntegerValueOf(integerPointer);
		integerPointer = stackValue(3);
		if (!ObjectMemoryIsIntegerObject(integerPointer))
			return primitiveFailure(2);

		SMALLINTEGER start = ObjectMemoryIntegerValueOf(integerPointer);
		OTE* receiverPointer = reinterpret_cast<OTE*>(stackValue(4));
		
		// Validity checks
		TODO("Try to do cleverer faster check here - too many (reproducing V behaviour)")

		// Only works for byte objects
		#ifdef _DEBUG
			if (!receiverPointer->isBytes())
				return primitiveFailure(0);
		#else
			// Assume primitive used correctly - i.e. only in byte objects
		#endif

		if (ObjectMemoryIsIntegerObject(argPointer) || !argPointer->isBytes())
			return primitiveFailure(3);
		
		// Empty move if stop before start, is considered valid regardless (strange but true)
		TODO("Change this so that does fail if stop or start < 1, only like this for V compatibility")
		if (stop >= start)
		{
			POBJECT receiverBytes = receiverPointer->m_location;
			
			// The receiver can be an indirect pointer (e.g. an instance of ExternalAddress)
			BYTE* pTo;
			Behavior* byteClass = receiverPointer->m_oteClass->m_location;
			if (byteClass->isIndirect())
				pTo = static_cast<BYTE*>(static_cast<ExternalAddress*>(receiverBytes)->m_pointer);
			else
			{
				int length = receiverPointer->bytesSize();
				// We can only be in here if stop>=start, so if start>=1, then => stop >= 1
				// furthermore if stop <= length then => start <= length
				if (start < 1 || stop > length)
					return primitiveFailure(4);
				pTo = static_cast<ByteArray*>(receiverBytes)->m_elements;
			}

			POBJECT argBytes = argPointer->m_location;
			// The argument can also be an indirect pointer (e.g. an instance of ExternalAddress)
			BYTE* pFrom;
			Behavior* argClass = argPointer->m_oteClass->m_location;
			if (argClass->isIndirect())
				pFrom = static_cast<BYTE*>(static_cast<ExternalAddress*>(argBytes)->m_pointer);
			else
			{
				int length = argPointer->bytesSize();
				// We can only be in here if stop>=start, so => stop-start >= 0
				// therefore if startAt >= 1 then => stopAt >= 1, for similar
				// reasons (since stopAt >= startAt) we don't need to test 
				// that startAt <= length
				int stopAt = startAt+stop-start;
				if (startAt < 1 || stopAt > length)
					return primitiveFailure(4);
				pFrom = static_cast<ByteArray*>(argBytes)->m_elements;
			}

			// Remember that Smalltalk indices are 1 based
			// Might be overlapping
			memmove(pTo+start-1, pFrom+startAt-1, stop-start+1);
		}
		pop(4);
		return TRUE;
	}
Ejemplo n.º 19
0
//	This is a double dispatched primitive which knows that the argument is a byte object (though
//	we still check this to avoid GPFs), and the receiver is guaranteed to be an address object. e.g.
//
//		anExternalAddress replaceBytesOf: anOtherByteObject from: start to: stop startingAt: startAt
//
BOOL __fastcall Interpreter::primitiveIndirectReplaceBytes()
{
	Oop integerPointer = stackTop();
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(0);	// startAt is not an integer
	SMALLINTEGER startAt = ObjectMemoryIntegerValueOf(integerPointer);

	integerPointer = stackValue(1);
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(1);	// stop is not an integer
	SMALLINTEGER stop = ObjectMemoryIntegerValueOf(integerPointer);

	integerPointer = stackValue(2);
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(2);	// start is not an integer
	SMALLINTEGER start = ObjectMemoryIntegerValueOf(integerPointer);

	OTE* argPointer = reinterpret_cast<OTE*>(stackValue(3));
	if (ObjectMemoryIsIntegerObject(argPointer) || !argPointer->isBytes())
		return primitiveFailure(3);	// Argument MUST be a byte object

	// Empty move if stop before start, is considered valid regardless (strange but true)
	if (stop >= start)
	{
		if (start < 1 || startAt < 1)
			return primitiveFailure(4);		// out-of-bounds

		AddressOTE* receiverPointer = reinterpret_cast<AddressOTE*>(stackValue(4));
		// Only works for byte objects
		ASSERT(receiverPointer->isBytes());
		ExternalAddress* receiverBytes = receiverPointer->m_location;
		#ifdef _DEBUG
		{
			Behavior* behavior = receiverPointer->m_oteClass->m_location;
			ASSERT(behavior->isIndirect());
		}
		#endif

		// Because the receiver is an address, we do not know the size of the object
		// it points at, and so cannot perform any bounds checks - BEWARE
		BYTE* pFrom = static_cast<BYTE*>(receiverBytes->m_pointer);

		// We still permit the argument to be an address to cut down on the double dispatching
		// required.
		BYTE* pTo;
		Behavior* behavior = argPointer->m_oteClass->m_location;
		if (behavior->isIndirect())
		{
			AddressOTE* oteBytes = reinterpret_cast<AddressOTE*>(argPointer);
			// Cannot check length 
			pTo = static_cast<BYTE*>(oteBytes->m_location->m_pointer);
		}
		else
		{
			// Can check that not writing off the end of the argument
			int length = argPointer->bytesSize();
			// We can only be in here if stop>=start, so => stop-start >= 0
			// therefore if startAt >= 1 then => stopAt >= 1, for similar
			// reasons (since stopAt >= startAt) we don't need to test 
			// that startAt <= length
			if (stop > length)
				return primitiveFailure(4);		// Bounds error

			VariantByteObject* argBytes = reinterpret_cast<BytesOTE*>(argPointer)->m_location;
			pTo = argBytes->m_fields;
		}

		memmove(pTo+start-1, pFrom+startAt-1, stop-start+1);
	}
	// Answers the argument by moving it down over the receiver
	stackValue(4) = reinterpret_cast<Oop>(argPointer);
	pop(4);
	return TRUE;
}
Ejemplo n.º 20
0
//	This is a double dispatched primitive which knows that the argument is a byte object (though
//	we still check this to avoid GPFs), and the receiver is guaranteed to be a byte object. e.g.
//
//		aByteObject replaceBytesOf: anOtherByteObject from: start to: stop startingAt: startAt
//
BOOL __fastcall Interpreter::primitiveReplaceBytes()
{
	Oop integerPointer = stackTop();
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(0);	// startAt is not an integer
	SMALLINTEGER startAt = ObjectMemoryIntegerValueOf(integerPointer);

	integerPointer = stackValue(1);
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(1);	// stop is not an integer
	SMALLINTEGER stop = ObjectMemoryIntegerValueOf(integerPointer);

	integerPointer = stackValue(2);
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(2);	// start is not an integer
	SMALLINTEGER start = ObjectMemoryIntegerValueOf(integerPointer);

	OTE* argPointer = reinterpret_cast<OTE*>(stackValue(3));
	if (ObjectMemoryIsIntegerObject(argPointer) || !argPointer->isBytes())
		return primitiveFailure(3);	// Argument MUST be a byte object

	// Empty move if stop before start, is considered valid regardless (strange but true)
	// this is the convention adopted by most implementations.
	if (stop >= start)
	{
		if (startAt < 1 || start < 1)
			return primitiveFailure(4);		// Out-of-bounds

		// We still permit the argument to be an address to cut down on the number of primitives
		// and double dispatch methods we must implement (2 rather than 4)
		BYTE* pTo;

		Behavior* behavior = argPointer->m_oteClass->m_location;
		if (behavior->isIndirect())
		{
			AddressOTE* oteBytes = reinterpret_cast<AddressOTE*>(argPointer);
			// We don't know how big the object is the argument points at, so cannot check length
			// against stop point
			pTo = static_cast<BYTE*>(oteBytes->m_location->m_pointer);
		}
		else
		{
			// We can test that we're not going to write off the end of the argument
			int length = argPointer->bytesSize();

			// We can only be in here if stop>=start, so => stop-start >= 0
			// therefore if startAt >= 1 then => stopAt >= 1, for similar
			// reasons (since stopAt >= startAt) we don't need to test 
			// that startAt <= length
			if (stop > length)
				return primitiveFailure(4);		// Bounds error

			VariantByteObject* argBytes = reinterpret_cast<BytesOTE*>(argPointer)->m_location;
			pTo = argBytes->m_fields;
		}

		BytesOTE* receiverPointer = reinterpret_cast<BytesOTE*>(stackValue(4));

		// Now validate that the interval specified for copying from the receiver
		// is within the bounds of the receiver (we've already tested startAt)
		{
			int length = receiverPointer->bytesSize();
			// We can only be in here if stop>=start, so if start>=1, then => stop >= 1
			// furthermore if stop <= length then => start <= length
			int stopAt = startAt+stop-start;
			if (stopAt > length)
				return primitiveFailure(4);
		}

		// Only works for byte objects
		ASSERT(receiverPointer->isBytes());
		VariantByteObject* receiverBytes = receiverPointer->m_location;
		#ifdef _DEBUG
		{
			Behavior* behavior = receiverPointer->m_oteClass->m_location;
			ASSERT(!behavior->isIndirect());
		}
		#endif

		BYTE* pFrom = receiverBytes->m_fields;

		memmove(pTo+start-1, pFrom+startAt-1, stop-start+1);
	}

	// Answers the argument by moving it down over the receiver
	stackValue(4) = reinterpret_cast<Oop>(argPointer);
	pop(4);
	return TRUE;
}