// 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>>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 }
// 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 }