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 1/2] 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); From 5b56f5c1630cb396303bda9fbb771d66c2805782 Mon Sep 17 00:00:00 2001 From: Kade-gtihub <26305836+Kade-github@users.noreply.github.com> Date: Sun, 23 Aug 2026 02:56:03 -0700 Subject: [PATCH 2/2] correct subclass inheritance when calling super --- src/hx/cppia/Cppia.cpp | 81 ++++++++++++++++++++++++++++++----- src/hx/cppia/Cppia.h | 3 ++ src/hx/cppia/CppiaClasses.cpp | 39 ++++++++++++++++- 3 files changed, 112 insertions(+), 11 deletions(-) diff --git a/src/hx/cppia/Cppia.cpp b/src/hx/cppia/Cppia.cpp index 2e7eb7c19..aad964afd 100644 --- a/src/hx/cppia/Cppia.cpp +++ b/src/hx/cppia/Cppia.cpp @@ -1696,6 +1696,22 @@ inline void SetVal(String &out, const T &value) { out = String(value); } template inline void SetVal(Dynamic &out, const T &value) { out = value; } +struct SuperVTableSwap +{ + void ***location; + void **restore; + + SuperVTableSwap(hx::Object *inObj, int inOffset, void **inVTable, void **inSuper) + { + location = (void ***)((char *)inObj + inOffset); + restore = inVTable; + *location = inSuper; + } + + ~SuperVTableSwap() { *location = restore; } +}; + + //template struct CallHaxe : public CppiaExpr { @@ -1705,8 +1721,9 @@ struct CallHaxe : public CppiaExpr ExprType returnType; bool isStatic; bool isSuper; + int superSlot; - CallHaxe(CppiaExpr *inSrc,ScriptNamedFunction inFunction, CppiaExpr *inThis, Expressions &ioArgs, bool inIsStatic=false, bool inIsSuper = false ) + CallHaxe(CppiaExpr *inSrc,ScriptNamedFunction inFunction, CppiaExpr *inThis, Expressions &ioArgs, bool inIsStatic=false, bool inIsSuper = false, int inSuperSlot = -1 ) : CppiaExpr(inSrc) { args.swap(ioArgs); @@ -1714,6 +1731,34 @@ struct CallHaxe : public CppiaExpr function = inFunction; isStatic = inIsStatic; isSuper = inIsSuper; + superSlot = inSuperSlot; + } + + void invoke(CppiaCtx *ctx, hx::Object *thisVal) + { + if (superSlot >= 0 && thisVal) + { + void **vtable = thisVal->__GetScriptVTable(); + + if (vtable) + { + CppiaClassInfo *info = (CppiaClassInfo *)vtable[-1]; + void **super = info ? info->getSuperVTable(superSlot) : 0; + int offset = info ? info->getScriptVTableOffset() : 0; + + if (super && *(void ***)((char *)thisVal + offset) == vtable) + { + SuperVTableSwap swap(thisVal, offset, vtable, super); + function.execute(ctx); + return; + } + } + } + + if (isSuper) + function.superExecute(ctx); + else + function.execute(ctx); } ExprType getType() HXCPP_OVERRIDE { return returnType; } CppiaExpr *link(CppiaModule &inModule) HXCPP_OVERRIDE @@ -1762,7 +1807,8 @@ struct CallHaxe : public CppiaExpr void run(CppiaCtx *ctx,T &outValue) { unsigned char *pointer = ctx->pointer; - ctx->pushObject(isStatic ? 0: thisExpr ? thisExpr->runObject(ctx) : ctx->getThis(false)); + hx::Object *thisVal = isStatic ? 0: thisExpr ? thisExpr->runObject(ctx) : ctx->getThis(false); + ctx->pushObject(thisVal); BCR_VCHECK; const char *s = function.signature+1; @@ -1783,10 +1829,7 @@ struct CallHaxe : public CppiaExpr } AutoStack a(ctx,pointer); - if (isSuper) - function.superExecute(ctx); - else - function.execute(ctx); + invoke(ctx, thisVal); #ifdef DEBUG_RETURN_TYPE gLastRet = returnType; @@ -1851,6 +1894,13 @@ struct CallHaxe : public CppiaExpr CATCH_NATIVE } + static void SLJIT_CALL tryCallHaxeSuper( CallHaxe *expr, StackContext *ctx ) + { + TRY_NATIVE + expr->invoke(ctx, ctx->getThis(false)); + CATCH_NATIVE + } + void genCode(CppiaCompiler *compiler, const JitVal &inDest,ExprType destType) HXCPP_OVERRIDE { int framePos = compiler->getCurrentFrameSize(); @@ -1893,8 +1943,15 @@ struct CallHaxe : public CppiaExpr // Store new frame in context ... compiler->add( sJitCtxFrame, sJitFrame.as(jtPointer), JitVal(framePos) ); - void *func = (void *) ( isSuper ? function.superExecute : function.execute); - compiler->callNative( (void *)tryCallHaxe, JitVal(func), sJitCtx ); + if (superSlot >= 0) + { + compiler->callNative( (void *)tryCallHaxeSuper, JitVal((void *)this), sJitCtx ); + } + else + { + void *func = (void *) ( isSuper ? function.superExecute : function.execute); + compiler->callNative( (void *)tryCallHaxe, JitVal(func), sJitCtx ); + } genFunctionResult(compiler, inDest, destType, returnType, isBoolInt()); } @@ -3356,11 +3413,15 @@ struct CallMember : public CppiaExpr ScriptNamedFunction func = type->haxeBase->findFunction(field.utf8_str()); if (func.signature) { - if (callSuperField && !func.superExecute) + int superSlot = -1; + if (callSuperField && inModule.linkingClass) + superSlot = inModule.linkingClass->findFunctionSlot(fieldId); + + if (callSuperField && superSlot < 0 && !func.superExecute) CPPIALOG("Warning - calling super host '%s' from cppia can lead to infinte recursion\n", field.utf8_str()); //CPPIALOG(" found function %s\n", func.signature ); - replace = new CallHaxe( this, func, thisExpr, args, false, callSuperField && func.superExecute); + replace = new CallHaxe( this, func, thisExpr, args, false, callSuperField && func.superExecute, superSlot); } } diff --git a/src/hx/cppia/Cppia.h b/src/hx/cppia/Cppia.h index 981b553f3..8e877403c 100644 --- a/src/hx/cppia/Cppia.h +++ b/src/hx/cppia/Cppia.h @@ -676,6 +676,7 @@ class CppiaClassInfo int vtableSlotCount; bool unloaded; void **vtable; + std::vector superVtables; std::string name; std::map interfaceScriptTables; std::vector nativeInterfaceFunctions; @@ -756,6 +757,8 @@ class CppiaClassInfo void *getHaxeBaseVTable(); int getScriptVTableOffset(); + void **getSuperVTable(int inSlot); + void freeSuperVTables(); Dynamic getStaticValue(const String &inName,hx::PropertyAccess inCallProp); bool hasStaticValue(const String &inName); diff --git a/src/hx/cppia/CppiaClasses.cpp b/src/hx/cppia/CppiaClasses.cpp index f36e55998..0683532ee 100644 --- a/src/hx/cppia/CppiaClasses.cpp +++ b/src/hx/cppia/CppiaClasses.cpp @@ -412,6 +412,41 @@ int CppiaClassInfo::getScriptVTableOffset() return haxeBase ? (int)(haxeBase->mDataOffset - sizeof(void *)) : sizeof(hx::Object); } +void **CppiaClassInfo::getSuperVTable(int inSlot) +{ + if (!vtable || inSlot < 0 || inSlot >= vtableSlotCount) + return 0; + + if ((int)superVtables.size() <= inSlot) + superVtables.resize(inSlot + 1, 0); + + if (superVtables[inSlot]) + return superVtables[inSlot]; + + int count = vtableSlotCount + 2 + interfaceSlotSize; + void **base = vtable - interfaceSlotSize - 1; + + void **copy = new void *[count]; + memcpy(copy, base, sizeof(void *) * count); + + copy += interfaceSlotSize + 1; + copy[inSlot] = 0; + + superVtables[inSlot] = copy; + + return copy; +} + + +void CppiaClassInfo::freeSuperVTables() +{ + for(int i=0;i<(int)superVtables.size();i++) + if (superVtables[i]) + delete [] (superVtables[i] - interfaceSlotSize - 1); + + superVtables.clear(); +} + bool CppiaClassInfo::isNativeProperty(const String &inString) { @@ -693,6 +728,8 @@ void CppiaClassInfo::deactivate() return; unloaded = true; + freeSuperVTables(); + if (vtable) { void **base = vtable - interfaceSlotSize - 1; @@ -1475,7 +1512,7 @@ Dynamic CppiaClassInfo::getStaticValue(const String &inName,hx::PropertyAccess return var.getStaticValue(); } - printf("Get static field not found (%s) %s\n", name.c_str(),inName.out_str()); + DBGLOG("Get static field not found (%s) %s\n", name.c_str(),inName.out_str()); return null(); }