Пример #1
0
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");
    }
}
Пример #2
0
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);
}
Пример #3
0
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);
}
Пример #4
0
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);
  }
}
Пример #5
0
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());
  }
}
Пример #6
0
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()));
  }
}
Пример #7
0
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);
    }
}
Пример #8
0
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
}
Пример #9
0
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);
}
Пример #10
0
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
}
Пример #11
0
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);
}
Пример #12
0
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);
}
Пример #13
0
 // 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);
 }
Пример #14
0
 static int       get_index_u2_cpcache(JavaThread *thread, Bytecodes::Code bc)
                                                       { return bytecode(thread).get_index_u2_cpcache(bc); }
Пример #15
0
 // 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 );
}
Пример #17
0
 // Get 4-byte index, for invokedynamic.
 int get_index_u4() const {
   return bytecode()->get_index_u4(cur_bc_raw());
 }
Пример #18
0
 // 4-byte branch offset from current pc
 int get_far_dest() const {
   return cur_bci() + bytecode()->get_offset_s4(cur_bc_raw());
 }
Пример #19
0
 int get_constant_u2(bool is_wide = false) const { return bytecode()->get_constant_u2(instruction_size()-2, cur_bc_raw(), is_wide); }
Пример #20
0
 // Sign-extended index byte/short, no widening
 int get_constant_u1()                     const { return bytecode()->get_constant_u1(instruction_size()-1, cur_bc_raw()); }
Пример #21
0
 bool has_index_u4() const {
   return bytecode()->has_index_u4(cur_bc_raw());
 }
Пример #22
0
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;
}
Пример #23
0
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;
}
Пример #24
0
BytecodePtr Bytecode::make(const std::string& source) {
    BytecodePtr bytecode(new Bytecode);
    bytecode->generateBytecode(source);
    return bytecode;
}
Пример #25
0
 static int       get_index_u2(JavaThread *thread, Bytecodes::Code bc)
                                                       { return bytecode(thread)->get_index_u2(bc); }