#pragma once #include <stack> #include "../CommonDef.h" #include "gc/GarbageCollector.h" #include "vm/Exception.h" #include "vm/StackTrace.h" #include "../metadata/MetadataUtil.h" #include "../RuntimeConfig.h" #include "InterpreterDefs.h" #include "MemoryUtil.h" #include "MethodBridge.h" //#if DEBUG //#define PUSH_STACK_FRAME(method) do { \ // Il2CppStackFrameInfo stackFrameInfo = { method, (uintptr_t)method->methodPointer }; \ // il2cpp::vm::StackTrace::PushFrame(stackFrameInfo); \ //} while(0) // //#define POP_STACK_FRAME() do { il2cpp::vm::StackTrace::PopFrame(); } while(0) // //#else #define PUSH_STACK_FRAME(method) #define POP_STACK_FRAME() //#endif namespace hybridclr { namespace interpreter { class MachineState { public: MachineState() { _stackSize = -1; _stackBase = nullptr; _stackTopIdx = 0; _localPoolBottomIdx = -1; _frameBase = nullptr; _frameCount = -1; _frameTopIdx = 0; _exceptionFlowBase = nullptr; _exceptionFlowCount = -1; _exceptionFlowTopIdx = 0; } ~MachineState() { if (_stackBase) { //il2cpp::gc::GarbageCollector::FreeFixed(_stackBase); il2cpp::gc::GarbageCollector::UnregisterDynamicRoot(this); HYBRIDCLR_FREE(_stackBase); } if (_frameBase) { HYBRIDCLR_FREE(_frameBase); } if (_exceptionFlowBase) { HYBRIDCLR_FREE(_exceptionFlowBase); } } static std::pair<char*, size_t> GetGCRootData(void* root) { MachineState* machineState = (MachineState*)root; if (machineState->_stackBase && machineState->_stackTopIdx > 0) { return std::make_pair((char*)machineState->_stackBase, machineState->_stackTopIdx * sizeof(StackObject)); } else { return std::make_pair(nullptr, 0); } } StackObject* AllocArgments(int32_t argCount) { return AllocStackSlot(argCount); } StackObject* GetStackBasePtr() const { return _stackBase; } int32_t GetStackTop() const { return _stackTopIdx; } StackObject* AllocStackSlot(int32_t slotNum) { if (_stackTopIdx + slotNum > _localPoolBottomIdx) { if (!_stackBase) { InitEvalStack(); } if (_stackTopIdx + slotNum > _localPoolBottomIdx) { il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetStackOverflowException("AllocStackSlot")); } } StackObject* dataPtr = _stackBase + _stackTopIdx; _stackTopIdx += slotNum; #if DEBUG std::memset(dataPtr, 0xEA, slotNum * sizeof(StackObject)); #endif return dataPtr; } void* AllocLocalloc(size_t size) { IL2CPP_ASSERT(size % 8 == 0); int32_t slotNum = (int32_t)(size / 8); IL2CPP_ASSERT(slotNum > 0); if (_stackTopIdx + slotNum > _localPoolBottomIdx) { if (!_stackBase) { InitEvalStack(); } if (_stackTopIdx + slotNum > _localPoolBottomIdx) { il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetStackOverflowException("AllocLocalloc")); } } _localPoolBottomIdx -= slotNum; return _stackBase + _localPoolBottomIdx; } void SetStackTop(int32_t oldTop) { _stackTopIdx = oldTop; } uint32_t GetFrameTopIdx() const { return _frameTopIdx; } int32_t GetLocalPoolBottomIdx() const { return _localPoolBottomIdx; } void SetLocalPoolBottomIdx(int32_t idx) { _localPoolBottomIdx = idx; } InterpFrame* PushFrame() { if (_frameTopIdx >= _frameCount) { if (!_frameBase) { InitFrames(); } else { il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetStackOverflowException("AllocFrame")); } } return _frameBase + _frameTopIdx++; } void PopFrame() { IL2CPP_ASSERT(_frameTopIdx > 0); --_frameTopIdx; } void PopFrameN(int32_t count) { IL2CPP_ASSERT(count > 0 && _frameTopIdx >= count); _frameTopIdx -= count; } InterpFrame* GetTopFrame() const { if (_frameTopIdx > 0) { return _frameBase + _frameTopIdx - 1; } else { return nullptr; } } ExceptionFlowInfo* AllocExceptionFlow(int32_t count) { if (_exceptionFlowTopIdx + count >= _exceptionFlowCount) { if (!_exceptionFlowBase) { InitExceptionFlows(); } if (_exceptionFlowTopIdx + count >= _exceptionFlowCount) { il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetExecutionEngineException("AllocExceptionFlowZero")); } } ExceptionFlowInfo* efi = _exceptionFlowBase + _exceptionFlowTopIdx; _exceptionFlowTopIdx += count; return efi; } uint32_t GetExceptionFlowTopIdx() const { return _exceptionFlowTopIdx; } void SetExceptionFlowTopIdx(uint32_t exTopIdx) { _exceptionFlowTopIdx = exTopIdx; } void SetExceptionFlowTop(ExceptionFlowInfo* top) { _exceptionFlowTopIdx = (int32_t)(top - _exceptionFlowBase); IL2CPP_ASSERT(_exceptionFlowTopIdx >= 0 && _exceptionFlowTopIdx <= _exceptionFlowCount); } void PushExecutingImage(const Il2CppImage* image) { _executingImageStack.push(image); } void PopExecutingImage() { _executingImageStack.pop(); } const Il2CppImage* GetTopExecutingImage() const { if (_executingImageStack.empty()) { return nullptr; } else { return _executingImageStack.top(); } } void CollectFrames(il2cpp::vm::StackFrames* stackFrames) { if (_frameTopIdx <= 0) { return; } stackFrames->insert(stackFrames->begin(), _frameTopIdx, Il2CppStackFrameInfo()); for (int32_t i = 0; i < _frameTopIdx; i++) { InterpFrame* frame = _frameBase + i; const MethodInfo* method = frame->method->method; (*stackFrames)[i] = { method #if HYBRIDCLR_UNITY_2020_OR_NEW , (uintptr_t)method->methodPointer #endif }; } } private: void InitEvalStack() { _stackSize = (int32_t)RuntimeConfig::GetInterpreterThreadObjectStackSize(); _stackBase = (StackObject*)HYBRIDCLR_MALLOC_ZERO(RuntimeConfig::GetInterpreterThreadObjectStackSize() * sizeof(StackObject)); _stackTopIdx = 0; _localPoolBottomIdx = _stackSize; il2cpp::gc::GarbageCollector::RegisterDynamicRoot(this, GetGCRootData); } void InitFrames() { _frameBase = (InterpFrame*)HYBRIDCLR_CALLOC(RuntimeConfig::GetInterpreterThreadFrameStackSize(), sizeof(InterpFrame)); _frameCount = (int32_t)RuntimeConfig::GetInterpreterThreadFrameStackSize(); _frameTopIdx = 0; } void InitExceptionFlows() { _exceptionFlowBase = (ExceptionFlowInfo*)HYBRIDCLR_CALLOC(RuntimeConfig::GetInterpreterThreadExceptionFlowSize(), sizeof(ExceptionFlowInfo)); _exceptionFlowCount = (int32_t)RuntimeConfig::GetInterpreterThreadExceptionFlowSize(); _exceptionFlowTopIdx = 0; } StackObject* _stackBase; int32_t _stackSize; int32_t _stackTopIdx; int32_t _localPoolBottomIdx; InterpFrame* _frameBase; int32_t _frameTopIdx; int32_t _frameCount; ExceptionFlowInfo* _exceptionFlowBase; int32_t _exceptionFlowTopIdx; int32_t _exceptionFlowCount; std::stack<const Il2CppImage*> _executingImageStack; }; class ExecutingInterpImageScope { public: ExecutingInterpImageScope(MachineState& state, const Il2CppImage* image) : _state(state) { _state.PushExecutingImage(image); } ~ExecutingInterpImageScope() { _state.PopExecutingImage(); } private: MachineState& _state; }; class InterpFrameGroup { public: InterpFrameGroup(MachineState& ms) : _machineState(ms), _stackBaseIdx(ms.GetStackTop()), _frameBaseIdx(ms.GetFrameTopIdx()) { } void CleanUpFrames() { IL2CPP_ASSERT(_machineState.GetFrameTopIdx() >= _frameBaseIdx); uint32_t n = _machineState.GetFrameTopIdx() - _frameBaseIdx; if (n > 0) { for (uint32_t i = 0; i < n; i++) { LeaveFrame(); } } } InterpFrame* EnterFrameFromInterpreter(const InterpMethodInfo* imi, StackObject* argBase); InterpFrame* EnterFrameFromNative(const InterpMethodInfo* imi, StackObject* argBase); InterpFrame* LeaveFrame(); void* AllocLoc(size_t originSize, bool fillZero) { if (originSize == 0) { return nullptr; } size_t size = (originSize + 7) & ~(size_t)7; void* data = _machineState.AllocLocalloc(size); if (fillZero) { std::memset(data, 0, size); } return data; } size_t GetFrameCount() const { return _machineState.GetFrameTopIdx() - _frameBaseIdx; } private: MachineState& _machineState; int32_t _stackBaseIdx; uint32_t _frameBaseIdx; }; class StackObjectAllocScope { private: MachineState& _state; const int32_t _originStackTop; const int32_t _count; StackObject* _data; public: StackObjectAllocScope(MachineState& state, int32_t count) : _state(state), _count(count), _originStackTop(_state.GetStackTop()) { _data = state.AllocStackSlot(count); } ~StackObjectAllocScope() { IL2CPP_ASSERT(_state.GetStackTop() > _originStackTop); _state.SetStackTop(_originStackTop); } StackObject* GetData() const { return _data; } }; } }