コード例 #1
0
ファイル: PerformPrim.cpp プロジェクト: brunobuzzi/DolphinVM
Oop* __fastcall Interpreter::primitivePerformMethod(CompiledMethod& , unsigned)
{
	Oop * sp = m_registers.m_stackPointer;
	ArrayOTE* oteArg = reinterpret_cast<ArrayOTE*>(*(sp));
	if (ObjectMemory::fetchClassOf(Oop(oteArg)) != Pointers.ClassArray)
		return primitiveFailure(0);		// Arguments not an Array
	Array* arguments = oteArg->m_location;
	Oop receiverPointer = *(sp-1);
	MethodOTE* oteMethod = reinterpret_cast<MethodOTE*>(*(sp-2));
	// Adjust sp to point at slot where receiver will be moved
	sp -= 2;

	//ASSERT(ObjectMemory::isKindOf(oteMethod, Pointers.ClassCompiledMethod));
	CompiledMethod* method = oteMethod->m_location;
	if (!ObjectMemory::isKindOf(receiverPointer, method->m_methodClass))
		return primitiveFailure(1);		// Wrong class of receiver

	const unsigned argCount = oteArg->pointersSize();
	const unsigned methodArgCount = method->m_header.argumentCount;
	if (methodArgCount != argCount)
		return primitiveFailure(2);		// Wrong number of arguments

	// Push receiver and arguments on stack (over the top of array and receiver)
	sp[0] = receiverPointer;					// Write receiver over the top of the method
	for (MWORD i = 0; i < argCount; i++)
	{
		Oop pushee = arguments->m_elements[i];
		// Don't count up because we are adding a stack ref.
		sp[i+1] = pushee;
	}
	m_registers.m_stackPointer = sp+argCount;

	// Don't count down any args
	executeNewMethod(oteMethod, argCount);
	return primitiveSuccess(0);
}
コード例 #2
0
ファイル: PerformPrim.cpp プロジェクト: brunobuzzi/DolphinVM
// Value with args takes an array of arguments
Oop* __fastcall Interpreter::primitiveValueWithArgs()
{
	Oop* bp = m_registers.m_stackPointer;

	ArrayOTE* argumentArray = reinterpret_cast<ArrayOTE*>(*(bp));
	BlockOTE* oteBlock = reinterpret_cast<BlockOTE*>(*(bp-1));
	ASSERT(ObjectMemory::fetchClassOf(Oop(oteBlock)) == Pointers.ClassBlockClosure);
	BlockClosure* block = oteBlock->m_location;
	const MWORD blockArgumentCount = block->m_info.argumentCount;

	BehaviorOTE* arrayClass = ObjectMemory::fetchClassOf(Oop(argumentArray));
	if (arrayClass != Pointers.ClassArray)
		return primitiveFailure(1);

	const MWORD arrayArgumentCount = argumentArray->pointersSize();
	if (arrayArgumentCount != blockArgumentCount)
		return primitiveFailure(0);

	pop(2);								// N.B. ref count of Block will be assumed by storing into frame
	// Store old context details from interpreter registers
	m_registers.StoreContextRegisters();

	// Overwrite receiver block with receiver at time of closure.
	Oop closureReceiver = block->m_receiver;
	*(bp-1) = closureReceiver;
	// No need to count up the receiver since we've written it into a stack slot

	Array* args = argumentArray->m_location;

	// Code this carefully so compiler generates optimal code (it makes a poor job on its own)
	Oop* sp = bp;

	// Push the args from the array
	{
		for (unsigned i=0;i<arrayArgumentCount;i++)
		{
			Oop pushee = args->m_elements[i];
			*sp++ = pushee;
			// No need to count up since pushing on the stack
		}
	}

	const unsigned copiedValues = block->copiedValuesCount(oteBlock);
	{
		for (unsigned i=0;i<copiedValues;i++)
		{
			Oop oopCopied = block->m_copiedValues[i];
			*sp++ = oopCopied;
			// No need to count up since pushing on the stack
		}
	}

	// Nil out any extra stack temp slots we need
	const unsigned extraTemps = block->stackTempsCount();
	{
		const Oop nilPointer = Oop(Pointers.Nil);
		for (unsigned i=0;i<extraTemps;i++)
			*sp++ = nilPointer;
	}

	// Stack frame follows args...
	StackFrame* pFrame = reinterpret_cast<StackFrame*>(sp);

	pFrame->m_bp = reinterpret_cast<Oop>(bp)+1;
	m_registers.m_basePointer = reinterpret_cast<Oop*>(bp);

	// stack ref. removed so don't need to count down

	pFrame->m_caller = m_registers.activeFrameOop();
	// Having set caller can update the active frame Oop
	m_registers.m_pActiveFrame = pFrame;

	// Note that ref. count remains the same due dto overwritten receiver slot
	const unsigned envTemps = block->envTempsCount();
	if (envTemps > 0)
	{
		ContextOTE* oteContext = Context::New(envTemps, reinterpret_cast<Oop>(block->m_outer));
		pFrame->m_environment = reinterpret_cast<Oop>(oteContext);
		Context* context = oteContext->m_location;
		context->m_block = oteBlock;
		// Block has been written into a heap object slot, so must count up
		oteBlock->countUp();
	}
	else
		pFrame->m_environment = reinterpret_cast<Oop>(oteBlock);

	// We don't need to store down the IP and SP into the frame until it is suspended
	pFrame->m_ip = ZeroPointer;
	pFrame->m_sp = ZeroPointer;
	MethodOTE* oteMethod = block->m_method;
	pFrame->m_method = oteMethod;
	// Don't need to inc ref count for stack frame ref to method
	CompiledMethod* method = oteMethod->m_location;
	m_registers.m_pMethod = method;

	m_registers.m_instructionPointer = ObjectMemory::ByteAddressOfObjectContents(method->m_byteCodes) +
											block->initialIP() - 1;

	// New stack pointer points at last field of stack frame
	m_registers.m_stackPointer = reinterpret_cast<Oop*>(reinterpret_cast<BYTE*>(pFrame)+sizeof(StackFrame)) - 1;
	ASSERT(m_registers.m_stackPointer == &pFrame->m_bp);

	return primitiveSuccess(0);
}
コード例 #3
0
ファイル: PerformPrim.cpp プロジェクト: brunobuzzi/DolphinVM
Oop* __fastcall Interpreter::primitivePerformWithArgs()
{
	Oop* const sp = m_registers.m_stackPointer;
	ArrayOTE* argumentArray = reinterpret_cast<ArrayOTE*>(*(sp));
	BehaviorOTE* arrayClass = ObjectMemory::fetchClassOf(Oop(argumentArray));
	if (arrayClass != Pointers.ClassArray)
		return primitiveFailure(0);
	
	// N.B. We're using a large stack, so don't bother checking for overflow
	//		(standard stack overflow mechanism should catch it)
									   
	// We must not get the length outside, in case small integer arg
	const unsigned argCount = argumentArray->pointersSize();
	
	// Save old message selector in case of prim failure (need to reinstate)
	SymbolOTE* performSelector = m_oopMessageSelector;

	// To ensure the argumentArray doesn't go away when we push its contents
	// onto the stack, in case we need it for recovery from an argument
	// count mismatch we leave its ref. count elevated

	SymbolOTE* selectorToPerform = reinterpret_cast<SymbolOTE*>(*(sp-1));
	if (ObjectMemoryIsIntegerObject(selectorToPerform))
		return primitiveFailure(1);

	m_oopMessageSelector = selectorToPerform;	// Get selector from stack
	// Don't need to count down the stack ref.
	ASSERT(!selectorToPerform->isFree());

	Oop newReceiver = *(sp-2);			// receiver is under selector and arg array

	// Push the args from the array onto the stack. We must do this before
	// looking up the method, because if the receiver does not understand
	// the method then the lookup routines copy the arguments off the stack
	// into a Message object
	Array* args = argumentArray->m_location;
	for (MWORD i=0; i<argCount; i++)
	{
		Oop pushee = args->m_elements[i];
		// Note no need to inc the ref. count when pushing on the stack
		sp[i-1] = pushee;
	}
	// Args written over top of selector and argument array (hence -2)
	m_registers.m_stackPointer = sp+argCount-2;

	// There is a subtle complication here when the receiver does not
	// understand the message, by which lookupMethodInClass() converts
	// the message we're trying to perform to a #doesNotUnderstand: with
	// all arguments moved to a Message. We still want to execute this
	// does not understand, so we also execute the method if the argument
	// counts do not match, but it was not understood. Note that it is
	// possible for a doesNotUnderstand: to be executed thru the first
	// test if the argumentArray contained only one argument. We allow
	// this to happen to avoid testing for not understood in the normal
	// case - just be aware of this anomaly.
	MethodOTE* methodPointer = findNewMethodInClass(ObjectMemory::fetchClassOf(newReceiver), argCount);
	CompiledMethod& method = *methodPointer->m_location;
	const unsigned methodArgCount = method.m_header.argumentCount;
	if (methodArgCount == argCount ||
			m_oopMessageSelector == Pointers.DoesNotUnderstandSelector)
	{
		// WE no longer need the argument array, but don't count it down since we only have a stack ref.
		executeNewMethod(methodPointer, methodArgCount);
		return primitiveSuccess(0);
	}
	else
	{
		// Receiver must have understood the message, but we had wrong 
		// number of arguments, so reinstate the stack and fail the primitive
		pop(argCount);
		pushObject((OTE*)m_oopMessageSelector);
		// Argument array already has artificially increased ref. count
		push(Oop(argumentArray));
		m_oopMessageSelector = performSelector;
		return primitiveFailure(1);
	}
}
コード例 #4
0
ファイル: StreamPrim.cpp プロジェクト: jgfoster/DolphinVM
// 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
}
コード例 #5
0
ファイル: StreamPrim.cpp プロジェクト: jgfoster/DolphinVM
// 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
}
コード例 #6
0
ファイル: StreamPrim.cpp プロジェクト: jgfoster/DolphinVM
// 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
}