X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=ce8f26206905e5367a45e7a3c5c1ea720df4dd09;hp=b0083d749bbf03eca1eb91686573bf542e28d27b;hb=780e53f414a281c516c153a578638c4f7414434c;hpb=97dcdb638b7f59600544ca6b8d6737b380eba17c diff --git a/lib/as3/parser.y b/lib/as3/parser.y index b0083d7..ce8f262 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -139,6 +139,7 @@ extern int a3_lex(); %token KW_IN "in" %token KW_AS "as" +%token T_DICTSTART "{ (dictionary)" %token T_EQEQ "==" %token T_EQEQEQ "===" %token T_NE "!=" @@ -151,6 +152,7 @@ extern int a3_lex(); %token T_MULBY "*=" %token T_PLUSBY "+=" %token T_MINUSBY "-=" +%token T_XORBY "^=" %token T_SHRBY ">>=" %token T_SHLBY "<<=" %token T_USHRBY ">>>=" @@ -239,9 +241,9 @@ extern int a3_lex(); %left '(' %left new2 -%left '[' ']' "new" '{' '.' ".." "::" '@' +%left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@' -%nonassoc T_IDENTIFIER +%left T_IDENTIFIER %left above_identifier %left below_else %nonassoc "else" @@ -292,18 +294,23 @@ typedef struct _import { DECLARE_LIST(import); +DECLARE(methodstate); +DECLARE_LIST(methodstate); + typedef struct _classstate { /* class data */ classinfo_t*info; abc_class_t*abc; - code_t*init; - code_t*static_init; + + methodstate_t*init; + methodstate_t*static_init; + //code_t*init; + //code_t*static_init; + char has_constructor; } classstate_t; -DECLARE_LIST(methodstate); - -typedef struct _methodstate { +struct _methodstate { /* method data */ methodinfo_t*info; char late_binding; @@ -312,14 +319,22 @@ typedef 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 + char is_a_slot; // for inner methods + code_t*header; abc_exception_list_t*exceptions; methodstate_list_t*innerfunctions; -} methodstate_t; +}; typedef struct _state { struct _state*old; @@ -361,12 +376,12 @@ DECLARE_LIST(state); multiname_t m;\ namespace_t m##_ns;\ if(f) { \ - m##_ns.access = (f)->access; \ + m##_ns.access = ((slotinfo_t*)(f))->access; \ 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; \ @@ -408,6 +423,7 @@ static void new_state() state->has_own_imports = 0; state->vars = dict_new(); state->old = oldstate; + state->new_vars = 0; } static void state_has_imports() { @@ -452,7 +468,7 @@ static void old_state() free(leaving->method); leaving->method=0; } - if(leaving->cls && leaving->cls != state->cls) { + if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) { free(leaving->cls); leaving->cls=0; } @@ -460,6 +476,11 @@ static void old_state() state_destroy(leaving); } +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, char var0); + + static char* internal_filename_package = 0; void initialize_file(char*filename) { @@ -469,9 +490,6 @@ void initialize_file(char*filename) new_state(); state->package = internal_filename_package = strdup(filename); - state->method = rfx_calloc(sizeof(methodstate_t)); - state->method->variable_count = 1; - global->token2info = dict_lookup(global->file2token2info, current_filename // use long version ); @@ -479,6 +497,16 @@ void initialize_file(char*filename) global->token2info = dict_new2(&ptr_type); dict_put(global->file2token2info, current_filename, global->token2info); } + + if(as3_pass==1) { + state->method = rfx_calloc(sizeof(methodstate_t)); + 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, 1); + global->init = abc_initscript(global->file); + state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope + } } void finish_file() @@ -486,10 +514,15 @@ void finish_file() if(!state || state->level!=1) { syntaxerror("unexpected end of file in pass %d", as3_pass); } - free(state->method);state->method=0; - //free(state->package);state->package=0; // used in registry + if(as3_pass==2) { + code_t*header = method_header(state->method); + code_t*c = wrap_function(header, 0, global->init->method->body->code); + global->init->method->body->code = c; + free(state->method);state->method=0; + } + //free(state->package);state->package=0; // used in registry state_destroy(state);state=0; } @@ -500,23 +533,10 @@ void initialize_parser() global->file->flags &= ~ABCFILE_LAZY; global->file2token2info = dict_new(); global->token2info = 0; - - global->init = abc_initscript(global->file); - code_t*c = global->init->method->body->code; - c = abc_getlocal_0(c); - c = abc_pushscope(c); - global->init->method->body->code = c; } void* finish_parser() { - code_t*c = global->init->method->body->code; - /*c = abc_findpropstrict(c, "[package]::trace"); - c = abc_pushstring(c, "[leaving global init function]"); - c = abc_callpropvoid(c, "[package]::trace", 1);*/ - c = abc_returnvoid(c); - global->init->method->body->code = c; - dict_free_all(global->file2token2info, 1, (void*)dict_destroy); global->token2info=0; @@ -554,59 +574,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) @@ -653,6 +687,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) @@ -661,32 +703,44 @@ static void parsererror(const char*file, int line, const char*f) } -code_t* method_header() +static code_t* method_header(methodstate_t*m) { code_t*c = 0; - if(state->method->late_binding && !state->method->inner) { + if(m->uses_slots || (m->late_binding && !m->inner)) { c = abc_getlocal_0(c); c = abc_pushscope(c); } - /*if(state->method->innerfunctions) { + if(m->uses_slots) { c = abc_newactivation(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); } - methodstate_list_t*l = state->method->innerfunctions; + 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->var_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; } - list_free(state->method->innerfunctions); - state->method->innerfunctions = 0; + if(m->header) { + c = code_append(c, m->header); + m->header = 0; + } + if(m->is_constructor && !m->has_super) { + // call default constructor + c = abc_getlocal_0(c); + c = abc_constructsuper(c, 0); + } + list_free(m->innerfunctions); + m->innerfunctions = 0; return c; } @@ -747,6 +801,44 @@ static int flags2access(int flags) return access; } +static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0) +{ + 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 */ + m->variable_count = m->uses_slots; + } + + if(params) { + param_list_t*p=0; + for(p=params->list;p;p=p->next) { + new_variable(p->param->name, p->param->type, 0, 1); + } + } + + methodstate_list_t*l = m->innerfunctions; + while(l) { + methodstate_t*m = l->methodstate; + variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1); + m->var_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) { @@ -754,8 +846,6 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo syntaxerror("inner classes now allowed"); } new_state(); - state->cls = rfx_calloc(sizeof(classstate_t)); - token_list_t*t=0; classinfo_list_t*mlist=0; @@ -780,9 +870,18 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo } if(as3_pass==1) { - state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor - state->method->variable_count = 1; - dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method); + state->cls = rfx_calloc(sizeof(classstate_t)); + state->cls->init = rfx_calloc(sizeof(methodstate_t)); + state->cls->static_init = rfx_calloc(sizeof(methodstate_t)); + /* notice: we make no effort to initialize the top variable (local0) here, + even though it has special meaning. We just rely on the facat + that pass 1 won't do anything with variables */ + + dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls); + + /* set current method to constructor- all code within the class-level (except + static variable initializations) will be executed during construction time */ + state->method = state->cls->init; if(registry_find(package, classname)) { syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname); @@ -794,10 +893,13 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo } if(as3_pass == 2) { - state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); - - state->cls->info = (classinfo_t*)registry_find(package, classname); - parserassert((int)state->cls->info); + state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); + + state->method = state->cls->init; + parserassert(state->cls && state->cls->info); + + 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); @@ -892,6 +994,19 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo } } +static void setstaticfunction(int x) +{ + if(state->cls) { + if(x&FLAG_STATIC) { + state->method = state->cls->static_init; + } else { + state->method = state->cls->init; + } + } else { + parserassert(state->method); + } +} + static void endclass() { if(as3_pass == 2) { @@ -899,23 +1014,23 @@ static void endclass() code_t*c = 0; c = abc_getlocal_0(c); c = abc_constructsuper(c, 0); - state->cls->init = code_append(state->cls->init, c); + state->cls->init->header = code_append(state->cls->init->header, c); + state->cls->has_constructor=1; } - if(!state->method->late_binding) { - // class initialization code uses late binding - code_t*c = 0; - c = abc_getlocal_0(c); - c = abc_pushscope(c); - state->cls->static_init = code_append(c, state->cls->static_init); - } - 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->info->flags&FLAG_INTERFACE) { + if(state->cls->init->header) + syntaxerror("interface can not have class-level code"); + } else { + abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0); + code_t*c = method_header(state->cls->init); + m->body->code = wrap_function(c, 0, m->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); + code_t*c = method_header(state->cls->static_init); + m->body->code = wrap_function(c, 0, m->body->code); } } @@ -947,7 +1062,7 @@ static void check_constant_against_type(classinfo_t*t, constant_t*c) || c->type == CONSTANT_UINT); } else if(TYPE_IS_UINT(t)) { xassert(c->type == CONSTANT_UINT || - (c->type == CONSTANT_INT && c->i>0)); + (c->type == CONSTANT_INT && c->i>=0)); } else if(TYPE_IS_INT(t)) { xassert(c->type == CONSTANT_INT); } else if(TYPE_IS_BOOLEAN(t)) { @@ -1042,38 +1157,14 @@ static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*na return minfo; } -static void function_initvars(params_t*params, int flags) -{ - if(state->method->inner) - new_variable("this", 0, 0); - else if(!state->method->is_global) - new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0); - else - new_variable("globalscope", 0, 0); - - param_list_t*p=0; - for(p=params->list;p;p=p->next) { - new_variable(p->param->name, p->param->type, 0); - } - - methodstate_list_t*l = state->method->innerfunctions; - while(l) { - methodstate_t*m = l->methodstate; - m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0); - l = l->next; - } -} - static void innerfunction(char*name, params_t*params, classinfo_t*return_type) { - parserassert(state->method && state->method->info); + //parserassert(state->method && state->method->info); 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(); @@ -1086,20 +1177,26 @@ static void innerfunction(char*name, params_t*params, classinfo_t*return_type) state->method->abc = rfx_calloc(sizeof(abc_method_t)); NEW(methodinfo_t,minfo); + minfo->kind = INFOTYPE_METHOD; + minfo->access = ACCESS_PACKAGEINTERNAL; minfo->name = name; state->method->info = minfo; - list_append(parent_method->innerfunctions, state->method); + if(parent_method) + 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(params, 0); + function_initvars(state->method, params, 0, 1); } } @@ -1110,11 +1207,11 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n 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)); state->method->has_super = 0; - state->method->variable_count = 0; if(state->cls) { state->method->is_constructor = !strcmp(state->cls->info->name,name); @@ -1127,14 +1224,17 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n return_type = 0; state->method->info = registerfunction(getset, flags, name, params, return_type, 0); + + function_initvars(state->method, params, 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); @@ -1145,7 +1245,7 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n } state->method->info->return_type = return_type; - function_initvars(params, flags); + function_initvars(state->method, params, flags, 1); } } @@ -1153,71 +1253,139 @@ static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, params_t*params, classinfo_t*return_type, code_t*body) { 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 */ + if(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; + } + dict_destroy(xvars); + 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"); + }*/ - 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}; + abc_method_t*f = 0; - 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 + 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}; + + 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; } - 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"); + //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 + } + 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(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) @@ -1319,8 +1487,11 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) return c; if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to)) return c; - syntaxerror("can't convert type %s to %s", from->name, to->name); - return 0; // make gcc happy + + as3_error("can't convert type %s%s%s to %s%s%s", + from->package, from->package?".":"", from->name, + to->package, to->package?".":"", to->name); + return c; } code_t*defaultvalue(code_t*c, classinfo_t*type) @@ -1740,6 +1911,8 @@ CODE_STATEMENT: WITH CODE_STATEMENT: TRY CODE_STATEMENT: VOIDEXPRESSION CODE_STATEMENT: USE_NAMESPACE +CODE_STATEMENT: '{' CODE '}' {$$=$2;} +CODE_STATEMENT: '{' '}' {$$=0;} // code which may appear anywhere CODEPIECE: ';' {$$=0;} @@ -1753,8 +1926,8 @@ CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;} CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;} -CODEBLOCK : '{' CODE '}' {$$=$2;} -CODEBLOCK : '{' '}' {$$=0;} +//CODEBLOCK : '{' CODE '}' {$$=$2;} +//CODEBLOCK : '{' '}' {$$=0;} CODEBLOCK : CODEPIECE ';' {$$=$1;} CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;} @@ -1784,43 +1957,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 ------------------------- */ @@ -1829,7 +2020,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); @@ -1845,7 +2036,7 @@ IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { myjmp->branch = $$ = abc_nop($$); } $$ = var_block($$); - old_state(); + PASS12 old_state(); } FOR_INIT : {$$=code_new();} @@ -1853,15 +2044,19 @@ FOR_INIT : VARIABLE_DECLARATION FOR_INIT : VOIDEXPRESSION // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here? +// (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"); @@ -1880,15 +2075,15 @@ 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); 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); @@ -1920,13 +2115,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(); @@ -1942,10 +2138,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); @@ -1957,7 +2153,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 { @@ -1994,7 +2190,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($$); @@ -2018,12 +2214,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}; @@ -2034,20 +2234,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 @@ -2055,9 +2254,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);} @@ -2080,7 +2279,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); @@ -2092,7 +2291,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; @@ -2120,7 +2319,7 @@ TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST { list_concat(state->method->exceptions, $6.l); $$ = var_block($$); - old_state(); + PASS12 old_state(); } /* ------------ throw ------------------------------- */ @@ -2132,7 +2331,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($$); @@ -2161,8 +2360,9 @@ PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");} MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;} IMPORT : "import" PACKAGEANDCLASS { - PASS1 - if(!registry_find($2->package, $2->name)) { + PASS12 + slotinfo_t*s = registry_find($2->package, $2->name); + if(!s) {// || !(s->flags&FLAG_BUILTIN)) { as3_schedule_class($2->package, $2->name); } @@ -2175,7 +2375,7 @@ IMPORT : "import" PACKAGEANDCLASS { $$=0; } IMPORT : "import" PACKAGE '.' '*' { - PASS1 + PASS12 if(strncmp("flash.", $2, 6)) { as3_schedule_package($2); } @@ -2239,9 +2439,9 @@ CLASS_BODY_ITEM : SLOT_DECLARATION CLASS_BODY_ITEM : FUNCTION_DECLARATION CLASS_BODY_ITEM : CODE_STATEMENT { - code_t*c = state->cls->static_init; + code_t*c = state->cls->static_init->header; c = code_append(c, $1); - state->cls->static_init = c; + state->cls->static_init->header = c; } MAYBE_INTERFACE_BODY : @@ -2267,7 +2467,7 @@ IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LI VARCONST: "var" | "const" -SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION { +SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} MAYBETYPE MAYBEEXPRESSION { int flags = $1; U8 access = flags2access($1); @@ -2277,7 +2477,6 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSIO if(i) { check_override(i, flags); } - info = varinfo_register_onclass(state->cls->info, access, $3); } else { slotinfo_t*i = registry_find(state->package, $3); @@ -2287,7 +2486,7 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSIO info = varinfo_register_global(access, state->package, $3); } - info->type = $4; + info->type = $5; info->flags = flags; /* slot name */ @@ -2304,16 +2503,16 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSIO } else if(flags&FLAG_STATIC) { // static variable traits = &state->cls->abc->static_traits; - code = &state->cls->static_init; + code = &state->cls->static_init->header; } else { // instance variable traits = &state->cls->abc->traits; - code = &state->cls->init; + code = &state->cls->init->header; } trait_t*t=0; - if($4) { - MULTINAME(m, $4); + if($5) { + MULTINAME(m, $5); t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0); } else { t = trait_new_member(traits, 0, multiname_clone(&mname), 0); @@ -2322,10 +2521,10 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSIO /* initalization code (if needed) */ code_t*c = 0; - if($5.c && !is_pushundefined($5.c)) { + if($6.c && !is_pushundefined($6.c)) { c = abc_getlocal_0(c); - c = code_append(c, $5.c); - c = converttype(c, $5.t, $4); + c = code_append(c, $6.c); + c = converttype(c, $6.t, $5); c = abc_setslot(c, t->slot_id); } @@ -2336,6 +2535,7 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSIO } $$=0; + setstaticfunction(0); } /* ------------ constants -------------------------------------- */ @@ -2397,36 +2597,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 '}' { - PASS1 old_state();list_deep_free($6.list); + PASS1 + endfunction(0,$1,$3,$4,&$6,0,0); PASS2 if(!state->method->info) syntaxerror("internal error"); - code_t*c = method_header(); + code_t*c = method_header(state->method); c = wrap_function(c, 0, $11); endfunction(0,$1,$3,$4,&$6,$8,c); + PASS12 list_deep_free($6.list); $$=0; } @@ -2436,20 +2638,22 @@ 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,0,$2,&$4,0,0); PASS2 methodinfo_t*f = state->method->info; - if(!f) syntaxerror("internal error"); + if(!f || !f->kind) syntaxerror("internal error"); - code_t*c = method_header(); + code_t*c = method_header(state->method); 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); $$.c = abc_getlocal(0, index); $$.t = TYPE_FUNCTION(f); + + PASS12 list_deep_free($4.list); } @@ -2458,9 +2662,8 @@ INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE CLASS: T_IDENTIFIER { PASS1 $$=0; PASS2 - /* try current package */ slotinfo_t*s = find_class($1); - if(!s) syntaxerror("Could not find class/method %s\n", $1); + if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package); $$ = (classinfo_t*)s; } @@ -2530,7 +2733,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); @@ -2568,9 +2771,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. } @@ -2630,7 +2833,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 { @@ -2946,7 +3149,7 @@ EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION { //MAYBECOMMA: ',' //MAYBECOMMA: -E : '{' MAYBE_EXPRPAIR_LIST '}' { +E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' { $$.c = code_new(); $$.c = code_append($$.c, $2.cc); $$.c = abc_newobject($$.c, $2.len/2); @@ -3001,6 +3204,12 @@ E : E "|=" E { $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; } +E : E "^=" E { + code_t*c = abc_bitxor($3.c); + c=converttype(c, TYPE_INT, $1.t); + $$.c = toreadwrite($1.c, c, 0, 0); + $$.t = $1.t; + } E : E "+=" E { code_t*c = $3.c; @@ -3203,6 +3412,19 @@ E : E '.' T_IDENTIFIER } 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 *correct* because local variables of the parent function + would shadow those. + */ + if(state->method->inner && !find_variable(state, $1)) { + unknown_variable($1); + } + PASS2 + $$.t = 0; $$.c = 0; slotinfo_t*a = 0; @@ -3210,12 +3432,18 @@ 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;