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