X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=e643572fbdbd40d3f97181b597f8fa3ced02a63a;hb=3fb75ef418267cb0f7370d365b3a37b8013fbee9;hp=95f1feb85f14867088604685df372e9e16d2a683;hpb=aa9e39a7aa803ebc05df87dfb1def600b06b0666;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index 95f1feb..e643572 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -50,12 +50,14 @@ double number_float; code_t*code; typedcode_t value; - typedcode_list_t*value_list; + //typedcode_list_t*value_list; + codeandnumber_t value_list; param_t* param; params_t params; string_t str; char*id; constant_t*constant; + for_start_t for_start; } @@ -69,6 +71,11 @@ %token T_SHORT %token T_FLOAT +%token T_FOR "for" +%token T_WHILE "while" +%token T_DO "do" +%token T_SWITCH "switch" + %token KW_IMPLEMENTS %token KW_NAMESPACE "namespace" %token KW_PACKAGE "package" @@ -80,34 +87,44 @@ %token KW_NEW "new" %token KW_NATIVE %token KW_FUNCTION "function" -%token KW_FOR "for" +%token KW_UNDEFINED "undefined" +%token KW_CONTINUE "continue" %token KW_CLASS "class" %token KW_CONST "const" +%token KW_CATCH "catch" +%token KW_CASE "case" %token KW_SET "set" +%token KW_VOID "void" %token KW_STATIC +%token KW_INSTANCEOF "instanceof" %token KW_IMPORT "import" %token KW_RETURN "return" +%token KW_TYPEOF "typeof" %token KW_INTERFACE "interface" %token KW_NULL "null" %token KW_VAR "var" -%token KW_DYNAMIC +%token KW_DYNAMIC "dynamic" %token KW_OVERRIDE %token KW_FINAL +%token KW_EACH "each" %token KW_GET "get" +%token KW_TRY "try" +%token KW_SUPER "super" %token KW_EXTENDS %token KW_FALSE "false" %token KW_TRUE "true" %token KW_BOOLEAN "Boolean" %token KW_UINT "uint" %token KW_INT "int" -%token KW_WHILE "while" %token KW_NUMBER "Number" %token KW_STRING "String" +%token KW_DEFAULT "default" %token KW_DELETE "delete" %token KW_IF "if" %token KW_ELSE "else" %token KW_BREAK "break" %token KW_IS "is" +%token KW_IN "in" %token KW_AS "as" %token T_EQEQ "==" @@ -135,26 +152,27 @@ %token T_USHR ">>>" %token T_SHR ">>" -%type X_IDENTIFIER PACKAGE +%type FOR_START +%type X_IDENTIFIER PACKAGE FOR_IN_INIT %type VARCONST %type CODE -%type CODEPIECE -%type CODEBLOCK MAYBECODE -%type PACKAGE_DECLARATION -%type FUNCTION_DECLARATION +%type CODEPIECE CODE_STATEMENT +%type CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH +%type PACKAGE_DECLARATION SLOT_DECLARATION +%type FUNCTION_DECLARATION PACKAGE_INITCODE %type VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST -%type CLASS_DECLARATION -%type NAMESPACE_DECLARATION -%type INTERFACE_DECLARATION +%type CLASS_DECLARATION +%type NAMESPACE_DECLARATION +%type INTERFACE_DECLARATION %type VOIDEXPRESSION %type EXPRESSION NONCOMMAEXPRESSION %type MAYBEEXPRESSION %type E DELETE %type CONSTANT -%type FOR IF WHILE MAYBEELSE BREAK RETURN +%type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE %type USE_NAMESPACE %type FOR_INIT -%type IMPORT +%type IMPORT %type MAYBETYPE %type GETSET %type PARAM @@ -169,14 +187,13 @@ %type CLASS PACKAGEANDCLASS QNAME %type QNAME_LIST %type TYPE -%type VAR //%type VARIABLE %type VAR_READ %type NEW //%type T_IDENTIFIER %type MODIFIER %type FUNCTIONCALL -%type MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES +%type MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST // precedence: from low to high @@ -194,16 +211,18 @@ %nonassoc '^' %nonassoc '&' %nonassoc "==" "!=" "===" "!==" -%nonassoc "is" "as" +%nonassoc "is" "as" "in" %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax? %left "<<" ">>" ">>>" %left below_minus %left '-' '+' %left '/' '*' '%' -%left plusplus_prefix minusminus_prefix '~' '!' "delete" "typeof" //FIXME: *unary* + - should be here, too +%left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too %left "--" "++" +%nonassoc below_curly %left '[' ']' '{' "new" '.' ".." "::" %nonassoc T_IDENTIFIER +%left above_identifier %left below_else %nonassoc "else" %left '(' @@ -211,7 +230,8 @@ // needed for "return" precedence: %nonassoc T_STRING T_REGEXP %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT -%nonassoc "false" "true" "null" +%nonassoc "false" "true" "null" "undefined" "super" + %{ @@ -220,7 +240,18 @@ static int yyerror(char*s) { syntaxerror("%s", s); } -static char* concat3str(const char* t1, const char* t2, const char* t3) + +static char* concat2(const char* t1, const char* t2) +{ + int l1 = strlen(t1); + int l2 = strlen(t2); + char*text = malloc(l1+l2+1); + memcpy(text , t1, l1); + memcpy(text+l1, t2, l2); + text[l1+l2] = 0; + return text; +} +static char* concat3(const char* t1, const char* t2, const char* t3) { int l1 = strlen(t1); int l2 = strlen(t2); @@ -239,37 +270,46 @@ typedef struct _import { DECLARE_LIST(import); -typedef struct _state { - abc_file_t*file; - abc_script_t*init; - - int level; - - char*package; - char*function; +typedef struct _classstate { + /* class data */ + classinfo_t*info; + abc_class_t*abc; + code_t*init; + code_t*static_init; + char has_constructor; +} classstate_t; + +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; + char is_global; +} methodstate_t; + +typedef struct _state { + struct _state*old; + 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; @@ -278,7 +318,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,n) \ + 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 = n; \ + } /* warning: list length of namespace set is undefined */ #define MULTINAME_LATE(m, access, package) \ @@ -300,32 +360,20 @@ static namespace_list_t nl2 = {&ns2,&nl3}; static namespace_list_t nl1 = {&ns1,&nl2}; static namespace_set_t nopackage_namespace_set = {&nl1}; -static state_list_t*state_stack=0; - -static void init_globals() -{ - global = rfx_calloc(sizeof(global_t)); -} - static void new_state() { NEW(state_t, s); - NEW(state_list_t, sl); - state_t*oldstate = state; if(state) memcpy(s, state, sizeof(state_t)); //shallow copy - sl->next = state_stack; - sl->state = s; if(!s->imports) { s->imports = dict_new(); } - state_stack = sl; state = s; state->level++; - state->vars = dict_new(); - state->initcode = 0; - state->has_own_imports = 0; + state->has_own_imports = 0; + state->vars = dict_new(); + state->old = oldstate; } static void state_has_imports() { @@ -334,35 +382,55 @@ static void state_has_imports() state->has_own_imports = 1; } +static void state_destroy(state_t*state) +{ + if(state->has_own_imports) { + list_free(state->wildcard_imports); + dict_destroy(state->imports);state->imports=0; + } + if(state->imports && (!state->old || state->old->imports!=state->imports)) { + dict_destroy(state->imports);state->imports=0; + } + if(state->vars) { + int t; + for(t=0;tvars->hashsize;t++) { + dictentry_t*e =state->vars->slots[t]; + while(e) { + free(e->data);e->data=0; + e = e->next; + } + } + dict_destroy(state->vars);state->vars=0; + } + + free(state); +} + static void old_state() { - if(!state_stack || !state_stack->next) + if(!state || !state->old) syntaxerror("invalid nesting"); - state_t*oldstate = state; - state_list_t*old = state_stack; - state_stack = state_stack->next; - free(old); - state = state_stack->state; - /*if(state->initcode) { + state_t*leaving = state; + state = state->old; + /*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); + state_destroy(leaving); } void initialize_state() { - init_globals(); + global = rfx_calloc(sizeof(global_t)); new_state(); - state->file = abc_file_new(); - state->file->flags &= ~ABCFILE_LAZY; + state->package = current_filename; + + global->file = abc_file_new(); + global->file->flags &= ~ABCFILE_LAZY; + global->variable_count = 1; - 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); @@ -397,36 +465,41 @@ 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; + + state_destroy(state); + + return global->file; } static void startpackage(char*name) { - if(state->package) { - syntaxerror("Packages can not be nested."); - } new_state(); /*printf("entering package \"%s\"\n", name);*/ - state->package = name; + state->package = strdup(name); + global->variable_count = 1; } static void endpackage() { /*printf("leaving package \"%s\"\n", state->package);*/ + + //used e.g. in classinfo_register: + //free(state->package);state->package=0; + old_state(); } @@ -437,6 +510,8 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo syntaxerror("inner classes now allowed"); } new_state(); + global->variable_count = 1; + state->cls = rfx_calloc(sizeof(classstate_t)); token_list_t*t=0; classinfo_list_t*mlist=0; @@ -451,7 +526,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo printf("\n"); */ - if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL)) + if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC)) syntaxerror("invalid modifier(s)"); if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL)) @@ -475,39 +550,45 @@ 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) { + state->cls->info->flags |= CLASS_INTERFACE; + abc_class_interface(state->cls->abc); + } + + abc_class_protectedNS(state->cls->abc, classname); 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; @@ -546,7 +627,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); } @@ -555,7 +636,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo /* flash.display.MovieClip handling */ if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) { if(state->package && state->package[0]) { - globalclass = concat3str(state->package, ".", classname); + globalclass = concat3(state->package, ".", classname); } else { globalclass = strdup(classname); } @@ -563,31 +644,43 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo multiname_destroy(extends2); } +static code_t* wrap_function(code_t*c,code_t*initcode, code_t*body) +{ + c = code_append(c, initcode); + c = code_append(c, body); + /* append return if necessary */ + if(!c || c->opcode != OPCODE_RETURNVOID && + c->opcode != OPCODE_RETURNVALUE) { + c = abc_returnvoid(c); + } + return c; +} + 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); - 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; + if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) { + code_t*c = 0; + c = abc_getlocal_0(c); + c = abc_constructsuper(c, 0); + state->cls->init = code_append(state->cls->init, c); + } - } + if(state->cls->init) { + abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0); + m->body->code = wrap_function(0, state->cls->init, m->body->code); } - 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); - 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); - } + if(state->cls->static_init) { + abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0); + m->body->code = wrap_function(0, state->cls->static_init, m->body->code); + } else { + // handy for scope testing + /*code_t*c = 0; + c = abc_pop(c); + c = abc_pop(c); + abc_class_getstaticconstructor(state->cls->abc,0)->body->code = c;*/ } + free(state->cls);state->cls=0; old_state(); } @@ -596,49 +689,57 @@ typedef struct _variable { classinfo_t*type; } variable_t; -static int find_variable(char*name, classinfo_t**m) +static variable_t* find_variable(char*name) { - state_list_t* s = state_stack; + state_t* s = state; while(s) { - variable_t*v = dict_lookup(s->state->vars, name); + variable_t*v = 0; + if(s->method) + v = dict_lookup(s->vars, name); if(v) { - if(m) { - *m = v->type; - } - return v->index; + return v; } - s = s->next; + s = s->old; } - return -1; + return 0; } -static int find_variable_safe(char*name, classinfo_t**m) +static variable_t* find_variable_safe(char*name) { - int i = find_variable(name, m); - if(i<0) + variable_t* v = find_variable(name); + if(!v) syntaxerror("undefined variable: %s", name); - return i; + return v; } static char variable_exists(char*name) { return dict_lookup(state->vars, name)!=0; } -static int new_variable(char*name, classinfo_t*type) +code_t*defaultvalue(code_t*c, classinfo_t*type); +static int new_variable(char*name, classinfo_t*type, char init) { NEW(variable_t, v); v->index = global->variable_count; v->type = type; + dict_put(state->vars, name, v); + + if(init && state->method && type) { + /* if this is a typed variable: + push default value for type on stack at the very beginning of the + method, so that it always has that type regardless of the control + path */ + state->method->initcode = defaultvalue(state->method->initcode, type); + state->method->initcode = abc_setlocal(state->method->initcode, v->index); + } return global->variable_count++; } #define TEMPVARNAME "__as3_temp__" static int gettempvar() { - int i = find_variable(TEMPVARNAME, 0); - if(i<0) { - return new_variable(TEMPVARNAME, 0); - } else { - return i; - } + variable_t*v = find_variable(TEMPVARNAME); + if(v) + return v->index; + return new_variable(TEMPVARNAME, 0, 0); } code_t* killvars(code_t*c) @@ -651,13 +752,28 @@ code_t* killvars(code_t*c) //do this always, otherwise register types don't match //in the verifier when doing nested loops //if(!TYPE_IS_BUILTIN_SIMPLE(type)) { - c = abc_kill(c, v->index); + c = abc_kill(c, v->index); e = e->next; } } return c; } +void check_code_for_break(code_t*c) +{ + while(c) { + if(c->opcode == OPCODE___BREAK__) { + char*name = string_cstr(c->data[0]); + syntaxerror("Unresolved \"break %s\"", name); + } + if(c->opcode == OPCODE___CONTINUE__) { + char*name = string_cstr(c->data[0]); + syntaxerror("Unresolved \"continue %s\"", name); + } + c=c->prev; + } +} + static void check_constant_against_type(classinfo_t*t, constant_t*c) { @@ -677,26 +793,58 @@ static void check_constant_against_type(classinfo_t*t, constant_t*c) } } +static int flags2access(int flags) +{ + int access = 0; + if(flags&FLAG_PUBLIC) { + if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + access = ACCESS_PACKAGE; + } else if(flags&FLAG_PRIVATE) { + if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + access = ACCESS_PRIVATE; + } else if(flags&FLAG_PROTECTED) { + if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + access = ACCESS_PROTECTED; + } else { + access = ACCESS_PACKAGEINTERNAL; + } + return access; +} + static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot) { memberinfo_t*minfo = 0; - if(getset != KW_GET && getset != KW_SET) { - if(registry_findmember(state->clsinfo, name)) { - syntaxerror("class already contains a member/method called '%s'", name); + if(!state->cls) { + //package method + minfo = memberinfo_register_global(flags2access(flags), state->package, name, MEMBER_METHOD); + minfo->return_type = return_type; + } else if(getset != KW_GET && getset != KW_SET) { + //class method + if((minfo = registry_findmember(state->cls->info, name, 0))) { + if(minfo->parent == state->cls->info) { + syntaxerror("class already contains a member/method called '%s'", name); + } else if(!minfo->parent) { + syntaxerror("internal error: overriding method %s, which doesn't have parent", name); + } else { + if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE))) + syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?"); + } } - 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 { + //class getter/setter int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET; classinfo_t*type=0; if(getset == KW_GET) type = return_type; else if(params->list) type = params->list->param->type; - if((minfo=registry_findmember(state->clsinfo, name))) { + // not sure wether to look into superclasses here, too + if((minfo=registry_findmember(state->cls->info, name, 0))) { if(minfo->kind & ~(MEMBER_GET|MEMBER_SET)) syntaxerror("class already contains a member or method called '%s'", name); if(minfo->kind & gs) @@ -709,7 +857,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 */ @@ -720,59 +868,77 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE; if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED; if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL; + if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE; return minfo; } -static int flags2access(int flags) -{ - int access = 0; - if(flags&FLAG_PUBLIC) { - if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PACKAGE; - } else if(flags&FLAG_PRIVATE) { - if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PRIVATE; - } else if(flags&FLAG_PROTECTED) { - if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PROTECTED; - } else { - access = ACCESS_PACKAGEINTERNAL; - } - return access; -} - 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(); global->variable_count = 0; - state->function = name; - - if(state->m) { - syntaxerror("not able to start another method scope"); + state->method = rfx_calloc(sizeof(methodstate_t)); + state->method->initcode = 0; + state->method->has_super = 0; + if(state->cls) { + state->method->is_constructor = !strcmp(state->cls->info->name,name); + state->cls->has_constructor |= state->method->is_constructor; + + new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0); + } else { + state->method->is_global = 1; + state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack + + new_variable("globalscope", 0, 0); } - namespace_t mname_ns = {flags2access(flags), ""}; - multiname_t mname = {QNAME, &mname_ns, 0, name}; + /* state->vars is initialized by state_new */ + + param_list_t*p=0; + for(p=params->list;p;p=p->next) { + new_variable(p->param->name, p->param->type, 0); + } + 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) +{ + abc_method_t*f = 0; multiname_t*type2 = sig2mname(return_type); int slot = 0; - if(!strcmp(state->clsinfo->name,name)) { - state->m = abc_class_constructor(state->cls, type2); - name = "__as3_constructor__"; - } else { + if(state->method->is_constructor) { + f = abc_class_getconstructor(state->cls->abc, type2); + } else if(!state->method->is_global) { + namespace_t mname_ns = {flags2access(flags), ""}; + multiname_t mname = {QNAME, &mname_ns, 0, name}; + 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); - slot = state->m->trait->slot_id; + f = abc_class_method(state->cls->abc, type2, &mname); + slot = f->trait->slot_id; + } else { + namespace_t mname_ns = {flags2access(flags), state->package}; + multiname_t mname = {QNAME, &mname_ns, 0, name}; + + f = abc_method_new(global->file, type2, 1); + trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f); + //abc_code_t*c = global->init->method->body->code; } - state->minfo = registerfunction(getset, flags, name, params, return_type, slot); + //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(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE; + 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; @@ -781,42 +947,23 @@ 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"); } } + check_code_for_break(body); - /* state->vars is initialized by state_new */ - if(new_variable((flags&FLAG_STATIC)?"class":"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) -{ - - 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; - } + if(f->body) + f->body->code = body; + else //interface + if(body) + syntaxerror("interface methods can't have a method body"); + + free(state->method);state->method=0; old_state(); } @@ -827,16 +974,30 @@ char is_subtype_of(classinfo_t*type, classinfo_t*supertype) return 1; // FIXME } -void breakjumpsto(code_t*c, code_t*jump) +void breakjumpsto(code_t*c, char*name, code_t*jump) { - while(c->prev) - c=c->prev; while(c) { if(c->opcode == OPCODE___BREAK__) { - c->opcode = OPCODE_JUMP; - c->branch = jump; + string_t*name2 = c->data[0]; + if(!name2->len || !strncmp(name2->str, name, name2->len)) { + c->opcode = OPCODE_JUMP; + c->branch = jump; + } } - c = c->next; + c=c->prev; + } +} +void continuejumpsto(code_t*c, char*name, code_t*jump) +{ + while(c) { + if(c->opcode == OPCODE___CONTINUE__) { + string_t*name2 = c->data[0]; + if(!name2->len || !strncmp(name2->str, name, name2->len)) { + c->opcode = OPCODE_JUMP; + c->branch = jump; + } + } + c = c->prev; } } @@ -864,19 +1025,13 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) return abc_coerce2(c, &m); } - if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) { - return abc_coerce2(c, &m); - } - if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) { - return abc_coerce2(c, &m); - } - /* these are subject to overflow */ - if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) { - return abc_coerce2(c, &m); - } - if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) { + if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) && + (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) { + // allow conversion between number types return abc_coerce2(c, &m); } + //printf("%s.%s\n", from.package, from.name); + //printf("%s.%s\n", to.package, to.name); classinfo_t*supertype = from; while(supertype) { @@ -887,7 +1042,7 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) int t=0; while(supertype->interfaces[t]) { if(supertype->interfaces[t]==to) { - // to type is one of from's interfaces + // target type is one of from's interfaces return abc_coerce2(c, &m); } t++; @@ -903,8 +1058,12 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) code_t*defaultvalue(code_t*c, classinfo_t*type) { - if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) { + if(TYPE_IS_INT(type)) { c = abc_pushbyte(c, 0); + } else if(TYPE_IS_UINT(type)) { + c = abc_pushuint(c, 0); + } else if(TYPE_IS_FLOAT(type)) { + c = abc_pushnan(c); } else if(TYPE_IS_BOOLEAN(type)) { c = abc_pushfalse(c); } else { @@ -928,14 +1087,15 @@ static classinfo_t* find_class(char*name) classinfo_t*c=0; c = registry_findclass(state->package, name); + if(c) return c; /* try explicit imports */ dictentry_t* e = dict_get_slot(state->imports, name); + if(c) return c; while(e) { - if(c) - break; if(!strcmp(e->key, name)) { c = (classinfo_t*)e->data; + if(c) return c; } e = e->next; } @@ -943,18 +1103,21 @@ static classinfo_t* find_class(char*name) /* 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); + if(c) return c; l = l->next; } /* try global package */ - if(!c) { - c = registry_findclass("", name); - } - return c; + c = registry_findclass("", name); + if(c) return c; + + /* try local "filename" package */ + c = registry_findclass(current_filename, name); + if(c) return c; + + return 0; } static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore) @@ -1007,6 +1170,8 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r prefix = abc_getlocal(prefix, temp); prefix = abc_swap(prefix); prefix = abc_getlocal(prefix, temp); + if(!use_temp_var); + prefix = abc_kill(prefix, temp); } use_temp_var = 1; } else { @@ -1088,13 +1253,13 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r temp = gettempvar(); if(prefix) { c = code_append(c, prefix); - c = abc_dup(c); } c = code_append(c, middlepart); c = abc_dup(c); c = abc_setlocal(c, temp); c = code_append(c, write); c = abc_getlocal(c, temp); + c = abc_kill(c, temp); } } @@ -1111,34 +1276,73 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r /* ------------ code blocks / statements ---------------- */ -PROGRAM: MAYBECODE +PROGRAM: MAYBE_PROGRAM_CODE_LIST + +MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST +PROGRAM_CODE_LIST: PROGRAM_CODE + | PROGRAM_CODE_LIST PROGRAM_CODE + +PROGRAM_CODE: PACKAGE_DECLARATION + | INTERFACE_DECLARATION + | CLASS_DECLARATION + | FUNCTION_DECLARATION + | SLOT_DECLARATION + | PACKAGE_INITCODE + | ';' + +MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST +INPACKAGE_CODE_LIST: INPACKAGE_CODE + | INPACKAGE_CODE_LIST INPACKAGE_CODE + +INPACKAGE_CODE: INTERFACE_DECLARATION + | CLASS_DECLARATION + | FUNCTION_DECLARATION + | SLOT_DECLARATION + | PACKAGE_INITCODE + | ';' -MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/} -MAYBECODE: {$$=code_new();} +MAYBECODE: CODE {$$=$1;} +MAYBECODE: {$$=code_new();} CODE: CODE CODEPIECE {$$=code_append($1,$2);} CODE: CODEPIECE {$$=$1;} -CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/} -CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/} -CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/} -CODEPIECE: INTERFACE_DECLARATION {$$=code_new();} -CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/} -CODEPIECE: ';' {$$=code_new();} -CODEPIECE: VARIABLE_DECLARATION {$$=$1} -CODEPIECE: VOIDEXPRESSION {$$=$1} -CODEPIECE: FOR {$$=$1} -CODEPIECE: WHILE {$$=$1} -CODEPIECE: BREAK {$$=$1} -CODEPIECE: RETURN {$$=$1} -CODEPIECE: IF {$$=$1} -CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();} -CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();} - -CODEBLOCK : '{' MAYBECODE '}' {$$=$2;} +// code which also may appear outside a method +CODE_STATEMENT: IMPORT +CODE_STATEMENT: VOIDEXPRESSION +CODE_STATEMENT: FOR +CODE_STATEMENT: FOR_IN +CODE_STATEMENT: WHILE +CODE_STATEMENT: DO_WHILE +CODE_STATEMENT: SWITCH +CODE_STATEMENT: IF + +// code which may appear anywhere +//CODEPIECE: PACKAGE_DECLARATION +//CODEPIECE: CLASS_DECLARATION +//CODEPIECE: FUNCTION_DECLARATION +//CODEPIECE: INTERFACE_DECLARATION +CODEPIECE: ';' {$$=0;} +CODEPIECE: VARIABLE_DECLARATION +CODEPIECE: CODE_STATEMENT +CODEPIECE: BREAK +CODEPIECE: CONTINUE +CODEPIECE: RETURN + +CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;} +CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;} + +CODEBLOCK : '{' CODE '}' {$$=$2;} +CODEBLOCK : '{' '}' {$$=0;} CODEBLOCK : CODEPIECE ';' {$$=$1;} CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;} +/* ------------ package init code ------------------- */ + +PACKAGE_INITCODE: CODE_STATEMENT { + if($1) warning("code ignored"); +} + /* ------------ variables --------------------------- */ MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;} @@ -1146,43 +1350,36 @@ MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;} $$.t=TYPE_ANY; } -VAR : "const" | "var" -VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;} +VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;} +VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;} VARIABLE_LIST: ONE_VARIABLE {$$ = $1;} VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);} -ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION +ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION { - if(variable_exists($2)) - syntaxerror("Variable %s already defined", $2); + if(variable_exists($1)) + syntaxerror("Variable %s already defined", $1); - if(!is_subtype_of($4.t, $3)) { - syntaxerror("Can't convert %s to %s", $4.t->name, - $3->name); + if(!is_subtype_of($3.t, $2)) { + syntaxerror("Can't convert %s to %s", $3.t->name, + $2->name); } - int index = new_variable($2, $3); + int index = new_variable($1, $2, 1); - if($3) { - if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) { - $$ = $4.c; - $$ = converttype($$, $4.t, $3); + if($2) { + if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) { + $$ = $3.c; + $$ = converttype($$, $3.t, $2); $$ = abc_setlocal($$, index); } else { - $$ = defaultvalue(0, $3); + $$ = defaultvalue(0, $2); $$ = abc_setlocal($$, index); } - - /* 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); - } } else { - if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) { - $$ = $4.c; + if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) { + $$ = $3.c; $$ = abc_coerce_a($$); $$ = abc_setlocal($$, index); } else { @@ -1192,8 +1389,8 @@ 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:""); } @@ -1204,9 +1401,8 @@ 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; - +IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { + $$ = code_new(); $$ = code_append($$, $4.c); code_t*myjmp,*myif = $$ = abc_iffalse($$, 0); @@ -1214,10 +1410,10 @@ IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { if($7) { myjmp = $$ = abc_jump($$, 0); } - myif->branch = $$ = abc_label($$); + myif->branch = $$ = abc_nop($$); if($7) { $$ = code_append($$, $7); - myjmp->branch = $$ = abc_label($$); + myjmp->branch = $$ = abc_nop($$); } $$ = killvars($$);old_state(); @@ -1226,41 +1422,167 @@ IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { FOR_INIT : {$$=code_new();} FOR_INIT : VARIABLE_DECLARATION FOR_INIT : VOIDEXPRESSION +FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE { + $$=$2;new_variable($2,$3,1); +} +FOR_IN_INIT : T_IDENTIFIER { + $$=$1; +} -FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { - $$ = state->initcode;state->initcode=0; +FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;} +FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;} - $$ = code_append($$, $4); +FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { + if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement"); + $$ = code_new(); + $$ = code_append($$, $2); code_t*loopstart = $$ = abc_label($$); - $$ = code_append($$, $6.c); + $$ = code_append($$, $4.c); code_t*myif = $$ = abc_iffalse($$, 0); - $$ = code_append($$, $10); $$ = code_append($$, $8); + code_t*cont = $$ = abc_nop($$); + $$ = code_append($$, $6); $$ = abc_jump($$, loopstart); - code_t*out = $$ = abc_label($$); - breakjumpsto($$, out); + code_t*out = $$ = abc_nop($$); + breakjumpsto($$, $1.name, out); + continuejumpsto($$, $1.name, cont); myif->branch = out; $$ = killvars($$);old_state(); } -WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK { - $$ = state->initcode;state->initcode=0; +FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { + variable_t*var = find_variable($2); + char*tmp1name = concat2($2, "__tmp1__"); + int it = new_variable(tmp1name, TYPE_INT, 0); + char*tmp2name = concat2($2, "__array__"); + int array = new_variable(tmp1name, 0, 0); + + $$ = code_new(); + $$ = code_append($$, $4.c); + $$ = abc_coerce_a($$); + $$ = abc_setlocal($$, array); + $$ = abc_pushbyte($$, 0); + $$ = abc_setlocal($$, it); + + code_t*loopstart = $$ = abc_label($$); + + $$ = abc_hasnext2($$, array, it); + code_t*myif = $$ = abc_iffalse($$, 0); + $$ = abc_getlocal($$, array); + $$ = abc_getlocal($$, it); + if(!$1.each) + $$ = abc_nextname($$); + else + $$ = abc_nextvalue($$); + $$ = converttype($$, 0, var->type); + $$ = abc_setlocal($$, var->index); + + $$ = code_append($$, $6); + $$ = abc_jump($$, loopstart); + + code_t*out = $$ = abc_nop($$); + breakjumpsto($$, $1.name, out); + continuejumpsto($$, $1.name, loopstart); + + $$ = killvars($$); + + myif->branch = out; + + old_state(); + free(tmp1name); + free(tmp2name); +} + +WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { + $$ = code_new(); code_t*myjmp = $$ = abc_jump($$, 0); code_t*loopstart = $$ = abc_label($$); $$ = code_append($$, $6); - myjmp->branch = $$ = abc_label($$); + code_t*cont = $$ = abc_nop($$); + myjmp->branch = cont; $$ = code_append($$, $4.c); $$ = abc_iftrue($$, loopstart); - code_t*out = $$ = abc_label($$); - breakjumpsto($$, out); + code_t*out = $$ = abc_nop($$); + breakjumpsto($$, $1, out); + continuejumpsto($$, $1, cont); - $$ = killvars($$);old_state(); + $$ = killvars($$); + old_state(); } -BREAK : "break" { - $$ = abc___break__(0); +DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' { + $$ = code_new(); + code_t*loopstart = $$ = abc_label($$); + $$ = code_append($$, $3); + code_t*cont = $$ = abc_nop($$); + $$ = code_append($$, $6.c); + $$ = abc_iftrue($$, loopstart); + code_t*out = $$ = abc_nop($$); + breakjumpsto($$, $1, out); + continuejumpsto($$, $1, cont); + $$ = killvars($$); + old_state(); +} + +BREAK : "break" %prec prec_none { + $$ = abc___break__(0, ""); +} +BREAK : "break" T_IDENTIFIER { + $$ = abc___break__(0, $2); +} +CONTINUE : "continue" %prec prec_none { + $$ = abc___continue__(0, ""); +} +CONTINUE : "continue" T_IDENTIFIER { + $$ = abc___continue__(0, $2); +} + +MAYBE_CASE_LIST : {$$=0;} +MAYBE_CASE_LIST : CASE_LIST {$$=$1;} +MAYBE_CASE_LIST : DEFAULT {$$=$1;} +MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);} +CASE_LIST: CASE {$$=$1} +CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);} + +CASE: "case" E ':' MAYBECODE { + $$ = abc_dup(0); + $$ = code_append($$, $2.c); + code_t*j = $$ = abc_ifne($$, 0); + $$ = code_append($$, $4); + if($$->opcode != OPCODE___BREAK__) { + $$ = abc___fallthrough__($$, ""); + } + code_t*e = $$ = abc_nop($$); + j->branch = e; +} +DEFAULT: "default" ':' MAYBECODE { + $$ = $3; +} +SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' { + $$=$4.c; + $$ = code_append($$, $7); + code_t*out = $$ = abc_pop($$); + breakjumpsto($$, $1, out); + + code_t*c = $$,*lastblock=0; + while(c) { + if(c->opcode == OPCODE_IFNE) { + if(!c->next) syntaxerror("internal error in fallthrough handling"); + lastblock=c->next; + } else if(c->opcode == OPCODE___FALLTHROUGH__) { + if(lastblock) { + c->opcode = OPCODE_JUMP; + c->branch = lastblock; + } else { + /* fall through end of switch */ + c->opcode = OPCODE_NOP; + } + } + c=c->prev; + } + old_state(); } /* ------------ packages and imports ---------------- */ @@ -1268,11 +1590,11 @@ BREAK : "break" { X_IDENTIFIER: T_IDENTIFIER | "package" {$$="package";} -PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3str($1,".",$3);} -PACKAGE: X_IDENTIFIER {$$=$1;} +PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;} +PACKAGE: X_IDENTIFIER {$$=strdup($1);} -PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()} -PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()} +PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;} +PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;} IMPORT : "import" QNAME { classinfo_t*c = $2; @@ -1319,29 +1641,35 @@ IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;} CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER EXTENDS IMPLEMENTS_LIST '{' {startclass($1,$3,$4,$5, 0);} - MAYBE_DECLARATION_LIST - '}' {endclass();} + MAYBE_CLASS_BODY + '}' {endclass();$$=0;} INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER EXTENDS_LIST '{' {startclass($1,$3,0,$4,1);} - MAYBE_IDECLARATION_LIST - '}' {endclass();} + MAYBE_INTERFACE_BODY + '}' {endclass();$$=0;} /* ------------ classes and interfaces (body) -------------- */ -MAYBE_DECLARATION_LIST : -MAYBE_DECLARATION_LIST : DECLARATION_LIST -DECLARATION_LIST : DECLARATION -DECLARATION_LIST : DECLARATION_LIST DECLARATION -DECLARATION : ';' -DECLARATION : SLOT_DECLARATION -DECLARATION : FUNCTION_DECLARATION - -MAYBE_IDECLARATION_LIST : -MAYBE_IDECLARATION_LIST : IDECLARATION_LIST -IDECLARATION_LIST : IDECLARATION -IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION +MAYBE_CLASS_BODY : +MAYBE_CLASS_BODY : CLASS_BODY +CLASS_BODY : CLASS_BODY_ITEM +CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM +CLASS_BODY_ITEM : ';' +CLASS_BODY_ITEM : SLOT_DECLARATION +CLASS_BODY_ITEM : FUNCTION_DECLARATION + +CLASS_BODY_ITEM : CODE_STATEMENT { + code_t*c = state->cls->static_init; + c = code_append(c, $1); + state->cls->static_init = c; +} + +MAYBE_INTERFACE_BODY : +MAYBE_INTERFACE_BODY : INTERFACE_BODY +INTERFACE_BODY : IDECLARATION +INTERFACE_BODY : INTERFACE_BODY IDECLARATION IDECLARATION : ';' IDECLARATION : "var" T_IDENTIFIER { syntaxerror("variable declarations not allowed in interfaces"); @@ -1352,7 +1680,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 ) ------- */ @@ -1361,45 +1689,58 @@ 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 = state->cls? + memberinfo_register(state->cls->info, $3, MEMBER_SLOT): + memberinfo_register_global(flags2access($1), state->package, $3, MEMBER_SLOT); + info->type = $4; info->flags = flags; - trait_t*t=0; + /* slot name */ namespace_t mname_ns = {flags2access(flags), ""}; multiname_t mname = {QNAME, &mname_ns, 0, $3}; - - if(!(flags&FLAG_STATIC)) { - if($4) { - MULTINAME(m, $4); - t=abc_class_slot(state->cls, &mname, &m); - } else { - t=abc_class_slot(state->cls, &mname, 0); - } - info->slot = t->slot_id; + + trait_list_t**traits; + code_t**code; + if(!state->cls) { + // global variable + traits = &global->init->traits; + code = &global->init->method->body->code; + } else if(flags&FLAG_STATIC) { + // static variable + traits = &state->cls->abc->static_traits; + code = &state->cls->static_init; } else { - if($4) { - MULTINAME(m, $4); - t=abc_class_staticslot(state->cls, &mname, &m); - } else { - t=abc_class_staticslot(state->cls, &mname, 0); - } - info->slot = t->slot_id; + // instance variable + traits = &state->cls->abc->traits; + code = &state->cls->init; } + + trait_t*t=0; + if($4) { + MULTINAME(m, $4); + t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0); + } else { + t = trait_new_member(traits, 0, multiname_clone(&mname), 0); + } + info->slot = t->slot_id; + + /* initalization code (if needed) */ + code_t*c = 0; if($5.c && !is_pushundefined($5.c)) { - code_t*c = 0; c = abc_getlocal_0(c); c = code_append(c, $5.c); 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); - else - state->cls_static_init = code_append(state->cls_static_init, c); } + + *code = code_append(*code, c); + if($2==KW_CONST) { t->kind= TRAIT_CONST; } + + $$=0; } /* ------------ constants -------------------------------------- */ @@ -1413,9 +1754,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) ------- */ @@ -1468,8 +1809,19 @@ 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) { + // call default constructor + c = abc_getlocal_0(c); + c = abc_constructsuper(c, 0); + } + c = wrap_function(c, state->method->initcode, $11); + endfunction(0,$1,$3,$4,&$6,$8,c); + $$=0; } /* ------------- package + class ids --------------- */ @@ -1484,6 +1836,7 @@ CLASS: T_IDENTIFIER { PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER { $$ = registry_findclass($1, $3); if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3); + free($1);$1=0; } QNAME: PACKAGEANDCLASS @@ -1494,6 +1847,7 @@ QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);} TYPE : QNAME {$$=$1;} | '*' {$$=registry_getanytype();} + | "void" {$$=registry_getanytype();} /* | "String" {$$=registry_getstringclass();} | "int" {$$=registry_getintclass();} @@ -1507,19 +1861,18 @@ MAYBETYPE: {$$=0;} /* ----------function calls, delete, constructor calls ------ */ -MAYBE_PARAM_VALUES : %prec prec_none {$$=0;} +MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;} MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2} -MAYBE_EXPRESSION_LIST : {$$=0;} +MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;} MAYBE_EXPRESSION_LIST : EXPRESSION_LIST -EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new(); - typedcode_t*t = malloc(sizeof(typedcode_t)); - *t = $1; - list_append($$, t);} -EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1; - typedcode_t*t = malloc(sizeof(typedcode_t)); - *t = $3; - list_append($$, t);} +EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1; + $$.cc = $1.c; + } +EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION { + $$.len= $1.len+1; + $$.cc = code_append($1.cc, $3.c); + } NEW : "new" CLASS MAYBE_PARAM_VALUES { MULTINAME(m, $2); @@ -1532,17 +1885,12 @@ NEW : "new" CLASS MAYBE_PARAM_VALUES { $$.c = abc_findpropstrict2($$.c, &m); } - typedcode_list_t*l = $3; - int len = 0; - while(l) { - $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack - l = l->next; - len ++; - } + $$.c = code_append($$.c, $3.cc); + if($2->slot) - $$.c = abc_construct($$.c, len); + $$.c = abc_construct($$.c, $3.len); else - $$.c = abc_constructprop2($$.c, &m, len); + $$.c = abc_constructprop2($$.c, &m, $3.len); $$.t = $2; } @@ -1551,43 +1899,41 @@ NEW : "new" CLASS MAYBE_PARAM_VALUES { call (for closures) */ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { - typedcode_list_t*l = $3; - int len = 0; - code_t*paramcode = 0; - while(l) { - paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack - l = l->next; - len ++; - } - + $$.c = $1.c; if($$.c->opcode == OPCODE_COERCE_A) { $$.c = code_cutlast($$.c); } + code_t*paramcode = $3.cc; $$.t = TYPE_ANY; - multiname_t*name = 0; if($$.c->opcode == OPCODE_GETPROPERTY) { - name = multiname_clone($$.c->data[0]); + multiname_t*name = $$.c->data[0];$$.c->data[0]=0; $$.c = code_cutlast($$.c); $$.c = code_append($$.c, paramcode); - $$.c = abc_callproperty2($$.c, name, len); + $$.c = abc_callproperty2($$.c, name, $3.len); + multiname_destroy(name); } 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"); + //ok: flash allows to assign closures to members. } - name = t->name; + multiname_t*name = t->name; $$.c = code_cutlast($$.c); $$.c = code_append($$.c, paramcode); //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding - $$.c = abc_callproperty2($$.c, name, len); + $$.c = abc_callproperty2($$.c, name, $3.len); + } else if($$.c->opcode == OPCODE_GETSUPER) { + multiname_t*name = $$.c->data[0];$$.c->data[0]=0; + $$.c = code_cutlast($$.c); + $$.c = code_append($$.c, paramcode); + $$.c = abc_callsuper2($$.c, name, $3.len); + multiname_destroy(name); } else { $$.c = abc_getlocal_0($$.c); $$.c = code_append($$.c, paramcode); - $$.c = abc_call($$.c, len); + $$.c = abc_call($$.c, $3.len); } memberinfo_t*f = 0; @@ -1598,6 +1944,26 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { $$.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); + + $$.c = code_append($$.c, $3.cc); + /* + 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, $3.len); + $$.c = abc_pushundefined($$.c); + $$.t = TYPE_ANY; } DELETE: "delete" E { @@ -1610,7 +1976,7 @@ DELETE: "delete" E { $$.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,slot)->name; + multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name; $$.c = code_cutlast($$.c); $$.c = abc_deleteproperty2($$.c, name); } else { @@ -1639,7 +2005,9 @@ EXPRESSION : EXPRESSION ',' E %prec below_minus { $$.c = code_append($$.c,$3.c); $$.t = $3.t; } -VOIDEXPRESSION : EXPRESSION %prec below_minus {$$=cut_last_push($1.c);} +VOIDEXPRESSION : EXPRESSION %prec below_minus { + $$=cut_last_push($1.c); +} // ----------------------- expression evaluation ------------------------------------- @@ -1671,13 +2039,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; } @@ -1805,6 +2176,11 @@ E : E '*' E {$$.c = code_append($1.c,$3.c); } } +E : E "in" E {$$.c = code_append($1.c,$3.c); + $$.c = abc_in($$.c); + $$.t = TYPE_BOOLEAN; + } + 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); @@ -1817,12 +2193,35 @@ E : E "as" E {char use_astype=0; // flash player's astype works differently than } } +E : E "instanceof" E + {$$.c = code_append($1.c, $3.c); + $$.c = abc_instanceof($$.c); + $$.t = TYPE_BOOLEAN; + } + E : E "is" E {$$.c = code_append($1.c, $3.c); $$.c = abc_istypelate($$.c); $$.t = TYPE_BOOLEAN; } -E : '(' E ')' {$$=$2;} +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)) { @@ -1843,6 +2242,38 @@ E : E '[' E ']' { $$.t = 0; // array elements have unknown type } +E : '[' MAYBE_EXPRESSION_LIST ']' { + $$.c = code_new(); + $$.c = code_append($$.c, $2.cc); + $$.c = abc_newarray($$.c, $2.len); + $$.t = registry_getarrayclass(); +} + +MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;} +MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1}; + +EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION { + $$.cc = 0; + $$.cc = code_append($$.cc, $1.c); + $$.cc = code_append($$.cc, $3.c); + $$.len = 2; +} +EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION { + $$.cc = $1.cc; + $$.len = $1.len+2; + $$.cc = code_append($$.cc, $3.c); + $$.cc = code_append($$.cc, $5.c); +} +//MAYBECOMMA: ',' +//MAYBECOMMA: + +E : '{' MAYBE_EXPRPAIR_LIST '}' { + $$.c = code_new(); + $$.c = code_append($$.c, $2.cc); + $$.c = abc_newobject($$.c, $2.len/2); + $$.t = registry_getobjectclass(); +} + E : E "*=" E { code_t*c = $3.c; if(BOTH_INT($1,$3)) { @@ -1982,49 +2413,48 @@ E : "--" %prec minusminus_prefix 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, 1); + namespace_t ns = {flags2access(f->flags), ""}; + MEMBER_MULTINAME(m, f, $3); + $$.c = 0; + $$.c = abc_getlocal_0($$.c); + $$.c = abc_getsuper2($$.c, &m); + $$.t = memberinfo_gettype(f); + } + E : E '.' T_IDENTIFIER {$$.c = $1.c; classinfo_t*t = $1.t; char is_static = 0; - if(TYPE_IS_CLASS(t)) { - memberinfo_t*m = registry_findmember($1.t, "prototype"); - if(!m) syntaxerror("identifier '%s' not found in anonymous class", $3); - t = m->type; + if(TYPE_IS_CLASS(t) && t->cls) { + t = t->cls; is_static = 1; } if(t) { - memberinfo_t*f = registry_findmember(t, $3); + memberinfo_t*f = registry_findmember(t, $3, 1); 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, $3); + $$.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); @@ -2035,24 +2465,25 @@ E : E '.' T_IDENTIFIER VAR_READ : T_IDENTIFIER { $$.t = 0; $$.c = 0; - int i; classinfo_t*a = 0; memberinfo_t*f = 0; + variable_t*v; /* look at variables */ - if((i = find_variable($1, &$$.t)) >= 0) { + if((v = find_variable($1))) { // $1 is a local variable - $$.c = abc_getlocal($$.c, i); + $$.c = abc_getlocal($$.c, v->index); + $$.t = v->type; /* look at current class' members */ - } else if((f = registry_findmember(state->clsinfo, $1))) { + } else if(state->cls && (f = registry_findmember(state->cls->info, $1, 1))) { // $1 is a function in this class int var_is_static = (f->flags&FLAG_STATIC); - int i_am_static = (state->minfo?(state->minfo->flags&FLAG_STATIC):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}; @@ -2075,22 +2506,29 @@ VAR_READ : T_IDENTIFIER { $$.t = f->type; } - /* look at classes in the current package and imported classes */ + /* look at actual classes, in the current package and imported */ } else if((a = find_class($1))) { - if(a->slot) { - $$.c = abc_getglobalscope($$.c); - $$.c = abc_getslot($$.c, a->slot); - } else { + if(a->flags & FLAG_METHOD) { MULTINAME(m, a); - $$.c = abc_getlex2($$.c, &m); + $$.c = abc_findpropstrict2($$.c, &m); + $$.c = abc_getproperty2($$.c, &m); + $$.t = TYPE_FUNCTION(a->function); + } else { + 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); } - $$.t = TYPE_CLASS(a); /* unknown object, let the avm2 resolve it */ } else { 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}; @@ -2107,9 +2545,9 @@ VAR_READ : T_IDENTIFIER { // ----------------- namespaces ------------------------------------------------- -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;} -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;} -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;} -USE_NAMESPACE : "use" "namespace" T_IDENTIFIER +USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {$$=0;}