// N.B. Like the other instantiate methods in ObjectMemory, this method for instantiating // objects in virtual space (used for allocating Processes, for example), does not adjust // the ref. count of the class, because this is often unecessary, and does not adjust the // sizes to allow for fixed fields - callers must do this VirtualOTE* ObjectMemory::newVirtualObject(BehaviorOTE* classPointer, MWORD initialSize, MWORD maxSize) { #ifdef _DEBUG { ASSERT(isBehavior(Oop(classPointer))); Behavior& behavior = *classPointer->m_location; ASSERT(behavior.isIndexable()); } #endif // Trim the sizes to acceptable bounds if (initialSize <= dwOopsPerPage) initialSize = dwOopsPerPage; else initialSize = _ROUND2(initialSize, dwOopsPerPage); if (maxSize < initialSize) maxSize = initialSize; else maxSize = _ROUND2(maxSize, dwOopsPerPage); // We have to allow for the virtual allocation overhead. The allocation function will add in // space for this. The maximum size should include this, the initial size should not initialSize -= sizeof(VirtualObjectHeader)/sizeof(MWORD); unsigned byteSize = initialSize*sizeof(MWORD); VariantObject* pLocation = reinterpret_cast<VariantObject*>(AllocateVirtualSpace(maxSize * sizeof(MWORD), byteSize)); if (pLocation) { // No need to alter ref. count of process class, as it is sticky // Fill space with nils for initial values const Oop nil = Oop(Pointers.Nil); const unsigned loopEnd = initialSize; for (unsigned i = 0; i < loopEnd; i++) pLocation->m_fields[i] = nil; OTE* ote = ObjectMemory::allocateOop(static_cast<POBJECT>(pLocation)); ote->setSize(byteSize); ote->m_oteClass = classPointer; classPointer->countUp(); ote->m_flags = m_spaceOTEBits[OTEFlags::VirtualSpace]; ASSERT(ote->isPointers()); return reinterpret_cast<VirtualOTE*>(ote); } return nullptr; }
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; }
BOOL __fastcall Interpreter::primitiveHookWindowCreate() { Oop argPointer = stackTop(); OTE* underConstruction = m_oteUnderConstruction; OTE* receiverPointer = reinterpret_cast<OTE*>(stackValue(1)); if (!underConstruction->isNil() && underConstruction != receiverPointer) { // Hooked by another window - fail the primitive return primitiveFailureWith(1, underConstruction); } if (argPointer == Oop(Pointers.True)) { // Hooking if (underConstruction != receiverPointer) { ASSERT(underConstruction->isNil()); m_oteUnderConstruction= receiverPointer; receiverPointer->countUp(); } } else { if (argPointer == Oop(Pointers.False)) { // Unhooking if (underConstruction == receiverPointer) { tracelock lock(TRACESTREAM); TRACESTREAM << "WARNING: Unhooking create for " << hex << underConstruction << " before HCBT_CREATEWND" << endl; ObjectMemory::nilOutPointer(m_oteUnderConstruction); } else ASSERT(underConstruction->isNil()); } else return primitiveFailureWith(0, argPointer); // Invalid argument } popStack(); return TRUE; }
inline void Interpreter::subclassWindow(OTE* window, HWND hWnd) { ASSERT(!ObjectMemoryIsIntegerObject(window)); // As this is called from an external entry point, we must ensure that OT/stack overflows // are handled, and also that we catch the SE_VMCALLBACKUNWIND exceptions __try { bool bDisabled = disableInterrupts(true); Oop retVal = performWith(Oop(window), Pointers.subclassWindowSymbol, Oop(ExternalHandle::New(hWnd))); ObjectMemory::countDown(retVal); ASSERT(m_bInterruptsDisabled); disableInterrupts(bDisabled); } __except (callbackExceptionFilter(GetExceptionInformation())) { trace("WARNING: Unwinding Interpreter::subclassWindow(%#x, %#x)\n", window, hWnd); } }
extern "C" Oop __stdcall PrimCompileForEval(Oop compilerOop, const char* szSource, Oop aClass, Oop aWorkspacePool, int flags, Oop notifier) { ICompilerPtr piCompiler = NewCompiler(); if (piCompiler == NULL) return Oop(GetVM()->NilPointer()); #ifdef _AFX flags |= Boot; #endif return (Oop)piCompiler->CompileForEval(GetVM(), compilerOop, szSource, (POTE)aClass, (POTE)aWorkspacePool, FLAGS(flags), notifier); }
PointersOTE* __fastcall ObjectMemory::newPointerObject(BehaviorOTE* classPointer, MWORD oops) { PointersOTE* ote = newUninitializedPointerObject(classPointer, oops); // Initialise the fields to nils const Oop nil = Oop(Pointers.Nil); // Loop invariant (otherwise compiler reloads each time) VariantObject* pLocation = ote->m_location; const MWORD loopEnd = oops; for (MWORD i = 0; i<loopEnd; i++) pLocation->m_fields[i] = nil; ASSERT(ote->isPointers()); return reinterpret_cast<PointersOTE*>(ote); }
void GarbageCollector::updatePointer(Oop *pointer) { // I only can update reference objects. if(!pointer->isPointer()) return; // Is this a weak reference that was collected? if(pointer->header->gcColor == White) { *pointer = Oop(); return; } // Is this object in the heap? auto heap = memoryManager->getHeap(); if(!heap->containsPointer(pointer->pointer)) return; // Use the forwarding pointer. uint8_t **forwardingPointer = reinterpret_cast<uint8_t**> (pointer->pointer - sizeof(uint8_t*)); pointer->pointer = *forwardingPointer; }
Oop* __fastcall Interpreter::primitivePerformMethod(CompiledMethod& , unsigned) { Oop * sp = m_registers.m_stackPointer; ArrayOTE* oteArg = reinterpret_cast<ArrayOTE*>(*(sp)); if (ObjectMemory::fetchClassOf(Oop(oteArg)) != Pointers.ClassArray) return primitiveFailure(0); // Arguments not an Array Array* arguments = oteArg->m_location; Oop receiverPointer = *(sp-1); MethodOTE* oteMethod = reinterpret_cast<MethodOTE*>(*(sp-2)); // Adjust sp to point at slot where receiver will be moved sp -= 2; //ASSERT(ObjectMemory::isKindOf(oteMethod, Pointers.ClassCompiledMethod)); CompiledMethod* method = oteMethod->m_location; if (!ObjectMemory::isKindOf(receiverPointer, method->m_methodClass)) return primitiveFailure(1); // Wrong class of receiver const unsigned argCount = oteArg->pointersSize(); const unsigned methodArgCount = method->m_header.argumentCount; if (methodArgCount != argCount) return primitiveFailure(2); // Wrong number of arguments // Push receiver and arguments on stack (over the top of array and receiver) sp[0] = receiverPointer; // Write receiver over the top of the method for (MWORD i = 0; i < argCount; i++) { Oop pushee = arguments->m_elements[i]; // Don't count up because we are adding a stack ref. sp[i+1] = pushee; } m_registers.m_stackPointer = sp+argCount; // Don't count down any args executeNewMethod(oteMethod, argCount); return primitiveSuccess(0); }
BOOL __fastcall Interpreter::primitiveSnapshot(CompiledMethod&, unsigned argCount) { Oop arg = stackValue(argCount - 1); char* szFileName; if (arg == Oop(Pointers.Nil)) szFileName = 0; else if (ObjectMemory::fetchClassOf(arg) == Pointers.ClassString) { StringOTE* oteString = reinterpret_cast<StringOTE*>(arg); String* fileName = oteString->m_location; szFileName = fileName->m_characters; } else return primitiveFailure(0); bool bBackup; if (argCount >= 2) bBackup = reinterpret_cast<OTE*>(stackValue(argCount - 2)) == Pointers.True; else bBackup = false; SMALLINTEGER nCompressionLevel; if (argCount >= 3) { Oop oopCompressionLevel = stackValue(argCount - 3); nCompressionLevel = ObjectMemoryIsIntegerObject(oopCompressionLevel) ? ObjectMemoryIntegerValueOf(oopCompressionLevel) : 0; } else nCompressionLevel = 0; SMALLUNSIGNED nMaxObjects = 0; if (argCount >= 4) { Oop oopMaxObjects = stackValue(argCount - 4); if (ObjectMemoryIsIntegerObject(oopMaxObjects)) { nMaxObjects = ObjectMemoryIntegerValueOf(oopMaxObjects); } } // N.B. It is not necessary to clear down the memory pools as the free list is rebuild on every image // load and the pool members, though not on the free list at present, are marked as free entries // in the object table // ZCT is reconciled, so objects may be deleted flushAtCaches(); // Store the active frame of the active process before saving so available on image reload // We're not actually suspending the process now, but it appears like that to the snapshotted // image on restarting m_registers.PrepareToSuspendProcess(); #ifdef OAD DWORD timeStart = timeGetTime(); #endif int saveResult = ObjectMemory::SaveImageFile(szFileName, bBackup, nCompressionLevel, nMaxObjects); #ifdef OAD DWORD timeEnd = timeGetTime(); TRACESTREAM << "Time to save image: " << (timeEnd - timeStart) << " mS" << endl; #endif if (!saveResult) { // Success popStack(); return primitiveSuccess(); } else { // Failure return primitiveFailure(saveResult); } }
// Value with args takes an array of arguments Oop* __fastcall Interpreter::primitiveValueWithArgs() { Oop* bp = m_registers.m_stackPointer; ArrayOTE* argumentArray = reinterpret_cast<ArrayOTE*>(*(bp)); BlockOTE* oteBlock = reinterpret_cast<BlockOTE*>(*(bp-1)); ASSERT(ObjectMemory::fetchClassOf(Oop(oteBlock)) == Pointers.ClassBlockClosure); BlockClosure* block = oteBlock->m_location; const MWORD blockArgumentCount = block->m_info.argumentCount; BehaviorOTE* arrayClass = ObjectMemory::fetchClassOf(Oop(argumentArray)); if (arrayClass != Pointers.ClassArray) return primitiveFailure(1); const MWORD arrayArgumentCount = argumentArray->pointersSize(); if (arrayArgumentCount != blockArgumentCount) return primitiveFailure(0); pop(2); // N.B. ref count of Block will be assumed by storing into frame // Store old context details from interpreter registers m_registers.StoreContextRegisters(); // Overwrite receiver block with receiver at time of closure. Oop closureReceiver = block->m_receiver; *(bp-1) = closureReceiver; // No need to count up the receiver since we've written it into a stack slot Array* args = argumentArray->m_location; // Code this carefully so compiler generates optimal code (it makes a poor job on its own) Oop* sp = bp; // Push the args from the array { for (unsigned i=0;i<arrayArgumentCount;i++) { Oop pushee = args->m_elements[i]; *sp++ = pushee; // No need to count up since pushing on the stack } } const unsigned copiedValues = block->copiedValuesCount(oteBlock); { for (unsigned i=0;i<copiedValues;i++) { Oop oopCopied = block->m_copiedValues[i]; *sp++ = oopCopied; // No need to count up since pushing on the stack } } // Nil out any extra stack temp slots we need const unsigned extraTemps = block->stackTempsCount(); { const Oop nilPointer = Oop(Pointers.Nil); for (unsigned i=0;i<extraTemps;i++) *sp++ = nilPointer; } // Stack frame follows args... StackFrame* pFrame = reinterpret_cast<StackFrame*>(sp); pFrame->m_bp = reinterpret_cast<Oop>(bp)+1; m_registers.m_basePointer = reinterpret_cast<Oop*>(bp); // stack ref. removed so don't need to count down pFrame->m_caller = m_registers.activeFrameOop(); // Having set caller can update the active frame Oop m_registers.m_pActiveFrame = pFrame; // Note that ref. count remains the same due dto overwritten receiver slot const unsigned envTemps = block->envTempsCount(); if (envTemps > 0) { ContextOTE* oteContext = Context::New(envTemps, reinterpret_cast<Oop>(block->m_outer)); pFrame->m_environment = reinterpret_cast<Oop>(oteContext); Context* context = oteContext->m_location; context->m_block = oteBlock; // Block has been written into a heap object slot, so must count up oteBlock->countUp(); } else pFrame->m_environment = reinterpret_cast<Oop>(oteBlock); // We don't need to store down the IP and SP into the frame until it is suspended pFrame->m_ip = ZeroPointer; pFrame->m_sp = ZeroPointer; MethodOTE* oteMethod = block->m_method; pFrame->m_method = oteMethod; // Don't need to inc ref count for stack frame ref to method CompiledMethod* method = oteMethod->m_location; m_registers.m_pMethod = method; m_registers.m_instructionPointer = ObjectMemory::ByteAddressOfObjectContents(method->m_byteCodes) + block->initialIP() - 1; // New stack pointer points at last field of stack frame m_registers.m_stackPointer = reinterpret_cast<Oop*>(reinterpret_cast<BYTE*>(pFrame)+sizeof(StackFrame)) - 1; ASSERT(m_registers.m_stackPointer == &pFrame->m_bp); return primitiveSuccess(0); }
Oop* __fastcall Interpreter::primitivePerformWithArgs() { Oop* const sp = m_registers.m_stackPointer; ArrayOTE* argumentArray = reinterpret_cast<ArrayOTE*>(*(sp)); BehaviorOTE* arrayClass = ObjectMemory::fetchClassOf(Oop(argumentArray)); if (arrayClass != Pointers.ClassArray) return primitiveFailure(0); // N.B. We're using a large stack, so don't bother checking for overflow // (standard stack overflow mechanism should catch it) // We must not get the length outside, in case small integer arg const unsigned argCount = argumentArray->pointersSize(); // Save old message selector in case of prim failure (need to reinstate) SymbolOTE* performSelector = m_oopMessageSelector; // To ensure the argumentArray doesn't go away when we push its contents // onto the stack, in case we need it for recovery from an argument // count mismatch we leave its ref. count elevated SymbolOTE* selectorToPerform = reinterpret_cast<SymbolOTE*>(*(sp-1)); if (ObjectMemoryIsIntegerObject(selectorToPerform)) return primitiveFailure(1); m_oopMessageSelector = selectorToPerform; // Get selector from stack // Don't need to count down the stack ref. ASSERT(!selectorToPerform->isFree()); Oop newReceiver = *(sp-2); // receiver is under selector and arg array // Push the args from the array onto the stack. We must do this before // looking up the method, because if the receiver does not understand // the method then the lookup routines copy the arguments off the stack // into a Message object Array* args = argumentArray->m_location; for (MWORD i=0; i<argCount; i++) { Oop pushee = args->m_elements[i]; // Note no need to inc the ref. count when pushing on the stack sp[i-1] = pushee; } // Args written over top of selector and argument array (hence -2) m_registers.m_stackPointer = sp+argCount-2; // There is a subtle complication here when the receiver does not // understand the message, by which lookupMethodInClass() converts // the message we're trying to perform to a #doesNotUnderstand: with // all arguments moved to a Message. We still want to execute this // does not understand, so we also execute the method if the argument // counts do not match, but it was not understood. Note that it is // possible for a doesNotUnderstand: to be executed thru the first // test if the argumentArray contained only one argument. We allow // this to happen to avoid testing for not understood in the normal // case - just be aware of this anomaly. MethodOTE* methodPointer = findNewMethodInClass(ObjectMemory::fetchClassOf(newReceiver), argCount); CompiledMethod& method = *methodPointer->m_location; const unsigned methodArgCount = method.m_header.argumentCount; if (methodArgCount == argCount || m_oopMessageSelector == Pointers.DoesNotUnderstandSelector) { // WE no longer need the argument array, but don't count it down since we only have a stack ref. executeNewMethod(methodPointer, methodArgCount); return primitiveSuccess(0); } else { // Receiver must have understood the message, but we had wrong // number of arguments, so reinstate the stack and fail the primitive pop(argCount); pushObject((OTE*)m_oopMessageSelector); // Argument array already has artificially increased ref. count push(Oop(argumentArray)); m_oopMessageSelector = performSelector; return primitiveFailure(1); } }
inline void Process::SetThread(void* handle) { ObjectMemory::storePointerWithUnrefCntdValue(m_thread, Oop(handle) + 1); }
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; }
void ObjectMemory::FixupObject(OTE* ote, MWORD* oldLocation, const ImageHeader* pHeader) { // Convert the class now separately BehaviorOTE* classPointer = reinterpret_cast<BehaviorOTE*>(FixupPointer(reinterpret_cast<OTE*>(ote->m_oteClass), static_cast<OTE*>(pHeader->BasePointer))); #ifdef _DEBUG { PointersOTE* oteObj = reinterpret_cast<PointersOTE*>(ote); if (ote->isPointers() && (oteObj->getSize() % 2 == 1 || classPointer == _Pointers.ClassByteArray || classPointer == _Pointers.ClassAnsiString || classPointer == _Pointers.ClassUtf8String || classPointer == _Pointers.ClassSymbol)) { TRACESTREAM<< L"Bad OTE for byte array " << LPVOID(&ote)<< L" marked as pointer" << std::endl; ote->beBytes(); } } #endif ote->m_oteClass = classPointer; if (ote->isPointers()) { PointersOTE* otePointers = reinterpret_cast<PointersOTE*>(ote); VariantObject* obj = otePointers->m_location; const SMALLUNSIGNED numFields = ote->pointersSize(); ASSERT(SMALLINTEGER(numFields) >= 0); // Fixup all the Oops for (SMALLUNSIGNED i = 0; i < numFields; i++) { Oop instPointer = obj->m_fields[i]; if (!isIntegerObject(instPointer)) obj->m_fields[i] = Oop(FixupPointer(reinterpret_cast<OTE*>(instPointer), static_cast<OTE*>(pHeader->BasePointer))); } if (classPointer == _Pointers.ClassProcess) { Process* process = static_cast<Process*>(ote->m_location); // We use the callbackDepth slot to store the delta in location // which we later use to adjust all the stack references. // The previous value is lost, but this is not important // as it must be reestablished as zero anyway process->BasicSetCallbackDepth(Oop(process) - Oop(oldLocation) + 1); } // else // MethodContext objects contain a pointer to their frame in the // stack, but we must fix that up later when walking down the stack. } else { if (classPointer == _Pointers.ClassExternalHandle) { // In Dolphin 4.0, all ExternalHandles are automatically nulled // on image load. ExternalHandle* handle = static_cast<ExternalHandle*>(ote->m_location); handle->m_handle = NULL; } // Look for the special image stamp object else if (classPointer == _Pointers.ClassContext) { ASSERT(ote->heapSpace() == OTEFlags::PoolSpace || ote->heapSpace() == OTEFlags::NormalSpace); // Can't deallocate now - must leave for collection later - maybe could go in the Zct though. VERIFY(ote->decRefs()); deallocate(reinterpret_cast<OTE*>(ote)); } } }
PointersOTE* __fastcall ObjectMemory::newPointerObject(BehaviorOTE* classPointer) { ASSERT(isBehavior(Oop(classPointer))); return newPointerObject(classPointer, classPointer->m_location->fixedFields()); }