Example #1
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;
}
Example #2
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;
	}
Example #3
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;
}