#include #include "Cppia.h" #include "CppiaStream.h" namespace hx { static int sTypeSize[] = { 0, 0, sizeof(hx::Object *), sizeof(String), sizeof(Float), sizeof(int) }; // --- ScriptCallable ---------------------------------------------------- String sInvalidArgCount = HX_CSTRING("Invalid arguement count"); #ifdef CPPIA_JIT void SLJIT_CALL argToInt(CppiaCtx *ctx) { ctx->pushInt( (* (hx::Object **)(ctx->pointer))->__ToInt() ); } void SLJIT_CALL argToDouble(CppiaCtx *ctx) { ctx->pushFloat( (* (hx::Object **)(ctx->pointer))->__ToDouble() ); } void SLJIT_CALL argToString(CppiaCtx *ctx) { ctx->pushString( (* (hx::Object **)(ctx->pointer))->__ToString() ); } #endif ScriptCallable::ScriptCallable(CppiaStream &stream) { body = 0; stackSize = 0; data = 0; returnTypeId = stream.getInt(); returnType = etVoid; argCount = stream.getInt(); args.resize(argCount); hasDefault.resize(argCount); initVals.resize(argCount); captureSize = 0; hasDefaults = false; #ifdef CPPIA_JIT compiled = 0; #endif for(int a=0;asignature; int argCount = strlen(signature)-1; if (argCount<0) throw "ScriptNamedFunction: Invalid arg count"; args.resize(argCount); hasDefault.resize(argCount); initVals.resize(argCount); for(int i=0;iexecute); #ifdef CPPIA_JIT // magically already compiled for us compiled = inFunction->execute; #endif } ScriptCallable::~ScriptCallable() { #ifdef CPPIA_JIT if (compiled) CppiaCompiler::freeCompiled(compiled); #endif } CppiaExpr *ScriptCallable::link(CppiaModule &inModule) { StackLayout *oldLayout = inModule.layout; StackLayout layout(oldLayout); inModule.layout = &layout; data = &inModule; returnType = inModule.types[ returnTypeId ]->expressionType; layout.returnType = returnType; for(int a=0;alink(inModule); captureVars.swap(layout.captureVars); captureSize = layout.captureSize; #ifdef HXCPP_STACK_SCRIPTABLE std::swap(varMap,layout.varMap); position.scriptCallable = this; #endif stackSize = layout.size; inModule.layout = oldLayout; position.className = className; position.functionName = functionName; position.fileName = filename; position.fullName = filename; #ifdef HXCPP_DEBUGGER position.fileHash = Hash(0,filename); int hash = Hash(0,className); hash = Hash(hash,"."); position.classFuncHash = Hash(hash,functionName); #endif #ifdef CPPIA_JIT //printf("Compile?\n"); #endif return this; } #ifdef HXCPP_STACK_SCRIPTABLE void ScriptCallable::getScriptableVariables(unsigned char *inStack, Array outNames) { unsigned char *frame = inStack - stackSize; hx::Object *thizz = *(hx::Object **)frame; if (thizz) outNames->push(HX_CSTRING("this")); for(CppiaStackVarMap::iterator i=varMap.begin();i!=varMap.end();++i) { CppiaStackVar *var = i->second; outNames->push(var->module->strings[ var->nameId] ); } } bool ScriptCallable::getScriptableValue(unsigned char *inStack, String inName, ::Dynamic &outValue) { unsigned char *frame = inStack - stackSize; if (inName == HX_CSTRING("this")) { outValue = *(hx::Object **)frame; return true; } for(CppiaStackVarMap::iterator i=varMap.begin();i!=varMap.end();++i) { CppiaStackVar *var = i->second; if ( var->module->strings[ var->nameId]==inName) { outValue = var->getInFrame(frame); return true; } } return false; } bool ScriptCallable::setScriptableValue(unsigned char *inStack, String inName, ::Dynamic inValue) { unsigned char *frame = inStack - stackSize; for(CppiaStackVarMap::iterator i=varMap.begin();i!=varMap.end();++i) { CppiaStackVar *var = i->second; if ( var->module->strings[ var->nameId]==inName) { var->setInFrame(frame,inValue); return true; } } return false; } #endif int ScriptCallable::Hash(int value, const char *inString) { if (inString) while(*inString) value = value*223 + *inString++; return value; } #ifdef CPPIA_JIT int SLJIT_CALL objectToInt(hx::Object *obj) { return obj->__ToInt(); } void SLJIT_CALL frameToDouble(CppiaCtx *inCtx) { inCtx->returnFloat( inCtx->getObject() ); } //void objectToInt(CppiaCtx *inCtx) { inCtx->returnInt( inCtx->getObject() ); } void SLJIT_CALL objectToDouble(CppiaCtx *inCtx) { inCtx->returnFloat( inCtx->getObject() ); } void SLJIT_CALL objectToDoublePointer(CppiaCtx *inCtx) { *(double *)inCtx->pointer = (*(hx::Object **)inCtx->pointer)->__ToDouble(); } void SLJIT_CALL objectToStringPointer(CppiaCtx *inCtx) { *(String *)inCtx->pointer = (*(hx::Object **)inCtx->pointer)->toString(); } void SLJIT_CALL objectToString(CppiaCtx *inCtx) { inCtx->returnString( inCtx->getObject() ); } void SLJIT_CALL stringToObject(CppiaCtx *inCtx) { inCtx->returnObject( inCtx->getString() ); } void SLJIT_CALL intToObject(CppiaCtx *inCtx) { inCtx->returnObject( inCtx->getInt() ); } void SLJIT_CALL doubleToObject(CppiaCtx *inCtx) { inCtx->returnObject( inCtx->getFloat() ); } void ScriptCallable::genArgs(CppiaCompiler *compiler, CppiaExpr *inThis, Expressions &inArgs, const JitVal &inThisVal) { int inCount = inArgs.size(); bool badCount = argCountgetCurrentFrameSize(); // Push this ... if (inThis) { inThis->genCode(compiler, JitFramePos(framePos), etObject); } else if (inThisVal.valid()) { compiler->move( JitFramePos(framePos), inThisVal ); } else { compiler->move( JitFramePos(framePos), JitVal( (void *)0 )); } compiler->addFrame(etObject); for(int a=0;a=inCount) { int framePos = compiler->getCurrentFrameSize(); switch(var.argType) { case etInt: compiler->move( JitFramePos(framePos).as(jtInt), 0 ); break; case etObject: compiler->move( JitFramePos(framePos).as(jtPointer), (void *)0 ); break; case etFloat: compiler->move( JitFramePos(framePos).as(jtInt), 0 ); compiler->move( JitFramePos(framePos).as(jtInt) + 4, 0 ); break; case etString: compiler->move( JitFramePos(framePos).as(jtInt), 0 ); compiler->move( JitFramePos(framePos).as(jtPointer) + StringOffset::Ptr, (void *)0 ); break; default: ; } compiler->addFrame(var.argType); } else { int framePos = compiler->getCurrentFrameSize(); inArgs[a]->genCode(compiler, JitFramePos(framePos).as( getJitType(var.argType) ), var.argType); } compiler->addFrame(var.argType); } } void ScriptCallable::genDefaults(CppiaCompiler *compiler) { if (hasDefaults) { for(int i=0;ipush( inThis ); for(int a=0;a=inCount) { pushDefault(ctx,a); } else { switch(var.argType) { case etInt: ctx->pushInt(inArgs[a]->runInt(ctx)); break; case etFloat: ctx->pushFloat(inArgs[a]->runFloat(ctx)); break; case etString: ctx->pushString(inArgs[a]->runString(ctx)); break; default: ctx->pushObject(inArgs[a]->runObject(ctx)); } BCR_VCHECK; } } } void ScriptCallable::pushArgsDynamic(CppiaCtx *ctx, hx::Object *inThis, Array &inArgs) { BCR_VCHECK; int inCount = inArgs==null() ? 0 : inArgs->length; ctx->push( inThis ); for(int a=0;apushInt(inArgs[a]); break; case etFloat: ctx->pushFloat(inArgs[a]); break; case etString: ctx->pushString(inArgs[a]); break; default: ctx->pushObject(inArgs[a].mPtr); } BCR_VCHECK; } else { pushDefault(ctx,a); } } } // Return the closure hx::Object *ScriptCallable::runObject(CppiaCtx *ctx) { return createClosure(ctx,this); } const char *ScriptCallable::getName() { return "ScriptCallable"; } String ScriptCallable::runString(CppiaCtx *ctx) { return HX_CSTRING("#function"); } void ScriptCallable::runVoid(CppiaCtx *ctx) { } #ifdef CPPIA_JIT struct AutoFrame { StackContext *ctx; unsigned char *frame; AutoFrame(StackContext *inCtx) : ctx(inCtx) { frame = ctx->frame; } ~AutoFrame() { ctx->frame = frame; } }; #endif // Run the actual function void ScriptCallable::runFunction(CppiaCtx *ctx) { #ifdef CPPIA_JIT if (compiled) { AutoFrame frame(ctx); //printf("Running compiled code...\n"); compiled(ctx); //printf("Done.\n"); } else #endif { if (stackSize) { memset(ctx->pointer, 0 , stackSize ); ctx->pointer += stackSize; } if (hasDefaults) { for(int i=0;irunVoid(ctx); } } // Run the actual function - like runFunction, but stack may already contain values void ScriptCallable::runFunctionClosure(CppiaCtx *ctx) { #ifdef CPPIA_JIT if (compiled) { AutoFrame frame(ctx); //printf("Running compiled code...\n"); compiled(ctx); //printf("Done.\n"); } else #endif { if (hasDefaults) { for(int i=0;irunVoid(ctx); } } void ScriptCallable::addStackVarsSpace(CppiaCtx *ctx) { if (stackSize) { memset(ctx->pointer, 0 , stackSize ); ctx->pointer += stackSize; } } #ifdef CPPIA_JIT void ScriptCallable::genCode(CppiaCompiler *compiler,const JitVal &inDest,ExprType destType) { compiler->move(sJitCtxFrame, sJitFrame); compiler->callNative( (void *)createClosure, sJitCtx, (void *)this ); compiler->convertReturnReg(etObject, inDest, destType); } #endif bool ScriptCallable::pushDefault(CppiaCtx *ctx,int arg) { switch(args[arg].argType) { case etInt: ctx->pushInt(0); break; case etFloat: ctx->pushFloat(0); break; case etString: ctx->pushString( String() ); break; default: ctx->pushObject(null()); } return true; } void ScriptCallable::addExtraDefaults(CppiaCtx *ctx,int inHave) { if (inHave>argCount) return; for(int a=inHave;apushFrame(inFrame); } static void SLJIT_CALL popFrame(StackContext *inCtx, StackFrame *inFrame) { inCtx->popFrame(inFrame); } static void onReturn( CppiaCompiler *inCompiler, int stackSize ) { inCompiler->add(sJitArg1.as(jtPointer), sJitFrame.as(jtPointer), stackSize ); inCompiler->callNative( (void *)popFrame, sJitCtx.as(jtPointer), sJitArg1.as(jtPointer)); } #endif void ScriptCallable::compile() { if (!compiled && body) { int size = stackSize; #ifdef HXCPP_STACK_TRACE size += sizeof(StackFrame); #endif CppiaCompiler *compiler = CppiaCompiler::create(size); // First pass calculates size... genDefaults(compiler); #ifdef HXCPP_STACK_TRACE compiler->move(sJitFrame.star(jtPointer)+(offsetof(StackFrame,position) + stackSize), (void *)&position ); compiler->add(sJitArg1.as(jtPointer), sJitFrame.as(jtPointer), stackSize ); compiler->callNative( (void *)pushFrame, sJitCtx.as(jtPointer), sJitArg1.as(jtPointer) ); compiler->setOnReturn( onReturn, stackSize ); #ifdef HXCPP_STACK_LINE compiler->setLineOffset( stackSize + offsetof(StackFrame,lineNumber) ); #endif #endif body->genCode(compiler); compiler->beginGeneration(1); // Second pass does the job genDefaults(compiler); #ifdef HXCPP_STACK_TRACE compiler->move(sJitFrame.star(jtPointer)+(offsetof(StackFrame,position) + stackSize), (void *)&position ); compiler->add(sJitArg1.as(jtPointer), sJitFrame.as(jtPointer), stackSize ); compiler->callNative( (void *)pushFrame, sJitCtx.as(jtPointer), sJitArg1.as(jtPointer) ); #endif body->genCode(compiler); compiled = compiler->finishGeneration(); delete compiler; } } #endif // --- CppiaClosure ---- class CppiaClosure : public hx::Object { public: inline void *operator new( size_t inSize, int inExtraDataSize ) { return hx::InternalNew(inSize + inExtraDataSize,true); } inline void operator delete(void *,int) {} ScriptCallable *function; CppiaClosure(CppiaCtx *ctx, ScriptCallable *inFunction) { function = inFunction; unsigned char *base = ((unsigned char *)this) + sizeof(CppiaClosure); *(hx::Object **)base = ctx->getThis(false); for(int i=0;icaptureVars.size();i++) { CppiaStackVar *var = function->captureVars[i]; int size = sTypeSize[var->expressionType]; memcpy( base+var->capturePos, ctx->frame + var->fromStackPos, size ); } } hx::Object **getThis() const { unsigned char *base = ((unsigned char *)this) + sizeof(CppiaClosure); return (hx::Object **)base; } // Create member closure... CppiaClosure(hx::Object *inThis, ScriptCallable *inFunction) { function = inFunction; *getThis() = inThis; } Dynamic doRun(CppiaCtx *ctx, int inHaveArgs) { function->addExtraDefaults(ctx,inHaveArgs); function->addStackVarsSpace(ctx); unsigned char *base = ((unsigned char *)this) + sizeof(CppiaClosure); // this pointer... *(hx::Object **)ctx->frame = *(hx::Object **)base; for(int i=0;icaptureVars.size();i++) { CppiaStackVar *var = function->captureVars[i]; int size = sTypeSize[var->expressionType]; memcpy( ctx->frame+var->stackPos, base + var->capturePos, size); } #ifdef CPPIA_JIT if (function->compiled) { { AutoFrame frame(ctx); function->compiled(ctx); } if (!ctx->exception) { switch(function->returnType) { case etFloat: return ctx->getFloat(); case etInt: return ctx->getInt(); case etString: return ctx->getString(); case etObject: return ctx->getObject(); default: ; } } return null(); } //printf("Not compiled %d!\n", function->captureVars.size()); //todo - compiled? #endif // TODO - stack var space added twice? function->runFunctionClosure(ctx); ctx->breakContReturn = 0; switch(function->returnType) { case etFloat: return ctx->getFloat( ); case etInt: return ctx->getInt( ); case etString: return ctx->getString( ); case etObject: return ctx->getObject( ); default: break; } return null(); } void pushArgDynamic(CppiaCtx *ctx, int a, Dynamic inValue) { // Developer has used dynamic to call a closure with wrong # parameters if (a>=function->args.size()) return; if (!inValue.mPtr) { function->pushDefault(ctx,a); return; } switch(function->args[a].argType) { case etInt: ctx->pushInt(inValue->__ToInt()); return; case etFloat: ctx->pushFloat(inValue->__ToDouble()); break; case etString: ctx->pushString(inValue.mPtr ? inValue->toString() : String()); break; default: { hx::Object *value = inValue.mPtr; if (value) { ArrayType want = function->args[a].type->arrayType; if (want!=arrNotArray) value = DynamicToArrayType(value, want); } ctx->pushObject(value); } } } Dynamic __Run(const Array &inArgs) { CppiaCtx *ctx = CppiaCtx::getCurrent(); AutoStack a(ctx); ctx->pointer += sizeof(hx::Object *); int haveArgs = !inArgs.mPtr ? 0 : inArgs->length; if (haveArgs>function->argCount) throw sInvalidArgCount; for(int a=0; apointer += sizeof(hx::Object *); return doRun(ctx,0); } Dynamic __run(D a) { CppiaCtx *ctx = CppiaCtx::getCurrent(); AutoStack aut(ctx); ctx->pointer += sizeof(hx::Object *); pushArgDynamic(ctx,0,a); return doRun(ctx,1); } Dynamic __run(D a,D b) { CppiaCtx *ctx = CppiaCtx::getCurrent(); AutoStack aut(ctx); ctx->pointer += sizeof(hx::Object *); pushArgDynamic(ctx,0,a); pushArgDynamic(ctx,1,b); return doRun(ctx,2); } Dynamic __run(D a,D b,D c) { CppiaCtx *ctx = CppiaCtx::getCurrent(); AutoStack aut(ctx); ctx->pointer += sizeof(hx::Object *); pushArgDynamic(ctx,0,a); pushArgDynamic(ctx,1,b); pushArgDynamic(ctx,2,c); return doRun(ctx,3); } Dynamic __run(D a,D b,D c,D d) { CppiaCtx *ctx = CppiaCtx::getCurrent(); AutoStack aut(ctx); ctx->pointer += sizeof(hx::Object *); pushArgDynamic(ctx,0,a); pushArgDynamic(ctx,1,b); pushArgDynamic(ctx,2,c); pushArgDynamic(ctx,3,d); return doRun(ctx,4); } Dynamic __run(D a,D b,D c,D d,D e) { CppiaCtx *ctx = CppiaCtx::getCurrent(); AutoStack aut(ctx); ctx->pointer += sizeof(hx::Object *); pushArgDynamic(ctx,0,a); pushArgDynamic(ctx,1,b); pushArgDynamic(ctx,2,c); pushArgDynamic(ctx,3,d); pushArgDynamic(ctx,4,e); return doRun(ctx,5); } void __Mark(hx::MarkContext *__inCtx) { HX_MARK_MEMBER(*getThis()); char *base = ((char *)this) + sizeof(CppiaClosure); for(int i=0;icaptureVars.size();i++) function->captureVars[i]->markClosure(base,__inCtx); } #ifdef HXCPP_VISIT_ALLOCS void __Visit(hx::VisitContext *__inCtx) { HX_VISIT_MEMBER(*getThis()); char *base = ((char *)this) + sizeof(CppiaClosure); for(int i=0;icaptureVars.size();i++) function->captureVars[i]->visitClosure(base,__inCtx); } #endif virtual void *__GetHandle() const { return *getThis(); } int __Compare(const hx::Object *inRHS) const { const CppiaClosure *other = dynamic_cast(inRHS); if (!other) return -1; return (function==other->function && *getThis()==*other->getThis())? 0 : -1; } int __GetType() const { return vtFunction; } int __ArgCount() const { return function->args.size(); } String toString() { return HX_CSTRING("function"); } }; hx::Object * CPPIA_CALL createClosure(CppiaCtx *ctx, ScriptCallable *inFunction) { return new (inFunction->captureSize) CppiaClosure(ctx,inFunction); } hx::Object *createMemberClosure(hx::Object *inThis, ScriptCallable *inFunction) { return new (sizeof(hx::Object *)) CppiaClosure(inThis,inFunction); } // --- CppiaFunction ---- CppiaFunction::CppiaFunction(CppiaModule *inCppia,bool inIsStatic,bool inIsDynamic) : cppia(*inCppia), isStatic(inIsStatic), isDynamic(inIsDynamic), funExpr(0) { linked = false; vtableSlot = -1; } void CppiaFunction::load(CppiaStream &stream,bool inExpectBody) { nameId = stream.getInt(); name = cppia.strings[ nameId ].utf8_str(); stream.module->creatingFunction = name.c_str(); returnType = stream.getInt(); argCount = stream.getInt(); DBGLOG(" Function %s(%d) : %s %s%s\n", name.c_str(), argCount, cppia.typeStr(returnType), isStatic?"static":"instance", isDynamic ? " DYNAMIC": ""); args.resize(argCount); for(int a=0;acreatingFunction = 0; } void CppiaFunction::link( ) { if (!linked) { linked = true; if (funExpr) funExpr = (ScriptCallable *)(funExpr->link(cppia)); } } #ifdef CPPIA_JIT void CppiaFunction::compile() { if (funExpr) funExpr->compile(); } #endif }