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.cpp b/src/hx/cppia/Cppia.cpp index 2e7eb7c19..edaa5c84b 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()); } @@ -1979,6 +2036,18 @@ struct CallStatic : public CppiaExpr #ifdef CPPIA_JIT +static hx::Object *unloadedException = 0; +void genUnloadedSlotCheck(CppiaCompiler *compiler, const JitVal ®) +{ + if (!unloadedException) + unloadedException = sUnloadedMessage.makePermanentObject(); + + JumpId loaded = compiler->compare(cmpP_NOT_EQUAL, reg.as(jtPointer), (void *)0); + compiler->move( sJitCtx.star(jtPointer, offsetof(hx::StackContext,exception)), (void *)unloadedException ); + compiler->addThrow(); + compiler->comeFrom(loaded); +} + static hx::Object *nullException = 0; void genNullReferenceExceptionCheck(CppiaCompiler *compiler, const JitVal ®) { @@ -2176,6 +2245,7 @@ struct CallMemberVTable : public CppiaExpr ScriptCallable **vtable = (!isInterfaceCall ? (*(ScriptCallable ***)((char *)thisVal +scriptVTableOffset)) : (ScriptCallable **) thisVal->__GetScriptVTable()); \ unsigned char *pointer = ctx->pointer; \ ScriptCallable *func = vtable[slot]; \ + if (!func) hx::Throw( sUnloadedMessage ); \ func->pushArgs(ctx, thisVal, args); \ /* TODO */; \ AutoStack save(ctx,pointer); @@ -2262,6 +2332,8 @@ struct CallMemberVTable : public CppiaExpr // sJitTemp1 = table[slot] compiler->move(sJitTemp1, sJitTemp1.star(jtPointer, slot*sizeof(void *)) ); + genUnloadedSlotCheck(compiler, sJitTemp1); + // Implementation may return a more general type, with different byte representation if (checkInterfaceReturnType) { @@ -3356,11 +3428,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 28252bd15..1792f403f 100644 --- a/src/hx/cppia/Cppia.h +++ b/src/hx/cppia/Cppia.h @@ -152,6 +152,10 @@ extern int gArrayArgCount[]; typedef std::map CppiaStackVarMap; extern String sInvalidArgCount; +extern String sUnloadedMessage; + +void _hx_cppia_track_expr(void *inExpr); +void _hx_cppia_untrack_expr(void *inExpr); struct CppiaExpr { @@ -161,6 +165,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 +299,8 @@ struct ScriptCallable : public CppiaDynamicExpr int captureSize; + void deactivate(); + ScriptCallable(CppiaStream &stream); ScriptCallable(CppiaExpr *inBody); ScriptCallable(CppiaModule &inModule,ScriptNamedFunction *inFunction); @@ -372,7 +390,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 +405,12 @@ class CppiaModule ScriptCallable *main; + bool unloaded; + CppiaModule(); ~CppiaModule(); + void unload(); void link(); void compile(); void setDebug(CppiaExpr *outExpr, int inFileId, int inLine); @@ -650,7 +674,11 @@ class CppiaClassInfo bool containsPointers; int dynamicMapOffset; int interfaceSlotSize; + int vtableSlotCount; + int hostVTableSlots; + bool unloaded; void **vtable; + std::vector superVtables; std::string name; std::map interfaceScriptTables; std::vector nativeInterfaceFunctions; @@ -704,6 +732,7 @@ class CppiaClassInfo void link(); void linkTypes(); + void deactivate(); inline bool isNativeProperty(const String &inString); @@ -730,6 +759,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 5cfe6f171..c7e0166ee 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,9 @@ CppiaClassInfo::CppiaClassInfo(CppiaModule &inCppia) : cppia(inCppia) enumMeta = 0; isInterface = false; interfaceSlotSize = 0; + vtableSlotCount = 0; + hostVTableSlots = 0; + unloaded = false; superType = 0; typeId = 0; vtable = 0; @@ -363,6 +366,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 +381,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); @@ -404,6 +413,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) { @@ -678,6 +722,54 @@ 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; + + freeSuperVTables(); + + if (vtable) + { + 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 +1148,7 @@ void CppiaClassInfo::linkTypes() DBGLOG(" script member vars size = %d\n", extraData); - + for(int i=0;inameId)); @@ -1080,6 +1172,7 @@ void CppiaClassInfo::linkTypes() int vtableSlot = table.size(); + hostVTableSlots = vtableSlot; for(int i=0;ipointer; ctx->push( (hx::Object *) 0 ); // this AutoStack save(ctx,pointer); - + if (inPhase==0) { for(int i=0;ipushInt( (* (hx::Object **)(ctx->pointer))->__ToInt() ); } @@ -143,7 +149,7 @@ ScriptCallable::ScriptCallable(CppiaModule &inModule,ScriptNamedFunction *inFunc ScriptCallable::~ScriptCallable() { #ifdef CPPIA_JIT - if (compiled) + if (compiled && compiled != unloadedCompiled) CppiaCompiler::freeCompiled(compiled); #endif } @@ -681,6 +687,41 @@ void ScriptCallable::compile() } #endif +struct UnloadedBody : public CppiaExpr +{ + const char *getName() HXCPP_OVERRIDE { return "UnloadedBody"; } + + void runVoid(CppiaCtx *ctx) HXCPP_OVERRIDE + { + hx::Throw( sUnloadedMessage ); + } + + int runInt(CppiaCtx *ctx) HXCPP_OVERRIDE { runVoid(ctx); return 0; } + Float runFloat(CppiaCtx *ctx) HXCPP_OVERRIDE { runVoid(ctx); return 0; } + ::String runString(CppiaCtx *ctx) HXCPP_OVERRIDE { runVoid(ctx); return null(); } + hx::Object *runObject(CppiaCtx *ctx) HXCPP_OVERRIDE { runVoid(ctx); return 0; } +}; + +static UnloadedBody sUnloadedBody; + +#ifdef CPPIA_JIT +static void SLJIT_CALL unloadedCompiled(CppiaCtx *ctx) +{ + TRY_NATIVE + hx::Throw( sUnloadedMessage ); + CATCH_NATIVE +} +#endif + +void ScriptCallable::deactivate() +{ + body = &sUnloadedBody; + #ifdef CPPIA_JIT + compiled = unloadedCompiled; + #endif +} + + // --- CppiaClosure ---- diff --git a/src/hx/cppia/CppiaModule.cpp b/src/hx/cppia/CppiaModule.cpp index 303e0443f..c1875e166 100644 --- a/src/hx/cppia/CppiaModule.cpp +++ b/src/hx/cppia/CppiaModule.cpp @@ -16,6 +16,20 @@ int sgNativeNameSlotCount = 0; Array 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);