X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=02a968d4a1ba18ce6b7b0abe399708999ca93926;hb=d9028caacb25b27d07c6b642556e8d372bb267a1;hp=b1e81e8c0281efd566b50e6ed58d1f5dcf73fba7;hpb=639ac6b9f7a89f10d02c5d9ef41bca3bad4eaf2b;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index b1e81e8..02a968d 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -138,6 +138,7 @@ extern int a3_lex(); %token KW_NUMBER "Number" %token KW_STRING "String" %token KW_DEFAULT "default" +%token KW_DEFAULT_XML "default xml" %token KW_DELETE "delete" %token KW_IF "if" %token KW_ELSE "else" @@ -175,14 +176,14 @@ extern int a3_lex(); %token T_USHR ">>>" %token T_SHR ">>" -%type CONDITIONAL_COMPILATION +%type CONDITIONAL_COMPILATION EMBED_START %type FOR_START %type X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER ID_OR_NS SUBNODE %type NAMESPACE_ID %type VARCONST %type CODE %type CODEPIECE CODE_STATEMENT -%type CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH +%type CODEBLOCK IF_CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH %type PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT %type FUNCTION_DECLARATION PACKAGE_INITCODE %type VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW @@ -199,7 +200,7 @@ extern int a3_lex(); %type VAR_READ %type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY %type INNERFUNCTION -%type USE_NAMESPACE +%type USE_NAMESPACE DEFAULT_NAMESPACE %type FOR_INIT %type IMPORT %type MAYBETYPE @@ -216,7 +217,7 @@ extern int a3_lex(); %type EXTENDS_LIST %type CLASS PACKAGEANDCLASS %type CLASS_SPEC_LIST -%type XML XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT XML_ID_OR_EXPR XMLEXPR1 XMLEXPR2 +%type XMLEXPR1 XMLEXPR2 XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT XML_ID_OR_EXPR XML %type TYPE //%type VARIABLE %type MEMBER @@ -265,7 +266,8 @@ extern int a3_lex(); // needed for "return" precedence: %nonassoc T_STRING T_REGEXP -%nonassoc T_INT T_UINT T_FLOAT KW_NAN +%nonassoc T_INT T_UINT T_FLOAT KW_NAN +%left T_NAMESPACE %nonassoc "false" "true" "null" "undefined" "super" "function" %left above_function @@ -327,6 +329,7 @@ typedef struct _classstate { methodstate_t*static_init; //code_t*init; //code_t*static_init; + parsedclass_t*dependencies; char has_constructor; } classstate_t; @@ -346,6 +349,7 @@ struct _methodstate { char inner; char uses_parent_function; + char no_variable_scoping; int uses_slots; dict_t*slots; int activation_var; @@ -365,6 +369,13 @@ struct _methodstate { methodstate_list_t*innerfunctions; }; +void methodstate_destroy(methodstate_t*m) +{ + dict_destroy(m->unresolved_variables); + m->unresolved_variables = 0; + list_free(m->innerfunctions);m->innerfunctions=0; +} + typedef struct _state { struct _state*old; int level; @@ -525,8 +536,7 @@ static void old_state() state = state->old; if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) { - free(leaving->method); - leaving->method=0; + methodstate_destroy(leaving->method);leaving->method=0; } if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) { free(leaving->cls); @@ -568,10 +578,11 @@ void initialize_file(char*filename) state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope } else { state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); + state->method->variable_count = 0; if(!state->method) syntaxerror("internal error: skewed tokencount"); function_initvars(state->method, 0, 0, 0, 1); - global->init = abc_initscript(global->file); + global->init = 0; } } @@ -584,9 +595,12 @@ void finish_file() if(as3_pass==2) { dict_del(global->file2token2info, current_filename); code_t*header = method_header(state->method); - code_t*c = wrap_function(header, 0, global->init->method->body->code); - global->init->method->body->code = abc_returnvoid(c); - free(state->method);state->method=0; + //if(global->init->method->body->code || global->init->traits) { + if(global->init) { + code_t*c = wrap_function(header, 0, global->init->method->body->code); + global->init->method->body->code = abc_returnvoid(c); + free(state->method);state->method=0; + } } //free(state->package);state->package=0; // used in registry @@ -617,26 +631,31 @@ typedef struct _variable { int index; classinfo_t*type; char init; + char kill; char is_parameter; methodstate_t*is_inner_method; } variable_t; static variable_t* find_variable(state_t*s, char*name) { - state_t*top = s; - while(s) { - variable_t*v = 0; - v = dict_lookup(s->vars, name); - if(v) return v; - if(s->new_vars) break; - s = s->old; + if(s->method->no_variable_scoping) { + return dict_lookup(s->allvars, name); + } else { + state_t*top = s; + while(s) { + variable_t*v = 0; + v = dict_lookup(s->vars, name); + if(v) return v; + if(s->new_vars) break; + s = s->old; + } + return 0; } - return dict_lookup(top->allvars, name); } -static variable_t* find_slot(state_t*s, const char*name) +static variable_t* find_slot(methodstate_t*m, const char*name) { - if(s->method && s->method->slots) - return dict_lookup(s->method->slots, name); + if(m && m->slots) + return dict_lookup(m->slots, name); return 0; } @@ -663,6 +682,9 @@ static code_t*defaultvalue(code_t*c, classinfo_t*type) c = abc_pushnan(c); } else if(TYPE_IS_BOOLEAN(type)) { c = abc_pushfalse(c); + } else if(TYPE_IS_STRING(type)) { + c = abc_pushnull(c); + c = abc_coerce_s(c); } else if(!type) { //c = abc_pushundefined(c); syntaxerror("internal error: can't generate default value for * type"); @@ -679,29 +701,47 @@ static int alloc_local() return state->method->variable_count++; } -static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot) +static variable_t* new_variable2(methodstate_t*method, const char*name, classinfo_t*type, char init, char maybeslot) { if(maybeslot) { - variable_t*v = find_slot(state, name); - if(v) + variable_t*v = find_slot(method, name); + if(v) { + alloc_local(); return v; + } } NEW(variable_t, v); v->index = alloc_local(); v->type = type; - v->init = init; + v->init = v->kill = init; if(name) { - dict_put(state->vars, name, v); + if(!method->no_variable_scoping) + { + if(dict_contains(state->vars, name)) { + *(int*)0=0; + syntaxerror("variable %s already defined", name); + } + dict_put(state->vars, name, v); + } + if(method->no_variable_scoping && + as3_pass==2 && + dict_contains(state->allvars, name)) + { + variable_t*v = dict_lookup(state->allvars, name); + if(v->type != type) + syntaxerror("variable %s already defined.", name); + return v; + } dict_put(state->allvars, name, v); } return v; } -static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot) +static int new_variable(methodstate_t*method, const char*name, classinfo_t*type, char init, char maybeslot) { - return new_variable2(name, type, init, maybeslot)->index; + return new_variable2(method, name, type, init, maybeslot)->index; } #define TEMPVARNAME "__as3_temp__" @@ -712,28 +752,23 @@ int gettempvar() if(v) i = v->index; else - i = new_variable(TEMPVARNAME, 0, 0, 0); + i = new_variable(state->method, TEMPVARNAME, 0, 0, 0); parserassert(i); return i; } -static code_t* var_block(code_t*body) +static code_t* var_block(code_t*body, dict_t*vars) { code_t*c = 0; code_t*k = 0; int t; - int num=0; - for(t=0;tvars->hashsize;t++) { - dictentry_t*e = state->vars->slots[t]; - while(e) { - variable_t*v = (variable_t*)e->data; - if(v->type && v->init) { - c = defaultvalue(c, v->type); - c = abc_setlocal(c, v->index); - k = abc_kill(k, v->index); - num++; - } - e = e->next; + DICT_ITERATE_DATA(vars, variable_t*, v) { + if(v->type && v->init) { + c = defaultvalue(c, v->type); + c = abc_setlocal(c, v->index); + } + if(v->type && v->kill) { + k = abc_kill(k, v->index); } } @@ -771,7 +806,7 @@ static void unknown_variable(char*name) static code_t* add_scope_code(code_t*c, methodstate_t*m, char init) { - if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope + if(m->uses_slots || m->innerfunctions || (m->late_binding && !m->inner)) { c = abc_getlocal_0(c); c = abc_pushscope(c); } @@ -847,7 +882,7 @@ static code_t* method_header(methodstate_t*m) static code_t* wrap_function(code_t*c,code_t*header, code_t*body) { c = code_append(c, header); - c = code_append(c, var_block(body)); + c = code_append(c, var_block(body, state->method->no_variable_scoping?state->allvars:state->vars)); /* append return if necessary */ if(!c || (c->opcode != OPCODE_RETURNVOID && c->opcode != OPCODE_RETURNVALUE)) { @@ -916,65 +951,68 @@ static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char rec return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse); } +static void innerfunctions2vars(methodstate_t*m) +{ + methodstate_list_t*l = m->innerfunctions; + while(l) { + methodstate_t*m = l->methodstate; + + variable_t* v = new_variable2(state->method, m->info->name, TYPE_FUNCTION(m->info), 0, 0); + m->var_index = v->index; + if(m->is_a_slot) + m->slot_index = m->is_a_slot; + v->is_inner_method = m; + l = l->next; + } +} + static void function_initvars(methodstate_t*m, char has_params, params_t*params, int flags, char var0) { if(var0) { int index = -1; if(m->inner) - index = new_variable("this", 0, 0, 0); + index = new_variable(m, "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); + index = new_variable(m, (flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0); else - index = new_variable("globalscope", 0, 0, 0); + index = new_variable(m, "globalscope", 0, 0, 0); + if(index) { + DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) { + printf("%s %d\n", name, v->index); + } + } parserassert(!index); } if(has_params) { param_list_t*p=0; for(p=params->list;p;p=p->next) { - variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1); + variable_t*v = new_variable2(m, p->param->name, p->param->type, 0, 1); v->is_parameter = 1; } - variable_t*v = new_variable2("arguments", TYPE_ARRAY, 0, 0); if(as3_pass==2 && m->need_arguments) { + /* arguments can never be used by an innerfunction (the inner functions + have their own arguments var), so it's ok to not initialize this until + pass 2. (We don't know whether we need it before, anyway) */ + variable_t*v = new_variable2(m, "arguments", TYPE_ARRAY, 0, 0); m->need_arguments = v->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; - } - - - 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; - m->slot_index = v->index; - v->is_inner_method = m; - - l = l->next; - } + innerfunctions2vars(m); if(as3_pass==2) { m->scope_code = add_scope_code(m->scope_code, m, 0); - } - - if(as3_pass==2 && m->slots) { - /* exchange unresolved identifiers with the actual objects */ - DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) { - if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) { - classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type); - if(!type || type->kind != INFOTYPE_CLASS) { - syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name); + if(m->slots) { + /* exchange unresolved identifiers with the actual objects */ + DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) { + if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) { + classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type); + if(!type || type->kind != INFOTYPE_CLASS) { + syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name); + } + v->type = type; } - v->type = type; } } } @@ -1026,7 +1064,6 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl state->cls->init = rfx_calloc(sizeof(methodstate_t)); state->cls->static_init = rfx_calloc(sizeof(methodstate_t)); state->cls->static_init->is_static=FLAG_STATIC; - state->cls->static_init->variable_count=1; /* notice: we make no effort to initialize the top variable (local0) here, even though it has special meaning. We just rely on the fact that pass 1 won't do anything with variables */ @@ -1056,10 +1093,12 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl if(as3_pass == 2) { state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); - state->method = state->cls->init; parserassert(state->cls && state->cls->info); + state->method = state->cls->static_init; + function_initvars(state->cls->init, 0, 0, 0, 1); + state->cls->static_init->variable_count=1; function_initvars(state->cls->static_init, 0, 0, 0, 0); if(extends && (extends->flags & FLAG_FINAL)) @@ -1094,10 +1133,8 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl abc_class_add_interface(state->cls->abc, &m); } - NEW(parsedclass_t,p); - p->cls = state->cls->info; - p->abc = state->cls->abc; - list_append(global->classes, p); + state->cls->dependencies = parsedclass_new(state->cls->info, state->cls->abc); + list_append(global->classes, state->cls->dependencies); /* flash.display.MovieClip handling */ if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) { @@ -1301,9 +1338,13 @@ static void innerfunction(char*name, params_t*params, classinfo_t*return_type) //parserassert(state->method && state->method->info); methodstate_t*parent_method = state->method; + variable_t*v = 0; if(as3_pass==1) { return_type = 0; // not valid in pass 1 + if(name) { + v = new_variable2(parent_method, name, 0, 0, 0); + } } new_state(); @@ -1313,8 +1354,12 @@ static void innerfunction(char*name, params_t*params, classinfo_t*return_type) if(as3_pass == 1) { state->method = rfx_calloc(sizeof(methodstate_t)); state->method->inner = 1; + state->method->is_static = parent_method->is_static; state->method->variable_count = 0; state->method->abc = rfx_calloc(sizeof(abc_method_t)); + if(v) { + v->is_inner_method = state->method; + } NEW(methodinfo_t,minfo); minfo->kind = INFOTYPE_METHOD; @@ -1389,56 +1434,65 @@ static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name, } } +static void insert_unresolved(methodstate_t*m, dict_t*xvars, dict_t*allvars) +{ + parserassert(m->inner); + if(m->unresolved_variables) { + dict_t*d = m->unresolved_variables; + int t; + DICT_ITERATE_KEY(d, char*, id) { + /* check parent method's variables */ + variable_t*v; + if(dict_contains(allvars, id)) { + m->uses_parent_function = 1; + state->method->uses_slots = 1; + dict_put(xvars, id, 0); + } + } + } + methodstate_list_t*ml = m->innerfunctions; + while(ml) { + insert_unresolved(ml->methodstate, xvars, allvars); + ml = ml->next; + } +} + static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name, 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, 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(state->method->unresolved_variables) { + DICT_ITERATE_KEY(state->method->unresolved_variables, char*, vname) { + if(!state->method->no_variable_scoping && dict_contains(state->allvars, vname)) { + variable_t*v = dict_lookup(state->allvars, vname); + if(!v->is_inner_method) { + state->method->no_variable_scoping = 1; + as3_warning("function %s uses forward or outer block variable references (%s): switching into compatiblity mode", name, vname); } - if(l) break; } - - dict_destroy(m->unresolved_variables); - m->unresolved_variables = 0; } + } + + methodstate_list_t*ml = state->method->innerfunctions; + while(ml) { + insert_unresolved(ml->methodstate, xvars, state->allvars); 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) { + DICT_ITERATE_ITEMS(state->allvars, char*, name, variable_t*, v) { if(!name) syntaxerror("internal error"); if(v->index && dict_contains(xvars, name)) { - v->init = 0; - v->index = i++; + v->init = v->kill = 0; + v->index = i; if(v->is_inner_method) { - v->is_inner_method->is_a_slot = 1; + v->is_inner_method->is_a_slot = i; } - //v->type = 0; + i++; dict_put(state->method->slots, name, v); } } @@ -1478,6 +1532,7 @@ static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char* multiname_t mname = {QNAME, &mname_ns, 0, name}; f = abc_method_new(global->file, type2, 1); + if(!global->init) global->init = abc_initscript(global->file); trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f); //abc_code_t*c = global->init->method->body->code; } @@ -1502,7 +1557,7 @@ static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char* 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"); + syntaxerror("function %s: non-optional parameter not allowed after optional parameters", name); } } if(state->method->slots) { @@ -1599,12 +1654,18 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) if(TYPE_IS_STRING(to)) return abc_convert_s(c); if(TYPE_IS_OBJECT(to)) - return abc_convert_o(c); + return abc_coerce2(c, &m); + if(TYPE_IS_OBJECT(from) && TYPE_IS_XMLLIST(to)) + return abc_coerce2(c, &m); + if(TYPE_IS_OBJECT(from) && TYPE_IS_ARRAY(to)) + return abc_coerce2(c, &m); classinfo_t*supertype = from; while(supertype) { if(supertype == to) { - // target type is one of from's superclasses + /* target type is one of from's superclasses. + (not sure we need this coerce - as far as the verifier + is concerned, object==object (i think) */ return abc_coerce2(c, &m); } int t=0; @@ -1630,7 +1691,17 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) return c; } -/* move to ast.c todo end */ +code_t* coerce_to_type(code_t*c, classinfo_t*t) +{ + if(!t) { + return abc_coerce_a(c); + } else if(TYPE_IS_STRING(t)) { + return abc_coerce_s(c); + } else { + MULTINAME(m, t); + return abc_coerce2(c, &m); + } +} char is_pushundefined(code_t*c) { @@ -1717,6 +1788,7 @@ typedcode_t push_class(slotinfo_t*a) infotypename(a), a->name, a->package, state->package); } + if(a->kind != INFOTYPE_CLASS) { MULTINAME(m, a); x.c = abc_findpropstrict2(x.c, &m); @@ -1728,7 +1800,13 @@ typedcode_t push_class(slotinfo_t*a) varinfo_t*v = (varinfo_t*)a; x.t = v->type; } + return x; } else { + if(state->cls && state->method == state->cls->static_init) { + /* we're in the static initializer. + record the fact that we're using this class here */ + parsedclass_add_dependency(state->cls->dependencies, (classinfo_t*)a); + } classinfo_t*c = (classinfo_t*)a; //if(c->slot) { if(0) { //Error #1026: Slot 1 exceeds slotCount=0 of global @@ -1912,6 +1990,7 @@ INPACKAGE_CODE: INTERFACE_DECLARATION | SLOT_DECLARATION | PACKAGE_INITCODE | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;} + | '[' EMBED_START E ']' {PASS_ALWAYS as3_pass=$2;PASS1 as3_warning("embed command ignored");} | ';' MAYBECODE: CODE {$$=$1;} @@ -1923,6 +2002,7 @@ CODE: CODE CODEPIECE { CODE: CODEPIECE {$$=$1;} // code which may appear outside of methods +CODE_STATEMENT: DEFAULT_NAMESPACE CODE_STATEMENT: IMPORT CODE_STATEMENT: FOR CODE_STATEMENT: FOR_IN @@ -1938,7 +2018,7 @@ CODE_STATEMENT: NAMESPACE_DECLARATION CODE_STATEMENT: '{' CODE '}' {$$=$2;} CODE_STATEMENT: '{' '}' {$$=0;} -// code which may appear in methods +// code which may appear in methods (includes the above) CODEPIECE: ';' {$$=0;} CODEPIECE: CODE_STATEMENT CODEPIECE: VARIABLE_DECLARATION @@ -1964,8 +2044,20 @@ CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;} /* ------------ package init code ------------------- */ PACKAGE_INITCODE: CODE_STATEMENT { - code_t**cc = &global->init->method->body->code; - *cc = code_append(*cc, $1); + if($1) { + if(!global->init) + global->init = abc_initscript(global->file); + code_t**cc = &global->init->method->body->code; + *cc = code_append(*cc, $1); + } +} + +/* ------------ embed code ------------- */ + +EMBED_START: %prec above_function { + PASS_ALWAYS + $$ = as3_pass; + as3_pass=0; } /* ------------ conditional compilation ------------- */ @@ -1987,6 +2079,14 @@ CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER { { return 1; // FIXME } + char do_init_variable(char*name) + { + if(!state->method->no_variable_scoping) + return 0; + if(!state->new_vars) + return 1; + return 1; + } }; MAYBEEXPRESSION : '=' E {$$=$2;} @@ -2001,84 +2101,86 @@ VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);} ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION { PASS12 - if(variable_exists($1)) + if(variable_exists($1)) syntaxerror("Variable %s already defined", $1); PASS1 - new_variable($1, 0, 1, 0); + new_variable(state->method, $1, 0, 1, 0); PASS2 char slot = 0; int index = 0; + variable_t*v = 0; if(state->method->uses_slots) { - variable_t* v = find_slot(state, $1); + v = find_slot(state->method, $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); + if(!v) { + v = new_variable2(state->method, $1, $2, 1, 0); } $$ = slot?abc_getscopeobject(0, 1):0; - typedcode_t v = node_read($3); - if(!is_subtype_of(v.t, $2)) { - syntaxerror("Can't convert %s to %s", v.t->name, $2->name); + typedcode_t val = node_read($3); + if(!is_subtype_of(val.t, $2)) { + syntaxerror("Can't convert %s to %s", val.t->name, $2->name); } if($2) { - if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) { - $$ = code_append($$, v.c); - $$ = converttype($$, v.t, $2); + if(val.c->prev || val.c->opcode != OPCODE_PUSHUNDEFINED) { + $$ = code_append($$, val.c); + $$ = converttype($$, val.t, $2); } else { - code_free(v.c); + code_free(val.c); $$ = defaultvalue($$, $2); } } else { - if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) { - $$ = code_append($$, v.c); + if(val.c->prev || val.c->opcode != OPCODE_PUSHUNDEFINED) { + $$ = code_append($$, val.c); $$ = abc_coerce_a($$); } else { // don't do anything - code_free(v.c); + code_free(val.c); code_free($$); $$ = 0; break; } } if(slot) { - $$ = abc_setslot($$, index); + $$ = abc_setslot($$, v->index); } else { - $$ = abc_setlocal($$, index); + $$ = abc_setlocal($$, v->index); + v->init = do_init_variable($1); } } /* ------------ control flow ------------------------- */ +IF_CODEBLOCK: {PASS12 new_state();} CODEBLOCK { + $$ = var_block($2, state->vars); + PASS12 old_state(); +} MAYBEELSE: %prec below_else {$$ = code_new();} -MAYBEELSE: "else" CODEBLOCK {$$=$2;} +MAYBEELSE: "else" IF_CODEBLOCK {$$=$2;} //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;} -IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { - +IF : "if" '(' EXPRESSION ')' IF_CODEBLOCK MAYBEELSE { $$ = code_new(); - $$ = code_append($$, $4.c); + $$ = code_append($$, $3.c); code_t*myjmp,*myif = $$ = abc_iffalse($$, 0); - $$ = code_append($$, $6); - if($7) { + $$ = code_append($$, $5); + if($6) { myjmp = $$ = abc_jump($$, 0); } myif->branch = $$ = abc_nop($$); - if($7) { - $$ = code_append($$, $7); + if($6) { + $$ = code_append($$, $6); myjmp->branch = $$ = abc_nop($$); } - $$ = var_block($$); - PASS12 old_state(); } FOR_INIT : {$$=code_new();} @@ -2089,8 +2191,8 @@ 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 { - PASS1 $$=$2;new_variable($2,0,1,0); - PASS2 $$=$2;new_variable($2,$3,1,0); + PASS1 $$=$2;new_variable(state->method, $2,0,1,0); + PASS2 $$=$2;new_variable(state->method, $2,$3,1,0); } FOR_IN_INIT : T_IDENTIFIER { PASS12 @@ -2100,7 +2202,7 @@ FOR_IN_INIT : T_IDENTIFIER { 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 { +FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' IF_CODEBLOCK { if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement"); $$ = code_new(); $$ = code_append($$, $2); @@ -2116,20 +2218,17 @@ FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { continuejumpsto($$, $1.name, cont); myif->branch = out; - $$ = var_block($$); + $$ = var_block($$, state->vars); PASS12 old_state(); } -FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { +FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' IF_CODEBLOCK { 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, 0); - char*tmp2name = concat2($2, "__array__"); - int array = new_variable(tmp1name, 0, 0, 0); + int it = alloc_local(); + int array = alloc_local(); $$ = code_new(); $$ = code_append($$, $4.c); @@ -2160,46 +2259,39 @@ FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { myif->branch = out; - $$ = var_block($$); - - free(tmp1name); - free(tmp2name); + $$ = abc_kill($$, it); + $$ = abc_kill($$, array); + $$ = var_block($$, state->vars); PASS12 old_state(); } -WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK { +WHILE : T_WHILE '(' EXPRESSION ')' IF_CODEBLOCK { $$ = code_new(); code_t*myjmp = $$ = abc_jump($$, 0); code_t*loopstart = $$ = abc_label($$); - $$ = code_append($$, $6); + $$ = code_append($$, $5); code_t*cont = $$ = abc_nop($$); myjmp->branch = cont; - $$ = code_append($$, $4.c); + $$ = code_append($$, $3.c); $$ = abc_iftrue($$, loopstart); code_t*out = $$ = abc_nop($$); breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - - $$ = var_block($$); - PASS12 old_state(); } -DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' { +DO_WHILE : T_DO IF_CODEBLOCK "while" '(' EXPRESSION ')' { $$ = code_new(); code_t*loopstart = $$ = abc_label($$); - $$ = code_append($$, $3); + $$ = code_append($$, $2); code_t*cont = $$ = abc_nop($$); - $$ = code_append($$, $6.c); + $$ = code_append($$, $5.c); $$ = abc_iftrue($$, loopstart); code_t*out = $$ = abc_nop($$); breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - - $$ = var_block($$); - PASS12 old_state(); } BREAK : "break" %prec prec_none { @@ -2261,7 +2353,7 @@ SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ') c=c->prev; } - $$ = var_block($$); + $$ = var_block($$, state->vars); PASS12 old_state(); } @@ -2269,8 +2361,8 @@ SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ') 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); + PASS1 new_variable(state->method, $3, 0, 0, 0); + PASS2 new_variable(state->method, $3, $4, 0, 0); } '{' MAYBECODE '}' { namespace_t name_ns = {ACCESS_PACKAGE, ""}; @@ -2289,11 +2381,11 @@ CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state(); c = code_append(c, $8); c = abc_kill(c, i); - c = var_block(c); + c = var_block(c, state->vars); PASS12 old_state(); } FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' { - $4 = var_block($4); + $4 = var_block($4, state->vars); if(!$4) { $$=0; } else { @@ -2343,7 +2435,7 @@ TRY : "try" '{' {PASS12 new_state(); int tmp; if($6.finally) - tmp = new_variable("__finally__", 0, 0, 0); + tmp = alloc_local(); abc_exception_list_t*l = $6.l; int count=0; @@ -2371,7 +2463,7 @@ TRY : "try" '{' {PASS12 new_state(); list_concat(state->method->exceptions, $6.l); - $$ = var_block($$); + $$ = var_block($$, state->vars); PASS12 old_state(); } @@ -2421,6 +2513,8 @@ WITH : WITH_HEAD CODEBLOCK { X_IDENTIFIER: T_IDENTIFIER | "package" {PASS12 $$="package";} + | "namespace" {PASS12 $$="namespace";} + | "NaN" {PASS12 $$="NaN";} | T_NAMESPACE {PASS12 $$=$1;} PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;} @@ -2451,18 +2545,24 @@ PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");} free(s); } }; + +IMPORT : "import" T_IDENTIFIER { + PASS12 + slotinfo_t*s = registry_find(state->package, $2); + if(!s && as3_pass==1) {as3_schedule_class(state->package, $2);} + state_has_imports(); + dict_put(state->imports, state->package, $2); + $$=0; +} IMPORT : "import" PACKAGEANDCLASS { PASS12 slotinfo_t*s = registry_find($2->package, $2->name); if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) { as3_schedule_class($2->package, $2->name); } - classinfo_t*c = $2; - if(!c) - syntaxerror("Couldn't import class\n"); state_has_imports(); - dict_put(state->imports, c->name, c); - import_toplevel(c->package); + dict_put(state->imports, $2->name, $2); + import_toplevel($2->package); $$=0; } IMPORT : "import" PACKAGE '.' '*' { @@ -2598,6 +2698,7 @@ IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LI code_t**code=0; if(!state->cls) { // global variable + if(!global->init) global->init = abc_initscript(global->file); ns.name = state->package; traits = &global->init->traits; code = &global->init->method->body->code; @@ -2609,6 +2710,10 @@ IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LI // instance variable traits = &state->cls->abc->traits; code = &state->cls->init->header; + + if(ns.access == ACCESS_PROTECTED) { + ns.name = concat3(state->cls->info->package,":",state->cls->info->name); + } } if(c) *c = code; @@ -2732,14 +2837,9 @@ CONSTANT : "null" {$$ = constant_new_null($1);} CONSTANT : "undefined" {$$ = constant_new_undefined($1);} CONSTANT : KW_NAN {$$ = constant_new_float(__builtin_nan(""));} -/* -CONSTANT : T_IDENTIFIER { - if(!strcmp($1, "NaN")) { - $$ = constant_new_float(__builtin_nan("")); - } else { - as3_warning("Couldn't evaluate constant value of %s", $1); - $$ = constant_new_null($1); - } +/*CONSTANT : T_NAMESPACE { + // TODO + $$ = constant_new_namespace(namespace_new_namespace($1.url)); }*/ /* ---------------------------xml ------------------------------ */ @@ -2748,66 +2848,97 @@ CONSTANT : T_IDENTIFIER { static int xml_level = 0; }; -XML: XMLNODE +XML: XMLNODE { + multiname_t m = {QNAME, &stdns, 0, "XML"}; + typedcode_t v; + v.c = 0; + v.c = abc_getlex2(v.c, &m); + v.c = code_append(v.c, node_read($1).c); + v.c = abc_construct(v.c, 1); + v.t = TYPE_XML; + $$ = mkcodenode(v); +} OPEN : '<' {PASS_ALWAYS if(!xml_level++) tokenizer_begin_xml();} CLOSE : '>' {PASS_ALWAYS tokenizer_begin_xmltext();} CLOSE2 : {PASS_ALWAYS if(!--xml_level) tokenizer_end_xml(); else tokenizer_begin_xmltext();} XMLEXPR1 : '{' E {PASS_ALWAYS tokenizer_begin_xmltext();} '}' { - $$=strdup("{...}"); - as3_warning("xml string substitution not yet supported"); + $$ = $2; } XMLEXPR2 : '{' E {PASS_ALWAYS tokenizer_begin_xml();} '}' { - $$=strdup("{...}"); - as3_warning("xml string substitution not yet supported"); + $$ = $2; } -XMLTEXT : {$$="";} +XMLTEXT : {$$=mkstringnode("");} XMLTEXT : XMLTEXT XMLEXPR1 { - $$ = concat2($1, "{...}"); + $$ = mkaddnode($1,$2); +} +XMLTEXT : XMLTEXT T_STRING { + char* str = string_cstr(&$2); + $$ = mkaddnode($1,mkstringnode(str)); + free(str); +} +XMLTEXT : XMLTEXT '>' { + $$ = mkaddnode($1, mkstringnode(">")); +} +XML2 : XMLNODE XMLTEXT { + $$ = mkaddnode($1,$2); +} +XML2 : XML2 XMLNODE XMLTEXT { + $$ = mkaddnode($1, mkaddnode($2,$3)); +} +XML_ID_OR_EXPR: T_IDENTIFIER { + $$ = mkstringnode($1); +} +XML_ID_OR_EXPR: XMLEXPR2 { + $$ = $1; } -XMLTEXT : XMLTEXT T_STRING {$$=concat2($1, string_cstr(&$2));} -XMLTEXT : XMLTEXT '>' {$$=concat2($1, ">");} - -XML2 : XMLNODE XMLTEXT {$$=concat2($1,$2);} -XML2 : XML2 XMLNODE XMLTEXT {$$=concat3($1,$2,$3);free($1);free($2);free($3);} - -XML_ID_OR_EXPR: T_IDENTIFIER {$$=$1;} -XML_ID_OR_EXPR: XMLEXPR2 {$$=$1;} -XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' XML_ID_OR_EXPR CLOSE2 '>' { - $$ = allocprintf("<%s%s>%s", $2, $3, $5, $8); - free($2);free($3);free($5);free($8); +MAYBE_XMLATTRIBUTES: { + $$ = mkstringnode(""); } +MAYBE_XMLATTRIBUTES: XMLATTRIBUTES { + $$ = mkaddnode(mkstringnode(" "),$1); +} + XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' { - $$ = allocprintf("<%s%s/>", $2, $3); + //$$ = allocprintf("<%s%s/>", $2, $3, $5, $8); + $$ = mkaddnode(mkaddnode(mkaddnode(mkstringnode("<"),$2),$3),mkstringnode("/>")); +} +XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' XML_ID_OR_EXPR CLOSE2 '>' { + //$$ = allocprintf("<%s%s>%s", $2, $3, $5, $8); + $$ = mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode( + mkstringnode("<"),$2),$3),mkstringnode(">")),$5),mkstringnode("")); } XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT XML2 '<' '/' XML_ID_OR_EXPR CLOSE2 '>' { - $$ = allocprintf("<%s%s>%s%s", $2, $3, $5, $6, $9); - free($2);free($3);free($5);free($6);free($9); + //$$ = allocprintf("<%s%s>%s%s", $2, $3, $5, $6, $9); + $$ = mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode( + mkstringnode("<"),$2),$3),mkstringnode(">")),$5),$6),mkstringnode("")); } -MAYBE_XMLATTRIBUTES: {$$=strdup("");} -MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {$$=concat2(" ",$1);} -XMLATTRIBUTES: XMLATTRIBUTE {$$=$1;} -XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE {$$=concat3($1," ",$2);free($1);free($2);} - +XMLATTRIBUTES: XMLATTRIBUTE { + $$ = $1; +} +XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE { + $$ = mkaddnode($1, mkaddnode(mkstringnode(" "),$2)); +} XMLATTRIBUTE: XMLEXPR2 { - $$ = strdup("{...}"); + $$ = $1; } XMLATTRIBUTE: XMLEXPR2 '=' T_STRING { char* str = string_cstr(&$3); - $$ = concat2("{...}=",str); + $$ = mkaddnode($1, mkstringnode(concat2("=",str))); + free(str); } XMLATTRIBUTE: XMLEXPR2 '=' XMLEXPR2 { - $$ = strdup("{...}={...}"); + $$ = mkaddnode($1, mkaddnode(mkstringnode("=\""), mkaddnode($3, mkstringnode("\"")))); } XMLATTRIBUTE: T_IDENTIFIER '=' XMLEXPR2 { - $$ = concat2($1,"={...}"); + $$ = mkaddnode(mkaddnode(mkstringnode(concat2($1,"=\"")), $3), mkstringnode("\"")); } XMLATTRIBUTE: T_IDENTIFIER '=' T_STRING { char* str = string_cstr(&$3); - $$=allocprintf("%s=\"%s\"", $1,str); + $$=mkstringnode(allocprintf("%s=%s", $1,str)); free(str); free($1);free((char*)$3.str); } @@ -3075,8 +3206,8 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { // calling a class is like a typecast $$.t = (classinfo_t*)v.t->data; } else { - $$.c = abc_coerce_a($$.c); $$.t = TYPE_ANY; + $$.c = abc_coerce_a($$.c); } } @@ -3189,14 +3320,7 @@ E : CONSTANT { } E : XML { - typedcode_t v; - v.c = 0; - multiname_t m = {QNAME, &stdns, 0, "XML"}; - v.c = abc_getlex2(v.c, &m); - v.c = abc_pushstring(v.c, $1); - v.c = abc_construct(v.c, 1); - v.t = TYPE_XML; - $$ = mkcodenode(v); + $$ = $1; } /* regexp */ @@ -3275,7 +3399,7 @@ E : E "in" E {$$ = mknode2(&node_in, $1, $3);} E : E "as" E {$$ = mknode2(&node_as, $1, $3);} E : E "instanceof" E {$$ = mknode2(&node_instanceof, $1, $3);} E : E "is" E {$$ = mknode2(&node_is, $1, $3);} -E : "typeof" '(' E ')' {$$ = mknode1(&node_typeof, $3);} +E : "typeof" E {$$ = mknode1(&node_typeof, $2);} E : "void" E {$$ = mknode1(&node_void, $2);} E : "void" { $$ = mkconstnode(constant_new_undefined());} E : '(' COMMA_EXPRESSION ')' { $$=$2;} @@ -3369,7 +3493,7 @@ E : E '.' '(' {PASS12 new_state();state->xmlfilter=1;} E ')' { c = abc_kill(c, result); c = abc_kill(c, index); - c = var_block(c); + c = var_block(c, state->vars); old_state(); typedcode_t r; r.c = c; @@ -3378,10 +3502,17 @@ E : E '.' '(' {PASS12 new_state();state->xmlfilter=1;} E ')' { } ID_OR_NS : T_IDENTIFIER {$$=$1;} +ID_OR_NS : '*' {$$="*";} ID_OR_NS : T_NAMESPACE {$$=(char*)$1;} -SUBNODE: T_IDENTIFIER +SUBNODE: X_IDENTIFIER | '*' {$$="*";} +/* +MAYBE_NS: T_IDENTIFIER "::" {$$=$1;} + | T_NAMESPACE "::" {$$=(char*)$1;} + | '*' "::" {$$="*";} + | {$$=0;}*/ + E : E '.' ID_OR_NS "::" SUBNODE { typedcode_t v = node_read($1); typedcode_t w = node_read(resolve_identifier($3)); @@ -3479,7 +3610,9 @@ MEMBER : E '.' SUBNODE { $$.c = abc_getslot($$.c, f->slot); } else { if(!f) { - as3_softwarning("Access of undefined property '%s' in %s", $3, t->name); + if(!TYPE_IS_XMLLIST(t)) { + as3_softwarning("Access of undefined property '%s' in %s", $3, t->name); + } } MEMBER_MULTINAME(m, f, $3); $$.c = abc_getproperty2($$.c, &m); @@ -3534,7 +3667,7 @@ MEMBER : E '.' SUBNODE { o.t = v->type; return mkcodenode(o); } - if((v = find_slot(state, name))) { + if((v = find_slot(state->method, name))) { o.c = abc_getscopeobject(o.c, 1); o.c = abc_getslot(o.c, v->index); o.t = v->type; @@ -3642,9 +3775,7 @@ VAR_READ : T_IDENTIFIER { */ if(!find_variable(state, $1)) { - if(state->method->inner) { - unknown_variable($1); - } + unknown_variable($1); /* let the compiler know that it might want to check the current directory/package for this identifier- maybe there's a file $1.as defining $1. */ as3_schedule_class_noerror(state->package, $1); @@ -3710,6 +3841,12 @@ NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID { $$=0; } +DEFAULT_NAMESPACE : "default xml" "namespace" '=' E +{ + as3_warning("default xml namespaces not supported yet"); + $$ = 0; +} + USE_NAMESPACE : "use" "namespace" CLASS_SPEC { PASS12 const char*url = $3->name;