Esempio n. 1
0
// The primitive handles PositionableStream>>atEnd, but only for arrays/strings
// Does not use successFlag. Unary, so does not modify the stack pointer
BOOL __fastcall Interpreter::primitiveAtEnd()
{
	PosStreamOTE* streamPointer = reinterpret_cast<PosStreamOTE*>(stackTop());		// Access receiver
	//ASSERT(!ObjectMemoryIsIntegerObject(streamPointer) && ObjectMemory::isKindOf(streamPointer, Pointers.ClassPositionableStream));
	PositionableStream* readStream = streamPointer->m_location;

	// Ensure valid stream (see BBB p632)
	if (!ObjectMemoryIsIntegerObject(readStream->m_index) ||
		!ObjectMemoryIsIntegerObject(readStream->m_readLimit))
		return primitiveFailure(0);

	SMALLINTEGER index = ObjectMemoryIntegerValueOf(readStream->m_index);
	SMALLINTEGER limit = ObjectMemoryIntegerValueOf(readStream->m_readLimit);
	BehaviorOTE* bufClass = readStream->m_array->m_oteClass;

	OTE* boolResult;
	if (bufClass == Pointers.ClassString || bufClass == Pointers.ClassByteArray)
		boolResult = index >= limit || (MWORD(index) >= readStream->m_array->bytesSize()) ?
			Pointers.True : Pointers.False;
	else if (bufClass == Pointers.ClassArray)
		boolResult = index >= limit || (MWORD(index) >= readStream->m_array->pointersSize()) ?
			Pointers.True : Pointers.False;
	else
		return primitiveFailure(1);		// Doesn't work for non-Strings/ByteArrays/Arrays, or if out of bounds
	
	stackTop() = reinterpret_cast<Oop>(boolResult);
	return primitiveSuccess();
}
Esempio n. 2
0
Oop* Interpreter::primitiveCopyFromTo(Oop* const sp, unsigned)
{
	Oop oopToArg = *sp;
	Oop oopFromArg = *(sp - 1);
	OTE* oteReceiver = reinterpret_cast<OTE*>(*(sp - 2));
	if (ObjectMemoryIsIntegerObject(oopToArg) && ObjectMemoryIsIntegerObject(oopFromArg))
	{
		SMALLINTEGER from = ObjectMemoryIntegerValueOf(oopFromArg);
		SMALLINTEGER to = ObjectMemoryIntegerValueOf(oopToArg);

		if (from > 0)
		{
			SMALLINTEGER count = to - from + 1;
			if (count >= 0)
			{
				OTE* oteAnswer = ObjectMemory::CopyElements(oteReceiver, from - 1, count);
				if (oteAnswer != nullptr)
				{
					*(sp - 2) = (Oop)oteAnswer;
					ObjectMemory::AddToZct(oteAnswer);
					return sp - 2;
				}
			}
		}
		// Bounds error
		return primitiveFailure(1);
	}
	else
	{
		// Non-SmallInteger from and/or to
		return primitiveFailure(0);
	}
}
Esempio n. 3
0
// Locate the next occurrence of the given character in the receiver between the specified indices.
BOOL __fastcall Interpreter::primitiveStringNextIndexOfFromTo()
{
	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);

	StringOTE* receiverPointer = reinterpret_cast<StringOTE*>(stackValue(3));

	Oop answer = ZeroPointer;
	if ((ObjectMemory::fetchClassOf(valuePointer) == Pointers.ClassCharacter) && to >= from)
	{
		ASSERT(!receiverPointer->isPointers());

		// Search a byte object

		const SMALLINTEGER length = receiverPointer->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
		CharOTE* oteChar = reinterpret_cast<CharOTE*>(valuePointer);
		Character* charObj = oteChar->m_location;
		const char charValue = static_cast<char>(ObjectMemoryIntegerValueOf(charObj->m_asciiValue));

		String* chars = receiverPointer->m_location;

		from--;
		while (from < to)
		{
			if (chars->m_characters[from++] == charValue)
			{
				answer = ObjectMemoryIntegerObjectOf(from);
				break;
			}
		}
	}

	stackValue(3) = answer;
	pop(3);
	return primitiveSuccess();
}
Esempio n. 4
0
Oop* __fastcall Interpreter::primitiveNewPinned(Oop* const sp, unsigned)
{
	BehaviorOTE* oteClass = reinterpret_cast<BehaviorOTE*>(*(sp - 1));
	Oop oopArg = (*sp);
	SMALLINTEGER size;
	if (isIntegerObject(oopArg) && (size = ObjectMemoryIntegerValueOf(oopArg)) >= 0)
	{
		InstanceSpecification instSpec = oteClass->m_location->m_instanceSpec;
		if (!(instSpec.m_pointers || instSpec.m_nonInstantiable))
		{
			BytesOTE* newObj = ObjectMemory::newByteObject<true, true>(oteClass, size);
			*(sp - 1) = reinterpret_cast<Oop>(newObj);
			ObjectMemory::AddToZct(reinterpret_cast<OTE*>(newObj));
			return sp - 1;
		}
		else
		{
			// Not indexable, or non-instantiable
			return primitiveFailure(instSpec.m_nonInstantiable ? 1 : 2);
		}
	}
	else
	{
		return primitiveFailure(0);	// Size must be positive SmallInteger
	}
}
Esempio n. 5
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::primitiveStringSearch()
{
	Oop* const sp = m_registers.m_stackPointer;
	Oop integerPointer = *sp;
	if (!ObjectMemoryIsIntegerObject(integerPointer))
		return primitiveFailure(0);				// startingAt not an integer
	const SMALLINTEGER startingAt = ObjectMemoryIntegerValueOf(integerPointer);

	Oop oopSubString = *(sp-1);
	BytesOTE* oteReceiver = reinterpret_cast<BytesOTE*>(*(sp-2));

	if (ObjectMemory::fetchClassOf(oopSubString) != oteReceiver->m_oteClass)
		return primitiveFailure(2);

	// We know it can't be a SmallInteger because it has the same class as the receiver
	BytesOTE* oteSubString = reinterpret_cast<BytesOTE*>(oopSubString);

	VariantByteObject* bytesPattern = oteSubString->m_location;
	VariantByteObject* bytesReceiver = oteReceiver->m_location;
	const int M = oteSubString->bytesSize();
	const int N = oteReceiver->bytesSize();

	// Check 'startingAt' is in range
	if (startingAt < 1 || startingAt > N)
		return primitiveFailure(1);	// out of bounds

	int nOffset = M == 0 || ((startingAt + M) - 1 > N)
					? -1 
					: stringSearch(bytesReceiver->m_fields, N, bytesPattern->m_fields, M, startingAt - 1);
	
	*(sp-2) = ObjectMemoryIntegerObjectOf(nOffset+1);
	return sp-2;
}
Esempio n. 6
0
Oop* __fastcall Interpreter::primitiveNewFromStack(Oop* const stackPointer, unsigned)
{
	BehaviorOTE* oteClass = reinterpret_cast<BehaviorOTE*>(*(stackPointer - 1));

	Oop oopArg = (*stackPointer);
	SMALLINTEGER count;
	if (isIntegerObject(oopArg) && (count = ObjectMemoryIntegerValueOf(oopArg)) >= 0)
	{
		// Note that instantiateClassWithPointers counts up the class,
		PointersOTE* oteObj = ObjectMemory::newUninitializedPointerObject(oteClass, count);
		VariantObject* obj = oteObj->m_location;

		Oop* sp = stackPointer;
		sp = sp - count - 1;
		while (--count >= 0)
		{
			oopArg = *(sp + count);
			ObjectMemory::countUp(oopArg);
			obj->m_fields[count] = oopArg;
		}

		// Save down SP in case ZCT is reconciled on adding result
		m_registers.m_stackPointer = sp;
		*sp = reinterpret_cast<Oop>(oteObj);
		ObjectMemory::AddToZct((OTE*)oteObj);
		return sp;
	}
	else
	{
		return primitiveFailure(0);
	}
}
Esempio n. 7
0
// This primitive handles PositionableStream>>nextSDWORD, but only for byte-arrays
// Unary message, so does not modify stack pointer
BOOL __fastcall Interpreter::primitiveNextSDWORD()
{
	PosStreamOTE* streamPointer = reinterpret_cast<PosStreamOTE*>(stackTop());		// Access receiver
	PositionableStream* readStream = streamPointer->m_location;

	// Ensure valid stream - unusually this validity check is included in the Blue Book spec
	// and appears to be implemented in most Smalltalks, so we implement here too.
	if (!ObjectMemoryIsIntegerObject(readStream->m_index) ||
		!ObjectMemoryIsIntegerObject(readStream->m_readLimit))
		return primitiveFailure(0);	// Receiver fails invariant check

	SMALLINTEGER index = ObjectMemoryIntegerValueOf(readStream->m_index);
	SMALLINTEGER limit = ObjectMemoryIntegerValueOf(readStream->m_readLimit);

	// Is the current index within the limits of the collection?
	// Remember that the index is 1 based (it's a Smalltalk index), and we're 0 based,
	// so we don't need to increment it until after we've got the next object
	if (index < 0 || index >= limit)
		return primitiveFailure(2);		// No, fail it

	OTE* oteBuf = readStream->m_array;
	BehaviorOTE* bufClass = oteBuf->m_oteClass;
	
	if (bufClass != Pointers.ClassByteArray)
		return primitiveFailure(1);		// Collection cannot be handled by primitive, rely on Smalltalk code

	ByteArrayOTE* oteBytes = reinterpret_cast<ByteArrayOTE*>(oteBuf);

	const int newIndex = index + sizeof(SDWORD);
	if (MWORD(newIndex) > oteBytes->bytesSize())
		return primitiveFailure(3);

	const Oop oopNewIndex = ObjectMemoryIntegerObjectOf(newIndex);
	if (int(oopNewIndex) < 0)
		return primitiveFailure(4);	// index overflowed SmallInteger range

	// When incrementing the index we must allow for it overflowing a SmallInteger, even though
	// this is extremely unlikely in practice
	readStream->m_index = oopNewIndex;

	// Receiver is overwritten
	ByteArray* byteArray = oteBytes->m_location;
	replaceStackTopWithNew(Integer::NewSigned32(*reinterpret_cast<SDWORD*>(byteArray->m_elements+index)));

	return primitiveSuccess();									// Succeed
}
Esempio n. 8
0
// Answer a new process with an initial stack size specified by the first argument, and a maximum
// stack size specified by the second argument.
Oop* __fastcall Interpreter::primitiveNewVirtual(Oop* const sp, unsigned)
{
	Oop maxArg = *sp;
	SMALLINTEGER maxSize;
	if (ObjectMemoryIsIntegerObject(maxArg) && (maxSize = ObjectMemoryIntegerValueOf(maxArg)) >= 0)
	{
		Oop initArg = *(sp - 1);
		SMALLINTEGER initialSize;
		if (ObjectMemoryIsIntegerObject(initArg) && (initialSize = ObjectMemoryIntegerValueOf(initArg)) >= 0)
		{
			BehaviorOTE* receiverClass = reinterpret_cast<BehaviorOTE*>(*(sp - 2));
			InstanceSpecification instSpec = receiverClass->m_location->m_instanceSpec;
			if (instSpec.m_indexable && !instSpec.m_nonInstantiable)
			{
				unsigned fixedFields = instSpec.m_fixedFields;
				VirtualOTE* newObject = ObjectMemory::newVirtualObject(receiverClass, initialSize + fixedFields, maxSize);
				if (newObject)
				{
					*(sp - 2) = reinterpret_cast<Oop>(newObject);
					// No point saving down SP before potential Zct reconcile as the init & max args must be SmallIntegers
					ObjectMemory::AddToZct((OTE*)newObject);
					return sp - 2;
				}
				else
					return primitiveFailure(4);	// OOM
			}
			else
			{
				return primitiveFailure(instSpec.m_nonInstantiable ? 3 : 2);	// Non-indexable or abstract class
			}
		}
		else
		{
			return primitiveFailure(1);	// initialSize arg not a SmallInteger
		}
	}
	else
	{
		return primitiveFailure(0);	// maxsize arg not a SmallInteger
	}
}
Esempio n. 9
0
Oop* __fastcall Interpreter::primitiveNewWithArg(Oop* const sp, unsigned)
{
	BehaviorOTE* oteClass = reinterpret_cast<BehaviorOTE*>(*(sp - 1));
	Oop oopArg = (*sp);
	// Unfortunately the compiler can't be persuaded to perform this using just the sar and conditional jumps on no-carry and signed;
	// it generates both the bit test and the shift.
	SMALLINTEGER size;
	if (isIntegerObject(oopArg) && (size = ObjectMemoryIntegerValueOf(oopArg)) >= 0)
	{
		InstanceSpecification instSpec = oteClass->m_location->m_instanceSpec;
		if ((instSpec.m_value & (InstanceSpecification::IndexableMask | InstanceSpecification::NonInstantiableMask)) == InstanceSpecification::IndexableMask)
		{
			if (instSpec.m_pointers)
			{
				PointersOTE* newObj = ObjectMemory::newPointerObject(oteClass, size + instSpec.m_fixedFields);
				*(sp - 1) = reinterpret_cast<Oop>(newObj);
				ObjectMemory::AddToZct(reinterpret_cast<OTE*>(newObj));
				return sp - 1;
			}
			else
			{
				BytesOTE* newObj = ObjectMemory::newByteObject<true, true>(oteClass, size);
				*(sp - 1) = reinterpret_cast<Oop>(newObj);
				ObjectMemory::AddToZct(reinterpret_cast<OTE*>(newObj));
				return sp - 1;
			}
		}
		else
		{
			// Not indexable, or non-instantiable
			return primitiveFailure(instSpec.m_nonInstantiable ? 1 : 2);
		}
	}
	else
	{
		return primitiveFailure(0);	// Size must be positive SmallInteger
	}
}
Esempio n. 10
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;
	}
Esempio n. 11
0
// This primitive handles PositionableStream>>next, but only for Arrays, Strings and ByteArrays
// Unary message, so does not modify stack pointer, and is therefore called directly from the ASM 
// primitive table without indirection through an ASM thunk.
BOOL __fastcall Interpreter::primitiveNext()
{
	PosStreamOTE* streamPointer = reinterpret_cast<PosStreamOTE*>(stackTop());		// Access receiver
	
	// Only works for subclasses of PositionableStream (or look alikes)
	//ASSERT(!ObjectMemoryIsIntegerObject(streamPointer) && ObjectMemory::isKindOf(streamPointer, Pointers.ClassPositionableStream));
	
	PositionableStream* readStream = streamPointer->m_location;
	
	// Ensure valid stream - unusually this validity check is included in the Blue Book spec
	// and appears to be implemented in most Smalltalks, so we implement here too.
	if (!ObjectMemoryIsIntegerObject(readStream->m_index) ||
		!ObjectMemoryIsIntegerObject(readStream->m_readLimit))
		return primitiveFailure(0);	// Receiver fails invariant check

	SMALLINTEGER index = ObjectMemoryIntegerValueOf(readStream->m_index);
	SMALLINTEGER limit = ObjectMemoryIntegerValueOf(readStream->m_readLimit);

	// Is the current index within the limits of the collection?
	// Remember that the index is 1 based (it's a Smalltalk index), and we're 0 based,
	// so we don't need to increment it until after we've got the next object
	if (index < 0 || index >= limit)
		return primitiveFailure(2);		// No, fail it

	OTE* oteBuf = readStream->m_array;
	BehaviorOTE* bufClass = oteBuf->m_oteClass;
	
	if (bufClass == Pointers.ClassString)
	{
		StringOTE* oteString = reinterpret_cast<StringOTE*>(oteBuf);

		// A sanity check - ensure within bounds of object too (again in Blue Book spec)
		if (MWORD(index) >= oteString->bytesSize())
			return primitiveFailure(3);

		String* buf = oteString->m_location;
		stackTop() = reinterpret_cast<Oop>(Character::New(buf->m_characters[index]));
	}
	// We also support ByteArrays in our primitiveNext (unlike BB).
	else if (bufClass == Pointers.ClassByteArray)
	{
		ByteArrayOTE* oteBytes = reinterpret_cast<ByteArrayOTE*>(oteBuf);

		if (MWORD(index) >= oteBytes->bytesSize())
			return primitiveFailure(3);

		ByteArray* buf = oteBytes->m_location;
		stackTop() = ObjectMemoryIntegerObjectOf(buf->m_elements[index]);
	}
	else if (bufClass == Pointers.ClassArray)
	{
		ArrayOTE* oteArray = reinterpret_cast<ArrayOTE*>(oteBuf);
		if (MWORD(index) >= oteArray->pointersSize())
			return primitiveFailure(3);

		Array* buf = oteArray->m_location;
		stackTop() = buf->m_elements[index];
	}
	else
		return primitiveFailure(1);		// Collection cannot be handled by primitive, rely on Smalltalk code
	
	// When incrementing the index we must allow for it overflowing a SmallInteger, even though
	// this is extremely unlikely in practice
	readStream->m_index = Integer::NewSigned32WithRef(index+1);

	return primitiveSuccess();									// Succeed
}
Esempio n. 12
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
}
Esempio n. 13
0
// This primitive handles WriteStream>>NextPut:, but only for Arrays, Strings & ByteArrays
// Uses but does not modify stack pointer, instead returns the number of bytes to 
// pop from the Smalltalk stack.
BOOL __fastcall Interpreter::primitiveNextPut()
{
	Oop* sp = m_registers.m_stackPointer;
	WriteStreamOTE* streamPointer = reinterpret_cast<WriteStreamOTE*>(*(sp-1));		// Access receiver under argument
	
	//ASSERT(!ObjectMemoryIsIntegerObject(streamPointer) && ObjectMemory::isKindOf(streamPointer, Pointers.ClassPositionableStream));

	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);

	// Within the bounds of the limit
	if (index < 0 || index >= limit)
		return primitiveFailure(2);
	
	Oop value = *(sp);
	OTE* oteBuf = writeStream->m_array;
	BehaviorOTE* bufClass = oteBuf->m_oteClass;
	
	if (bufClass == Pointers.ClassString)
	{
		if (ObjectMemory::fetchClassOf(value) != Pointers.ClassCharacter)
			return primitiveFailure(4);	// Attempt to put non-character

		StringOTE* oteString = reinterpret_cast<StringOTE*>(oteBuf);
		
		if (index >= oteString->bytesSizeForUpdate())
			return primitiveFailure(3);	// Attempt to put non-character or off end of String

		String* buf = oteString->m_location;
		CharOTE* oteChar = reinterpret_cast<CharOTE*>(value);
		buf->m_characters[index] = static_cast<char>(oteChar->getIndex() - ObjectMemory::FirstCharacterIdx);
	}
	else if (bufClass == Pointers.ClassArray)
	{
		ArrayOTE* oteArray = reinterpret_cast<ArrayOTE*>(oteBuf);
		
		// In bounds of Array?
		if (index >= oteArray->pointersSizeForUpdate())
			return primitiveFailure(3);

		Array* buf = oteArray->m_location;
		// We must ref. count value here as we're storing into a heap object slot
		ObjectMemory::storePointerWithValue(buf->m_elements[index], value);
	}
	else if (bufClass == Pointers.ClassByteArray)
	{
		if (!ObjectMemoryIsIntegerObject(value))
			return primitiveFailure(4);	// Attempt to put non-SmallInteger
		SMALLINTEGER intValue = ObjectMemoryIntegerValueOf(value);
		if (intValue < 0 || intValue > 255)
			return primitiveFailure(4);	// Can only store 0..255

		ByteArrayOTE* oteByteArray = reinterpret_cast<ByteArrayOTE*>(oteBuf);
		
		if (index >= oteByteArray->bytesSizeForUpdate())
			return primitiveFailure(3);	// Attempt to put non-character or off end of String

		oteByteArray->m_location->m_elements[index] = static_cast<BYTE>(intValue);
	}
	else
		return primitiveFailure(1);
	
	writeStream->m_index = Integer::NewSigned32WithRef(index + 1);		// 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
}
Esempio n. 14
0
VOID CALLBACK Interpreter::SamplerProc(PVOID , BOOLEAN TimerOrWaitFired)
{
	if (!TimerOrWaitFired)
		return;
	if (_InterlockedDecrement(&m_nInputPollCounter) == 0)
	{
		NotifyAsyncPending();
#if 0//def _DEBUG
		DWORD dwTicksNow = timeGetTime();
		Semaphore* sem = Pointers.InputSemaphore->m_location;
		TRACE("Fired after %d, last reset at %d, signals %d\n\r", dwTicksNow-dwTicksReset, dwTicksReset, ObjectMemoryIntegerValueOf(sem->m_excessSignals));
#endif
	}
}
Esempio n. 15
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);
}
Esempio n. 16
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;
}
Esempio n. 17
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;
}
Esempio n. 18
0
BOOL __fastcall Interpreter::primitiveSnapshot(CompiledMethod&, unsigned argCount)
{
	Oop arg = stackValue(argCount - 1);
	char* szFileName;
	if (arg == Oop(Pointers.Nil))
		szFileName = 0;
	else if (ObjectMemory::fetchClassOf(arg) == Pointers.ClassString)
	{
		StringOTE* oteString = reinterpret_cast<StringOTE*>(arg);
		String* fileName = oteString->m_location;
		szFileName = fileName->m_characters;
	}
	else
		return primitiveFailure(0);

	bool bBackup;
	if (argCount >= 2)
		bBackup = reinterpret_cast<OTE*>(stackValue(argCount - 2)) == Pointers.True;
	else
		bBackup = false;

	SMALLINTEGER nCompressionLevel;
	if (argCount >= 3)
	{
		Oop oopCompressionLevel = stackValue(argCount - 3);
		nCompressionLevel = ObjectMemoryIsIntegerObject(oopCompressionLevel) ? ObjectMemoryIntegerValueOf(oopCompressionLevel) : 0;
	}
	else
		nCompressionLevel = 0;

	SMALLUNSIGNED nMaxObjects = 0;
	if (argCount >= 4)
	{
		Oop oopMaxObjects = stackValue(argCount - 4);
		if (ObjectMemoryIsIntegerObject(oopMaxObjects))
		{
			nMaxObjects = ObjectMemoryIntegerValueOf(oopMaxObjects);
		}
	}

	// N.B. It is not necessary to clear down the memory pools as the free list is rebuild on every image
	// load and the pool members, though not on the free list at present, are marked as free entries
	// in the object table

	// ZCT is reconciled, so objects may be deleted
	flushAtCaches();

	// Store the active frame of the active process before saving so available on image reload
	// We're not actually suspending the process now, but it appears like that to the snapshotted
	// image on restarting
	m_registers.PrepareToSuspendProcess();

#ifdef OAD
	DWORD timeStart = timeGetTime();
#endif

	int saveResult = ObjectMemory::SaveImageFile(szFileName, bBackup, nCompressionLevel, nMaxObjects);

#ifdef OAD
	DWORD timeEnd = timeGetTime();
	TRACESTREAM << "Time to save image: " << (timeEnd - timeStart) << " mS" << endl;
#endif

	if (!saveResult)
	{
		// Success
		popStack();
		return primitiveSuccess();
	}
	else
	{
		// Failure
		return primitiveFailure(saveResult);
	}
}
Esempio n. 19
0
// Signal a specified semaphore after the specified milliseconds duration (the argument). 
// NOTE: NOT ABSOLUTE VALUE!
// If the specified time has already passed, then the TimingSemaphore is signalled immediately. 
Oop* __fastcall Interpreter::primitiveSignalAtTick(CompiledMethod&, unsigned argumentCount)
{
	Oop tickPointer = stackTop();
	SMALLINTEGER nDelay;

	if (ObjectMemoryIsIntegerObject(tickPointer))
		nDelay = ObjectMemoryIntegerValueOf(tickPointer);
	else
	{
		OTE* oteArg = reinterpret_cast<OTE*>(tickPointer);
		return primitiveFailureWith(PrimitiveFailureNonInteger, oteArg);	// ticks must be SmallInteger
	}

	// To avoid any race conditions against the global timerID value (it is quite
	// common for the timer to fire, for example, before the timeSetEvent() call
	// has actually returned in the duration is very short because the timer thread
	// is operating at a very high priority), we use an interlocked operation

	UINT outstandingID = InterlockedExchange(reinterpret_cast<SHAREDLONG*>(&timerID), 0);
	// If outstanding timer now fires, it will do nothing. We'll end up killing something which is already
	// dead of course, but that should be OK
	if (outstandingID)
	{
#ifdef OAD
		TRACESTREAM << "Killing existing timer with id " << outstandingID << endl;
#endif
		UINT kill = ::timeKillEvent(outstandingID);
		if (kill != TIMERR_NOERROR)
			trace("Failed to kill timer %u (%d,%d)!\n\r", outstandingID, kill, GetLastError());
	}

	if (nDelay > 0)
	{
		// Temporarily handle old image code that passes timer semaphore as an argument
		if (argumentCount > 1 && (POTE)Pointers.TimingSemaphore == Pointers.Nil)
		{
			ObjectMemory::ProtectConstSpace(PAGE_READWRITE);
			_Pointers.TimingSemaphore = (SemaphoreOTE*)stackValue(1);
			ObjectMemory::ProtectConstSpace(PAGE_READONLY);
		}

		// Clamp the requested delay to the maximum if it is too large. This simplifies the Delay code in the image a little.
		if (nDelay > SMALLINTEGER(wTimerMax))
		{
			nDelay = wTimerMax;
		}

		// Set the timerID to a non-zero value just in case the timer fires before timeSetEvent() returns.
		// This allows the TimerProc to recognise the timer as valid (it doesn't really care about the 
		// timerID anyway, just that we're interested in it).
		// N.B. We shouldn't need an interlocked operation here because, assuming no bugs in the Win32 MM
		// timers, we've killed any outstanding timer, and the timer thread should be dormant
		timerID = UINT(-1);		// -1 is not used as a timer ID.

		UINT newTimerID = ::timeSetEvent(nDelay, 0, TimeProc, 0, TIME_ONESHOT);
		if (newTimerID && newTimerID != UINT(-1))
		{
			// Unless timer has already fired, record the timer id so can cancel if necessary
			_InterlockedCompareExchange(reinterpret_cast<SHAREDLONG*>(&timerID), newTimerID, -1);
			pop(argumentCount);		// No ref. counting required
		}
		else
		{
			// System refused to set timer for some reason
			DWORD error = GetLastError();
			trace("Oh no, failed to set a timer for %d mS (%d)!\n\r", nDelay, error);
			return primitiveFailureWithInt(PrimitiveFailureSystemError, error);
		}
	}
	else if (nDelay == 0)
	{
#ifdef _DEBUG
		TRACESTREAM << "Requested delay " << dec << nDelay << " passed, signalling immediately" << endl;
#endif
		// The request time has already passed, or does not fall within the
		// available timer resolution (i.e. it will happen too soon), so signal
		// it immediately
		// We must adjust stack before signalling, as may change Process (and therefore stack!)
		pop(argumentCount);

		// N.B. Signalling may detect a process switch, but does not actually perform it
		signalSemaphore(Pointers.TimingSemaphore);
	}
	// else requested delay was negative - we allow this to clear down the existing timer

#ifdef _DEBUG
	if (newProcessWaiting())
	{
		ASSERT(m_oteNewProcess->m_oteClass == Pointers.ClassProcess);
		ProcessOTE* activeProcess = scheduler()->m_activeProcess;

		TRACESTREAM << "signalAtTick: Caused process switch to " << m_oteNewProcess
			<< endl << "\t\tfrom " << activeProcess << endl
			<< "\tasync signals " << m_qAsyncSignals.isEmpty() << ')' << endl;
	}
#endif

	// Delay could already have fired
	CheckProcessSwitch();

	return primitiveSuccess(0);
}