VariantObject* ObjectMemory::resize(PointersOTE* ote, MWORD newPointers, bool bRefCount) { ASSERT(!ObjectMemoryIsIntegerObject(ote) && ote->isPointers()); VariantObject* pObj = ote->m_location; const MWORD oldPointers = ote->pointersSize(); // If resizing the active process, then we don't do any ref. counting as refs from // the current stack are not counted (we used deferred ref. counting) if (bRefCount) { for (MWORD i=newPointers;i<oldPointers;i++) countDown(pObj->m_fields[i]); } // Reallocate the object to the new size (bigger or smaller) pObj = reinterpret_cast<VariantObject*>(basicResize(reinterpret_cast<POTE>(ote), SizeOfPointers(newPointers), 0)); if (pObj) { ASSERT(newPointers == ote->pointersSize()); newPointers = ote->pointersSize(); // Initialize any new pointers to Nil (indices must fit in a SmallInteger) const Oop nil = Oop(Pointers.Nil); for (MWORD i=oldPointers; i<newPointers; i++) pObj->m_fields[i] = nil; } return pObj; }
VariantByteObject* ObjectMemory::resize(BytesOTE* ote, MWORD newByteSize) { ASSERT(!ObjectMemoryIsIntegerObject(ote) && ote->isBytes()); MWORD totalByteSize = newByteSize + SizeOfPointers(0); // Add header size VariantByteObject* pByteObj = ote->m_location; MWORD oldByteSize = ote->getSize(); if (ote->isNullTerminated()) { pByteObj = reinterpret_cast<VariantByteObject*>(ObjectMemory::basicResize(reinterpret_cast<POTE>(ote), totalByteSize, 1)); ASSERT(pByteObj); // Null-terminated objects should always be resizeable // Ensure we have a null-terminator reinterpret_cast<BYTE*>(pByteObj)[totalByteSize] = 0; } else pByteObj = reinterpret_cast<VariantByteObject*>(ObjectMemory::basicResize(reinterpret_cast<POTE>(ote), totalByteSize, 0)); if (pByteObj && totalByteSize > oldByteSize) { // The object grew, so ensure it is properly initialized memset(reinterpret_cast<BYTE*>(pByteObj)+oldByteSize, 0, totalByteSize-oldByteSize); } return pByteObj; }
PointersOTE* __fastcall ObjectMemory::newUninitializedPointerObject(BehaviorOTE* classPointer, MWORD oops) { // Total size must fit in a DWORD bits ASSERT(oops < ((DWORD(1) << 30) - ObjectHeaderSize)); // Don't worry, compiler will not really use multiply instruction here MWORD objectSize = SizeOfPointers(oops); OTE* ote; allocObject(objectSize, ote); ASSERT((objectSize > MaxSizeOfPoolObject && ote->heapSpace() == OTEFlags::NormalSpace) || ote->heapSpace() == OTEFlags::PoolSpace); // These are stored in the object itself ASSERT(ote->getSize() == objectSize); classPointer->countUp(); ote->m_oteClass = classPointer; // DO NOT Initialise the fields to nils ASSERT(ote->isPointers()); return reinterpret_cast<PointersOTE*>(ote); }
OTE* ObjectMemory::CopyElements(OTE* oteObj, MWORD startingAt, MWORD count) { // Note that startingAt is expected to be a zero-based index ASSERT(startingAt >= 0); OTE* oteSlice; if (oteObj->isBytes()) { BytesOTE* oteBytes = reinterpret_cast<BytesOTE*>(oteObj); size_t elementSize = ObjectMemory::GetBytesElementSize(oteBytes); if (count == 0 || ((startingAt + count) * elementSize <= oteBytes->bytesSize())) { MWORD objectSize = elementSize * count; if (oteBytes->m_flags.m_weakOrZ) { // TODO: Allocate the correct number of null term bytes based on the encoding auto newBytes = static_cast<VariantByteObject*>(allocObject(objectSize + NULLTERMSIZE, oteSlice)); // When copying strings, the slices has the same string class (oteSlice->m_oteClass = oteBytes->m_oteClass)->countUp(); memcpy(newBytes->m_fields, oteBytes->m_location->m_fields + (startingAt * elementSize), objectSize); *reinterpret_cast<NULLTERMTYPE*>(&newBytes->m_fields[objectSize]) = 0; oteSlice->beNullTerminated(); return oteSlice; } else { VariantByteObject* newBytes = static_cast<VariantByteObject*>(allocObject(objectSize, oteSlice)); // When copying bytes, the slice is always a ByteArray oteSlice->m_oteClass = Pointers.ClassByteArray; oteSlice->beBytes(); memcpy(newBytes->m_fields, oteBytes->m_location->m_fields + (startingAt * elementSize), objectSize); return oteSlice; } } } else { // Pointers PointersOTE* otePointers = reinterpret_cast<PointersOTE*>(oteObj); BehaviorOTE* oteClass = otePointers->m_oteClass; InstanceSpecification instSpec = oteClass->m_location->m_instanceSpec; if (instSpec.m_indexable) { startingAt += instSpec.m_fixedFields; if (count == 0 || (startingAt + count) <= otePointers->pointersSize()) { MWORD objectSize = SizeOfPointers(count); auto pSlice = static_cast<VariantObject*>(allocObject(objectSize, oteSlice)); // When copying pointers, the slice is always an Array oteSlice->m_oteClass = Pointers.ClassArray; VariantObject* pSrc = otePointers->m_location; for (MWORD i = 0; i < count; i++) { countUp(pSlice->m_fields[i] = pSrc->m_fields[startingAt + i]); } return oteSlice; } } } return nullptr; }
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); }