void BytecodeTranslatorVisitor::visitUnaryOpNode(UnaryOpNode* node) { onVisitNode(node); VISIT(node->operand()); switch (node->kind()) { case tNOT: { ensureTopType(VT_INT); /* if (!3.14) should fail */ Label L_True(bytecode()); Label L_End(bytecode()); EMIT(BC_ILOAD0); EMIT_BRANCH(BC_IFICMPE, L_True); EMIT(BC_ILOAD0); EMIT_BRANCH(BC_JA, L_End); EMIT_BIND(L_True); EMIT(BC_ILOAD1); EMIT_BIND(L_End); break; } case tSUB: ensureTopIsNumeric(); EMIT(TYPED(NEG)); break; default: ERROR("Unknown unary op"); } }
void BytecodeTranslatorVisitor::visitForNode(ForNode* node) { onVisitNode(node); const AstVar* i = node->var(); if (i->type() != VT_INT) ERROR("Non-iterable type in for loop"); const BinaryOpNode* expr = node->inExpr()->asBinaryOpNode(); if (expr == NULL || expr->kind() != tRANGE) ERROR("Invalid range in for loop"); CONTEXT(function(), locals(), node->body()->scope(), typeStack()); beforeProcessBlock(); bool needTempVar = !expr->right()->isIntLiteralNode(); AstVar* temp = NULL; if (needTempVar) { if (!scope()->declareVariable("<tempForEnd>", VT_INT)) ERROR("internal error: temp name is unavailable"); temp = scope()->lookupVariable("<tempForEnd>", false); } Label L_Begin(bytecode()); Label L_End(bytecode()); VISIT(expr->left()); EMIT_STORE(i); if (needTempVar) { VISIT(expr->right()); EMIT_STORE(temp); popType(VT_INT); } popType(VT_INT); EMIT_BIND(L_Begin); if (needTempVar) EMIT_LOAD(temp); else VISIT(expr->right()); EMIT_LOAD(i); EMIT_BRANCH(BC_IFICMPG, L_End); processBlockNode(node->body()); afterProcessBlock(); /* i += 1 */ EMIT_LOAD(i); EMIT(BC_ILOAD1); EMIT(BC_IADD); EMIT_STORE(i); EMIT_BRANCH(BC_JA, L_Begin); EMIT_BIND(L_End); pushType(VT_VOID); }
void BytecodeTranslatorVisitor::visitWhileNode(WhileNode* node) { onVisitNode(node); Label L_Begin(bytecode()); Label L_End(bytecode()); EMIT_BIND(L_Begin); falseJump(node->whileExpr(), L_End); VISIT(node->loopBlock()); EMIT_BRANCH(BC_JA, L_Begin); EMIT_BIND(L_End); }
void InterpreterCodelet::print_on(outputStream* st) const { if (PrintInterpreter) { st->cr(); st->print_cr("----------------------------------------------------------------------"); } if (description() != NULL) st->print("%s ", description()); if (bytecode() >= 0 ) st->print("%d %s ", bytecode(), Bytecodes::name(bytecode())); st->print_cr("[" INTPTR_FORMAT ", " INTPTR_FORMAT "] %d bytes", code_begin(), code_end(), code_size()); if (PrintInterpreter) { st->cr(); Disassembler::decode(code_begin(), code_end(), st); } }
void BaseBytecodeStream::assert_raw_index_size(int size) const { if (raw_code() == Bytecodes::_invokedynamic && is_raw()) { // in raw mode, pretend indy is "bJJ__" assert(size == 2, "raw invokedynamic instruction has 2-byte index only"); } else { bytecode()->assert_index_size(size, raw_code(), is_wide()); } }
void InterpreterCodelet::print_on(outputStream* st) const { ttyLocker ttyl; if (PrintInterpreter) { st->cr(); st->print_cr("----------------------------------------------------------------------"); } if (description() != NULL) st->print("%s ", description()); if (bytecode() >= 0 ) st->print("%d %s ", bytecode(), Bytecodes::name(bytecode())); st->print_cr("[" INTPTR_FORMAT ", " INTPTR_FORMAT "] %d bytes", p2i(code_begin()), p2i(code_end()), code_size()); if (PrintInterpreter) { st->cr(); Disassembler::decode(code_begin(), code_end(), st, DEBUG_ONLY(_strings) NOT_DEBUG(CodeStrings())); } }
void BytecodeTranslatorVisitor::visitIfNode(IfNode* node) { onVisitNode(node); Label L_End(bytecode()); if (!node->elseBlock()) { falseJump(node->ifExpr(), L_End); VISIT(node->thenBlock()); EMIT_BIND(L_End); } else { Label L_Else(bytecode()); falseJump(node->ifExpr(), L_Else); visitTyped(node->thenBlock(), VT_VOID); EMIT_BRANCH(BC_JA, L_End); EMIT_BIND(L_Else); VISIT(node->elseBlock()); EMIT_BIND(L_End); } }
void FunctionExecutable::generateJITCode(ExecState* exec, ScopeChainNode* scopeChainNode) { CodeBlock* codeBlock = &bytecode(exec, scopeChainNode); m_jitCode = JIT::compile(scopeChainNode->globalData, codeBlock); #if !ENABLE(OPCODE_SAMPLING) if (!BytecodeGenerator::dumpsGeneratedCode()) codeBlock->discardBytecode(); #endif }
void BytecodeTranslatorVisitor::processLazyLogic(BinaryOpNode* node) { Label L_Lazy(bytecode()); Label L_End(bytecode()); bool isOr = node->kind() == tOR; VISIT(node->left()); popType(VT_INT); EMIT(BC_ILOAD0); EMIT_BRANCH(isOr ? BC_IFICMPNE : BC_IFICMPE, L_Lazy); VISIT(node->right()); ensureTopType(VT_INT); EMIT_BRANCH(BC_JA, L_End); EMIT_BIND(L_Lazy); EMIT(isOr ? BC_ILOAD1 : BC_ILOAD0); EMIT_BIND(L_End); }
void ProgramExecutable::generateJITCode(ExecState* exec, ScopeChainNode* scopeChainNode) { #if ENABLE(INTERPRETER) ASSERT(scopeChainNode->globalData->canUseJIT()); #endif CodeBlock* codeBlock = &bytecode(exec, scopeChainNode); m_jitCode = JIT::compile(scopeChainNode->globalData, codeBlock); #if !ENABLE(OPCODE_SAMPLING) if (!BytecodeGenerator::dumpsGeneratedCode()) codeBlock->discardBytecode(); #endif }
void ConversionStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); assert(bytecode() == Bytecodes::_f2i || bytecode() == Bytecodes::_d2i, "other conversions do not require stub"); if (input()->is_single_xmm()) { __ comiss(input()->as_xmm_float_reg(), ExternalAddress((address)&float_zero)); } else if (input()->is_double_xmm()) { __ comisd(input()->as_xmm_double_reg(), ExternalAddress((address)&double_zero)); } else { LP64_ONLY(ShouldNotReachHere()); __ push(rax); __ ftst(); __ fnstsw_ax(); __ sahf(); __ pop(rax); } Label NaN, do_return; __ jccb(Assembler::parity, NaN); __ jccb(Assembler::below, do_return); // input is > 0 -> return maxInt // result register already contains 0x80000000, so subtracting 1 gives 0x7fffffff __ decrement(result()->as_register()); __ jmpb(do_return); // input is NaN -> return 0 __ bind(NaN); __ xorptr(result()->as_register(), result()->as_register()); __ bind(do_return); __ jmp(_continuation); }
void BytecodeTranslatorVisitor::processComparison(TokenKind kind) { castTopsToCommonType(); Instruction cmp = TYPED(CMP); int hack = topType() == VT_INT ? 3 : 0; popType(); popType(); pushType(VT_INT); if (kind == tNEQ) { EMIT(cmp); return; } if (!hack) { EMIT(cmp); EMIT(BC_ILOAD0); } Label L_Fail(bytecode()); Label L_End(bytecode()); switch (kind) { case tEQ: EMIT_BRANCH(BC_IFICMPE, L_Fail); break; case tGT: EMIT_BRANCH(BC_IFICMPG, L_Fail); break; case tGE: EMIT_BRANCH(BC_IFICMPGE, L_Fail); break; case tLT: EMIT_BRANCH(BC_IFICMPL, L_Fail); break; default : EMIT_BRANCH(BC_IFICMPLE, L_Fail); break; } EMIT(BC_ILOAD1 - hack); EMIT_BRANCH(BC_JA, L_End); EMIT_BIND(L_Fail); EMIT(BC_ILOAD0 + hack); EMIT_BIND(L_End); }
// Get 2-byte index (byte swapping depending on which bytecode) int get_index_u2(bool is_wide = false) const { return bytecode()->get_index_u2(cur_bc_raw(), is_wide); }
static int get_index_u2_cpcache(JavaThread *thread, Bytecodes::Code bc) { return bytecode(thread).get_index_u2_cpcache(bc); }
// Get 2-byte index in native byte order. (Rewriter::rewrite makes these.) int get_index_u2_cpcache() const { return bytecode()->get_index_u2_cpcache(cur_bc_raw()); }
// ============================================================================ // pxpByteCode // ============================================================================ pxpByteCode::pxpByteCode( pkgDecompiler* decompiler ) : Decompiler(decompiler) , CToken(NULL) , CTokenTree(NULL) , CTokenGroup(NULL) , CTokenGroupTree(NULL) , CTokenItem(NULL) , CTokenItemTree(NULL) , CTokenGroupCnd(NULL) , CTokenGroupCndTree(NULL) , unXmlParser() { // temp tree nodes unXmlParseTree bytecode ( wxT("bytecode") ); unXmlParseTree bgroup ( wxT("group") ); unXmlParseTree gname ( wxT("name") ); unXmlParseTree gmemo ( wxT("memo") ); unXmlParseTree gtoken ( wxT("token") ); unXmlParseTree tcode ( wxT("code") ); unXmlParseTree tname ( wxT("name") ); unXmlParseTree tdesc ( wxT("desc") ); unXmlParseTree tdata ( wxT("data") ); unXmlParseTree titem ( wxT("item") ); unXmlParseTree itype ( wxT("type") ); unXmlParseTree iname ( wxT("name") ); unXmlParseTree ttext ( wxT("text") ); unXmlParseTree gcond ( wxT("gcond") ); unXmlParseTree cif ( wxT("if") ); unXmlParseTree ceq ( wxT("eq") ); unXmlParseTree cthen ( wxT("then") ); unXmlParseTree cleft ( wxT("left") ); unXmlParseTree cright ( wxT("right") ); unXmlParseTree ctstream ( wxT("tstream") ); unXmlParseTree cnum ( wxT("num") ); unXmlParseTree nfunc ( wxT("nativefunctions") ); unXmlParseTree ffirst ( wxT("first") ); unXmlParseTree fextended ( wxT("extended") ); // token group - pre bgroup.AddCommand( new_xpObjCreate<pkgTokenGroup>(txpParseTree) ); bgroup.AddCommand( new_xpFunc1( this, &pxpByteCode::SetTokenGroup, txpTokenGroupObject ) ); bgroup.AddCommand( new_xpFunc1( this, &pxpByteCode::SetTokenGroupTree, txpParseTree ) ); gname.AddCommand( new_xpFunc1( txpTokenGroup, &pkgTokenGroup::SetName, txpNodeName(txpParseTree) ) ); gname.AddPostCommand( new_xpFunc1( Decompiler, &pkgDecompiler::AddTokenGroup, txpTokenGroup ) ); // token group - post bgroup.AddPostCommand( new_xpObjClear(txpParseTree) ); bgroup.AddPostCommand( new_xpFunc0( this, &pxpByteCode::ClearTokenGroup ) ); bgroup.AddPostCommand( new_xpFunc0( this, &pxpByteCode::ClearTokenGroupTree ) ); // gcond = pre gcond.AddCommand( new_xpObjCreate<pkgTokenCondition>(txpParseTree) ); gcond.AddCommand( new_xpFunc1( this, &pxpByteCode::SetTokenGroupCnd, txpTokenGroupCndObject ) ); gcond.AddCommand( new_xpFunc1( this, &pxpByteCode::SetTokenGroupCndTree, txpParseTree ) ); // gcond = post gcond.AddPostCommand( new_xpObjClear(txpParseTree) ); gcond.AddPostCommand( new_xpFunc0( this, &pxpByteCode::ClearTokenGroupCnd ) ); gcond.AddPostCommand( new_xpFunc0( this, &pxpByteCode::ClearTokenGroupCndTree ) ); // token - pre gtoken.AddCommand( new_xpObjCreate<dtToken>(txpParseTree) ); gtoken.AddCommand( new_xpFunc1( this, &pxpByteCode::SetToken, txpTokenObject ) ); gtoken.AddCommand( new_xpFunc1( this, &pxpByteCode::SetTokenTree, txpParseTree ) ); tcode.AddCommand( new_xpFunc1( txpTokenTreeObject, &dtToken::SetTokenData, txpNodeData(txpParseTree) ) ); tname.AddCommand( new_xpFunc1( txpTokenTreeObject, &dtToken::SetTokenName, txpNodeName(txpParseTree) ) ); tdesc.AddCommand( new_xpFunc1( txpTokenTreeObject, &dtToken::SetDesc, txpNodeName(txpParseTree) ) ); tdesc.AddCommand( new_xpFunc1( txpTokenGroup, &pkgTokenGroup::AddToken, txpToken ) ); tdesc.AddCommand( new_xpFunc2( Decompiler, &pkgDecompiler::AddToken, txpToken, txpTokenGroupCnd ) ); // token - post gtoken.AddPostCommand( new_xpFunc0( txpToken, &dtToken::DumpInfo ) ); gtoken.AddPostCommand( new_xpObjClear(txpParseTree) ); gtoken.AddPostCommand( new_xpFunc0( this, &pxpByteCode::ClearToken ) ); gtoken.AddPostCommand( new_xpFunc0( this, &pxpByteCode::ClearTokenTree ) ); // titem - pre titem.AddCommand( new_xpFunc1( this, &pxpByteCode::SetTokenItemTree, txpParseTree ) ); itype.AddCommand( new_xpObjFactory( txpTokenItemTree, &GDataTypeFactory, &pkgDataTypeFactory::Create, txpNodeName(txpParseTree) ) ); itype.AddCommand( new_xpFunc1( this, &pxpByteCode::SetTokenItem, txpTokenItemTreeObject ) ); iname.AddCommand( new_xpFunc1( txpTokenItem, &pkgDataType::SetDesc, txpNodeName(txpParseTree) ) ); // titem - post titem.AddPostCommand( new_xpFunc1( txpToken, &dtToken::AddItem, txpTokenItem ) ); titem.AddPostCommand( new_xpObjClear(txpParseTree) ); titem.AddPostCommand( new_xpFunc0( this, &pxpByteCode::ClearTokenItem ) ); titem.AddPostCommand( new_xpFunc0( this, &pxpByteCode::ClearTokenItemTree ) ); ffirst.AddCommand( new_xpFunc1( Decompiler, &pkgDecompiler::SetFunctionIdFirst, txpNodeData(txpParseTree) ) ); fextended.AddCommand( new_xpFunc1( Decompiler, &pkgDecompiler::SetFunctionIdExtended, txpNodeData(txpParseTree) ) ); // construct tree starting from leaves // ie: node on right side *cannot* appear anywhere below on left side // token gtoken.AddChild( tcode, pl::one ); gtoken.AddChild( tname, pl::one ); gtoken.AddChild( tdesc, pl::one ); titem.AddChild( itype, pl::one ); titem.AddChild( iname, pl::one ); tdata.AddChild( titem, pl::minone ); tdata.AddChild( ttext, pl::maxone ); gtoken.AddChild( tdata, pl::maxone ); // if cleft.AddChild( ctstream, pl::maxone ); cleft.AddChild( cnum, pl::maxone ); cright.AddChild( ctstream, pl::maxone ); cright.AddChild( cnum, pl::maxone ); ceq.AddChild( cleft, pl::one ); ceq.AddChild( cright, pl::one ); cif.AddChild( ceq, pl::one ); // then cthen.AddChild( gtoken, pl::any ); // group bgroup.AddChild( gname, pl::one ); bgroup.AddChild( gmemo, pl::any ); gcond.AddChild( cif, pl::one ); gcond.AddChild( cthen, pl::one ); bgroup.AddChild( gcond, pl::maxone ); bgroup.AddChild( gtoken, pl::any ); // native functions nfunc.AddChild( fextended, pl::one ); nfunc.AddChild( ffirst, pl::one ); // bytecode bytecode.AddChild( bgroup, pl::minone ); bytecode.AddChild( nfunc, pl::maxone ); ParseTree = new unXmlParseTree( bytecode ); }
// Get 4-byte index, for invokedynamic. int get_index_u4() const { return bytecode()->get_index_u4(cur_bc_raw()); }
// 4-byte branch offset from current pc int get_far_dest() const { return cur_bci() + bytecode()->get_offset_s4(cur_bc_raw()); }
int get_constant_u2(bool is_wide = false) const { return bytecode()->get_constant_u2(instruction_size()-2, cur_bc_raw(), is_wide); }
// Sign-extended index byte/short, no widening int get_constant_u1() const { return bytecode()->get_constant_u1(instruction_size()-1, cur_bc_raw()); }
bool has_index_u4() const { return bytecode()->has_index_u4(cur_bc_raw()); }
bool Test() { if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) { printf("Skipped due to AS_MAX_PORTABILITY\n"); return false; } bool fail = false; int r; COutStream out; asIScriptEngine *engine; asIScriptModule *mod; asIScriptContext *ctx; CBufferedOutStream bout; const char *script; // opEquals with funcdef // http://www.gamedev.net/topic/647797-difference-between-xopequalsy-and-xy-with-funcdefs/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void CALLBACK(); \n" "class Test { \n" " bool opEquals(CALLBACK @f) { \n" " return f is func; \n" " } \n" " CALLBACK @func; \n" "} \n" "void func() {} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test t; \n" "@t.func = func; \n" "assert( t == func );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void CALLBACK(); \n" "class Test { \n" " bool opEquals(CALLBACK @f) { \n" " return f is func; \n" " } \n" " CALLBACK @func; \n" "} \n" "namespace ns { \n" "void func() {} \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Test t; \n" "@t.func = ns::func; \n" "assert( t == ns::func );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test funcdefs and namespaces // http://www.gamedev.net/topic/644586-application-function-returning-a-funcdef-handle-crashes-when-called-in-as/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "bool called = false; \n" "funcdef void simpleFuncDef(); \n" "namespace foo { \n" " void simpleFunction() { called = true; } \n" "} \n" "void takeSimpleFuncDef(simpleFuncDef@ f) { f(); } \n" "void main() { \n" " takeSimpleFuncDef(foo::simpleFunction); \n" " assert( called ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; mod->AddScriptSection("test", "bool called = false; \n" "funcdef void simpleFuncDef();\n" "namespace foo {\n" " void simpleFunction() { called = true; }\n" "}\n" "void main() {\n" " simpleFuncDef@ bar = foo::simpleFunction;\n" " bar(); \n" " assert( called ); \n" "}\n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test registering global property of funcdef type // http://www.gamedev.net/topic/644586-application-function-returning-a-funcdef-handle-crashes-when-called-in-as/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); asIScriptFunction *f = 0; engine->RegisterFuncdef("void myfunc()"); r = engine->RegisterGlobalProperty("myfunc @f", &f); if( r < 0 ) TEST_FAILED; mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "void func() {} \n"); mod->Build(); r = ExecuteString(engine, "@f = func; \n", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( f == 0 ) TEST_FAILED; if( strcmp(f->GetName(), "func") != 0 ) TEST_FAILED; f->Release(); f = 0; engine->Release(); } // Test casting with funcdefs // http://www.gamedev.net/topic/644586-application-function-returning-a-funcdef-handle-crashes-when-called-in-as/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule("mod", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void myfunc1(); \n" "funcdef void myfunc2(); \n" "funcdef void myfunc3(); \n" "bool called = false; \n" "void func() { called = true; } \n" "void main() \n" "{ \n" " myfunc1 @f1 = func; \n" " myfunc2 @f2 = cast<myfunc2>(f1); \n" // explicit cast " myfunc3 @f3 = f2; \n" // implicit cast " assert( f1 is f2 ); \n" " assert( f2 is f3 ); \n" " assert( f3 is func ); \n" " f3(); \n" " assert( called ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Don't allow application to register additional behaviours to funcdefs // http://www.gamedev.net/topic/644586-application-function-returning-a-funcdef-handle-crashes-when-called-in-as/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; engine->RegisterObjectType("jjOBJ", 0, asOBJ_REF | asOBJ_NOCOUNT); engine->RegisterFuncdef("void jjBEHAVIOR(jjOBJ@)"); engine->RegisterFuncdef("void DifferentFunctionPointer()"); r = engine->RegisterObjectBehaviour("jjBEHAVIOR", asBEHAVE_IMPLICIT_REF_CAST, "DifferentFunctionPointer@ a()", asFUNCTION(0), asCALL_CDECL_OBJLAST); if( r >= 0 ) TEST_FAILED; if( bout.buffer != " (0, 0) : Error : Failed in call to function 'RegisterObjectBehaviour' with 'jjBEHAVIOR' and 'DifferentFunctionPointer@ a()' (Code: -12)\n" ) TEST_FAILED; engine->Release(); } // Test delegate function pointers for object methods { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); RegisterStdString(engine); mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "class Test { \n" " void method() {} \n" " int func(int) { return 0; } \n" " void func() { called = true; } \n" // The compiler should pick the right overload " bool called = false; \n" "} \n" "funcdef void CALLBACK(); \n" "void main() { \n" " Test t; \n" " CALLBACK @cb = CALLBACK(t.func); \n" // instanciate a delegate " cb(); \n" // invoke the delegate " assert( t.called ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Must be possible to save/load bytecode CBytecodeStream stream("test"); mod->SaveByteCode(&stream); mod = engine->GetModule("test2", asGM_ALWAYS_CREATE); r = mod->LoadByteCode(&stream); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Must be possible to create delegate from within class method, i.e. implicit this.method bout.buffer = ""; mod->AddScriptSection("test", "funcdef void CALL(); \n" "class Test { \n" " bool called = false; \n" " void callMe() { called = true; } \n" " CALL @GetCallback() { return CALL(callMe); } \n" "} \n" "void main() { \n" " Test t; \n" " CALL @cb = t.GetCallback(); \n" " cb(); \n" " assert( t.called ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // A delegate to own method held as member of class must be properly resolved by gc mod->AddScriptSection("test", "funcdef void CALL(); \n" "class Test { \n" " void call() {}; \n" " CALL @c; \n" "} \n" "void main() { \n" " Test t; \n" " t.c = CALL(t.call); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->GarbageCollect(); asUINT currSize, totalDestr, totalDetect; engine->GetGCStatistics(&currSize, &totalDestr, &totalDetect); r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->GarbageCollect(); asUINT currSize2, totalDestr2, totalDetect2; engine->GetGCStatistics(&currSize2, &totalDestr2, &totalDetect2); if( totalDetect2 == totalDetect ) TEST_FAILED; // Must be possible to call delegate from application mod->AddScriptSection("test", "funcdef void CALL(); \n" "class Test { \n" " bool called = false; \n" " void call() { called = true; } \n" "} \n" "Test t; \n" "CALL @callback = CALL(t.call); \n"); r = mod->Build(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; int idx = mod->GetGlobalVarIndexByDecl("CALL @callback"); if( idx < 0 ) TEST_FAILED; asIScriptFunction *callback = *(asIScriptFunction**)mod->GetAddressOfGlobalVar(idx); if( callback == 0 ) TEST_FAILED; if( callback->GetFuncType() != asFUNC_DELEGATE ) TEST_FAILED; if( callback->GetDelegateObject() == 0 ) TEST_FAILED; if( std::string(callback->GetDelegateFunction()->GetDeclaration()) != "void Test::call()" ) TEST_FAILED; ctx = engine->CreateContext(); ctx->Prepare(callback); r = ctx->Execute(); if( r != asEXECUTION_FINISHED ) TEST_FAILED; ctx->Release(); r = ExecuteString(engine, "assert( t.called );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Must be possible to create the delegate from the application asIScriptObject *obj = (asIScriptObject*)mod->GetAddressOfGlobalVar(mod->GetGlobalVarIndexByDecl("Test t")); asIScriptFunction *func = obj->GetObjectType()->GetMethodByName("call"); asIScriptFunction *delegate = engine->CreateDelegate(func, obj); if( delegate == 0 ) TEST_FAILED; delegate->Release(); // Must be possible to create delegate for registered type too mod->AddScriptSection("test", "funcdef bool EMPTY(); \n" "void main() { \n" " array<int> a; \n" " EMPTY @empty = EMPTY(a.isEmpty); \n" " assert( empty() == true ); \n" " a.insertLast(42); \n" " assert( empty() == false ); \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // Must not be possible to create delegate with const object and non-const method bout.buffer = ""; mod->AddScriptSection("test", "funcdef void F(); \n" "class Test { \n" " void f() {} \n" "} \n" "void main() { \n" " const Test @t; \n" " F @f = F(t.f); \n" // t is read-only, so this delegate must not be allowed "} \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; // TODO: Error message should be better, so it is understood that the error is because of const object if( bout.buffer != "test (5, 1) : Info : Compiling void main()\n" "test (7, 9) : Error : No matching signatures to 'void F()'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Must not be possible to create delegates for non-reference types bout.buffer = ""; mod->AddScriptSection("test", "funcdef bool CB(); \n" "string s; \n" "CB @cb = CB(s.isEmpty); \n"); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "test (3, 5) : Info : Compiling CB@ cb\n" "test (3, 10) : Error : Can't create delegate for types that do not support handles\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } engine->Release(); } // Test ordinary function pointers for global functions { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); // Test the declaration of new function signatures script = "funcdef void functype();\n" // It must be possible to declare variables of the funcdef type "functype @myFunc = null;\n" // It must be possible to initialize the function pointer directly "functype @myFunc1 = @func;\n" "void func() { called = true; }\n" "bool called = false;\n" // It must be possible to compare the function pointer with another "void main() { \n" " assert( myFunc1 !is null ); \n" " assert( myFunc1 is func ); \n" // It must be possible to call a function through the function pointer " myFunc1(); \n" " assert( called ); \n" // Local function pointer variables are also possible " functype @myFunc2 = @func;\n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r != 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to save the byte code with function handles CBytecodeStream bytecode(__FILE__"1"); mod->SaveByteCode(&bytecode); { asIScriptModule *mod2 = engine->GetModule("mod2", asGM_ALWAYS_CREATE); mod2->LoadByteCode(&bytecode); r = ExecuteString(engine, "main()", mod2); if( r != asEXECUTION_FINISHED ) TEST_FAILED; } // Test function pointers as members of classes. It should be possible to call the function // from within a class method. It should also be possible to call it from outside through the . operator. script = "funcdef void FUNC(); \n" "class CMyObj \n" "{ \n" " CMyObj() { @f = @func; } \n" " FUNC@ f; \n" " void test() \n" " { \n" " this.f(); \n" " f(); \n" " CMyObj o; \n" " o.f(); \n" " main(); \n" " assert( called == 4 ); \n" " } \n" "} \n" "void main() \n" "{ \n" " CMyObj o; \n" " o.f(); \n" "} \n" "int called = 0; \n" "void func() { called++; } \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "CMyObj o; o.test();", mod, ctx); if( r != asEXECUTION_FINISHED ) { TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); } ctx->Release(); // It must not be possible to declare a non-handle variable of the funcdef type engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; script = "funcdef void functype();\n" "functype myFunc;\n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (2, 1) : Error : Data type can't be 'functype'\n" "script (2, 10) : Info : Compiling functype myFunc\n" "script (2, 10) : Error : No default constructor for object of type 'functype'.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // It must not be possible to invoke the funcdef bout.buffer = ""; script = "funcdef void functype();\n" "void func() { functype(); } \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (2, 1) : Info : Compiling void func()\n" "script (2, 15) : Error : No matching signatures to 'functype()'\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // Test that a funcdef can't have the same name as other global entities bout.buffer = ""; script = "funcdef void test(); \n" "int test; \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; if( bout.buffer != "script (2, 5) : Error : Name conflict. 'test' is a funcdef.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // It is possible to take the address of class methods, but not to assign to funcdef variable bout.buffer = ""; script = "funcdef void F(); \n" "class t { \n" " void func() { \n" " @func; \n" // TODO: Should warn about expression that doesn't do anything " F @f = @func; \n" " } \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) TEST_FAILED; // TODO: The error message should be better if( bout.buffer != "script (3, 3) : Info : Compiling void t::func()\n" "script (5, 12) : Error : Can't implicitly convert from 't' to 'F@&'.\n" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } // A more complex sample bout.buffer = ""; script = "funcdef bool CALLBACK(int, int); \n" "funcdef bool CALLBACK2(CALLBACK @); \n" "void main() \n" "{ \n" " CALLBACK @func = @myCompare; \n" " CALLBACK2 @func2 = @test; \n" " func2(func); \n" "} \n" "bool test(CALLBACK @func) \n" "{ \n" " return func(1, 2); \n" "} \n" "bool myCompare(int a, int b) \n" "{ \n" " return a > b; \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; // It must be possible to register the function signature from the application r = engine->RegisterFuncdef("void AppCallback()"); if( r < 0 ) TEST_FAILED; r = engine->RegisterGlobalFunction("void ReceiveFuncPtr(AppCallback @)", asFUNCTION(ReceiveFuncPtr), asCALL_CDECL); assert( r >= 0 ); // It must be possible to use the registered funcdef // It must be possible to receive a function pointer for a registered func def bout.buffer = ""; script = "void main() \n" "{ \n" " AppCallback @func = @test; \n" " func(); \n" " ReceiveFuncPtr(func); \n" "} \n" "void test() \n" "{ \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( !receivedFuncPtrIsOK ) TEST_FAILED; mod->SaveByteCode(&bytecode); { receivedFuncPtrIsOK = false; asIScriptModule *mod2 = engine->GetModule("mod2", asGM_ALWAYS_CREATE); mod2->LoadByteCode(&bytecode); r = ExecuteString(engine, "main()", mod2); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( !receivedFuncPtrIsOK ) TEST_FAILED; } // The compiler should be able to determine the right function overload // by the destination of the function pointer bout.buffer = ""; mod->AddScriptSection("test", "funcdef void f(); \n" "f @fp = @func; \n" "bool called = false; \n" "void func() { called = true; } \n" "void func(int) {} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; if( bout.buffer != "" ) { printf("%s", bout.buffer.c_str()); TEST_FAILED; } r = ExecuteString(engine, "fp(); assert( called );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; //---------------------------------------------------------- // TODO: Future improvements below // If the function referred to when taking a function pointer is removed from the module, // the code must not be invalidated. After removing func() from the module, it must still // be possible to execute func2() script = "funcdef void FUNC(); \n" "void func() {} \n"; "void func2() { FUNC@ f = @func; f(); } \n"; // Test that the function in a function pointer isn't released while the function // is being executed, even though the function pointer itself is cleared script = "DYNFUNC@ funcPtr; \n" "funcdef void DYNFUNC(); \n" "@funcPtr = @CompileDynFunc('void func() { @funcPtr = null; }'); \n"; // Test that it is possible to declare the function signatures out of order // This also tests the circular reference between the function signatures script = "funcdef void f1(f2@) \n" "funcdef void f2(f1@) \n"; // It must be possible to identify a function handle type from the type id // It must be possible enumerate the function definitions in the module, // and to enumerate the parameters the function accepts // A funcdef defined in multiple modules must share the id and signature so that a function implemented // in one module can be called from another module by storing the handle in the funcdef variable // An interface that takes a funcdef as parameter must still have its typeid shared if the funcdef can also be shared // If the funcdef takes an interface as parameter, it must still be shared // Must have a generic function pointer that can store any signature. The function pointer // can then be dynamically cast to the correct function signature so that the function it points // to can be invoked. engine->Release(); } // Test function pointers with virtual property accessors // http://www.gamedev.net/topic/639243-funcdef-inside-shared-interface-interface-already-implement-warning/ { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("test", "funcdef void funcdef1( ifuncdef1_1& i ); \n" "shared interface ifuncdef1_1 \n" "{ \n" " ifuncdef1_2@ events { get; set; } \n" " void crashme(); \n" "} \n" "shared interface ifuncdef1_2 \n" "{ \n" " funcdef1@ f { get; set; } \n" "} \n" "class cfuncdef1_1 : ifuncdef1_1 \n" "{ \n" " ifuncdef1_2@ _events_; \n" " cfuncdef1_1() { @this._events_ = cfuncdef1_2(); } \n" " ifuncdef1_2@ get_events() { return( this._events_ ); } \n" " void set_events( ifuncdef1_2@ events ) { @this._events_ = events; } \n" " void crashme() \n" " { \n" " if( @this._events_ != null && @this._events_.f != null ) \n" " { \n" " this.events.f( this ); \n" // " this.get_events().get_f()( this ); \n" // This should produce the same bytecode as the above " } \n" " } \n" "} \n" "class cfuncdef1_2 : ifuncdef1_2 \n" "{ \n" " funcdef1@ ff; \n" " cfuncdef1_2() { @ff = null; } \n" " funcdef1@ get_f() { return( @this.ff ); } \n" " void set_f( funcdef1@ _f ) { @this.ff = _f; } \n" "} \n" "void start() \n" "{ \n" " ifuncdef1_1@ i = cfuncdef1_1(); \n" " i.events.f = end; \n" // TODO: Shouldn't this give an error? It's attempting to do an value assignment to a function pointer " i.crashme(); \n" "} \n" "bool called = false; \n" "void end( ifuncdef1_1& i ) \n" "{ \n" " called = true; \n" "} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "start(); assert( called );", mod, ctx); if( r != asEXECUTION_FINISHED ) TEST_FAILED; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx, true); ctx->Release(); CBytecodeStream stream(__FILE__"1"); r = mod->SaveByteCode(&stream); if( r < 0 ) TEST_FAILED; engine->Release(); // Load the bytecode engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream,Callback), &out, asCALL_THISCALL); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); stream.Restart(); mod = engine->GetModule("A", asGM_ALWAYS_CREATE); r = mod->LoadByteCode(&stream); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "start(); assert( called );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; ctx->Release(); stream.Restart(); mod = engine->GetModule("B", asGM_ALWAYS_CREATE); r = mod->LoadByteCode(&stream); if( r < 0 ) TEST_FAILED; ctx = engine->CreateContext(); r = ExecuteString(engine, "start(); assert( called );", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; ctx->Release(); engine->Release(); } // Test clean up with registered function definitions { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); RegisterScriptString(engine); r = engine->RegisterFuncdef("void MSG_NOTIFY_CB(const string& strCommand, const string& strTarget)"); assert(r>=0); engine->Release(); } // Test registering function pointer as property { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); r = engine->RegisterFuncdef("void fptr()"); r = engine->RegisterGlobalProperty("fptr f", 0); if( r >= 0 ) TEST_FAILED; engine->RegisterObjectType("obj", 0, asOBJ_REF); r = engine->RegisterObjectProperty("obj", "fptr f", 0); if( r >= 0 ) TEST_FAILED; engine->Release(); } // Test passing handle to function pointer { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "class CTempObj \n" "{ \n" " int Temp; \n" "} \n" "funcdef void FUNC2(CTempObj@);\n" "class CMyObj \n" "{ \n" " CMyObj() { @f2= @func2; }\n" " FUNC2@ f2; \n" "} \n" "void main() \n" "{ \n" " CMyObj o; \n" " CTempObj t; \n" " o.f2(t); \n" " assert( called == 1 ); \n" "} \n" "int called = 0; \n" "void func2(CTempObj@ Obj) \n" "{ called++; } \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // Test out of order declaration with function pointers { engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("script", "funcdef void FUNC2(CTempObj@);\n" "class CTempObj {} \n"); r = mod->Build(); if( r < 0 ) TEST_FAILED; engine->Release(); } // It must be possible calling system functions through pointers too { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterFuncdef("bool fun(bool)"); engine->RegisterGlobalFunction("bool assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); r = ExecuteString(engine, "fun @f = assert; f(true);"); if( r != asEXECUTION_FINISHED ) TEST_FAILED; engine->Release(); } // It should be possible to call functions through function pointers returned by an expression // http://www.gamedev.net/topic/627386-bug-with-parsing-of-callable-expressions/ { asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); RegisterScriptArray(engine, false); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); mod->AddScriptSection("Test", "funcdef void F(int); \n" "array<F@> arr(1); \n" "F@ g() \n" "{ \n" " return test; \n" "} \n" "void test(int a) \n" "{ \n" " assert(a == 42); \n" " called++; \n" "} \n" "int called = 0; \n" "void f() \n" "{ \n" " @arr[0] = test; \n" " arr[0](42); \n" " g()(42); \n" " F@ p; \n" " (@p = arr[0])(42); \n" " (@p = g())(42); \n" "} \n"); // engine->SetEngineProperty(asEP_OPTIMIZE_BYTECODE, false); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "f()", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; int idx = mod->GetGlobalVarIndexByName("called"); int *called = (int*)mod->GetAddressOfGlobalVar(idx); if( *called != 4 ) TEST_FAILED; engine->Release(); } // Global function pointers must not overload local class methods // Local variables take precedence over class methods // http://www.gamedev.net/topic/626746-function-call-operators-in-the-future/ { const char *script = "funcdef void FUNC(); \n" "FUNC @func; \n" "class Class \n" "{ \n" " void func() {} \n" " void method() \n" " { \n" " func(); \n" // Should call Class::func() " } \n" " void func2() {} \n" " void method2() \n" " { \n" " FUNC @func2; \n" " func2(); \n" // Should call variable func2 " } \n" "} \n"; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule("test", asGM_ALWAYS_CREATE); mod->AddScriptSection("test", script); r = mod->Build(); if( r < 0 ) TEST_FAILED; r = ExecuteString(engine, "Class c; c.method();", mod); if( r != asEXECUTION_FINISHED ) TEST_FAILED; r = ExecuteString(engine, "Class c; c.method2();", mod); if( r != asEXECUTION_EXCEPTION ) TEST_FAILED; engine->Release(); } // Success return fail; }
bool Test() { bool fail = false; int r; COutStream out; asIScriptEngine *engine; asIScriptModule *mod; CBufferedOutStream bout; const char *script; engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC); engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL); mod = engine->GetModule(0, asGM_ALWAYS_CREATE); // Test the declaration of new function signatures script = "funcdef void functype();\n" // It must be possible to declare variables of the funcdef type "functype @myFunc = null;\n" // It must be possible to initialize the function pointer directly "functype @myFunc1 = @func;\n" "void func() { called = true; }\n" "bool called = false;\n" // It must be possible to compare the function pointer with another "void main() { \n" " assert( myFunc1 !is null ); \n" " assert( myFunc1 is func ); \n" // It must be possible to call a function through the function pointer " myFunc1(); \n" " assert( called ); \n" // Local function pointer variables are also possible " functype @myFunc2 = @func;\n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r != 0 ) fail = true; r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) fail = true; // It must be possible to save the byte code with function handles CBytecodeStream bytecode(__FILE__"1"); mod->SaveByteCode(&bytecode); { asIScriptModule *mod2 = engine->GetModule("mod2", asGM_ALWAYS_CREATE); mod2->LoadByteCode(&bytecode); r = ExecuteString(engine, "main()", mod2); if( r != asEXECUTION_FINISHED ) fail = true; } // Test function pointers as members of classes. It should be possible to call the function // from within a class method. It should also be possible to call it from outside through the . operator. script = "funcdef void FUNC(); \n" "class CMyObj \n" "{ \n" " CMyObj() { @f = @func; } \n" " FUNC@ f; \n" " void test() \n" " { \n" " this.f(); \n" " f(); \n" " CMyObj o; \n" " o.f(); \n" " assert( called == 3 ); \n" " } \n" "} \n" "int called = 0; \n" "void func() { called++; } \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) fail = true; asIScriptContext *ctx = engine->CreateContext(); r = ExecuteString(engine, "CMyObj o; o.test();", mod, ctx); if( r != asEXECUTION_FINISHED ) { fail = true; if( r == asEXECUTION_EXCEPTION ) PrintException(ctx); } ctx->Release(); // It must not be possible to declare a non-handle variable of the funcdef type engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL); bout.buffer = ""; script = "funcdef void functype();\n" "functype myFunc;\n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (2, 1) : Error : Data type can't be 'functype'\n" "script (2, 10) : Error : No default constructor for object of type 'functype'.\n" ) { printf(bout.buffer.c_str()); fail = true; } // It must not be possible to invoke the funcdef bout.buffer = ""; script = "funcdef void functype();\n" "void func() { functype(); } \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (2, 1) : Info : Compiling void func()\n" "script (2, 15) : Error : No matching signatures to 'functype()'\n" ) { printf(bout.buffer.c_str()); fail = true; } // Test that a funcdef can't have the same name as other global entities bout.buffer = ""; script = "funcdef void test(); \n" "int test; \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (2, 5) : Error : Name conflict. 'test' is a funcdef.\n" ) { printf(bout.buffer.c_str()); fail = true; } // Must not be possible to take the address of class methods bout.buffer = ""; script = "class t { \n" " void func() { @func; } \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r >= 0 ) fail = true; if( bout.buffer != "script (2, 3) : Info : Compiling void t::func()\n" "script (2, 18) : Error : 'func' is not declared\n" ) { printf(bout.buffer.c_str()); fail = true; } // A more complex sample bout.buffer = ""; script = "funcdef bool CALLBACK(int, int); \n" "funcdef bool CALLBACK2(CALLBACK @); \n" "void main() \n" "{ \n" " CALLBACK @func = @myCompare; \n" " CALLBACK2 @func2 = @test; \n" " func2(func); \n" "} \n" "bool test(CALLBACK @func) \n" "{ \n" " return func(1, 2); \n" "} \n" "bool myCompare(int a, int b) \n" "{ \n" " return a > b; \n" "} \n"; mod->AddScriptSection("script", script); r = mod->Build(); if( r < 0 ) fail = true; if( bout.buffer != "" ) { printf(bout.buffer.c_str()); fail = true; } r = ExecuteString(engine, "main()", mod); if( r != asEXECUTION_FINISHED ) fail = true; //---------------------------------------------------------- // TODO: Future improvements below // If the function referred to when taking a function pointer is removed from the module, // the code must not be invalidated. After removing func() from the module, it must still // be possible to execute func2() script = "funcdef void FUNC(); \n" "void func() {} \n"; "void func2() { FUNC@ f = @func; f(); } \n"; // Test that the function in a function pointer isn't released while the function // is being executed, even though the function pointer itself is cleared script = "DYNFUNC@ funcPtr; \n" "funcdef void DYNFUNC(); \n" "@funcPtr = @CompileDynFunc('void func() { @funcPtr = null; }'); \n"; // The compiler should be able to determine the right function overload by the destination of the function pointer script = "funcdef void f(); \n" "f @fp = @func(); \n" "void func() {} \n" "void func(int) {} \n"; // Test that it is possible to declare the function signatures out of order // This also tests the circular reference between the function signatures script = "funcdef void f1(f2@) \n" "funcdef void f2(f1@) \n"; // It must be possible to register the function signature from the application // engine->RegisterFunctionSignature("void CALLBACK()"); // It must also be possible to enumerate the registered callbacks // It must be possible to identify a function handle type from the type id // It must be possible enumerate the function definitions in the module, // and to enumerate the parameters the function accepts // A funcdef defined in multiple modules must share the id and signature so that a function implemented // in one module can be called from another module by storing the handle in the funcdef variable // An interface that takes a funcdef as parameter must still have its typeid shared if the funcdef can also be shared // If the funcdef takes an interface as parameter, it must still be shared // Must have a generic function pointer that can store any signature. The function pointer // can then be dynamically cast to the correct function signature so that the function it points // to can be invoked. engine->Release(); // Success return fail; }
BytecodePtr Bytecode::make(const std::string& source) { BytecodePtr bytecode(new Bytecode); bytecode->generateBytecode(source); return bytecode; }
static int get_index_u2(JavaThread *thread, Bytecodes::Code bc) { return bytecode(thread)->get_index_u2(bc); }