// 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; }
// 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(); }
// 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 }
// 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); }
// 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 }
void Process::PostLoadFix(ProcessOTE* oteThis) { // Any overlapped call running when the image was saved is no longer valid, so // we must clear down the "pointer" and remove the back reference if (m_thread != reinterpret_cast<Oop>(Pointers.Nil)) { m_thread = reinterpret_cast<Oop>(Pointers.Nil); oteThis->countDown(); } // Patch any badly created or corrupted processes if (!ObjectMemoryIsIntegerObject(m_fpControl)) { m_fpControl = ObjectMemoryIntegerObjectOf(_DN_SAVE | _RC_NEAR | _PC_64 | _EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW | _EM_DENORMAL); } Oop* pFramePointer = &m_suspendedFrame; Oop framePointer = *pFramePointer; const void* stackBase = m_stack; const void* stackEnd = reinterpret_cast<BYTE*>(this) + oteThis->getSize() - 1; // Wind down the stack adjusting references to self as we go // Start with the suspended context const int delta = m_callbackDepth - 1; while (isIntegerObject(framePointer) && framePointer != ZeroPointer) { framePointer += delta; if (framePointer < Oop(stackBase) || framePointer > Oop(stackEnd)) { trace(L"Warning: Process at %#x has corrupt frame pointer at %#x which will be nilled\n", this, pFramePointer); *pFramePointer = Oop(Pointers.Nil); break; } else *pFramePointer += delta; StackFrame* pFrame = StackFrame::FromFrameOop(*pFramePointer); if (isIntegerObject(pFrame->m_bp)) { pFrame->m_bp += delta; } ASSERT(reinterpret_cast<void*>(pFrame->m_bp) > stackBase && reinterpret_cast<void*>(pFrame->m_bp) < stackEnd); // If a stack only frame, then adjust the BP if (!isIntegerObject(pFrame->m_environment)) { // The frame has an object context, we need to adjust // its back pointer to the frame if it is a MethodContext // The context objects contain no other addresses any more OTE* oteContext = reinterpret_cast<OTE*>(pFrame->m_environment); if (ObjectMemory::isAContext(oteContext)) { Context* ctx = static_cast<Context*>(oteContext->m_location); if (isIntegerObject(ctx->m_frame) && ctx->m_frame != ZeroPointer) { ctx->m_frame += delta; ASSERT(reinterpret_cast<void*>(ctx->m_frame) > stackBase && reinterpret_cast<void*>(ctx->m_frame) < stackEnd); } } } // Adjust the contexts SP if (isIntegerObject(pFrame->m_sp)) { pFrame->m_sp += delta; ASSERT(reinterpret_cast<void*>(pFrame->m_sp) >= stackBase && reinterpret_cast<void*>(pFrame->m_sp) <= stackEnd); } pFramePointer = &pFrame->m_caller; framePointer = *pFramePointer; } framePointer = SuspendedFrame(); if (isIntegerObject(framePointer) && framePointer != ZeroPointer) { // The size of the process should exactly correspond with that required to // hold up to the SP of the suspended frame StackFrame* pFrame = StackFrame::FromFrameOop(framePointer); int size = (pFrame->m_sp - 1) - reinterpret_cast<DWORD>(this) + sizeof(Oop); if (size > 0 && unsigned(size) < oteThis->getSize()) { TRACE(L"WARNING: Resizing process %p from %u to %u\n", oteThis, oteThis->getSize(), size); oteThis->setSize(size); } } // else its dead or not started yet // Later we'll use this slot to count the callback depth m_callbackDepth = ZeroPointer; }