X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=7d5404c07994fd553ce5f52a3309134faecbb72d;hb=cad2e08956148922b5a8b8bff8c8dbee884c854b;hp=89d80ecb15e22e33dc9b11b80defa4157c087a20;hpb=6ead8227212dc074744a3a4282a6a572d120fecc;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index 89d80ec..7d5404c 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -80,13 +80,16 @@ %token KW_NEW "new" %token KW_NATIVE %token KW_FUNCTION "function" +%token KW_UNDEFINED "undefined" %token KW_FOR "for" %token KW_CLASS "class" %token KW_CONST "const" %token KW_SET "set" +%token KW_VOID "void" %token KW_STATIC %token KW_IMPORT "import" %token KW_RETURN "return" +%token KW_TYPEOF "typeof" %token KW_INTERFACE "interface" %token KW_NULL "null" %token KW_VAR "var" @@ -94,6 +97,7 @@ %token KW_OVERRIDE %token KW_FINAL %token KW_GET "get" +%token KW_SUPER "super" %token KW_EXTENDS %token KW_FALSE "false" %token KW_TRUE "true" @@ -103,6 +107,7 @@ %token KW_WHILE "while" %token KW_NUMBER "Number" %token KW_STRING "String" +%token KW_DELETE "delete" %token KW_IF "if" %token KW_ELSE "else" %token KW_BREAK "break" @@ -112,6 +117,7 @@ %token T_EQEQ "==" %token T_EQEQEQ "===" %token T_NE "!=" +%token T_NEE "!==" %token T_LE "<=" %token T_GE ">=" %token T_DIVBY "/=" @@ -132,9 +138,6 @@ %token T_SHL "<<" %token T_USHR ">>>" %token T_SHR ">>" -%token T_SEMICOLON ';' -%token T_STAR '*' -%token T_DOT '.' %type X_IDENTIFIER PACKAGE %type VARCONST @@ -150,7 +153,7 @@ %type VOIDEXPRESSION %type EXPRESSION NONCOMMAEXPRESSION %type MAYBEEXPRESSION -%type E +%type E DELETE %type CONSTANT %type FOR IF WHILE MAYBEELSE BREAK RETURN %type USE_NAMESPACE @@ -180,45 +183,39 @@ %type MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES // precedence: from low to high -// http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000012.html %left prec_none + +%left below_semicolon +%left ';' +%left ',' +%nonassoc below_assignment // for ?:, contrary to spec +%right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|=" %right '?' ':' -%right '=' "/=" "%=" "*=" "+=" "-=" ">>=" "<<=" ">>>=" %left "||" %left "&&" %nonassoc '|' %nonassoc '^' %nonassoc '&' -%nonassoc "!=" "==" "===" "<=" '<' ">=" '>' // TODO: support "a < b < c" syntax? -%nonassoc "is" -%left prec_belowminus -%left '-' -%left '+' -%left "<<" -%left ">>>" -%left ">>" -%left '%' -%left '/' -%left '*' -%left '!' -%left '~' -%left "--" "++" -%left '[' -%nonassoc "as" -%left '.' ".." "::" +%nonassoc "==" "!=" "===" "!==" +%nonassoc "is" "as" +%nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax? +%left "<<" ">>" ">>>" +%left below_minus +%left '-' '+' +%left '/' '*' '%' +%left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too +%left "--" "++" +%left '[' ']' '{' "new" '.' ".." "::" %nonassoc T_IDENTIFIER -%left below_semicolon -%left ';' +%left below_else %nonassoc "else" %left '(' // needed for "return" precedence: %nonassoc T_STRING T_REGEXP %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT -%nonassoc "new" "false" "true" "null" - -%left prec_highest +%nonassoc "false" "true" "null" "undefined" "super" %{ @@ -246,37 +243,43 @@ typedef struct _import { DECLARE_LIST(import); -typedef struct _state { - abc_file_t*file; - abc_script_t*init; - - int level; +typedef struct _classstate { + /* class data */ + classinfo_t*info; + abc_class_t*abc; + code_t*init; + code_t*static_init; +} classstate_t; - char*package; - char*function; +typedef struct _methodstate { + /* method data */ + memberinfo_t*info; + char late_binding; /* code that needs to be executed at the start of a method (like initializing local registers) */ code_t*initcode; + char is_constructor; + char has_super; +} methodstate_t; + +typedef struct _state { + int level; + char*package; import_list_t*wildcard_imports; dict_t*imports; char has_own_imports; - - /* class data */ - classinfo_t*clsinfo; - abc_class_t*cls; - code_t*cls_init; - code_t*cls_static_init; + + classstate_t*cls; + methodstate_t*method; - /* method data */ - memberinfo_t*minfo; - abc_method_t*m; - dict_t*vars; - char late_binding; } state_t; typedef struct _global { + abc_file_t*file; + abc_script_t*init; + int variable_count; } global_t; @@ -285,7 +288,27 @@ static state_t* state = 0; DECLARE_LIST(state); -#define MULTINAME(m,x) multiname_t m;namespace_t m##_ns;registry_fill_multiname(&m, &m##_ns, x); +#define MULTINAME(m,x) \ + multiname_t m;\ + namespace_t m##_ns;\ + registry_fill_multiname(&m, &m##_ns, x); + +#define MEMBER_MULTINAME(m,f) \ + multiname_t m;\ + namespace_t m##_ns;\ + if(f) { \ + m##_ns.access = flags2access(f->flags); \ + m##_ns.name = ""; \ + m.type = QNAME; \ + m.ns = &m##_ns; \ + m.namespace_set = 0; \ + m.name = f->name; \ + } else { \ + m.type = MULTINAME; \ + m.ns =0; \ + m.namespace_set = &nopackage_namespace_set; \ + m.name = f->name; \ + } /* warning: list length of namespace set is undefined */ #define MULTINAME_LATE(m, access, package) \ @@ -330,9 +353,8 @@ static void new_state() state_stack = sl; state = s; state->level++; + state->has_own_imports = 0; state->vars = dict_new(); - state->initcode = 0; - state->has_own_imports = 0; } static void state_has_imports() { @@ -350,26 +372,29 @@ static void old_state() state_stack = state_stack->next; free(old); state = state_stack->state; - /*if(state->initcode) { + /*if(state->method->initcode) { printf("residual initcode\n"); - code_dump(state->initcode, 0, 0, "", stdout); + code_dump(state->method->initcode, 0, 0, "", stdout); }*/ if(oldstate->has_own_imports) { list_free(oldstate->wildcard_imports); dict_destroy(oldstate->imports);oldstate->imports=0; } - state->initcode = code_append(state->initcode, oldstate->initcode); + if(state->method) + state->method->initcode = + code_append(state->method->initcode, + oldstate->method->initcode); } void initialize_state() { init_globals(); new_state(); - state->file = abc_file_new(); - state->file->flags &= ~ABCFILE_LAZY; + global->file = abc_file_new(); + global->file->flags &= ~ABCFILE_LAZY; - state->init = abc_initscript(state->file, 0); - code_t*c = state->init->method->body->code; + global->init = abc_initscript(global->file, 0); + code_t*c = global->init->method->body->code; c = abc_getlocal_0(c); c = abc_pushscope(c); @@ -404,21 +429,21 @@ void initialize_state() c = abc_pushstring(c, "[entering global init function]"); c = abc_callpropvoid(c, "[package]::trace", 1); - state->init->method->body->code = c; + global->init->method->body->code = c; } void* finalize_state() { if(state->level!=1) { syntaxerror("unexpected end of file"); } - abc_method_body_t*m = state->init->method->body; + abc_method_body_t*m = global->init->method->body; //__ popscope(m); __ findpropstrict(m, "[package]::trace"); __ pushstring(m, "[leaving global init function]"); __ callpropvoid(m, "[package]::trace", 1); __ returnvoid(m); - return state->file; + return global->file; } @@ -444,6 +469,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo syntaxerror("inner classes now allowed"); } new_state(); + state->cls = rfx_calloc(sizeof(classstate_t)); token_list_t*t=0; classinfo_list_t*mlist=0; @@ -482,39 +508,40 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname); } + /* build info struct */ int num_interfaces = (list_length(implements)); - state->clsinfo = classinfo_register(access, package, classname, num_interfaces); - state->clsinfo->superclass = extends; + state->cls->info = classinfo_register(access, package, classname, num_interfaces); + state->cls->info->superclass = extends?extends:TYPE_OBJECT; int pos = 0; classinfo_list_t*l = implements; for(l=implements;l;l=l->next) { - state->clsinfo->interfaces[pos++] = l->classinfo; + state->cls->info->interfaces[pos++] = l->classinfo; } - - MULTINAME(classname2,state->clsinfo); multiname_t*extends2 = sig2mname(extends); + MULTINAME(classname2,state->cls->info); + /*if(extends) { state->cls_init = abc_getlocal_0(state->cls_init); state->cls_init = abc_constructsuper(state->cls_init, 0); }*/ - state->cls = abc_class_new(state->file, &classname2, extends2); - if(flags&FLAG_FINAL) abc_class_final(state->cls); - if(flags&FLAG_DYNAMIC) abc_class_sealed(state->cls); - if(interface) abc_class_interface(state->cls); + state->cls->abc = abc_class_new(global->file, &classname2, extends2); + if(flags&FLAG_FINAL) abc_class_final(state->cls->abc); + if(flags&FLAG_DYNAMIC) abc_class_sealed(state->cls->abc); + if(interface) abc_class_interface(state->cls->abc); for(mlist=implements;mlist;mlist=mlist->next) { MULTINAME(m, mlist->classinfo); - abc_class_add_interface(state->cls, &m); + abc_class_add_interface(state->cls->abc, &m); } /* now write the construction code for this class */ - int slotindex = abc_initscript_addClassTrait(state->init, &classname2, state->cls); + int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc); - abc_method_body_t*m = state->init->method->body; + abc_method_body_t*m = global->init->method->body; __ getglobalscope(m); classinfo_t*s = extends; @@ -553,7 +580,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo __ getlocal_0(m); __ pushscope(m);count++; } - __ newclass(m,state->cls); + __ newclass(m,state->cls->abc); while(count--) { __ popscope(m); } @@ -572,26 +599,26 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo static void endclass() { - if(state->cls_init) { - if(!state->cls->constructor) { - abc_method_t*m = abc_class_constructor(state->cls, 0); - m->body->code = code_append(m->body->code, state->cls_init); + if(state->cls->init) { + if(!state->cls->abc->constructor) { + abc_method_t*m = abc_class_constructor(state->cls->abc, 0); + m->body->code = code_append(m->body->code, state->cls->init); m->body->code = abc_returnvoid(m->body->code); } else { - code_t*c = state->cls->constructor->body->code; - c = code_append(state->cls_init, c); - state->cls->constructor->body->code = c; + code_t*c = state->cls->abc->constructor->body->code; + c = code_append(state->cls->init, c); + state->cls->abc->constructor->body->code = c; } } - if(state->cls_static_init) { - if(!state->cls->static_constructor) { - abc_method_t*m = abc_class_staticconstructor(state->cls, 0); - m->body->code = code_append(m->body->code, state->cls_static_init); + if(state->cls->static_init) { + if(!state->cls->abc->static_constructor) { + abc_method_t*m = abc_class_staticconstructor(state->cls->abc, 0); + m->body->code = code_append(m->body->code, state->cls->static_init); m->body->code = abc_returnvoid(m->body->code); } else { - state->cls->static_constructor->body->code = - code_append(state->cls_static_init, state->cls->static_constructor->body->code); + state->cls->abc->static_constructor->body->code = + code_append(state->cls->static_init, state->cls->abc->static_constructor->body->code); } } @@ -607,7 +634,9 @@ static int find_variable(char*name, classinfo_t**m) { state_list_t* s = state_stack; while(s) { - variable_t*v = dict_lookup(s->state->vars, name); + variable_t*v = 0; + if(s->state->method) + v = dict_lookup(s->state->vars, name); if(v) { if(m) { *m = v->type; @@ -642,10 +671,9 @@ static int gettempvar() { int i = find_variable(TEMPVARNAME, 0); if(i<0) { - return new_variable(TEMPVARNAME, 0); - } else { - return i; + i = new_variable(TEMPVARNAME, 0); } + return i; } code_t* killvars(code_t*c) @@ -688,14 +716,14 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na { memberinfo_t*minfo = 0; if(getset != KW_GET && getset != KW_SET) { - if(registry_findmember(state->clsinfo, name)) { + if(registry_findmember(state->cls->info, name)) { syntaxerror("class already contains a member/method called '%s'", name); } - minfo = memberinfo_register(state->clsinfo, name, MEMBER_METHOD); + minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD); minfo->return_type = return_type; // getslot on a member slot only returns "undefined", so no need // to actually store these - //state->minfo->slot = state->m->method->trait->slot_id; + //state->minfo->slot = state->method->abc->method->trait->slot_id; } else { int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET; classinfo_t*type=0; @@ -703,7 +731,7 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na type = return_type; else if(params->list) type = params->list->param->type; - if((minfo=registry_findmember(state->clsinfo, name))) { + if((minfo=registry_findmember(state->cls->info, name))) { if(minfo->kind & ~(MEMBER_GET|MEMBER_SET)) syntaxerror("class already contains a member or method called '%s'", name); if(minfo->kind & gs) @@ -716,7 +744,7 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na if(type && minfo->type != type) syntaxerror("different type in getter and setter"); } else { - minfo = memberinfo_register(state->clsinfo, name, gs); + minfo = memberinfo_register(state->cls->info, name, gs); minfo->type = type; } /* can't assign a slot as getter and setter might have different slots */ @@ -751,33 +779,53 @@ static int flags2access(int flags) static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type) { - token_list_t*t; + if(state->method) { + syntaxerror("not able to start another method scope"); + } new_state(); + state->method = rfx_calloc(sizeof(methodstate_t)); + state->method->initcode = 0; + state->method->is_constructor = !strcmp(state->cls->info->name,name); + state->method->has_super = 0; + global->variable_count = 0; - state->function = name; - - if(state->m) { - syntaxerror("not able to start another method scope"); + + /* state->vars is initialized by state_new */ + if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info)!=0) syntaxerror("Internal error"); + param_list_t*p=0; + for(p=params->list;p;p=p->next) { + new_variable(p->param->name, p->param->type); } + if(state->method->is_constructor) + name = "__as3_constructor__"; + state->method->info = registerfunction(getset, flags, name, params, return_type, 0); +} +static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name, + params_t*params, classinfo_t*return_type, code_t*body) +{ namespace_t mname_ns = {flags2access(flags), ""}; multiname_t mname = {QNAME, &mname_ns, 0, name}; + abc_method_t*f = 0; + multiname_t*type2 = sig2mname(return_type); - if(!strcmp(state->clsinfo->name,name)) { - state->m = abc_class_constructor(state->cls, type2); + int slot = 0; + if(state->method->is_constructor) { + f = abc_class_constructor(state->cls->abc, type2); } else { if(flags&FLAG_STATIC) - state->m = abc_class_staticmethod(state->cls, type2, &mname); + f = abc_class_staticmethod(state->cls->abc, type2, &mname); else - state->m = abc_class_method(state->cls, type2, &mname); - int slot = state->m->trait->slot_id; - state->minfo = registerfunction(getset, flags, name, params, return_type, slot); + f = abc_class_method(state->cls->abc, type2, &mname); + slot = f->trait->slot_id; } + //flash doesn't seem to allow us to access function slots + //state->method->info->slot = slot; - if(getset == KW_GET) state->m->trait->kind = TRAIT_GETTER; - if(getset == KW_SET) state->m->trait->kind = TRAIT_SETTER; - if(params->varargs) state->m->flags |= METHOD_NEED_REST; + if(getset == KW_GET) f->trait->kind = TRAIT_GETTER; + if(getset == KW_SET) f->trait->kind = TRAIT_SETTER; + if(params->varargs) f->flags |= METHOD_NEED_REST; char opt=0; param_list_t*p=0; @@ -786,42 +834,16 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n break; //varargs: omit last parameter in function signature } multiname_t*m = sig2mname(p->param->type); - list_append(state->m->parameters, m); + list_append(f->parameters, m); if(p->param->value) { check_constant_against_type(p->param->type, p->param->value); - opt=1;list_append(state->m->optional_parameters, p->param->value); + opt=1;list_append(f->optional_parameters, p->param->value); } else if(opt) { syntaxerror("non-optional parameter not allowed after optional parameters"); } } - - /* state->vars is initialized by state_new */ - if(new_variable("this", state->clsinfo)!=0) syntaxerror("Internal error"); - - for(p=params->list;p;p=p->next) { - new_variable(p->param->name, p->param->type); - } -} -static void endfunction(code_t*body) -{ + f->body->code = body; - if(!(state->cls->flags & CLASS_INTERFACE)) { - code_t*c = 0; - if(state->late_binding) { - c = abc_getlocal_0(c); - c = abc_pushscope(c); - } - c = code_append(c, state->initcode); - c = code_append(c, body); - - /* append return if necessary */ - if(!c || c->opcode != OPCODE_RETURNVOID && - c->opcode != OPCODE_RETURNVALUE) { - c = abc_returnvoid(c); - } - if(state->m->body->code) syntaxerror("internal error"); - state->m->body->code = c; - } old_state(); } @@ -901,6 +923,8 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) } if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to)) return c; + if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to)) + return c; syntaxerror("can't convert type %s to %s", from->name, to->name); } @@ -926,6 +950,40 @@ void parserassert(int b) if(!b) syntaxerror("internal error: assertion failed"); } +static classinfo_t* find_class(char*name) +{ + classinfo_t*c=0; + + c = registry_findclass(state->package, name); + + /* try explicit imports */ + dictentry_t* e = dict_get_slot(state->imports, name); + while(e) { + if(c) + break; + if(!strcmp(e->key, name)) { + c = (classinfo_t*)e->data; + } + e = e->next; + } + + /* try package.* imports */ + import_list_t*l = state->wildcard_imports; + while(l) { + if(c) + break; + //printf("does package %s contain a class %s?\n", l->import->package, name); + c = registry_findclass(l->import->package, name); + l = l->next; + } + + /* try global package */ + if(!c) { + c = registry_findclass("", name); + } + return c; +} + static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore) { /* converts this: @@ -961,12 +1019,26 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r write->opcode = OPCODE_SETPROPERTY; multiname_t*m = (multiname_t*)r->data[0]; write->data[0] = multiname_clone(m); - if(m->type != QNAME && m->type != MULTINAME) - syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)"); - if(!justassign) { - prefix = abc_dup(prefix); // we need the object, too + if(m->type == QNAME || m->type == MULTINAME) { + if(!justassign) { + prefix = abc_dup(prefix); // we need the object, too + } + use_temp_var = 1; + } else if(m->type == MULTINAMEL) { + if(!justassign) { + /* dupping two values on the stack requires 5 operations and one register- + couldn't adobe just have given us a dup2? */ + int temp = gettempvar(); + prefix = abc_setlocal(prefix, temp); + prefix = abc_dup(prefix); + prefix = abc_getlocal(prefix, temp); + prefix = abc_swap(prefix); + prefix = abc_getlocal(prefix, temp); + } + use_temp_var = 1; + } else { + syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)"); } - use_temp_var = 1; } else if(r->opcode == OPCODE_GETSLOT) { write->opcode = OPCODE_SETSLOT; write->data[0] = r->data[0]; @@ -1056,6 +1128,8 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r return c; } +#define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t)) +#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b)) %} @@ -1066,7 +1140,7 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r PROGRAM: MAYBECODE -MAYBECODE: CODE {$$=$1;} +MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/} MAYBECODE: {$$=code_new();} CODE: CODE CODEPIECE {$$=code_append($1,$2);} @@ -1130,8 +1204,8 @@ ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION /* if this is a typed variable: push default value for type on stack */ if($3) { - state->initcode = defaultvalue(state->initcode, $3); - state->initcode = abc_setlocal(state->initcode, index); + state->method->initcode = defaultvalue(state->method->initcode, $3); + state->method->initcode = abc_setlocal(state->method->initcode, index); } } else { if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) { @@ -1145,20 +1219,20 @@ ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION /* that's the default for a local register, anyway else { - state->initcode = abc_pushundefined(state->initcode); - state->initcode = abc_setlocal(state->initcode, index); + state->method->initcode = abc_pushundefined(state->method->initcode); + state->method->initcode = abc_setlocal(state->method->initcode, index); }*/ //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:""); } /* ------------ control flow ------------------------- */ -MAYBEELSE: %prec prec_none {$$ = code_new();} +MAYBEELSE: %prec below_else {$$ = code_new();} MAYBEELSE: "else" CODEBLOCK {$$=$2;} //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;} IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { - $$ = state->initcode;state->initcode=0; + $$ = state->method->initcode;state->method->initcode=0; $$ = code_append($$, $4.c); code_t*myjmp,*myif = $$ = abc_iffalse($$, 0); @@ -1181,7 +1255,7 @@ FOR_INIT : VARIABLE_DECLARATION FOR_INIT : VOIDEXPRESSION FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { - $$ = state->initcode;state->initcode=0; + $$ = state->method->initcode;state->method->initcode=0; $$ = code_append($$, $4); code_t*loopstart = $$ = abc_label($$); @@ -1198,7 +1272,7 @@ FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CO } WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK { - $$ = state->initcode;state->initcode=0; + $$ = state->method->initcode;state->method->initcode=0; code_t*myjmp = $$ = abc_jump($$, 0); code_t*loopstart = $$ = abc_label($$); @@ -1305,7 +1379,7 @@ IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LI syntaxerror("invalid method modifiers: interface methods always need to be public"); } startfunction(0,$1,$3,$4,&$6,$8); - endfunction(0); + endfunction(0,$1,$3,$4,&$6,$8, 0); } /* ------------ classes and interfaces (body, slots ) ------- */ @@ -1314,7 +1388,7 @@ VARCONST: "var" | "const" SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION { int flags = $1; - memberinfo_t* info = memberinfo_register(state->clsinfo, $3, MEMBER_SLOT); + memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT); info->type = $4; info->flags = flags; trait_t*t=0; @@ -1325,19 +1399,19 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSIO if(!(flags&FLAG_STATIC)) { if($4) { MULTINAME(m, $4); - t=abc_class_slot(state->cls, &mname, &m); - info->slot = t->slot_id; + t=abc_class_slot(state->cls->abc, &mname, &m); } else { - t=abc_class_slot(state->cls, &mname, 0); + t=abc_class_slot(state->cls->abc, &mname, 0); } + info->slot = t->slot_id; } else { if($4) { MULTINAME(m, $4); - t=abc_class_staticslot(state->cls, &mname, &m); - //info->slot = t->slot_id; + t=abc_class_staticslot(state->cls->abc, &mname, &m); } else { - t=abc_class_staticslot(state->cls, &mname, 0); + t=abc_class_staticslot(state->cls->abc, &mname, 0); } + info->slot = t->slot_id; } if($5.c && !is_pushundefined($5.c)) { code_t*c = 0; @@ -1346,9 +1420,9 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSIO c = converttype(c, $5.t, $4); c = abc_setslot(c, t->slot_id); if(!(flags&FLAG_STATIC)) - state->cls_init = code_append(state->cls_init, c); + state->cls->init = code_append(state->cls->init, c); else - state->cls_static_init = code_append(state->cls_static_init, c); + state->cls->static_init = code_append(state->cls->static_init, c); } if($2==KW_CONST) { t->kind= TRAIT_CONST; @@ -1366,9 +1440,9 @@ STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);} STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);} STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);} //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);} -STATICCONSTANT : KW_TRUE {$$ = constant_new_true($1);} -STATICCONSTANT : KW_FALSE {$$ = constant_new_false($1);} -STATICCONSTANT : KW_NULL {$$ = constant_new_null($1);} +STATICCONSTANT : "true" {$$ = constant_new_true($1);} +STATICCONSTANT : "false" {$$ = constant_new_false($1);} +STATICCONSTANT : "null" {$$ = constant_new_null($1);} /* ------------ classes and interfaces (body, functions) ------- */ @@ -1421,8 +1495,26 @@ GETSET : "get" {$$=$1;} FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}' { - if(!state->m) syntaxerror("internal error: undefined function"); - endfunction($11); + code_t*c = 0; + if(state->method->late_binding) { + c = abc_getlocal_0(c); + c = abc_pushscope(c); + } + if(state->method->is_constructor && !state->method->has_super) { + // generate default constructor + c = abc_getlocal_0(c); + c = abc_constructsuper(c, 0); + } + + c = code_append(c, state->method->initcode); + c = code_append(c, $11); + + /* append return if necessary */ + if(!c || c->opcode != OPCODE_RETURNVOID && + c->opcode != OPCODE_RETURNVALUE) { + c = abc_returnvoid(c); + } + endfunction(0,$1,$3,$4,&$6,$8,c); } /* ------------- package + class ids --------------- */ @@ -1430,34 +1522,7 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P CLASS: T_IDENTIFIER { /* try current package */ - $$ = registry_findclass(state->package, $1); - - /* try explicit imports */ - dictentry_t* e = dict_get_slot(state->imports, $1); - while(e) { - if($$) - break; - if(!strcmp(e->key, $1)) { - $$ = (classinfo_t*)e->data; - } - e = e->next; - } - - /* try package.* imports */ - import_list_t*l = state->wildcard_imports; - while(l) { - if($$) - break; - //printf("does package %s contain a class %s?\n", l->import->package, $1); - $$ = registry_findclass(l->import->package, $1); - l = l->next; - } - - /* try global package */ - if(!$$) { - $$ = registry_findclass("", $1); - } - + $$ = find_class($1); if(!$$) syntaxerror("Could not find class %s\n", $1); } @@ -1474,16 +1539,18 @@ QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);} TYPE : QNAME {$$=$1;} | '*' {$$=registry_getanytype();} + /* | "String" {$$=registry_getstringclass();} | "int" {$$=registry_getintclass();} | "uint" {$$=registry_getuintclass();} | "Boolean" {$$=registry_getbooleanclass();} | "Number" {$$=registry_getnumberclass();} + */ MAYBETYPE: ':' TYPE {$$=$2;} MAYBETYPE: {$$=0;} -/* ----------function calls, constructor calls ------ */ +/* ----------function calls, delete, constructor calls ------ */ MAYBE_PARAM_VALUES : %prec prec_none {$$=0;} MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2} @@ -1503,8 +1570,12 @@ NEW : "new" CLASS MAYBE_PARAM_VALUES { MULTINAME(m, $2); $$.c = code_new(); - /* TODO: why do we have to *find* our own classes? */ - $$.c = abc_findpropstrict2($$.c, &m); + if($2->slot) { + $$.c = abc_getglobalscope($$.c); + $$.c = abc_getslot($$.c, $2->slot); + } else { + $$.c = abc_findpropstrict2($$.c, &m); + } typedcode_list_t*l = $3; int len = 0; @@ -1513,7 +1584,10 @@ NEW : "new" CLASS MAYBE_PARAM_VALUES { l = l->next; len ++; } - $$.c = abc_constructprop2($$.c, &m, len); + if($2->slot) + $$.c = abc_construct($$.c, len); + else + $$.c = abc_constructprop2($$.c, &m, len); $$.t = $2; } @@ -1545,7 +1619,7 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { $$.c = abc_callproperty2($$.c, name, len); } else if($$.c->opcode == OPCODE_GETSLOT) { int slot = (int)(ptroff_t)$$.c->data[0]; - trait_t*t = abc_class_find_slotid(state->cls,slot);//FIXME + trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME if(t->kind!=TRAIT_METHOD) { //flash allows to assign closures to members. //syntaxerror("not a function"); @@ -1555,6 +1629,11 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { $$.c = code_append($$.c, paramcode); //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding $$.c = abc_callproperty2($$.c, name, len); + } else if($$.c->opcode == OPCODE_GETSUPER) { + name = multiname_clone($$.c->data[0]); + $$.c = code_cutlast($$.c); + $$.c = code_append($$.c, paramcode); + $$.c = abc_callsuper2($$.c, name, len); } else { $$.c = abc_getlocal_0($$.c); $$.c = code_append($$.c, paramcode); @@ -1563,14 +1642,56 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { memberinfo_t*f = 0; - if(TYPE_IS_FUNCTION($1.t) && - (f = registry_findmember($1.t, "call"))) { - $$.t = f->return_type; + if(TYPE_IS_FUNCTION($1.t) && $1.t->function) { + $$.t = $1.t->function->return_type; } else { $$.c = abc_coerce_a($$.c); $$.t = TYPE_ANY; } } +FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' { + if(!state->cls) syntaxerror("super() not allowed outside of a class"); + if(!state->method) syntaxerror("super() not allowed outside of a function"); + if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor"); + + $$.c = code_new(); + $$.c = abc_getlocal_0($$.c); + typedcode_list_t*l = 0; + int len = 0; + for(l=$3;l;l=l->next) { + $$.c = code_append($$.c, l->typedcode->c);len++; + } + /* + this is dependent on the control path, check this somewhere else + if(state->method->has_super) + syntaxerror("constructor may call super() only once"); + */ + state->method->has_super = 1; + $$.c = abc_constructsuper($$.c, len); + $$.c = abc_pushundefined($$.c); + $$.t = TYPE_ANY; +} + +DELETE: "delete" E { + $$.c = $2.c; + if($$.c->opcode == OPCODE_COERCE_A) { + $$.c = code_cutlast($$.c); + } + multiname_t*name = 0; + if($$.c->opcode == OPCODE_GETPROPERTY) { + $$.c->opcode = OPCODE_DELETEPROPERTY; + } else if($$.c->opcode == OPCODE_GETSLOT) { + int slot = (int)(ptroff_t)$$.c->data[0]; + multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name; + $$.c = code_cutlast($$.c); + $$.c = abc_deleteproperty2($$.c, name); + } else { + $$.c = abc_getlocal_0($$.c); + MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, ""); + $$.c = abc_deleteproperty2($$.c, &m); + } + $$.t = TYPE_BOOLEAN; +} RETURN: "return" %prec prec_none { $$ = abc_returnvoid(0); @@ -1579,23 +1700,27 @@ RETURN: "return" EXPRESSION { $$ = $2.c; $$ = abc_returnvalue($$); } + // ----------------------- expression types ------------------------------------- -NONCOMMAEXPRESSION : E %prec prec_belowminus {$$=$1;} -EXPRESSION : E %prec prec_belowminus {$$ = $1;} -EXPRESSION : EXPRESSION ',' E %prec prec_belowminus { +NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;} +EXPRESSION : E %prec below_minus {$$ = $1;} +EXPRESSION : EXPRESSION ',' E %prec below_minus { $$.c = $1.c; $$.c = cut_last_push($$.c); $$.c = code_append($$.c,$3.c); $$.t = $3.t; } -VOIDEXPRESSION : EXPRESSION %prec prec_belowminus {$$=cut_last_push($1.c);} +VOIDEXPRESSION : EXPRESSION %prec below_minus { + $$=cut_last_push($1.c); +} // ----------------------- expression evaluation ------------------------------------- E : CONSTANT E : VAR_READ %prec T_IDENTIFIER {$$ = $1;} E : NEW {$$ = $1;} +E : DELETE {$$ = $1;} E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */ $$.t = TYPE_ANY; } @@ -1620,13 +1745,16 @@ CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1); CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1); $$.t = TYPE_STRING; } -CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0); +CONSTANT : "undefined" {$$.c = abc_pushundefined(0); + $$.t = TYPE_ANY; + } +CONSTANT : "true" {$$.c = abc_pushtrue(0); $$.t = TYPE_BOOLEAN; } -CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0); +CONSTANT : "false" {$$.c = abc_pushfalse(0); $$.t = TYPE_BOOLEAN; } -CONSTANT : KW_NULL {$$.c = abc_pushnull(0); +CONSTANT : "null" {$$.c = abc_pushnull(0); $$.t = TYPE_NULL; } @@ -1648,6 +1776,9 @@ E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c); } E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c); $$.t = TYPE_BOOLEAN; + } +E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c); + $$.t = TYPE_BOOLEAN; } E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c); $$.t = TYPE_BOOLEAN; @@ -1687,22 +1818,115 @@ E : '!' E {$$.c=$2.c; $$.t = TYPE_BOOLEAN; } -E : E '-' E -E : E '/' E -E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c); - $$.t = join_types($1.t, $3.t, '+'); +E : '~' E {$$.c=$2.c; + $$.c = abc_bitnot($$.c); + $$.t = TYPE_INT; + } + +E : E '&' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_bitand($$.c); + $$.t = TYPE_INT; + } + +E : E '^' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_bitxor($$.c); + $$.t = TYPE_INT; + } + +E : E '|' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_bitor($$.c); + $$.t = TYPE_INT; + } + +E : E '-' E {$$.c = code_append($1.c,$3.c); + if(BOTH_INT($1,$3)) { + $$.c = abc_subtract_i($$.c); + $$.t = TYPE_INT; + } else { + $$.c = abc_subtract($$.c); + $$.t = TYPE_NUMBER; + } + } +E : E ">>" E {$$.c = code_append($1.c,$3.c); + $$.c = abc_rshift($$.c); + $$.t = TYPE_INT; + } +E : E ">>>" E {$$.c = code_append($1.c,$3.c); + $$.c = abc_urshift($$.c); + $$.t = TYPE_INT; + } +E : E "<<" E {$$.c = code_append($1.c,$3.c); + $$.c = abc_lshift($$.c); + $$.t = TYPE_INT; } -E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c); - $$.t = join_types($1.t, $3.t, '%'); + +E : E '/' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_divide($$.c); + $$.t = TYPE_NUMBER; + } +E : E '+' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_add($$.c); + $$.t = TYPE_NUMBER; + } +E : E '%' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_modulo($$.c); + $$.t = TYPE_NUMBER; } -E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c); - $$.t = join_types($1.t, $3.t, '*'); +E : E '*' E {$$.c = code_append($1.c,$3.c); + if(BOTH_INT($1,$3)) { + $$.c = abc_multiply_i($$.c); + $$.t = TYPE_INT; + } else { + $$.c = abc_multiply($$.c); + $$.t = TYPE_NUMBER; + } } -E : E "as" E -E : E "is" E -E : '(' E ')' {$$=$2;} -E : '-' E {$$=$2;} +E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate + if(use_astype && TYPE_IS_CLASS($3.t)) { + MULTINAME(m,$3.t->cls); + $$.c = abc_astype2($1.c, &m); + $$.t = $3.t->cls; + } else { + $$.c = code_append($1.c, $3.c); + $$.c = abc_astypelate($$.c); + $$.t = TYPE_ANY; + } + } + +E : E "is" E {$$.c = code_append($1.c, $3.c); + $$.c = abc_istypelate($$.c); + $$.t = TYPE_BOOLEAN; + } + +E : "typeof" '(' E ')' { + $$.c = $3.c; + $$.c = abc_typeof($$.c); + $$.t = TYPE_STRING; + } + +E : "void" E { + $$.c = cut_last_push($2.c); + $$.c = abc_pushundefined($$.c); + $$.t = TYPE_ANY; + } + +E : "void" { $$.c = abc_pushundefined(0); + $$.t = TYPE_ANY; + } + +E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too + +E : '-' E { + $$=$2; + if(IS_INT($2)) { + $$.c=abc_negate_i($$.c); + $$.t = TYPE_INT; + } else { + $$.c=abc_negate($$.c); + $$.t = TYPE_NUMBER; + } +} E : E '[' E ']' { $$.c = $1.c; @@ -1710,11 +1934,12 @@ E : E '[' E ']' { MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, ""); $$.c = abc_getproperty2($$.c, &m); + $$.t = 0; // array elements have unknown type } E : E "*=" E { code_t*c = $3.c; - if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) { + if(BOTH_INT($1,$3)) { c=abc_multiply_i(c); } else { c=abc_multiply(c); @@ -1723,6 +1948,7 @@ E : E "*=" E { $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; } + E : E "%=" E { code_t*c = abc_modulo($3.c); c=converttype(c, join_types($1.t, $3.t, '%'), $1.t); @@ -1783,6 +2009,17 @@ E : E '=' E { code_t*c = 0; $$.t = $1.t; } +E : E '?' E ':' E %prec below_assignment { + $$.c = $1.c; + code_t*j1 = $$.c = abc_iffalse($$.c, 0); + $$.c = code_append($$.c, $3.c); + code_t*j2 = $$.c = abc_jump($$.c, 0); + $$.c = j1->branch = abc_label($$.c); + $$.c = code_append($$.c, $5.c); + $$.c = j2->branch = abc_label($$.c); + $$.t = join_types($3.t,$5.t,'?'); + } + // TODO: use inclocal where appropriate E : E "++" { code_t*c = 0; classinfo_t*type = $1.t; @@ -1811,7 +2048,7 @@ E : E "--" { code_t*c = 0; $$.t = $1.t; } -E : "++" E { code_t*c = 0; +E : "++" %prec plusplus_prefix E { code_t*c = 0; classinfo_t*type = $2.t; if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { c=abc_increment_i(c); @@ -1825,7 +2062,7 @@ E : "++" E { code_t*c = 0; $$.t = $2.t; } -E : "--" E { code_t*c = 0; +E : "--" %prec minusminus_prefix E { code_t*c = 0; classinfo_t*type = $2.t; if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { c=abc_decrement_i(c); @@ -1839,38 +2076,48 @@ E : "--" E { code_t*c = 0; $$.t = $2.t; } +E : "super" '.' T_IDENTIFIER + { if(!state->cls->info) + syntaxerror("super keyword not allowed outside a class"); + classinfo_t*t = state->cls->info->superclass; + if(!t) t = TYPE_OBJECT; + + memberinfo_t*f = registry_findmember(t, $3); + namespace_t ns = {flags2access(f->flags), ""}; + MEMBER_MULTINAME(m, f); + $$.c = 0; + $$.c = abc_getlocal_0($$.c); + $$.c = abc_getsuper2($$.c, &m); + $$.t = memberinfo_gettype(f); + } + E : E '.' T_IDENTIFIER {$$.c = $1.c; - if($$.t) { - memberinfo_t*f = registry_findmember($$.t, $3); - - if(f && f->slot) { + classinfo_t*t = $1.t; + char is_static = 0; + if(TYPE_IS_CLASS(t) && t->cls) { + t = t->cls; + is_static = 1; + } + if(t) { + memberinfo_t*f = registry_findmember(t, $3); + char noslot = 0; + if(f && !is_static != !(f->flags&FLAG_STATIC)) + noslot=1; + if(f && f->slot && !noslot) { $$.c = abc_getslot($$.c, f->slot); } else { - if(f) { - namespace_t ns = {flags2access(f->flags), ""}; // needs to be "", not $$.t->package (!) - multiname_t m = {QNAME, &ns, 0, $3}; - $$.c = abc_getproperty2($$.c, &m); - } else { - multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3}; - $$.c = abc_getproperty2($$.c, &m); - } + MEMBER_MULTINAME(m, f); + $$.c = abc_getproperty2($$.c, &m); } /* determine type */ - if(f) { - if(f->kind == MEMBER_METHOD) { - $$.t = TYPE_FUNCTION(f); - } else { - $$.t = f->type; - } - } else { + $$.t = memberinfo_gettype(f); + if(!$$.t) $$.c = abc_coerce_a($$.c); - $$.t = registry_getanytype(); - } } else { /* when resolving a property on an unknown type, we do know the name of the property (and don't seem to need the package), but - we do need to make avm2 try out all access modes */ + we need to make avm2 try out all access modes */ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3}; $$.c = abc_getproperty2($$.c, &m); $$.c = abc_coerce_a($$.c); @@ -1882,16 +2129,23 @@ VAR_READ : T_IDENTIFIER { $$.t = 0; $$.c = 0; int i; + classinfo_t*a = 0; memberinfo_t*f = 0; + + /* look at variables */ if((i = find_variable($1, &$$.t)) >= 0) { // $1 is a local variable $$.c = abc_getlocal($$.c, i); - } else if(f = registry_findmember(state->clsinfo, $1)) { + + /* look at current class' members */ + } else if((f = registry_findmember(state->cls->info, $1))) { // $1 is a function in this class - if(f->flags&FLAG_STATIC) { + int var_is_static = (f->flags&FLAG_STATIC); + int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC); + if(var_is_static != i_am_static) { /* there doesn't seem to be any "static" way to access static properties of a class */ - state->late_binding = 1; + state->method->late_binding = 1; $$.t = f->type; namespace_t ns = {flags2access(f->flags), ""}; multiname_t m = {QNAME, &ns, 0, $1}; @@ -1913,11 +2167,23 @@ VAR_READ : T_IDENTIFIER { } else { $$.t = f->type; } + + /* look at classes in the current package and imported classes */ + } else if((a = find_class($1))) { + if(a->slot) { + $$.c = abc_getglobalscope($$.c); + $$.c = abc_getslot($$.c, a->slot); + } else { + MULTINAME(m, a); + $$.c = abc_getlex2($$.c, &m); + } + $$.t = TYPE_CLASS(a); + + /* unknown object, let the avm2 resolve it */ } else { - // let the avm2 resolve $1 if(strcmp($1,"trace")) - warning("Couldn't resolve %s, doing late binding", $1); - state->late_binding = 1; + warning("Couldn't resolve '%s', doing late binding", $1); + state->method->late_binding = 1; multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};