BytesOTE* __fastcall ObjectMemory::shallowCopy(BytesOTE* ote) { ASSERT(ote->isBytes()); // Copying byte objects is simple and fast VariantByteObject& bytes = *ote->m_location; BehaviorOTE* classPointer = ote->m_oteClass; MWORD objectSize = ote->sizeOf(); OTE* copyPointer; // Allocate an uninitialized object ... VariantByteObject* pLocation = static_cast<VariantByteObject*>(allocObject(objectSize, copyPointer)); ASSERT((objectSize > MaxSizeOfPoolObject && copyPointer->heapSpace() == OTEFlags::NormalSpace) || copyPointer->heapSpace() == OTEFlags::PoolSpace); ASSERT(copyPointer->getSize() == objectSize); // This set does not want to copy over the immutability bit - i.e. even if the original was immutable, the // copy will never be. copyPointer->setSize(ote->getSize()); copyPointer->m_dwFlags = (copyPointer->m_dwFlags & ~OTEFlags::WeakMask) | (ote->m_dwFlags & OTEFlags::WeakMask); ASSERT(copyPointer->isBytes()); copyPointer->m_oteClass = classPointer; classPointer->countUp(); // Copy the entire object over the other one, including any null terminator and object header memcpy(pLocation, &bytes, objectSize); return reinterpret_cast<BytesOTE*>(copyPointer); }
// There are some fixups that we can only apply after all the objects are loaded, because // they involve reference from one object to other objects which may not be available // during the normal load process. These fixes are applied here void ObjectMemory::PostLoadFix() { // Special case handling for Contexts because we store // the sp's as integers in the image file, but at // run-time they are expected to be direct pointers const OTE* pEnd = m_pOT + m_nOTSize; // Loop invariant for (OTE* ote = m_pOT; ote < pEnd; ote++) { if (!ote->isFree()) { if (ote->isBytes()) { #ifdef _DEBUG { // Its a byte object, and may be null terminated const Behavior* behavior = ote->m_oteClass->m_location; const BytesOTE* oteBytes = reinterpret_cast<const BytesOTE*>(ote); const VariantByteObject* object = oteBytes->m_location; ASSERT(behavior->m_instanceSpec.m_nullTerminated == ote->isNullTerminated()); } #endif } else if (ote->m_oteClass == _Pointers.ClassProcess) { ASSERT(ote->heapSpace() == OTEFlags::VirtualSpace); ProcessOTE* oteProcess = reinterpret_cast<ProcessOTE*>(ote); Process* process = oteProcess->m_location; process->PostLoadFix(oteProcess); } } } ProtectConstSpace(PAGE_READONLY); #if defined(_DEBUG) && 0 { // Dump out the pointers TRACESTREAM << NumPointers<< L" VM Pointers..." << std::endl; for (int i = 0; i < NumPointers; i++) { VariantObject* obj = static_cast<VariantObject*>(m_pConstObjs); POTE pote = POTE(obj->m_fields[i]); TRACESTREAM << i<< L": " << pote << std::endl; } } #endif }
/* 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 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; }
// 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; }
template <bool MaybeZ, bool Initialized> BytesOTE* ObjectMemory::newByteObject(BehaviorOTE* classPointer, MWORD elementCount) { Behavior& byteClass = *classPointer->m_location; OTE* ote; if (!MaybeZ || !byteClass.m_instanceSpec.m_nullTerminated) { ASSERT(!classPointer->m_location->m_instanceSpec.m_nullTerminated); VariantByteObject* newBytes = static_cast<VariantByteObject*>(allocObject(elementCount + SizeOfPointers(0), ote)); ASSERT((elementCount > MaxSizeOfPoolObject && ote->heapSpace() == OTEFlags::NormalSpace) || ote->heapSpace() == OTEFlags::PoolSpace); ASSERT(ote->getSize() == elementCount + SizeOfPointers(0)); if (Initialized) { // Byte objects are initialized to zeros (but not the header) // Note that we round up to initialize to the next DWORD // This can be useful when working on a 32-bit word machine ZeroMemory(newBytes->m_fields, _ROUND2(elementCount, sizeof(DWORD))); classPointer->countUp(); } ote->m_oteClass = classPointer; ote->beBytes(); } else { ASSERT(classPointer->m_location->m_instanceSpec.m_nullTerminated); MWORD objectSize; switch (reinterpret_cast<const StringClass&>(byteClass).Encoding) { case StringEncoding::Ansi: case StringEncoding::Utf8: objectSize = elementCount * sizeof(AnsiString::CU); break; case StringEncoding::Utf16: objectSize = elementCount * sizeof(Utf16String::CU); break; case StringEncoding::Utf32: objectSize = elementCount * sizeof(Utf32String::CU); break; default: __assume(false); break; } // TODO: Allocate the correct number of null term bytes based on the encoding objectSize += NULLTERMSIZE; VariantByteObject* newBytes = static_cast<VariantByteObject*>(allocObject(objectSize + SizeOfPointers(0), ote)); ASSERT((objectSize > MaxSizeOfPoolObject && ote->heapSpace() == OTEFlags::NormalSpace) || ote->heapSpace() == OTEFlags::PoolSpace); ASSERT(ote->getSize() == objectSize + SizeOfPointers(0)); if (Initialized) { // Byte objects are initialized to zeros (but not the header) // Note that we round up to initialize to the next DWORD // This can be useful when working on a 32-bit word machine ZeroMemory(newBytes->m_fields, _ROUND2(objectSize, sizeof(DWORD))); classPointer->countUp(); } else { // We still want to ensure the null terminator is set, even if not initializing the rest of the object *reinterpret_cast<NULLTERMTYPE*>(&newBytes->m_fields[objectSize - NULLTERMSIZE]) = 0; } ote->m_oteClass = classPointer; ote->beNullTerminated(); HARDASSERT(ote->isBytes()); } return reinterpret_cast<BytesOTE*>(ote); }