X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=2de22f625b2530ef16723569fd4d0b9036b8376a;hp=eb252f6c24823ae2bbcae478ae23ce062077b941;hb=0fd17f47ee30d90181e51f30fc17a6c31646137e;hpb=d4a628240912736bac904767394e3dbcc520b5b5 diff --git a/lib/as3/parser.y b/lib/as3/parser.y index eb252f6..2de22f6 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -43,10 +43,11 @@ extern int a3_lex(); %union tokenunion { enum yytokentype token; - int flags; classinfo_t*classinfo; classinfo_list_t*classinfo_list; + slotinfo_t*slotinfo; + slotinfo_list_t*slotinfo_list; int number_int; unsigned int number_uint; @@ -63,6 +64,7 @@ extern int a3_lex(); for_start_t for_start; abc_exception_t *exception; regexp_t regexp; + modifiers_t flags; struct { abc_exception_list_t *l; code_t*finally; @@ -168,14 +170,14 @@ extern int a3_lex(); %token T_SHR ">>" %type FOR_START -%type X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER +%type X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER NAMESPACE_ID %type VARCONST %type CODE %type CODEPIECE CODE_STATEMENT %type CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH -%type PACKAGE_DECLARATION SLOT_DECLARATION +%type PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT %type FUNCTION_DECLARATION PACKAGE_INITCODE -%type VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW +%type VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW %type CATCH FINALLY %type CATCH_LIST CATCH_FINALLY_LIST %type CLASS_DECLARATION @@ -198,18 +200,20 @@ extern int a3_lex(); %type MAYBE_PARAM_LIST %type MAYBE_MODIFIERS %type MODIFIER_LIST +%type MODIFIER %type STATICCONSTANT MAYBESTATICCONSTANT %type IMPLEMENTS_LIST -%type EXTENDS +%type EXTENDS CLASS_SPEC %type EXTENDS_LIST -%type CLASS PACKAGEANDCLASS CLASS_SPEC + +%type CLASS PACKAGEANDCLASS %type CLASS_SPEC_LIST + %type TYPE //%type VARIABLE %type VAR_READ %type NEW //%type T_IDENTIFIER -%type MODIFIER %type FUNCTIONCALL %type MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST @@ -319,9 +323,17 @@ struct _methodstate { char is_global; int variable_count; + dict_t*unresolved_variables; + char inner; + char uses_parent_function; + int uses_slots; + dict_t*slots; + abc_method_t*abc; int var_index; // for inner methods + int slot_index; // for inner methods + char is_a_slot; // for inner methods code_t*header; abc_exception_list_t*exceptions; @@ -335,7 +347,9 @@ typedef struct _state { char*package; import_list_t*wildcard_imports; + dict_t*import_toplevel_packages; dict_t*imports; + namespace_list_t*active_namespaces; char has_own_imports; char new_vars; // e.g. transition between two functions @@ -369,12 +383,14 @@ DECLARE_LIST(state); multiname_t m;\ namespace_t m##_ns;\ if(f) { \ - m##_ns.access = (f)->access; \ - m##_ns.name = ""; \ + if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \ + m##_ns.name = ((slotinfo_t*)(f))->package; \ + else \ + m##_ns.name = ""; \ m.type = QNAME; \ m.ns = &m##_ns; \ m.namespace_set = 0; \ - m.name = f->name; \ + m.name = ((slotinfo_t*)(f))->name; \ } else { \ m.type = MULTINAME; \ m.ns =0; \ @@ -411,11 +427,15 @@ static void new_state() if(!s->imports) { s->imports = dict_new(); } + if(!s->import_toplevel_packages) { + s->import_toplevel_packages = dict_new(); + } state = s; state->level++; state->has_own_imports = 0; state->vars = dict_new(); state->old = oldstate; + state->new_vars = 0; } static void state_has_imports() { @@ -423,6 +443,18 @@ static void state_has_imports() state->imports = dict_clone(state->imports); state->has_own_imports = 1; } +static void import_toplevel(const char*package) +{ + char* s = strdup(package); + while(1) { + dict_put(state->import_toplevel_packages, s, 0); + char*x = strrchr(s, '.'); + if(!x) + break; + *x = 0; + } + free(s); +} static void state_destroy(state_t*state) { @@ -470,7 +502,7 @@ static void old_state() static code_t* method_header(methodstate_t*m); static code_t* wrap_function(code_t*c,code_t*header, code_t*body); -static void function_initvars(methodstate_t*m, params_t*params, int flags); +static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0); static char* internal_filename_package = 0; @@ -495,7 +527,7 @@ void initialize_file(char*filename) dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method); } else { state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); - function_initvars(state->method, 0, 0); + function_initvars(state->method, 0, 0, 1); global->init = abc_initscript(global->file); state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope } @@ -566,59 +598,73 @@ static void xx_scopetest() c = abc_iftrue(c,xx);*/ } - typedef struct _variable { int index; classinfo_t*type; char init; + methodstate_t*is_inner_method; } variable_t; -static variable_t* find_variable(char*name) +static variable_t* find_variable(state_t*s, char*name) { - state_t* s = state; while(s) { variable_t*v = 0; - if(s->method) - v = dict_lookup(s->vars, name); - if(v) { - return v; - } - if(s->new_vars) - break; + v = dict_lookup(s->vars, name); + if(v) return v; + if(s->new_vars) break; s = s->old; } return 0; -} -static variable_t* find_variable_safe(char*name) +} +static variable_t* find_slot(state_t*s, const char*name) +{ + if(s->method && s->method->slots) + return dict_lookup(s->method->slots, name); + return 0; +} + +static variable_t* find_variable_safe(state_t*s, char*name) { - variable_t* v = find_variable(name); + variable_t* v = find_variable(s, name); if(!v) syntaxerror("undefined variable: %s", name); return v; } static char variable_exists(char*name) { - return dict_lookup(state->vars, name)!=0; + return dict_contains(state->vars, name); } code_t*defaultvalue(code_t*c, classinfo_t*type); -static int new_variable(const char*name, classinfo_t*type, char init) + +static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot) { + if(maybeslot) { + variable_t*v = find_slot(state, name); + if(v) + return v; + } + NEW(variable_t, v); - v->index = state->method->variable_count; + v->index = state->method->variable_count++; v->type = type; v->init = init; dict_put(state->vars, name, v); - return state->method->variable_count++; + return v; +} +static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot) +{ + return new_variable2(name, type, init, maybeslot)->index; } + #define TEMPVARNAME "__as3_temp__" static int gettempvar() { - variable_t*v = find_variable(TEMPVARNAME); + variable_t*v = find_variable(state, TEMPVARNAME); if(v) return v->index; - return new_variable(TEMPVARNAME, 0, 0); + return new_variable(TEMPVARNAME, 0, 0, 0); } code_t* var_block(code_t*body) @@ -665,6 +711,14 @@ code_t* var_block(code_t*body) return c; } +void unknown_variable(char*name) +{ + if(!state->method->unresolved_variables) + state->method->unresolved_variables = dict_new(); + if(!dict_contains(state->method->unresolved_variables, name)) + dict_put(state->method->unresolved_variables, name, 0); +} + #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);} static void parsererror(const char*file, int line, const char*f) @@ -676,19 +730,27 @@ static void parsererror(const char*file, int line, const char*f) static code_t* method_header(methodstate_t*m) { code_t*c = 0; - if(m->late_binding && !m->inner) { + if(m->uses_slots || (m->late_binding && !m->inner)) { c = abc_getlocal_0(c); c = abc_pushscope(c); } - /*if(m->innerfunctions) { + if(m->uses_slots) { c = abc_newactivation(c); c = abc_pushscope(c); - }*/ + } methodstate_list_t*l = m->innerfunctions; while(l) { parserassert(l->methodstate->abc); - c = abc_newfunction(c, l->methodstate->abc); - c = abc_setlocal(c, l->methodstate->var_index); + if(m->uses_slots && l->methodstate->is_a_slot) { + c = abc_getscopeobject(c, 1); + c = abc_newfunction(c, l->methodstate->abc); + c = abc_dup(c); + c = abc_setlocal(c, l->methodstate->var_index); + c = abc_setslot(c, l->methodstate->slot_index); + } else { + c = abc_newfunction(c, l->methodstate->abc); + c = abc_setlocal(c, l->methodstate->var_index); + } free(l->methodstate);l->methodstate=0; l = l->next; } @@ -742,75 +804,116 @@ static void endpackage() #define FLAG_PACKAGEINTERNAL 2048 #define FLAG_NAMESPACE 4096 -static int flags2access(int flags) +static namespace_t modifiers2access(modifiers_t*mod) { - int access = 0; - if(flags&FLAG_PUBLIC) { - if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + namespace_t ns; + ns.access = 0; + ns.name = ""; + if(mod->flags&FLAG_NAMESPACE) { + if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels and namespaces"); + ns.access = ACCESS_NAMESPACE; + ns.name = mod->ns; + } else if(mod->flags&FLAG_PUBLIC) { + if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PACKAGE; - } else if(flags&FLAG_PRIVATE) { - if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + ns.access = ACCESS_PACKAGE; + } else if(mod->flags&FLAG_PRIVATE) { + if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PRIVATE; - } else if(flags&FLAG_PROTECTED) { - if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL)) + ns.access = ACCESS_PRIVATE; + } else if(mod->flags&FLAG_PROTECTED) { + if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PROTECTED; + ns.access = ACCESS_PROTECTED; } else { - access = ACCESS_PACKAGEINTERNAL; + ns.access = ACCESS_PACKAGEINTERNAL; } - return access; + return ns; } +static slotinfo_t* find_class(const char*name); -static void function_initvars(methodstate_t*m, params_t*params, int flags) +static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0) { - if(m->inner) - new_variable("this", 0, 0); - else if(!m->is_global) - new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0); - else - new_variable("globalscope", 0, 0); + if(var0) { + int index = -1; + if(m->inner) + index = new_variable("this", 0, 0, 0); + else if(!m->is_global) + index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0); + else + index = new_variable("globalscope", 0, 0, 0); + if(index) + *(int*)0=0; + parserassert(!index); + } + if(m->uses_slots) { + /* as variables and slots share the same number, make sure + that those variable indices are reserved. It's up to the + optimizer to later shuffle the variables down to lower + indices */ + m->variable_count = m->uses_slots; + DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) { + if(v->type) { + if(v->type->package) + v->type = (classinfo_t*)registry_find(v->type->package, v->type->name); + else + v->type = (classinfo_t*)find_class(v->type->name); + if(!v->type || v->type->kind != INFOTYPE_CLASS) { + syntaxerror("Couldn't find class %s", v->type->name); + } + } + } + } if(params) { param_list_t*p=0; for(p=params->list;p;p=p->next) { - new_variable(p->param->name, p->param->type, 0); + new_variable(p->param->name, p->param->type, 0, 1); } } methodstate_list_t*l = m->innerfunctions; while(l) { methodstate_t*m = l->methodstate; - m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0); + variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1); + m->var_index = v->index; + m->slot_index = v->index; + v->is_inner_method = m; l = l->next; } } char*as3_globalclass=0; -static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements) +static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements) { if(state->cls) { syntaxerror("inner classes now allowed"); } + new_state(); token_list_t*t=0; classinfo_list_t*mlist=0; - if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE)) + if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE)) syntaxerror("invalid modifier(s)"); - if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) + if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) syntaxerror("public and internal not supported at the same time."); + + if(!(mod->flags&FLAG_INTERFACE) && !extends) { + // all classes extend object + extends = registry_getobjectclass(); + } /* create the class name, together with the proper attributes */ int access=0; char*package=0; - if(!(flags&FLAG_PUBLIC) && state->package==internal_filename_package) { + if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) { access = ACCESS_PRIVATE; package = internal_filename_package; - } else if(!(flags&FLAG_PUBLIC) && state->package!=internal_filename_package) { + } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) { access = ACCESS_PACKAGEINTERNAL; package = state->package; } else if(state->package!=internal_filename_package) { access = ACCESS_PACKAGE; package = state->package; @@ -838,7 +941,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo /* build info struct */ int num_interfaces = (list_length(implements)); state->cls->info = classinfo_register(access, package, classname, num_interfaces); - state->cls->info->flags |= flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL); + state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL); } if(as3_pass == 2) { @@ -846,15 +949,15 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo state->method = state->cls->init; parserassert(state->cls && state->cls->info); - - function_initvars(state->cls->init, 0, 0); - function_initvars(state->cls->static_init, 0, 0); + + function_initvars(state->cls->init, 0, 0, 1); + function_initvars(state->cls->static_init, 0, 0, 0); if(extends && (extends->flags & FLAG_FINAL)) syntaxerror("Can't extend final class '%s'", extends->name); /* fill out interfaces and extends (we couldn't resolve those during the first pass) */ - state->cls->info->superclass = extends?extends:TYPE_OBJECT; + state->cls->info->superclass = extends; int pos = 0; classinfo_list_t*l = implements; for(l=implements;l;l=l->next) { @@ -933,7 +1036,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo /* flash.display.MovieClip handling */ - if(!as3_globalclass && (flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) { + if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) { if(state->package && state->package[0]) { as3_globalclass = concat3(state->package, ".", classname); } else { @@ -943,10 +1046,14 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo } } -static void setstaticfunction(int x) +static int slotstate_varconst = 0; +static modifiers_t*slotstate_flags = 0; +static void setslotstate(modifiers_t* flags, int varconst) { + slotstate_varconst = varconst; + slotstate_flags = flags; if(state->cls) { - if(x&FLAG_STATIC) { + if(flags && flags->flags&FLAG_STATIC) { state->method = state->cls->static_init; } else { state->method = state->cls->init; @@ -997,6 +1104,10 @@ void check_code_for_break(code_t*c) char*name = string_cstr(c->data[0]); syntaxerror("Unresolved \"continue %s\"", name); } + if(c->opcode == OPCODE___PUSHPACKAGE__) { + char*name = string_cstr(c->data[0]); + syntaxerror("Can't reference a package (%s) as such", name); + } c=c->prev; } } @@ -1032,12 +1143,15 @@ static void check_override(memberinfo_t*m, int flags) return; if(m->flags & FLAG_FINAL) syntaxerror("can't override final member %s", m->name); + + /* allow this. it's no issue. if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC)) - syntaxerror("can't override static member %s", m->name); + syntaxerror("can't override static member %s", m->name);*/ + if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC)) syntaxerror("can't override non-static member %s with static declaration", m->name); - if(!(flags&FLAG_OVERRIDE)) { + if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) { if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) { if(m->kind == INFOTYPE_METHOD) syntaxerror("can't override without explicit 'override' declaration"); @@ -1047,22 +1161,25 @@ static void check_override(memberinfo_t*m, int flags) } } -static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot) +static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot) { methodinfo_t*minfo = 0; - U8 access = flags2access(flags); + namespace_t ns = modifiers2access(mod); if(!state->cls) { //package method - minfo = methodinfo_register_global(access, state->package, name); - minfo->return_type = return_type; + minfo = methodinfo_register_global(ns.access, state->package, name); + minfo->return_type = 0; // save this for pass 2 } else if(getset != KW_GET && getset != KW_SET) { //class method - memberinfo_t* m = registry_findmember(state->cls->info, name, 0); + memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0); if(m) { + printf("%s.%s | %s.%s\n", + m->package, m->name, + ns.name, name); syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name); } - minfo = methodinfo_register_onclass(state->cls->info, access, name); - minfo->return_type = return_type; + minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name); + minfo->return_type = 0; // save this for pass 2 // getslot on a member slot only returns "undefined", so no need // to actually store these //state->minfo->slot = state->method->abc->method->trait->slot_id; @@ -1070,12 +1187,14 @@ static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*na //class getter/setter int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET; classinfo_t*type=0; - if(getset == KW_GET) + if(getset == KW_GET) { type = return_type; - else if(params->list && params->list->param) + } else if(params->list && params->list->param && !params->list->next) { type = params->list->param->type; + } else + syntaxerror("setter function needs to take exactly one argument"); // not sure wether to look into superclasses here, too - minfo = (methodinfo_t*)registry_findmember(state->cls->info, name, 1); + minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1); if(minfo) { if(minfo->kind!=INFOTYPE_SLOT) syntaxerror("class already contains a method called '%s'", name); @@ -1085,24 +1204,30 @@ static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*na syntaxerror("getter/setter for '%s' already defined", name); /* make a setter or getter into a getset */ minfo->subtype |= gs; - if(!minfo->return_type) { - minfo->return_type = type; - } else { - if(minfo && minfo->return_type != type) - syntaxerror("different type in getter and setter"); - } + + /* + FIXME: this check needs to be done in pass 2 + + if((!minfo->return_type != !type) || + (minfo->return_type && type && + !strcmp(minfo->return_type->name, type->name))) { + syntaxerror("different type in getter and setter: %s and %s", + minfo->return_type?minfo->return_type->name:"*", + type?type->name:"*"); + }*/ } else { - minfo = methodinfo_register_onclass(state->cls->info, access, name); + minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name); minfo->kind = INFOTYPE_SLOT; //hack minfo->subtype = gs; - minfo->return_type = type; + minfo->return_type = 0; } /* can't assign a slot as getter and setter might have different slots */ //minfo->slot = slot; } - if(flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL; - if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC; - if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE; + if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL; + if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC; + if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE; + return minfo; } @@ -1113,9 +1238,7 @@ static void innerfunction(char*name, params_t*params, classinfo_t*return_type) methodstate_t*parent_method = state->method; if(as3_pass==1) { - // not valid yet - params = 0; - return_type = 0; + return_type = 0; // not valid in pass 1 } new_state(); @@ -1137,24 +1260,28 @@ static void innerfunction(char*name, params_t*params, classinfo_t*return_type) list_append(parent_method->innerfunctions, state->method); dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method); + + function_initvars(state->method, params, 0, 1); } if(as3_pass == 2) { state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); + state->method->variable_count = 0; parserassert(state->method); state->method->info->return_type = return_type; - function_initvars(state->method, params, 0); + function_initvars(state->method, params, 0, 1); } } -static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name, +static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type) { if(state->method && state->method->info) { syntaxerror("not able to start another method scope"); } new_state(); + state->new_vars = 1; if(as3_pass == 1) { state->method = rfx_calloc(sizeof(methodstate_t)); @@ -1169,19 +1296,21 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n if(state->method->is_constructor) name = "__as3_constructor__"; - return_type = 0; - state->method->info = registerfunction(getset, flags, name, params, return_type, 0); + state->method->info = registerfunction(getset, mod, name, params, return_type, 0); + + function_initvars(state->method, params, mod->flags, 1); dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method); } if(as3_pass == 2) { state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); + state->method->variable_count = 0; parserassert(state->method); - + if(state->cls) { - memberinfo_t*m = registry_findmember(state->cls->info, name, 2); - check_override(m, flags); + memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2); + check_override(m, mod->flags); } if(state->cls) { @@ -1189,79 +1318,157 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n } state->method->info->return_type = return_type; - function_initvars(state->method, params, flags); + function_initvars(state->method, params, mod->flags, 1); } } -static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name, +static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type, code_t*body) { + int flags = mod?mod->flags:0; + if(as3_pass==1) { + // store inner methods in variables + function_initvars(state->method, 0, 0, 0); + + methodstate_list_t*ml = state->method->innerfunctions; + + dict_t*xvars = dict_new(); + + while(ml) { + methodstate_t*m = ml->methodstate; + parserassert(m->inner); + if(m->unresolved_variables) { + dict_t*d = m->unresolved_variables; + int t; + for(t=0;thashsize;t++) { + dictentry_t*l = d->slots[t]; + while(l) { + /* check parent method's variables */ + variable_t*v; + if((v=find_variable(state, l->key))) { + m->uses_parent_function = 1; + state->method->uses_slots = 1; + dict_put(xvars, l->key, 0); + } + l = l->next; + } + if(l) break; + } + + dict_destroy(m->unresolved_variables); + m->unresolved_variables = 0; + } + ml = ml->next; + } + + if(state->method->uses_slots) { + state->method->slots = dict_new(); + int i = 1; + DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) { + if(v->index && dict_contains(xvars, name)) { + v->init = 0; + v->index = i++; + if(v->is_inner_method) { + v->is_inner_method->is_a_slot = 1; + } + //v->type = 0; + dict_put(state->method->slots, name, v); + } + } + state->method->uses_slots = i; + dict_destroy(state->vars);state->vars = 0; + } old_state(); return 0; } - abc_method_t*f = 0; + if(as3_pass==2) { + /*if(state->method->uses_parent_function){ + syntaxerror("accessing variables of parent function from inner functions not supported yet"); + }*/ + + abc_method_t*f = 0; + + multiname_t*type2 = sig2mname(return_type); + int slot = 0; + if(state->method->inner) { + f = state->method->abc; + abc_method_init(f, global->file, type2, 1); + } else if(state->method->is_constructor) { + f = abc_class_getconstructor(state->cls->abc, type2); + } else if(!state->method->is_global) { + namespace_t mname_ns = {state->method->info->access, ""}; + multiname_t mname = {QNAME, &mname_ns, 0, name}; + + if(flags&FLAG_STATIC) + f = abc_class_staticmethod(state->cls->abc, type2, &mname); + else + f = abc_class_method(state->cls->abc, type2, &mname); + slot = f->trait->slot_id; + } else { + namespace_t mname_ns = {state->method->info->access, state->package}; + multiname_t mname = {QNAME, &mname_ns, 0, name}; - multiname_t*type2 = sig2mname(return_type); - int slot = 0; - if(state->method->inner) { - f = state->method->abc; - abc_method_init(f, global->file, type2, 1); - } else if(state->method->is_constructor) { - f = abc_class_getconstructor(state->cls->abc, type2); - } else if(!state->method->is_global) { - namespace_t mname_ns = {state->method->info->access, ""}; - 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; + } + //flash doesn't seem to allow us to access function slots + //state->method->info->slot = slot; - if(flags&FLAG_STATIC) - f = abc_class_staticmethod(state->cls->abc, type2, &mname); - else - f = abc_class_method(state->cls->abc, type2, &mname); - slot = f->trait->slot_id; - } else { - namespace_t mname_ns = {state->method->info->access, 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; - } - //flash doesn't seem to allow us to access function slots - //state->method->info->slot = slot; - - 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; - for(p=params->list;p;p=p->next) { - if(params->varargs && !p->next) { - break; //varargs: omit last parameter in function signature + 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; + for(p=params->list;p;p=p->next) { + if(params->varargs && !p->next) { + break; //varargs: omit last parameter in function signature + } + multiname_t*m = sig2mname(p->param->type); + list_append(f->parameters, m); + if(p->param->value) { + check_constant_against_type(p->param->type, 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"); + } } - multiname_t*m = sig2mname(p->param->type); - list_append(f->parameters, m); - if(p->param->value) { - check_constant_against_type(p->param->type, 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"); + if(state->method->slots) { + DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) { + if(v->index) { + multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name); + multiname_t*type = sig2mname(v->type); + trait_t*t = trait_new_member(&f->body->traits, type, mname, 0); + t->slot_id = v->index; + } + } } - } - check_code_for_break(body); - if(f->body) { - f->body->code = body; - f->body->exceptions = state->method->exceptions; - } else { //interface - if(body) - syntaxerror("interface methods can't have a method body"); + check_code_for_break(body); + + if(state->method->exceptions && + (state->method->late_binding || state->method->uses_slots)) { + //syntaxerror("try/catch and activation or late binding not supported yet within the same method"); + as3_warning("try/catch and activation or late binding not supported yet within the same method"); + } + + if(f->body) { + f->body->code = body; + f->body->exceptions = state->method->exceptions; + } else { //interface + if(body) + syntaxerror("interface methods can't have a method body"); + } + + old_state(); + return f; } - - old_state(); - return f; + + return 0; } char is_subtype_of(classinfo_t*type, classinfo_t*supertype) @@ -1395,7 +1602,7 @@ char is_pushundefined(code_t*c) return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED); } -static slotinfo_t* find_class(char*name) +static slotinfo_t* find_class(const char*name) { slotinfo_t*c=0; @@ -1432,6 +1639,43 @@ static slotinfo_t* find_class(char*name) return 0; } +typedcode_t push_class(slotinfo_t*a) +{ + typedcode_t x; + x.c = 0; + x.t = 0; + if(a->access == ACCESS_PACKAGEINTERNAL && + strcmp(a->package, state->package) && + strcmp(a->package, internal_filename_package) + ) { + syntaxerror("Can't access internal %s %s in package '%s' from package '%s'", + infotypename(a), a->name, a->package, state->package); + } + + if(a->kind != INFOTYPE_CLASS) { + MULTINAME(m, a); + x.c = abc_findpropstrict2(x.c, &m); + x.c = abc_getproperty2(x.c, &m); + if(a->kind == INFOTYPE_METHOD) { + methodinfo_t*f = (methodinfo_t*)a; + x.t = TYPE_FUNCTION(f); + } else { + varinfo_t*v = (varinfo_t*)a; + x.t = v->type; + } + } else { + classinfo_t*c = (classinfo_t*)a; + if(c->slot) { + x.c = abc_getglobalscope(x.c); + x.c = abc_getslot(x.c, c->slot); + } else { + MULTINAME(m, c); + x.c = abc_getlex2(x.c, &m); + } + x.t = TYPE_CLASS(c); + } + return x; +} static char is_getlocal(code_t*c) { @@ -1479,7 +1723,7 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r } else { prefix = 0; } - + char use_temp_var = readbefore; /* generate the write instruction, and maybe append a dup to the prefix code */ @@ -1528,6 +1772,10 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r write->opcode = OPCODE_SETLOCAL_2; } else if(r->opcode == OPCODE_GETLOCAL_3) { write->opcode = OPCODE_SETLOCAL_3; + } else if(r->opcode == OPCODE_GETSUPER) { + write->opcode = OPCODE_SETSUPER; + multiname_t*m = (multiname_t*)r->data[0]; + write->data[0] = multiname_clone(m); } else { code_dump(r); syntaxerror("illegal lvalue: can't assign a value to this expression"); @@ -1775,7 +2023,7 @@ MAYBECODE: {$$=code_new();} CODE: CODE CODEPIECE {$$=code_append($1,$2);} CODE: CODEPIECE {$$=$1;} -// code which also may appear outside a method +// code which may appear outside of methods CODE_STATEMENT: IMPORT CODE_STATEMENT: FOR CODE_STATEMENT: FOR_IN @@ -1787,10 +2035,11 @@ CODE_STATEMENT: WITH CODE_STATEMENT: TRY CODE_STATEMENT: VOIDEXPRESSION CODE_STATEMENT: USE_NAMESPACE +CODE_STATEMENT: NAMESPACE_DECLARATION CODE_STATEMENT: '{' CODE '}' {$$=$2;} CODE_STATEMENT: '{' '}' {$$=0;} -// code which may appear anywhere +// code which may appear in methods CODEPIECE: ';' {$$=0;} CODEPIECE: CODE_STATEMENT CODEPIECE: VARIABLE_DECLARATION @@ -1800,8 +2049,6 @@ CODEPIECE: RETURN CODEPIECE: THROW CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;} -CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;} - //CODEBLOCK : '{' CODE '}' {$$=$2;} //CODEBLOCK : '{' '}' {$$=0;} CODEBLOCK : CODEPIECE ';' {$$=$1;} @@ -1833,43 +2080,61 @@ VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);} ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION { +PASS12 if(variable_exists($1)) syntaxerror("Variable %s already defined", $1); +PASS1 + new_variable($1, 0, 1, 0); +PASS2 if(!is_subtype_of($3.t, $2)) { syntaxerror("Can't convert %s to %s", $3.t->name, $2->name); } - int index = new_variable($1, $2, 1); + char slot = 0; + int index = 0; + if(state->method->uses_slots) { + variable_t* v = find_slot(state, $1); + if(v && !v->init) { + // this variable is stored in a slot + v->init = 1; + v->type = $2; + slot = 1; + index = v->index; + } + } + if(!index) { + index = new_variable($1, $2, 1, 0); + } + + $$ = slot?abc_getscopeobject(0, 1):0; if($2) { if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) { - $$ = $3.c; + $$ = code_append($$, $3.c); $$ = converttype($$, $3.t, $2); - $$ = abc_setlocal($$, index); } else { code_free($3.c); - $$ = defaultvalue(0, $2); - $$ = abc_setlocal($$, index); + $$ = defaultvalue($$, $2); } } else { if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) { - $$ = $3.c; + $$ = code_append($$, $3.c); $$ = abc_coerce_a($$); - $$ = abc_setlocal($$, index); } else { + // don't do anything code_free($3.c); - $$ = code_new(); + code_free($$); + $$ = 0; + break; } } - - /* that's the default for a local register, anyway - else { - 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:""); + if(slot) { + $$ = abc_setslot($$, index); + } else { + $$ = abc_setlocal($$, index); + } } /* ------------ control flow ------------------------- */ @@ -1878,7 +2143,7 @@ MAYBEELSE: %prec below_else {$$ = code_new();} MAYBEELSE: "else" CODEBLOCK {$$=$2;} //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;} -IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { +IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { $$ = code_new(); $$ = code_append($$, $4.c); @@ -1894,7 +2159,7 @@ IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { myjmp->branch = $$ = abc_nop($$); } $$ = var_block($$); - old_state(); + PASS12 old_state(); } FOR_INIT : {$$=code_new();} @@ -1905,14 +2170,16 @@ FOR_INIT : VOIDEXPRESSION // (I don't see any easy way to revolve this conflict otherwise, as we // can't touch VAR_READ without upsetting the precedence about "return") FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE { - $$=$2;new_variable($2,$3,1); + PASS1 $$=$2;new_variable($2,0,1,0); + PASS2 $$=$2;new_variable($2,$3,1,0); } FOR_IN_INIT : T_IDENTIFIER { + PASS12 $$=$1; } -FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;} -FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;} +FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;} +FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;} FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement"); @@ -1931,15 +2198,19 @@ FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { myif->branch = out; $$ = var_block($$); - old_state(); + PASS12 old_state(); } FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { - variable_t*var = find_variable($2); + variable_t*var = find_variable(state, $2); + if(!var) { + syntaxerror("variable %s not known in this scope", $2); + } + char*tmp1name = concat2($2, "__tmp1__"); - int it = new_variable(tmp1name, TYPE_INT, 0); + int it = new_variable(tmp1name, TYPE_INT, 0, 0); char*tmp2name = concat2($2, "__array__"); - int array = new_variable(tmp1name, 0, 0); + int array = new_variable(tmp1name, 0, 0, 0); $$ = code_new(); $$ = code_append($$, $4.c); @@ -1971,13 +2242,14 @@ FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { myif->branch = out; $$ = var_block($$); - old_state(); free(tmp1name); free(tmp2name); + + PASS12 old_state(); } -WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { +WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK { $$ = code_new(); @@ -1993,10 +2265,10 @@ WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { continuejumpsto($$, $1, cont); $$ = var_block($$); - old_state(); + PASS12 old_state(); } -DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' { +DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' { $$ = code_new(); code_t*loopstart = $$ = abc_label($$); $$ = code_append($$, $3); @@ -2008,7 +2280,7 @@ DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' { continuejumpsto($$, $1, cont); $$ = var_block($$); - old_state(); + PASS12 old_state(); } BREAK : "break" %prec prec_none { @@ -2045,7 +2317,7 @@ CASE: "case" E ':' MAYBECODE { DEFAULT: "default" ':' MAYBECODE { $$ = $3; } -SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' { +SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' { $$=$4.c; $$ = code_append($$, $7); code_t*out = $$ = abc_pop($$); @@ -2069,12 +2341,16 @@ SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' { } $$ = var_block($$); - old_state(); + PASS12 old_state(); } /* ------------ try / catch /finally ---------------- */ -CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);} +CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state(); + state->exception_name=$3; + PASS1 new_variable($3, 0, 0, 0); + PASS2 new_variable($3, $4, 0, 0); + } '{' MAYBECODE '}' { namespace_t name_ns = {ACCESS_PACKAGE, ""}; multiname_t name = {QNAME, &name_ns, 0, $3}; @@ -2085,20 +2361,19 @@ CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name $$ = e; code_t*c = 0; - int i = find_variable_safe($3)->index; + int i = find_variable_safe(state, $3)->index; e->target = c = abc_nop(0); c = abc_setlocal(c, i); c = code_append(c, $8); c = abc_kill(c, i); c = var_block(c); - old_state(); + PASS12 old_state(); } -FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' { +FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' { $4 = var_block($4); if(!$4) { $$=0; - old_state(); } else { NEW(abc_exception_t, e) e->exc_type = 0; //all exceptions @@ -2106,9 +2381,9 @@ FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' { e->target = 0; e->to = abc_nop(0); e->to = code_append(e->to, $4); - old_state(); $$ = e; } + PASS12 old_state(); } CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);} @@ -2131,7 +2406,7 @@ CATCH_FINALLY_LIST: FINALLY { } } -TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST { +TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST { code_t*out = abc_nop(0); code_t*start = abc_nop(0); @@ -2143,7 +2418,7 @@ TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST { int tmp; if($6.finally) - tmp = new_variable("__finally__", 0, 0); + tmp = new_variable("__finally__", 0, 0, 0); abc_exception_list_t*l = $6.l; int count=0; @@ -2171,7 +2446,7 @@ TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST { list_concat(state->method->exceptions, $6.l); $$ = var_block($$); - old_state(); + PASS12 old_state(); } /* ------------ throw ------------------------------- */ @@ -2183,7 +2458,7 @@ THROW : "throw" EXPRESSION { THROW : "throw" %prec prec_none { if(!state->exception_name) syntaxerror("re-throw only possible within a catch block"); - variable_t*v = find_variable(state->exception_name); + variable_t*v = find_variable(state, state->exception_name); $$=code_new(); $$=abc_getlocal($$, v->index); $$=abc_throw($$); @@ -2193,7 +2468,7 @@ THROW : "throw" %prec prec_none { WITH : "with" '(' EXPRESSION ')' CODEBLOCK { $$ = $3.c; - $$ = abc_pushscope($$); + $$ = abc_pushwith($$); $$ = code_append($$, $5); $$ = abc_popscope($$); } @@ -2214,7 +2489,7 @@ PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");} IMPORT : "import" PACKAGEANDCLASS { PASS12 slotinfo_t*s = registry_find($2->package, $2->name); - if(!s) {// || !(s->flags&FLAG_BUILTIN)) { + if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) { as3_schedule_class($2->package, $2->name); } @@ -2224,11 +2499,12 @@ IMPORT : "import" PACKAGEANDCLASS { syntaxerror("Couldn't import class\n"); state_has_imports(); dict_put(state->imports, c->name, c); + import_toplevel(c->package); $$=0; } IMPORT : "import" PACKAGE '.' '*' { PASS12 - if(strncmp("flash.", $2, 6)) { + if(strncmp("flash.", $2, 6) && as3_pass==1) { as3_schedule_package($2); } @@ -2237,28 +2513,37 @@ IMPORT : "import" PACKAGE '.' '*' { i->package = $2; state_has_imports(); list_append(state->wildcard_imports, i); + import_toplevel(i->package); $$=0; } /* ------------ classes and interfaces (header) -------------- */ -MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;} +MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;} MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;} MODIFIER_LIST : MODIFIER {PASS12 $$=$1;} -MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;} - -MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;} - | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;} - | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;} - | KW_STATIC {PASS12 $$=FLAG_STATIC;} - | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;} - | KW_FINAL {PASS12 $$=FLAG_FINAL;} - | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;} - | KW_NATIVE {PASS12 $$=FLAG_NATIVE;} - | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;} - | T_NAMESPACE {PASS12 $$=FLAG_NAMESPACE;} - -EXTENDS : {$$=registry_getobjectclass();} +MODIFIER_LIST : MODIFIER_LIST MODIFIER { + PASS12 + $$.flags=$1.flags|$2.flags; + if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration"); + $$.ns=$1.ns?$1.ns:$2.ns; + +} + +MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;} + | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;} + | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;} + | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;} + | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;} + | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;} + | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;} + | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;} + | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;} + | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE; + $$.ns=$1; + } + +EXTENDS : {$$=0;} EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;} EXTENDS_LIST : {PASS12 $$=list_new();} @@ -2269,13 +2554,14 @@ IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;} CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER EXTENDS IMPLEMENTS_LIST - '{' {PASS12 startclass($1,$3,$4,$5);} + '{' {PASS12 startclass(&$1,$3,$4,$5);} MAYBE_CLASS_BODY '}' {PASS12 endclass();$$=0;} INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER EXTENDS_LIST - '{' {PASS12 startclass($1|FLAG_INTERFACE,$3,0,$4);} + '{' {PASS12 $1.flags|=FLAG_INTERFACE; + startclass(&$1,$3,0,$4);} MAYBE_INTERFACE_BODY '}' {PASS12 endclass();$$=0;} @@ -2306,12 +2592,12 @@ IDECLARATION : "var" T_IDENTIFIER { } IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE { PASS12 - $1 |= FLAG_PUBLIC; - if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) { + $1.flags |= FLAG_PUBLIC; + if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) { syntaxerror("invalid method modifiers: interface methods always need to be public"); } - startfunction(0,$1,$3,$4,&$6,$8); - endfunction(0,$1,$3,$4,&$6,$8, 0); + startfunction(&$1,$3,$4,&$6,$8); + endfunction(&$1,$3,$4,&$6,$8, 0); list_deep_free($6.list); } @@ -2319,37 +2605,45 @@ IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LI VARCONST: "var" | "const" -SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} MAYBETYPE MAYBEEXPRESSION { - int flags = $1; - U8 access = flags2access($1); +SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);} + +SLOT_LIST: ONE_SLOT {$$ = $1;} +SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);} + +ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION +{ + int flags = slotstate_flags->flags; + namespace_t ns = modifiers2access(slotstate_flags); varinfo_t* info = 0; if(state->cls) { - memberinfo_t*i = registry_findmember(state->cls->info, $3, 1); + memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1); if(i) { check_override(i, flags); } - info = varinfo_register_onclass(state->cls->info, access, $3); + info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1); } else { - slotinfo_t*i = registry_find(state->package, $3); + slotinfo_t*i = registry_find(state->package, $1); if(i) { - syntaxerror("package %s already contains '%s'", state->package, $3); + syntaxerror("package %s already contains '%s'", state->package, $1); + } + if(ns.name && ns.name[0]) { + syntaxerror("namespaces not allowed on package-level variables"); } - info = varinfo_register_global(access, state->package, $3); + info = varinfo_register_global(ns.access, state->package, $1); } - info->type = $5; + info->type = $2; info->flags = flags; /* slot name */ - namespace_t mname_ns = {access, ""}; - multiname_t mname = {QNAME, &mname_ns, 0, $3}; + multiname_t mname = {QNAME, &ns, 0, $1}; trait_list_t**traits; code_t**code; if(!state->cls) { // global variable - mname_ns.name = state->package; + ns.name = state->package; traits = &global->init->traits; code = &global->init->method->body->code; } else if(flags&FLAG_STATIC) { @@ -2363,8 +2657,8 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} } trait_t*t=0; - if($5) { - MULTINAME(m, $5); + if($2) { + MULTINAME(m, $2); t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0); } else { t = trait_new_member(traits, 0, multiname_clone(&mname), 0); @@ -2373,21 +2667,20 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} /* initalization code (if needed) */ code_t*c = 0; - if($6.c && !is_pushundefined($6.c)) { + if($3.c && !is_pushundefined($3.c)) { c = abc_getlocal_0(c); - c = code_append(c, $6.c); - c = converttype(c, $6.t, $5); + c = code_append(c, $3.c); + c = converttype(c, $3.t, $2); c = abc_setslot(c, t->slot_id); } *code = code_append(*code, c); - if($2==KW_CONST) { + if(slotstate_varconst==KW_CONST) { t->kind= TRAIT_CONST; } $$=0; - setstaticfunction(0); } /* ------------ constants -------------------------------------- */ @@ -2405,9 +2698,12 @@ STATICCONSTANT : "true" {$$ = constant_new_true($1);} STATICCONSTANT : "false" {$$ = constant_new_false($1);} STATICCONSTANT : "null" {$$ = constant_new_null($1);} STATICCONSTANT : T_IDENTIFIER { - // TODO - as3_warning("Couldn't resolve %s", $1); - $$ = constant_new_null($1); + if(!strcmp($1, "NaN")) { + $$ = constant_new_float(__builtin_nan("")); + } else { + as3_warning("Couldn't evaluate constant value of %s", $1); + $$ = constant_new_null($1); + } } /* ------------ classes and interfaces (body, functions) ------- */ @@ -2449,36 +2745,38 @@ PARAM_LIST: PARAM { } PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT { - PASS1 $$=0; - PASS2 - $$ = malloc(sizeof(param_t)); + PASS12 + $$ = rfx_calloc(sizeof(param_t)); $$->name=$1; $$->type = $3; + PASS2 $$->value = $4; } PARAM: T_IDENTIFIER MAYBESTATICCONSTANT { - PASS1 $$=0; - PASS2 - $$ = malloc(sizeof(param_t)); + PASS12 + $$ = rfx_calloc(sizeof(param_t)); $$->name=$1; $$->type = TYPE_ANY; + PASS2 $$->value = $2; } -GETSET : "get" {$$=$1;} - | "set" {$$=$1;} - | {$$=0;} +GETSET : "get" + | "set" + | {PASS12 $$=0;} FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' - MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}' + MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}' { - PASS1 old_state();list_deep_free($6.list); + PASS1 + endfunction(&$1,$3,$4,&$6,0,0); PASS2 if(!state->method->info) syntaxerror("internal error"); code_t*c = method_header(state->method); c = wrap_function(c, 0, $11); - endfunction(0,$1,$3,$4,&$6,$8,c); + endfunction(&$1,$3,$4,&$6,$8,c); + PASS12 list_deep_free($6.list); $$=0; } @@ -2488,7 +2786,8 @@ MAYBE_IDENTIFIER: {PASS12 $$=0;} INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}' { - PASS1 old_state();list_deep_free($4.list); + PASS1 + endfunction(0,0,$2,&$4,0,0); PASS2 methodinfo_t*f = state->method->info; if(!f || !f->kind) syntaxerror("internal error"); @@ -2497,18 +2796,27 @@ INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE c = wrap_function(c, 0, $9); int index = state->method->var_index; - endfunction(0,0,0,$2,&$4,$6,c); - list_deep_free($4.list); + endfunction(0,0,$2,&$4,$6,c); $$.c = abc_getlocal(0, index); $$.t = TYPE_FUNCTION(f); + + PASS12 list_deep_free($4.list); } /* ------------- package + class ids --------------- */ CLASS: T_IDENTIFIER { - PASS1 $$=0; + PASS1 static classinfo_t c; + memset(&c, 0, sizeof(c)); + c.kind = INFOTYPE_CLASS; + c.subtype = 255; + c.name = $1; + $$ = &c; + + /* let the compiler know that we might be looking for this soon */ + as3_schedule_class_noerror(state->package, $1); PASS2 slotinfo_t*s = find_class($1); if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package); @@ -2518,9 +2826,11 @@ CLASS: T_IDENTIFIER { PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER { PASS1 static classinfo_t c; memset(&c, 0, sizeof(c)); + c.kind = INFOTYPE_CLASS; + c.subtype = 255; c.package = $1; c.name = $3; - $$=&c; + $$ = &c; PASS2 slotinfo_t*s = registry_find($1, $3); if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3); @@ -2534,9 +2844,9 @@ CLASS_SPEC: PACKAGEANDCLASS CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);} CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);} -TYPE : CLASS_SPEC {$$=$1;} - | '*' {$$=registry_getanytype();} - | "void" {$$=registry_getanytype();} +TYPE : CLASS_SPEC {PASS12 $$=$1;} + | '*' {PASS12 $$=registry_getanytype();} + | "void" {PASS12 $$=registry_getanytype();} /* | "String" {$$=registry_getstringclass();} | "int" {$$=registry_getintclass();} @@ -2545,8 +2855,8 @@ TYPE : CLASS_SPEC {$$=$1;} | "Number" {$$=registry_getnumberclass();} */ -MAYBETYPE: ':' TYPE {$$=$2;} -MAYBETYPE: {$$=0;} +MAYBETYPE: ':' TYPE {PASS12 $$=$2;} +MAYBETYPE: {PASS12 $$=0;} /* ----------function calls, delete, constructor calls ------ */ @@ -2581,7 +2891,7 @@ NEW : "new" E XX MAYBE_PARAM_VALUES { 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->abc,slot);//FIXME + trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME multiname_t*name = t->name; $$.c = code_cutlast($$.c); $$.c = code_append($$.c, paramcode); @@ -2619,9 +2929,9 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { $$.c = code_append($$.c, paramcode); $$.c = abc_callproperty2($$.c, name, $3.len); multiname_destroy(name); - } else if($$.c->opcode == OPCODE_GETSLOT) { + } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) { int slot = (int)(ptroff_t)$$.c->data[0]; - trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME + trait_t*t = traits_find_slotid(state->cls->abc->traits,slot); if(t->kind!=TRAIT_METHOD) { //ok: flash allows to assign closures to members. } @@ -2681,7 +2991,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->abc,slot)->name; + multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name; $$.c = code_cutlast($$.c); $$.c = abc_deleteproperty2($$.c, name); } else { @@ -3098,13 +3408,13 @@ E : E '?' E ':' E %prec below_assignment { code_t*j2 = $$.c = abc_jump($$.c, 0); $$.c = j1->branch = abc_label($$.c); $$.c = code_append($$.c, $5.c); - $$.c = converttype($$.c, $3.t, $$.t); + $$.c = converttype($$.c, $5.t, $$.t); $$.c = j2->branch = abc_label($$.c); } E : E "++" { code_t*c = 0; classinfo_t*type = $1.t; - if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) { + if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) { int nr = getlocalnr($1.c); code_free($1.c);$1.c=0; if(TYPE_IS_INT($1.t)) { @@ -3177,8 +3487,8 @@ E : "super" '.' T_IDENTIFIER classinfo_t*t = state->cls->info->superclass; if(!t) t = TYPE_OBJECT; - memberinfo_t*f = registry_findmember(t, $3, 1); - namespace_t ns = {f->access, ""}; + memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1); + MEMBER_MULTINAME(m, f, $3); $$.c = 0; $$.c = abc_getlocal_0($$.c); @@ -3225,41 +3535,74 @@ E : E '.' '(' E ')' { -E : E '.' T_IDENTIFIER - {$$.c = $1.c; - classinfo_t*t = $1.t; - char is_static = 0; - if(TYPE_IS_CLASS(t) && t->data) { - t = t->data; - is_static = 1; - } - if(t) { - 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 { - MEMBER_MULTINAME(m, f, $3); - $$.c = abc_getproperty2($$.c, &m); - } - /* determine type */ - $$.t = slotinfo_gettype((slotinfo_t*)f); - if(!$$.t) - $$.c = abc_coerce_a($$.c); - } 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 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); - $$.t = registry_getanytype(); - } - } +E : E '.' T_IDENTIFIER { + $$.c = $1.c; + classinfo_t*t = $1.t; + char is_static = 0; + if(TYPE_IS_CLASS(t) && t->data) { + t = t->data; + is_static = 1; + } + if(t) { + if(t->subtype==0xff) { + syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name); + } + memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $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 { + MEMBER_MULTINAME(m, f, $3); + $$.c = abc_getproperty2($$.c, &m); + } + /* determine type */ + $$.t = slotinfo_gettype((slotinfo_t*)f); + if(!$$.t) + $$.c = abc_coerce_a($$.c); + } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) { + string_t*package = $1.c->data[0]; + char*package2 = concat3(package->str, ".", $3); + if(dict_contains(state->import_toplevel_packages, package2)) { + $$.c = $1.c; + $$.c->data[0] = string_new4(package2); + $$.t = 0; + } else { + slotinfo_t*a = registry_find(package->str, $3); + if(!a) + syntaxerror("couldn't resolve %s", package2); + $$ = push_class(a); + } + } 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 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); + $$.t = registry_getanytype(); + } +} VAR_READ : T_IDENTIFIER { + PASS1 + /* Queue unresolved identifiers for checking against the parent + function's variables. + We consider everything which is not a local variable "unresolved". + This encompasses class names, members of the surrounding class + etc. which is *correct* because local variables of the parent function + would shadow those. + */ + if(state->method->inner && !find_variable(state, $1)) { + unknown_variable($1); + } + + /* let the compiler know that it might check the current directory/package + for this identifier- maybe there's a file $1.as defining $1. */ + as3_schedule_class_noerror(state->package, $1); + PASS2 + $$.t = 0; $$.c = 0; slotinfo_t*a = 0; @@ -3267,17 +3610,23 @@ VAR_READ : T_IDENTIFIER { variable_t*v; /* look at variables */ - if((v = find_variable($1))) { + if((v = find_variable(state, $1))) { // $1 is a local variable $$.c = abc_getlocal($$.c, v->index); $$.t = v->type; break; } + if((v = find_slot(state, $1))) { + $$.c = abc_getscopeobject($$.c, 1); + $$.c = abc_getslot($$.c, v->index); + $$.t = v->type; + break; + } int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC; /* look at current class' members */ - if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) && + if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) && (f->flags&FLAG_STATIC) >= i_am_static) { // $1 is a function in this class int var_is_static = (f->flags&FLAG_STATIC); @@ -3314,41 +3663,21 @@ VAR_READ : T_IDENTIFIER { /* look at actual classes, in the current package and imported */ if((a = find_class($1))) { - if(a->access == ACCESS_PACKAGEINTERNAL && - strcmp(a->package, state->package) && - strcmp(a->package, internal_filename_package) - ) - syntaxerror("Can't access internal %s %s in package '%s' from package '%s'", - infotypename(a),$1, a->package, state->package); - - if(a->kind != INFOTYPE_CLASS) { - MULTINAME(m, a); - $$.c = abc_findpropstrict2($$.c, &m); - $$.c = abc_getproperty2($$.c, &m); - if(a->kind == INFOTYPE_METHOD) { - methodinfo_t*f = (methodinfo_t*)a; - $$.t = TYPE_FUNCTION(f); - } else { - varinfo_t*v = (varinfo_t*)a; - $$.t = v->type; - } - } else { - classinfo_t*c = (classinfo_t*)a; - if(c->slot) { - $$.c = abc_getglobalscope($$.c); - $$.c = abc_getslot($$.c, c->slot); - } else { - MULTINAME(m, c); - $$.c = abc_getlex2($$.c, &m); - } - $$.t = TYPE_CLASS(c); - } + $$ = push_class(a); + break; + } + + /* look through package prefixes */ + if(dict_contains(state->import_toplevel_packages, $1)) { + $$.c = abc___pushpackage__($$.c, $1); + $$.t = 0; break; } /* unknown object, let the avm2 resolve it */ if(1) { - as3_softwarning("Couldn't resolve '%s', doing late binding", $1); + //as3_softwarning("Couldn't resolve '%s', doing late binding", $1); + as3_warning("Couldn't resolve '%s', doing late binding", $1); state->method->late_binding = 1; multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1}; @@ -3361,13 +3690,24 @@ VAR_READ : T_IDENTIFIER { // ----------------- namespaces ------------------------------------------------- -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;} +NAMESPACE_ID : "namespace" T_IDENTIFIER { + PASS12 + tokenizer_register_namespace($2); + $$=$2; +} -USE_NAMESPACE : "use" "namespace" T_IDENTIFIER { +NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID { + $$=0; +} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID '=' T_IDENTIFIER { + $$=0; +} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID '=' T_STRING { + $$=0; +} +USE_NAMESPACE : "use" "namespace" CLASS_SPEC { PASS12 - tokenizer_register_namespace($3); + tokenizer_register_namespace($3->name); $$=0; }