示例#1
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);
}
示例#2
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
}
示例#3
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;
	}
示例#4
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;
}
示例#5
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;
}
示例#6
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);
}