From b409008159b758a1ff04972de0567f57bbfe7954 Mon Sep 17 00:00:00 2001 From: Kade-gtihub <26305836+Kade-github@users.noreply.github.com> Date: Mon, 17 Aug 2026 20:03:30 -0700 Subject: [PATCH] Unloading of CPPIA classes --- include/hx/Scriptable.h | 1 + src/hx/cppia/Cppia.h | 26 ++++++++++++ src/hx/cppia/CppiaClasses.cpp | 76 +++++++++++++++++++++++++++++++--- src/hx/cppia/CppiaFunction.cpp | 21 ++++++++++ src/hx/cppia/CppiaModule.cpp | 71 +++++++++++++++++++++++++++---- 5 files changed, 183 insertions(+), 12 deletions(-) diff --git a/include/hx/Scriptable.h b/include/hx/Scriptable.h index 7a605dc01..dba2dacbf 100644 --- a/include/hx/Scriptable.h +++ b/include/hx/Scriptable.h @@ -191,6 +191,7 @@ class CppiaLoadedModule_obj : public ::hx::Object virtual void run() = 0; virtual void boot() = 0; virtual ::hx::Class resolveClass( ::String inName) = 0; + virtual void unload() { } }; typedef ::hx::ObjectPtr CppiaLoadedModule; diff --git a/src/hx/cppia/Cppia.h b/src/hx/cppia/Cppia.h index 28252bd15..981b553f3 100644 --- a/src/hx/cppia/Cppia.h +++ b/src/hx/cppia/Cppia.h @@ -153,6 +153,9 @@ typedef std::map CppiaStackVarMap; extern String sInvalidArgCount; +void _hx_cppia_track_expr(void *inExpr); +void _hx_cppia_untrack_expr(void *inExpr); + struct CppiaExpr { int line; @@ -161,6 +164,18 @@ struct CppiaExpr const char *functionName; int haxeTypeId; + void *operator new(size_t inSize) + { + void *result = ::operator new(inSize); + _hx_cppia_track_expr(result); + return result; + } + + void operator delete(void *inPtr) + { + _hx_cppia_untrack_expr(inPtr); + ::operator delete(inPtr); + } CppiaExpr() : line(0), filename(0), className(0), functionName(0) { @@ -283,6 +298,8 @@ struct ScriptCallable : public CppiaDynamicExpr int captureSize; + void deactivate(); + ScriptCallable(CppiaStream &stream); ScriptCallable(CppiaExpr *inBody); ScriptCallable(CppiaModule &inModule,ScriptNamedFunction *inFunction); @@ -372,7 +389,10 @@ class CppiaModule std::vector< TypeData * > types; std::vector< CppiaClassInfo * > classes; std::vector< CppiaExpr * > markable; + + hx::UnorderedSet< CppiaExpr * > allExprs; hx::UnorderedSet allFileIds; + typedef std::map< std::string, int > InterfaceSlots; InterfaceSlots interfaceSlots; @@ -384,9 +404,12 @@ class CppiaModule ScriptCallable *main; + bool unloaded; + CppiaModule(); ~CppiaModule(); + void unload(); void link(); void compile(); void setDebug(CppiaExpr *outExpr, int inFileId, int inLine); @@ -650,6 +673,8 @@ class CppiaClassInfo bool containsPointers; int dynamicMapOffset; int interfaceSlotSize; + int vtableSlotCount; + bool unloaded; void **vtable; std::string name; std::map interfaceScriptTables; @@ -704,6 +729,7 @@ class CppiaClassInfo void link(); void linkTypes(); + void deactivate(); inline bool isNativeProperty(const String &inString); diff --git a/src/hx/cppia/CppiaClasses.cpp b/src/hx/cppia/CppiaClasses.cpp index 5cfe6f171..f36e55998 100644 --- a/src/hx/cppia/CppiaClasses.cpp +++ b/src/hx/cppia/CppiaClasses.cpp @@ -28,7 +28,7 @@ struct CppiaEnumConstructor int nameId; int typeId; }; - + std::vector args; CppiaClassInfo *classInfo; int nameId; @@ -49,7 +49,7 @@ struct CppiaEnumConstructor int typeId = inStream.getInt(); args.push_back( Arg(nameId,typeId) ); } - + } hx::Object *create( Array inArgs ) { @@ -173,7 +173,7 @@ void SLJIT_CALL createEnum(hx::Class_obj *inClass, String *inName, int inArgs) for(int i=0;iConstructEnum(*inName, args).mPtr; - + CATCH_NATIVE ctx->pointer = oldPointer; } @@ -345,6 +345,8 @@ CppiaClassInfo::CppiaClassInfo(CppiaModule &inCppia) : cppia(inCppia) enumMeta = 0; isInterface = false; interfaceSlotSize = 0; + vtableSlotCount = 0; + unloaded = false; superType = 0; typeId = 0; vtable = 0; @@ -363,6 +365,9 @@ class CppiaClass *getCppiaClass() hx::Object *CppiaClassInfo::createInstance(CppiaCtx *ctx,Expressions &inArgs, bool inCallNew) { + if (unloaded) + hx::Throw( HX_CSTRING("Cannot construct ") + String(name.c_str()) + HX_CSTRING(", its class was unloaded") ); + hx::Object *obj = haxeBase->factory(vtable,extraData); createDynamicFunctions(obj); @@ -375,6 +380,9 @@ hx::Object *CppiaClassInfo::createInstance(CppiaCtx *ctx,Expressions &inArgs, bo hx::Object *CppiaClassInfo::createInstance(CppiaCtx *ctx,Array &inArgs) { + if (unloaded) + hx::Throw( HX_CSTRING("Cannot construct ") + String(name.c_str()) + HX_CSTRING(", its class was unloaded") ); + hx::Object *obj = haxeBase->factory(vtable,extraData); createDynamicFunctions(obj); @@ -678,6 +686,52 @@ CppiaEnumConstructor *CppiaClassInfo::findEnum(int inFieldId) return 0; } +// Drops the class back to its host base without freeing anything that a raw pointer still reaches. +void CppiaClassInfo::deactivate() +{ + if (unloaded) + return; + unloaded = true; + + if (vtable) + { + void **base = vtable - interfaceSlotSize - 1; + int count = vtableSlotCount + 2 + interfaceSlotSize; + memset(base, 0, sizeof(void *)*count); + vtable[-1] = this; + } + + // These all hold ScriptCallable pointers into the expression graph that is about to be freed. + memberFunctions.clear(); + staticFunctions.clear(); + memberGetters.clear(); + memberSetters.clear(); + staticGetters.clear(); + staticSetters.clear(); + newFunc = 0; + initExpr = 0; + enumMeta = 0; + + // Var initialisers are expressions, and nothing runs them again after this point. + for(int i=0;iinit = 0; + for(int i=0;iinit = 0; + + // Statics are still marked through the module, so clearing them is what actually releases the memory. + for(int i=0;iobjVal = null(); + staticVars[i]->stringVal = String(); + } + for(int i=0;iobjVal = null(); + staticDynamicFunctions[i]->stringVal = String(); + } +} + + void CppiaClassInfo::mark(hx::MarkContext *__inCtx) { HX_MARK_MEMBER(mClass); @@ -1056,7 +1110,7 @@ void CppiaClassInfo::linkTypes() DBGLOG(" script member vars size = %d\n", extraData); - + for(int i=0;inameId)); @@ -1162,6 +1216,7 @@ void CppiaClassInfo::linkTypes() if (interfaceSlotSize) interfaceSlotSize++; + vtableSlotCount = vtableSlot; vtable = new void*[vtableSlot + 2 + interfaceSlotSize]; memset(vtable, 0, sizeof(void *)*(vtableSlot+2+interfaceSlotSize)); vtable += interfaceSlotSize; @@ -1352,7 +1407,7 @@ void CppiaClassInfo::init(CppiaCtx *ctx, int inPhase) unsigned char *pointer = ctx->pointer; ctx->push( (hx::Object *) 0 ); // this AutoStack save(ctx,pointer); - + if (inPhase==0) { for(int i=0;i gAllCppiaModules; +static CppiaModule *sLoadingModule = 0; + +void _hx_cppia_track_expr(void *inExpr) +{ + if (sLoadingModule) + sLoadingModule->allExprs.insert((CppiaExpr *)inExpr); +} + +void _hx_cppia_untrack_expr(void *inExpr) +{ + if (sLoadingModule) + sLoadingModule->allExprs.erase((CppiaExpr *)inExpr); +} + std::vector scriptResources; @@ -29,6 +43,7 @@ CppiaModule::CppiaModule() creatingClass = 0; creatingFunction = 0; scriptId = ++sScriptId; + unloaded = false; strings = Array_obj::__new(0,0); if (sgNativeNameSlotCount>0) for(int i=2;ideactivate(); + + markable.clear(); + main = 0; + + std::vector toFree(allExprs.begin(), allExprs.end()); + allExprs.clear(); + + for(int i=0;i(toFree[i]); + if (callable) + callable->deactivate(); + else + delete toFree[i]; + } +} + + +CppiaModule::~CppiaModule() +{ + unload(); } void CppiaModule::link() { DBGLOG("Resolve registered - super\n"); HaxeNativeClass::link(); - + DBGLOG("Resolve typeIds\n"); for(int t=0;tlink(*this); @@ -299,6 +340,9 @@ class CppiaObject : public hx::CppiaLoadedModule_obj void boot() HXCPP_OVERRIDE { + if (cppia->unloaded) + hx::Throw( HX_CSTRING("Cannot boot a cppia module that has been unloaded") ); + if (booted) return; @@ -319,6 +363,9 @@ class CppiaObject : public hx::CppiaLoadedModule_obj void run() HXCPP_OVERRIDE { + if (cppia->unloaded) + hx::Throw( HX_CSTRING("Cannot run a cppia module that has been unloaded") ); + if (!booted) boot(); if (cppia->main) @@ -344,6 +391,11 @@ class CppiaObject : public hx::CppiaLoadedModule_obj } } + void unload() HXCPP_OVERRIDE + { + cppia->unload(); + } + ::hx::Class resolveClass( ::String inName) HXCPP_OVERRIDE { CppiaClassInfo *info = cppia->findClass(inName); @@ -369,6 +421,9 @@ CppiaLoadedModule LoadCppia(const unsigned char *inData, int inDataLength) CppiaLoadedModule loadedModule = new CppiaObject(cppiaPtr); gAllCppiaModules->push(loadedModule); + // Attribute every expression allocated from here on to this module. + sLoadingModule = cppiaPtr; + CppiaModule &cppia = *cppiaPtr; CppiaStream stream(cppiaPtr,inData, inDataLength); @@ -460,7 +515,7 @@ CppiaLoadedModule LoadCppia(const unsigned char *inData, int inDataLength) scriptResources[count].mDataLength = 0; scriptResources[count].mData = 0; scriptResources[count].mName = String(); - + RegisterResources(&scriptResources[0]); } else @@ -470,8 +525,8 @@ CppiaLoadedModule LoadCppia(const unsigned char *inData, int inDataLength) } catch(const char *errorString) { - error = HX_CSTRING("Error reading file ") + String(errorString) + - HX_CSTRING(", line ") + String(stream.line) + HX_CSTRING(", char ") + + error = HX_CSTRING("Error reading file ") + String(errorString) + + HX_CSTRING(", line ") + String(stream.line) + HX_CSTRING(", char ") + String(stream.pos); } @@ -502,6 +557,8 @@ CppiaLoadedModule LoadCppia(const unsigned char *inData, int inDataLength) #endif } + sLoadingModule = 0; + if (error.raw_ptr()) hx::Throw(error);