X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=96b9b59cfca3a7788539632e3194acdebe9fedf2;hb=b8aa0577aae67db4da5221459102202febc5c103;hp=a1ce232fd4213ef1fb46b933c947d50d120b661c;hpb=62a83d162b254d91da418cee25f5b87b067a3f92;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index a1ce232..96b9b59 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -33,7 +33,8 @@ #include "code.h" #include "opcodes.h" #include "compiler.h" -#include "ast.h" +#include "expr.h" +#include "initcode.h" extern int a3_lex(); @@ -68,6 +69,7 @@ extern int a3_lex(); regexp_t regexp; modifiers_t flags; namespace_decl_t* namespace_decl; + node_t*node; struct { abc_exception_list_t *l; code_t*finally; @@ -154,6 +156,7 @@ extern int a3_lex(); %token T_DIVBY "/=" %token T_MODBY "%=" %token T_MULBY "*=" +%token T_ANDBY "&=" %token T_PLUSBY "+=" %token T_MINUSBY "-=" %token T_XORBY "^=" @@ -189,8 +192,10 @@ extern int a3_lex(); %type INTERFACE_DECLARATION %type VOIDEXPRESSION %type EXPRESSION NONCOMMAEXPRESSION -%type MAYBEEXPRESSION -%type E DELETE +%type MAYBEEXPRESSION +%type DELETE +%type E COMMA_EXPRESSION +%type VAR_READ %type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY %type INNERFUNCTION %type USE_NAMESPACE @@ -214,11 +219,13 @@ extern int a3_lex(); %type TYPE //%type VARIABLE -%type VAR_READ +%type MEMBER %type NEW //%type T_IDENTIFIER %type FUNCTIONCALL -%type MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD +%type MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES +%type MAYBE_DICT_EXPRPAIR_LIST DICT_EXPRPAIR_LIST WITH_HEAD +%type DICTLH // precedence: from low to high @@ -376,11 +383,17 @@ typedef struct _state { int switch_var; dict_t*vars; + dict_t*allvars; // also contains variables from sublevels } state_t; typedef struct _global { abc_file_t*file; - abc_script_t*init; + + parsedclass_list_t*classes; + abc_script_t*classinit; + + abc_script_t*init; //package-level code + dict_t*token2info; dict_t*file2token2info; } global_t; @@ -390,12 +403,6 @@ static state_t* state = 0; DECLARE_LIST(state); -#define MULTINAME(m,x) \ - multiname_t m;\ - namespace_t m##_ns;\ - (x)->package; \ - registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x)); - #define MEMBER_MULTINAME(m,f,n) \ multiname_t m;\ namespace_t m##_ns;\ @@ -436,7 +443,7 @@ static namespace_list_t nl1 = {&ns1,&nl2}; static namespace_set_t nopackage_namespace_set = {&nl1}; static dict_t*definitions=0; -void as3_set_definition(const char*c) +void as3_set_define(const char*c) { if(!definitions) definitions = dict_new(); @@ -479,16 +486,15 @@ static void state_destroy(state_t*state) dict_destroy(state->imports);state->imports=0; } if(state->vars) { - int t; - for(t=0;tvars->hashsize;t++) { - dictentry_t*e =state->vars->slots[t]; - while(e) { - free(e->data);e->data=0; - e = e->next; - } - } dict_destroy(state->vars);state->vars=0; } + if(state->new_vars && state->allvars) { + parserassert(!state->old || state->old->allvars != state->allvars); + DICT_ITERATE_DATA(state->allvars, void*, data) { + free(data); + } + dict_destroy(state->allvars); + } list_free(state->active_namespace_urls) state->active_namespace_urls = 0; @@ -534,6 +540,7 @@ void initialize_file(char*filename) new_state(); state->package = internal_filename_package = strdup(filename); + state->allvars = dict_new(); global->token2info = dict_lookup(global->file2token2info, current_filename // use long version @@ -549,7 +556,10 @@ 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); + if(!state->method) + syntaxerror("internal error: skewed tokencount"); function_initvars(state->method, 0, 0, 1); + global->classinit = abc_initscript(global->file); global->init = abc_initscript(global->file); } } @@ -559,8 +569,10 @@ void finish_file() if(!state || state->level!=1) { syntaxerror("unexpected end of file in pass %d", as3_pass); } - + 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 = c; @@ -584,6 +596,9 @@ void* finish_parser() { dict_free_all(global->file2token2info, 1, (void*)dict_destroy); global->token2info=0; + + initcode_add_classlist(global->classinit, global->classes); + return global->file; } @@ -597,6 +612,7 @@ typedef struct _variable { 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); @@ -604,7 +620,7 @@ static variable_t* find_variable(state_t*s, char*name) 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) { @@ -665,8 +681,10 @@ static variable_t* new_variable2(const char*name, classinfo_t*type, char init, c v->type = type; v->init = init; - if(name) + if(name) { dict_put(state->vars, name, v); + dict_put(state->allvars, name, v); + } return v; } @@ -676,12 +694,16 @@ static int new_variable(const char*name, classinfo_t*type, char init, char maybe } #define TEMPVARNAME "__as3_temp__" -static int gettempvar() +int gettempvar() { variable_t*v = find_variable(state, TEMPVARNAME); + int i; if(v) - return v->index; - return new_variable(TEMPVARNAME, 0, 0, 0); + i = v->index; + else + i = new_variable(TEMPVARNAME, 0, 0, 0); + parserassert(i); + return i; } static code_t* var_block(code_t*body) @@ -892,8 +914,6 @@ static void function_initvars(methodstate_t*m, params_t*params, int flags, char 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); } @@ -985,6 +1005,7 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl state->cls = rfx_calloc(sizeof(classstate_t)); state->cls->init = rfx_calloc(sizeof(methodstate_t)); state->cls->static_init = rfx_calloc(sizeof(methodstate_t)); + 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 facat that pass 1 won't do anything with variables */ @@ -1002,6 +1023,7 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl int num_interfaces = (list_length(implements)); state->cls->info = classinfo_register(access, package, classname, num_interfaces); state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL); + state->cls->info->superclass = extends; int pos = 0; classinfo_list_t*l = implements; @@ -1030,14 +1052,12 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl pos++; } - /* fill out interfaces and extends (we couldn't resolve those during the first pass) */ - state->cls->info->superclass = extends; - /* generate the abc code for this class */ MULTINAME(classname2,state->cls->info); multiname_t*extends2 = sig2mname(extends); - state->cls->abc = abc_class_new(global->file, &classname2, extends2); + multiname_destroy(extends2); + if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc); if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc); if(state->cls->info->flags&FLAG_INTERFACE) { @@ -1051,58 +1071,12 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl abc_class_add_interface(state->cls->abc, &m); } - /* write the construction code for this class to the global init - function */ - int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc); - - abc_method_body_t*m = global->init->method->body; - __ getglobalscope(m); - classinfo_t*s = extends; - - int count=0; - - while(s) { - //TODO: take a look at the current scope stack, maybe - // we can re-use something - s = s->superclass; - if(!s) - break; - - multiname_t*s2 = sig2mname(s); - __ getlex2(m, s2); - multiname_destroy(s2); - - __ pushscope(m); count++; - m->code = m->code->prev->prev; // invert - } - /* continue appending after last op end */ - while(m->code && m->code->next) m->code = m->code->next; - - /* TODO: if this is one of *our* classes, we can also - do a getglobalscope/getslot (which references - the init function's slots) */ - if(extends2) { - __ getlex2(m, extends2); - __ dup(m); - /* notice: we get a Verify Error #1107 if the top elemnt on the scope - stack is not the superclass */ - __ pushscope(m);count++; - } else { - __ pushnull(m); - /* notice: we get a verify error #1107 if the top element on the scope - stack is not the global object */ - __ getlocal_0(m); - __ pushscope(m);count++; - } - __ newclass(m,state->cls->abc); - while(count--) { - __ popscope(m); - } - __ setslot(m, slotindex); - multiname_destroy(extends2); + NEW(parsedclass_t,p); + p->cls = state->cls->info; + p->abc = state->cls->abc; + list_append(global->classes, p); /* flash.display.MovieClip handling */ - 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); @@ -1223,7 +1197,7 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c if(!state->cls) { //package method minfo = methodinfo_register_global(ns.access, state->package, name); - minfo->return_type = 0; // save this for pass 2 + minfo->return_type = return_type; } else if(getset != KW_GET && getset != KW_SET) { //class method memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0); @@ -1231,7 +1205,7 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name); } minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name); - minfo->return_type = 0; // save this for pass 2 + minfo->return_type = return_type; // 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; @@ -1248,7 +1222,7 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c // not sure wether to look into superclasses here, too minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1); if(minfo) { - if(minfo->kind!=INFOTYPE_SLOT) + if(minfo->kind!=INFOTYPE_VAR) syntaxerror("class already contains a method called '%s'", name); if(!(minfo->subtype & (SUBTYPE_GETSET))) syntaxerror("class already contains a field called '%s'", name); @@ -1269,10 +1243,11 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c }*/ } else { minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name); - minfo->kind = INFOTYPE_SLOT; //hack + minfo->kind = INFOTYPE_VAR; //hack minfo->subtype = gs; - minfo->return_type = 0; + minfo->return_type = return_type; } + /* can't assign a slot as getter and setter might have different slots */ //minfo->slot = slot; } @@ -1295,6 +1270,7 @@ static void innerfunction(char*name, params_t*params, classinfo_t*return_type) new_state(); state->new_vars = 1; + state->allvars = dict_new(); if(as3_pass == 1) { state->method = rfx_calloc(sizeof(methodstate_t)); @@ -1334,7 +1310,8 @@ static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name, } new_state(); state->new_vars = 1; - + state->allvars = dict_new(); + if(as3_pass == 1) { state->method = rfx_calloc(sizeof(methodstate_t)); state->method->has_super = 0; @@ -1369,7 +1346,6 @@ static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name, state->cls->has_constructor |= state->method->is_constructor; } - state->method->info->return_type = return_type; function_initvars(state->method, params, mod->flags, 1); } } @@ -1429,6 +1405,8 @@ static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char* } state->method->uses_slots = i; dict_destroy(state->vars);state->vars = 0; + parserassert(state->new_vars); + dict_destroy(state->allvars);state->allvars = 0; } old_state(); return 0; @@ -1548,49 +1526,8 @@ void continuejumpsto(code_t*c, char*name, code_t*jump) } } -/* TODO: move this to ast.c */ -#define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a))) #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a))) -#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b)) -static classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op) -{ - if(!type1 || !type2) - return registry_getanytype(); - if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2)) - return registry_getanytype(); - - if(op=='+') { - if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) { - return TYPE_NUMBER; - } else { - return TYPE_ANY; - } - } - if(type1 == type2) - return type1; - return registry_getanytype(); -} -static char is_getlocal(code_t*c) -{ - if(!c || c->prev || c->next) - return 0; - return(c->opcode == OPCODE_GETLOCAL - || c->opcode == OPCODE_GETLOCAL_0 - || c->opcode == OPCODE_GETLOCAL_1 - || c->opcode == OPCODE_GETLOCAL_2 - || c->opcode == OPCODE_GETLOCAL_3); -} -static int getlocalnr(code_t*c) -{ - if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];} - else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;} - else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;} - else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;} - else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;} - else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode); - return 0; -} code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) { if(from==to) @@ -1766,168 +1703,6 @@ typedcode_t push_class(slotinfo_t*a) } -code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore, char pushvalue) -{ - /* converts this: - - [prefix code] [read instruction] - - to this: - - [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar] - */ - if(in && in->opcode == OPCODE_COERCE_A) { - in = code_cutlast(in); - } - if(in->next) - syntaxerror("internal error"); - - /* chop off read instruction */ - code_t*prefix = in; - code_t*r = in; - if(r->prev) { - prefix = r->prev;r->prev = 0; - prefix->next=0; - } else { - prefix = 0; - } - - char use_temp_var = readbefore; - - /* generate the write instruction, and maybe append a dup to the prefix code */ - code_t* write = abc_nop(0); - if(r->opcode == OPCODE_GETPROPERTY) { - write->opcode = OPCODE_SETPROPERTY; - multiname_t*m = (multiname_t*)r->data[0]; - write->data[0] = multiname_clone(m); - if(m->type == QNAME || m->type == MULTINAME) { - if(!justassign) { - prefix = abc_dup(prefix); // we need the object, too - } - use_temp_var = 1; - } else if(m->type == MULTINAMEL) { - if(!justassign) { - /* dupping two values on the stack requires 5 operations and one register- - couldn't adobe just have given us a dup2? */ - int temp = gettempvar(); - prefix = abc_setlocal(prefix, temp); - prefix = abc_dup(prefix); - prefix = abc_getlocal(prefix, temp); - prefix = abc_swap(prefix); - prefix = abc_getlocal(prefix, temp); - if(!use_temp_var); - prefix = abc_kill(prefix, temp); - } - use_temp_var = 1; - } else { - syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)"); - } - } else if(r->opcode == OPCODE_GETSLOT) { - write->opcode = OPCODE_SETSLOT; - write->data[0] = r->data[0]; - if(!justassign) { - prefix = abc_dup(prefix); // we need the object, too - } - use_temp_var = 1; - } else if(r->opcode == OPCODE_GETLOCAL) { - write->opcode = OPCODE_SETLOCAL; - write->data[0] = r->data[0]; - } else if(r->opcode == OPCODE_GETLOCAL_0) { - write->opcode = OPCODE_SETLOCAL_0; - } else if(r->opcode == OPCODE_GETLOCAL_1) { - write->opcode = OPCODE_SETLOCAL_1; - } else if(r->opcode == OPCODE_GETLOCAL_2) { - 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"); - } - code_t* c = 0; - - int temp = -1; - if(!justassign) { - if(use_temp_var) { - /* with getproperty/getslot, we have to be extra careful not - to execute the read code twice, as it might have side-effects - (e.g. if the property is in fact a setter/getter combination) - - So read the value, modify it, and write it again, - using prefix only once and making sure (by using a temporary - register) that the return value is what we just wrote */ - temp = gettempvar(); - c = code_append(c, prefix); - c = code_append(c, r); - if(pushvalue && readbefore) { - c = abc_dup(c); - c = abc_setlocal(c, temp); - } - c = code_append(c, middlepart); - if(pushvalue && !readbefore) { - c = abc_dup(c); - c = abc_setlocal(c, temp); - } - c = code_append(c, write); - if(pushvalue) { - c = abc_getlocal(c, temp); - c = abc_kill(c, temp); - } - } else { - /* if we're allowed to execute the read code twice *and* - the middlepart doesn't modify the code, things are easier. - */ - //c = code_append(c, prefix); - parserassert(!prefix); - code_t* r2 = 0; - if(pushvalue) { - r2 = code_dup(r); - } - c = code_append(c, r); - c = code_append(c, middlepart); - c = code_append(c, write); - if(pushvalue) { - c = code_append(c, r2); - } - } - } else { - /* even smaller version: overwrite the value without reading - it out first */ - if(!use_temp_var) { - if(prefix) { - c = code_append(c, prefix); - c = abc_dup(c); - } - c = code_append(c, middlepart); - c = code_append(c, write); - if(pushvalue) { - c = code_append(c, r); - } - } else { - code_free(r);r=0; - temp = gettempvar(); - if(prefix) { - c = code_append(c, prefix); - } - c = code_append(c, middlepart); - if(pushvalue) { - c = abc_dup(c); - c = abc_setlocal(c, temp); - } - c = code_append(c, write); - if(pushvalue) { - c = abc_getlocal(c, temp); - c = abc_kill(c, temp); - } - } - } - return c; -} - char is_break_or_jump(code_t*c) { if(!c) @@ -2101,7 +1876,9 @@ INPACKAGE_CODE: INTERFACE_DECLARATION MAYBECODE: CODE {$$=$1;} MAYBECODE: {$$=code_new();} -CODE: CODE CODEPIECE {$$=code_append($1,$2);} +CODE: CODE CODEPIECE { + $$=code_append($1,$2); +} CODE: CODEPIECE {$$=$1;} // code which may appear outside of methods @@ -2128,7 +1905,15 @@ CODEPIECE: BREAK CODEPIECE: CONTINUE CODEPIECE: RETURN CODEPIECE: THROW -CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {PASS_ALWAYS as3_pass=$1;} +CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' { + PASS_ALWAYS + if(as3_pass) { + $$ = $3; + } else { + $$ = 0; + } + as3_pass=$1; +} //CODEBLOCK : '{' CODE '}' {$$=$2;} //CODEBLOCK : '{' '}' {$$=0;} @@ -2163,10 +1948,8 @@ CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER { } }; -MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;} - | {$$.c=abc_pushundefined(0); - $$.t=TYPE_ANY; - } +MAYBEEXPRESSION : '=' E {$$=$2;} + | {$$=mkdummynode();} VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;} VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;} @@ -2183,11 +1966,6 @@ 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); - } - char slot = 0; int index = 0; if(state->method->uses_slots) { @@ -2206,21 +1984,25 @@ PASS2 $$ = 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); + } if($2) { - if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) { - $$ = code_append($$, $3.c); - $$ = converttype($$, $3.t, $2); + if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) { + $$ = code_append($$, v.c); + $$ = converttype($$, v.t, $2); } else { - code_free($3.c); + code_free(v.c); $$ = defaultvalue($$, $2); } } else { - if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) { - $$ = code_append($$, $3.c); + if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) { + $$ = code_append($$, v.c); $$ = abc_coerce_a($$); } else { // don't do anything - code_free($3.c); + code_free(v.c); code_free($$); $$ = 0; break; @@ -2401,7 +2183,7 @@ CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);} CASE: "case" E ':' MAYBECODE { $$ = abc_getlocal(0, state->switch_var); - $$ = code_append($$, $2.c); + $$ = code_append($$, node_read($2).c); code_t*j = $$ = abc_ifne($$, 0); $$ = code_append($$, $4); if($$->opcode != OPCODE___BREAK__) { @@ -2414,7 +2196,7 @@ DEFAULT: "default" ':' MAYBECODE { $$ = $3; } SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' { - $$=$4.c; + $$ = node_read($4).c; $$ = abc_setlocal($$, state->switch_var); $$ = code_append($$, $7); @@ -2830,20 +2612,29 @@ PASS12 t = trait_new_member(traits, 0, multiname_clone(&mname), 0); } info->slot = t->slot_id; - - /* initalization code (if needed) */ - code_t*c = 0; - if($3.c && !is_pushundefined($3.c)) { - c = abc_getlocal_0(c); - c = code_append(c, $3.c); - c = converttype(c, $3.t, $2); - c = abc_setslot(c, t->slot_id); + + constant_t cval = $3->type->eval($3); + if(cval.type!=CONSTANT_UNKNOWN) { + /* compile time constant */ + t->value = malloc(sizeof(constant_t)); + memcpy(t->value, &cval, sizeof(constant_t)); + info->value = constant_clone(t->value); + } else { + typedcode_t v = node_read($3); + /* initalization code (if needed) */ + code_t*c = 0; + if(v.c && !is_pushundefined(v.c)) { + c = abc_getlocal_0(c); + c = code_append(c, v.c); + c = converttype(c, v.t, $2); + c = abc_setslot(c, t->slot_id); + } + *code = code_append(*code, c); } - *code = code_append(*code, c); - if(slotstate_varconst==KW_CONST) { t->kind= TRAIT_CONST; + info->flags |= FLAG_CONST; } } @@ -2853,7 +2644,12 @@ PASS12 /* ------------ constants -------------------------------------- */ MAYBECONSTANT: {$$=0;} -MAYBECONSTANT: '=' CONSTANT {$$=$2;} +MAYBECONSTANT: '=' E { + $$ = malloc(sizeof(constant_t)); + *$$ = node_eval($2); + if($$->type == CONSTANT_UNKNOWN) + syntaxerror("can't evaluate default parameter value (needs to be a compile-time constant)"); +} //CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);} CONSTANT : T_INT {$$ = constant_new_int($1);} @@ -3019,8 +2815,8 @@ 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 {PASS12 $$=$1;} - | '*' {PASS12 $$=registry_getanytype();} - | "void" {PASS12 $$=registry_getanytype();} + | '*' {PASS12 $$=TYPE_ANY;} + | "void" {PASS12 $$=TYPE_ANY;} /* | "String" {$$=registry_getstringclass();} | "int" {$$=registry_getintclass();} @@ -3053,7 +2849,8 @@ EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION { XX : %prec new2 NEW : "new" E XX MAYBE_PARAM_VALUES { - $$.c = $2.c; + typedcode_t v = node_read($2); + $$.c = v.c; if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c); code_t*paramcode = $4.cc; @@ -3076,8 +2873,8 @@ NEW : "new" E XX MAYBE_PARAM_VALUES { } $$.t = TYPE_ANY; - if(TYPE_IS_CLASS($2.t) && $2.t->data) { - $$.t = $2.t->data; + if(TYPE_IS_CLASS(v.t) && v.t->data) { + $$.t = v.t->data; } else { $$.c = abc_coerce_a($$.c); $$.t = TYPE_ANY; @@ -3089,8 +2886,9 @@ NEW : "new" E XX MAYBE_PARAM_VALUES { call (for closures) */ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { - - $$.c = $1.c; + + typedcode_t v = node_read($1); + $$.c = v.c; if($$.c->opcode == OPCODE_COERCE_A) { $$.c = code_cutlast($$.c); } @@ -3126,8 +2924,11 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { $$.c = abc_call($$.c, $3.number); } - if(TYPE_IS_FUNCTION($1.t) && $1.t->data) { - $$.t = ((methodinfo_t*)($1.t->data))->return_type; + if(TYPE_IS_FUNCTION(v.t) && v.t->data) { + $$.t = ((methodinfo_t*)(v.t->data))->return_type; + } else if(TYPE_IS_CLASS(v.t) && v.t->data) { + // calling a class is like a typecast + $$.t = (classinfo_t*)v.t->data; } else { $$.c = abc_coerce_a($$.c); $$.t = TYPE_ANY; @@ -3156,7 +2957,8 @@ FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' { } DELETE: "delete" E { - $$.c = $2.c; + typedcode_t v = node_read($2); + $$.c = v.c; if($$.c->opcode == OPCODE_COERCE_A) { $$.c = code_cutlast($$.c); } @@ -3170,7 +2972,7 @@ DELETE: "delete" E { $$.c = abc_deleteproperty2($$.c, name); } else { $$.c = abc_getlocal_0($$.c); - MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, ""); + MULTINAME_LATE(m, v.t?v.t->access:ACCESS_PACKAGE, ""); $$.c = abc_deleteproperty2($$.c, &m); } $$.t = TYPE_BOOLEAN; @@ -3186,489 +2988,192 @@ RETURN: "return" EXPRESSION { // ----------------------- expression types ------------------------------------- -NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;} -EXPRESSION : E %prec below_minus {$$ = $1;} -EXPRESSION : EXPRESSION ',' E %prec below_minus { - $$.c = $1.c; - $$.c = cut_last_push($$.c); - $$.c = code_append($$.c,$3.c); - $$.t = $3.t; +NONCOMMAEXPRESSION : E %prec below_minus { + $$ = node_read($1); } -VOIDEXPRESSION : EXPRESSION %prec below_minus { - $$=cut_last_push($1.c); +EXPRESSION : COMMA_EXPRESSION { + $$ = node_read($1); } - -// ----------------------- expression evaluation ------------------------------------- - -E : INNERFUNCTION %prec prec_none {$$ = $1;} -E : VAR_READ %prec T_IDENTIFIER {$$ = $1;} -E : NEW {$$ = $1;} -E : DELETE {$$ = $1;} - -E : FUNCTIONCALL - -E : CONSTANT { - node_t*n = mkconstnode($1); - $$ = node_read(n); +COMMA_EXPRESSION : E %prec below_minus { + $$ = mkmultinode(&node_comma, $1); } - -E : T_REGEXP { - $$.c = 0; - namespace_t ns = {ACCESS_PACKAGE, ""}; - multiname_t m = {QNAME, &ns, 0, "RegExp"}; - if(!$1.options) { - $$.c = abc_getlex2($$.c, &m); - $$.c = abc_pushstring($$.c, $1.pattern); - $$.c = abc_construct($$.c, 1); - } else { - $$.c = abc_getlex2($$.c, &m); - $$.c = abc_pushstring($$.c, $1.pattern); - $$.c = abc_pushstring($$.c, $1.options); - $$.c = abc_construct($$.c, 2); - } - $$.t = TYPE_REGEXP; +COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_minus { + $$ = multinode_extend($1, $3); } - -E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c); - $$.t = TYPE_BOOLEAN; - } -E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c); - $$.t = TYPE_BOOLEAN; - } -E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c); - $$.t = TYPE_BOOLEAN; - } -E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c); - $$.t = TYPE_BOOLEAN; - } -E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c); - $$.t = TYPE_BOOLEAN; - } -E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c); - $$.t = TYPE_BOOLEAN; - } -E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c); - $$.t = TYPE_BOOLEAN; - } -E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c); - $$.t = TYPE_BOOLEAN; - } - -E : E "||" E {$$.t = join_types($1.t, $3.t, 'O'); - $$.c = $1.c; - $$.c = converttype($$.c, $1.t, $$.t); - $$.c = abc_dup($$.c); - code_t*jmp = $$.c = abc_iftrue($$.c, 0); - $$.c = cut_last_push($$.c); - $$.c = code_append($$.c,$3.c); - $$.c = converttype($$.c, $3.t, $$.t); - code_t*label = $$.c = abc_label($$.c); - jmp->branch = label; - } -E : E "&&" E { - $$.t = join_types($1.t, $3.t, 'A'); - /*printf("%08x:\n",$1.t); - code_dump($1.c, 0, 0, "", stdout); - printf("%08x:\n",$3.t); - code_dump($3.c, 0, 0, "", stdout); - printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/ - $$.c = $1.c; - $$.c = converttype($$.c, $1.t, $$.t); - $$.c = abc_dup($$.c); - code_t*jmp = $$.c = abc_iffalse($$.c, 0); - $$.c = cut_last_push($$.c); - $$.c = code_append($$.c,$3.c); - $$.c = converttype($$.c, $3.t, $$.t); - code_t*label = $$.c = abc_label($$.c); - jmp->branch = label; - } - -E : '!' E {$$.c=$2.c; - $$.c = abc_not($$.c); - $$.t = TYPE_BOOLEAN; - } - -E : '~' E {$$.c=$2.c; - $$.c = abc_bitnot($$.c); - $$.t = TYPE_INT; - } - -E : E '&' E {$$.c = code_append($1.c,$3.c); - $$.c = abc_bitand($$.c); - $$.t = TYPE_INT; - } - -E : E '^' E {$$.c = code_append($1.c,$3.c); - $$.c = abc_bitxor($$.c); - $$.t = TYPE_INT; - } - -E : E '|' E {$$.c = code_append($1.c,$3.c); - $$.c = abc_bitor($$.c); - $$.t = TYPE_INT; - } - -E : E ">>" E {$$.c = code_append($1.c,$3.c); - $$.c = abc_rshift($$.c); - $$.t = TYPE_INT; - } -E : E ">>>" E {$$.c = code_append($1.c,$3.c); - $$.c = abc_urshift($$.c); - $$.t = TYPE_INT; - } -E : E "<<" E {$$.c = code_append($1.c,$3.c); - $$.c = abc_lshift($$.c); - $$.t = TYPE_INT; - } - -E : E '/' E {$$.c = code_append($1.c,$3.c); - $$.c = abc_divide($$.c); - $$.t = TYPE_NUMBER; - } -E : E '%' E {$$.c = code_append($1.c,$3.c); - $$.c = abc_modulo($$.c); - $$.t = TYPE_NUMBER; - } -E : E '+' E {$$.c = code_append($1.c,$3.c); - if(BOTH_INT($1.t, $3.t)) { - $$.c = abc_add_i($$.c); - $$.t = TYPE_INT; - } else { - $$.c = abc_add($$.c); - $$.t = join_types($1.t,$3.t,'+'); - } - } -E : E '-' E {$$.c = code_append($1.c,$3.c); - if(BOTH_INT($1.t,$3.t)) { - $$.c = abc_subtract_i($$.c); - $$.t = TYPE_INT; - } else { - $$.c = abc_subtract($$.c); - $$.t = TYPE_NUMBER; - } - } -E : E '*' E {$$.c = code_append($1.c,$3.c); - if(BOTH_INT($1.t,$3.t)) { - $$.c = abc_multiply_i($$.c); - $$.t = TYPE_INT; - } else { - $$.c = abc_multiply($$.c); - $$.t = TYPE_NUMBER; - } - } - -E : E "in" E {$$.c = code_append($1.c,$3.c); - $$.c = abc_in($$.c); - $$.t = TYPE_BOOLEAN; - } - -E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate - if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) { - MULTINAME(m, (classinfo_t*)($3.t->data)); - $$.c = abc_astype2($1.c, &m); - $$.t = $3.t->data; - } else { - $$.c = code_append($1.c, $3.c); - $$.c = abc_astypelate($$.c); - $$.t = TYPE_ANY; - } - } - -E : E "instanceof" E - {$$.c = code_append($1.c, $3.c); - $$.c = abc_instanceof($$.c); - $$.t = TYPE_BOOLEAN; - } - -E : E "is" E {$$.c = code_append($1.c, $3.c); - $$.c = abc_istypelate($$.c); - $$.t = TYPE_BOOLEAN; - } - -E : "typeof" '(' E ')' { - $$.c = $3.c; - $$.c = abc_typeof($$.c); - $$.t = TYPE_STRING; - } - -E : "void" E { - $$.c = cut_last_push($2.c); - $$.c = abc_pushundefined($$.c); - $$.t = TYPE_ANY; - } - -E : "void" { $$.c = abc_pushundefined(0); - $$.t = TYPE_ANY; - } - -E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too - -E : '-' E { - $$.c = $2.c; - if(IS_INT($2.t)) { - $$.c=abc_negate_i($$.c); - $$.t = TYPE_INT; - } else { - $$.c=abc_negate($$.c); - $$.t = TYPE_NUMBER; - } +VOIDEXPRESSION : E %prec below_minus { + $$ = node_exec($1); } - -E : E '[' E ']' { - $$.c = $1.c; - $$.c = code_append($$.c, $3.c); - - MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, ""); - $$.c = abc_getproperty2($$.c, &m); - $$.t = 0; // array elements have unknown type +VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_minus { + $$ = $1; + $$ = code_append($$, node_exec($3)); } -E : '[' MAYBE_EXPRESSION_LIST ']' { - $$.c = code_new(); - $$.c = code_append($$.c, $2.cc); - $$.c = abc_newarray($$.c, $2.number); - $$.t = registry_getarrayclass(); -} +MAYBE_DICT_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;} +MAYBE_DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST {$$=$1;} -MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;} -MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;} +DICTLH: T_IDENTIFIER {$$=abc_pushstring(0,$1);} +DICTLH: T_STRING {$$=abc_pushstring2(0,&$1);} -EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION { +DICT_EXPRPAIR_LIST : DICTLH ':' NONCOMMAEXPRESSION { $$.cc = 0; - $$.cc = code_append($$.cc, $1.c); + $$.cc = code_append($$.cc, $1); $$.cc = code_append($$.cc, $3.c); $$.number = 2; } -EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION { +DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST ',' DICTLH ':' NONCOMMAEXPRESSION { $$.cc = $1.cc; $$.number = $1.number+2; - $$.cc = code_append($$.cc, $3.c); + $$.cc = code_append($$.cc, $3); $$.cc = code_append($$.cc, $5.c); } -//MAYBECOMMA: ',' -//MAYBECOMMA: - -E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' { - $$.c = code_new(); - $$.c = code_append($$.c, $2.cc); - $$.c = abc_newobject($$.c, $2.number/2); - $$.t = registry_getobjectclass(); -} - -E : E "*=" E { - code_t*c = $3.c; - if(BOTH_INT($1.t,$3.t)) { - c=abc_multiply_i(c); - } else { - c=abc_multiply(c); - } - c=converttype(c, join_types($1.t, $3.t, '*'), $1.t); - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.t = $1.t; - } - -E : E "%=" E { - code_t*c = abc_modulo($3.c); - c=converttype(c, join_types($1.t, $3.t, '%'), $1.t); - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.t = $1.t; - } -E : E "<<=" E { - code_t*c = abc_lshift($3.c); - c=converttype(c, join_types($1.t, $3.t, '<'), $1.t); - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.t = $1.t; - } -E : E ">>=" E { - code_t*c = abc_rshift($3.c); - c=converttype(c, join_types($1.t, $3.t, '>'), $1.t); - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.t = $1.t; - } -E : E ">>>=" E { - code_t*c = abc_urshift($3.c); - c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t); - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.t = $1.t; - } -E : E "/=" E { - code_t*c = abc_divide($3.c); - c=converttype(c, join_types($1.t, $3.t, '/'), $1.t); - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.t = $1.t; - } -E : E "|=" E { - code_t*c = abc_bitor($3.c); - c=converttype(c, TYPE_INT, $1.t); - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.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, 1); - $$.t = $1.t; - } -E : E "+=" E { - code_t*c = $3.c; - - if(TYPE_IS_INT($1.t)) { - c=abc_add_i(c); - } else { - c=abc_add(c); - c=converttype(c, join_types($1.t, $3.t, '+'), $1.t); - } - - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.t = $1.t; - } -E : E "-=" E { code_t*c = $3.c; - if(TYPE_IS_INT($1.t)) { - c=abc_subtract_i(c); - } else { - c=abc_subtract(c); - c=converttype(c, join_types($1.t, $3.t, '-'), $1.t); - } - - $$.c = toreadwrite($1.c, c, 0, 0, 1); - $$.t = $1.t; - } -E : E '=' E { code_t*c = 0; - c = code_append(c, $3.c); - c = converttype(c, $3.t, $1.t); - $$.c = toreadwrite($1.c, c, 1, 0, 1); - $$.t = $1.t; - } -E : E '?' E ':' E %prec below_assignment { - $$.t = join_types($3.t,$5.t,'?'); - $$.c = $1.c; - code_t*j1 = $$.c = abc_iffalse($$.c, 0); - $$.c = code_append($$.c, $3.c); - $$.c = converttype($$.c, $3.t, $$.t); - code_t*j2 = $$.c = abc_jump($$.c, 0); - $$.c = j1->branch = abc_label($$.c); - $$.c = code_append($$.c, $5.c); - $$.c = converttype($$.c, $5.t, $$.t); - $$.c = j2->branch = abc_label($$.c); - } +// ----------------------- expression evaluation ------------------------------------- -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))) { - int nr = getlocalnr($1.c); - code_free($1.c);$1.c=0; - if(TYPE_IS_INT($1.t)) { - $$.c = abc_getlocal(0, nr); - $$.c = abc_inclocal_i($$.c, nr); - } else if(TYPE_IS_NUMBER($1.t)) { - $$.c = abc_getlocal(0, nr); - $$.c = abc_inclocal($$.c, nr); - } else syntaxerror("internal error"); - } else { - if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { - c=abc_increment_i(c); - type = TYPE_INT; - } else { - c=abc_increment(c); - type = TYPE_NUMBER; - } - c=converttype(c, type, $1.t); - $$.c = toreadwrite($1.c, c, 0, 1, 1); - $$.t = $1.t; - } - } +E : INNERFUNCTION %prec prec_none {$$ = mkcodenode($1);} +E : MEMBER %prec '.' {$$ = mkcodenode($1);} +E : NEW {$$ = mkcodenode($1);} +E : DELETE {$$ = mkcodenode($1);} +E : FUNCTIONCALL {$$ = mkcodenode($1);} +E : VAR_READ %prec T_IDENTIFIER {$$ = $1;} -// TODO: use inclocal, like with ++ -E : E "--" { code_t*c = 0; - classinfo_t*type = $1.t; - if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { - c=abc_decrement_i(c); - type = TYPE_INT; - } else { - c=abc_decrement(c); - type = TYPE_NUMBER; - } - c=converttype(c, type, $1.t); - $$.c = toreadwrite($1.c, c, 0, 1, 1); - $$.t = $1.t; - } +E : CONSTANT { + $$ = mkconstnode($1); +} -E : "++" %prec plusplus_prefix E { code_t*c = 0; - classinfo_t*type = $2.t; - if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { - c=abc_increment_i(c); - type = TYPE_INT; - } else { - c=abc_increment(c); - type = TYPE_NUMBER; - } - c=converttype(c, type, $2.t); - $$.c = toreadwrite($2.c, c, 0, 0, 1); - $$.t = $2.t; - } +/* regexp */ +E : T_REGEXP { + typedcode_t v; + v.c = 0; + namespace_t ns = {ACCESS_PACKAGE, ""}; + multiname_t m = {QNAME, &ns, 0, "RegExp"}; + if(!$1.options) { + v.c = abc_getlex2(v.c, &m); + v.c = abc_pushstring(v.c, $1.pattern); + v.c = abc_construct(v.c, 1); + } else { + v.c = abc_getlex2(v.c, &m); + v.c = abc_pushstring(v.c, $1.pattern); + v.c = abc_pushstring(v.c, $1.options); + v.c = abc_construct(v.c, 2); + } + v.t = TYPE_REGEXP; + $$ = mkcodenode(v); +} -E : "--" %prec minusminus_prefix E { code_t*c = 0; - classinfo_t*type = $2.t; - if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { - c=abc_decrement_i(c); - type = TYPE_INT; - } else { - c=abc_decrement(c); - type = TYPE_NUMBER; - } - c=converttype(c, type, $2.t); - $$.c = toreadwrite($2.c, c, 0, 0, 1); - $$.t = $2.t; - } +/* array */ +E : '[' MAYBE_EXPRESSION_LIST ']' { + typedcode_t v; + v.c = code_new(); + v.c = code_append(v.c, $2.cc); + v.c = abc_newarray(v.c, $2.number); + v.t = registry_getarrayclass(); + $$ = mkcodenode(v); +} + +/* dictionary */ +E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' { + typedcode_t v; + v.c = code_new(); + v.c = code_append(v.c, $2.cc); + v.c = abc_newobject(v.c, $2.number/2); + v.t = registry_getobjectclass(); + $$ = mkcodenode(v); +} + +E : E '<' E {$$ = mknode2(&node_lt,$1,$3);} +E : E '>' E {$$ = mknode2(&node_gt,$1,$3);} +E : E "<=" E {$$ = mknode2(&node_le,$1,$3);} +E : E ">=" E {$$ = mknode2(&node_ge,$1,$3);} +E : E "==" E {$$ = mknode2(&node_eqeq,$1,$3);} +E : E "===" E {$$ = mknode2(&node_eqeqeq,$1,$3);} +E : E "!==" E {$$ = mknode2(&node_noteqeq,$1,$3);} +E : E "!=" E {$$ = mknode2(&node_noteq,$1,$3);} +E : E "||" E {$$ = mknode2(&node_oror,$1,$3);} +E : E "&&" E {$$ = mknode2(&node_andand,$1,$3);} +E : '!' E {$$ = mknode1(&node_not, $2);} +E : '~' E {$$ = mknode1(&node_bitnot, $2);} +E : E '&' E {$$ = mknode2(&node_bitand, $1, $3);} +E : E '^' E {$$ = mknode2(&node_bitxor, $1, $3);} +E : E '|' E {$$ = mknode2(&node_bitor, $1, $3);} +E : E ">>" E {$$ = mknode2(&node_shr, $1, $3);} +E : E ">>>" E {$$ = mknode2(&node_ushr, $1, $3);} +E : E "<<" E {$$ = mknode2(&node_shl, $1, $3);} +E : E '/' E {$$ = mknode2(&node_div, $1, $3);} +E : E '%' E {$$ = mknode2(&node_mod, $1, $3);} +E : E '+' E {$$ = mknode2(&node_plus, $1, $3);} +E : E '-' E {$$ = mknode2(&node_minus, $1, $3);} +E : E '*' E {$$ = mknode2(&node_multiply, $1, $3);} +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 : "void" E {$$ = mknode1(&node_void, $2);} +E : "void" { $$ = mkconstnode(constant_new_undefined());} +E : '(' COMMA_EXPRESSION ')' { $$=$2;} +E : '-' E {$$ = mknode1(&node_neg, $2);} +E : E '[' E ']' {$$ = mknode2(&node_arraylookup, $1,$3);} +E : E "*=" E {$$ = mknode2(&node_muleq, $1, $3);} +E : E "%=" E {$$ = mknode2(&node_modeq, $1, $3);} +E : E "<<=" E {$$ = mknode2(&node_shleq, $1, $3);} +E : E ">>=" E {$$ = mknode2(&node_shreq, $1, $3);} +E : E ">>>=" E {$$ = mknode2(&node_ushreq, $1, $3);} +E : E "/=" E { $$ = mknode2(&node_diveq, $1, $3);} +E : E "|=" E { $$ = mknode2(&node_bitoreq, $1, $3);} +E : E "^=" E { $$ = mknode2(&node_bitxoreq, $1, $3);} +E : E "&=" E { $$ = mknode2(&node_bitandeq, $1, $3);} +E : E "+=" E { $$ = mknode2(&node_pluseq, $1, $3);} +E : E "-=" E { $$ = mknode2(&node_minuseq, $1, $3);} +E : E '=' E { $$ = mknode2(&node_assign, $1, $3);} +E : E '?' E ':' E %prec below_assignment { $$ = mknode3(&node_tenary, $1, $3, $5);} + +E : E "++" { $$ = mknode1(&node_rplusplus, $1);} +E : E "--" { $$ = mknode1(&node_rminusminus, $1);} +E : "++" %prec plusplus_prefix E {$$ = mknode1(&node_lplusplus, $2); } +E : "--" %prec minusminus_prefix E {$$ = mknode1(&node_lminusminus, $2); } E : "super" '.' T_IDENTIFIER { if(!state->cls->info) syntaxerror("super keyword not allowed outside a class"); classinfo_t*t = state->cls->info->superclass; if(!t) t = TYPE_OBJECT; - memberinfo_t*f = findmember_nsset(t, $3, 1); - MEMBER_MULTINAME(m, f, $3); - $$.c = 0; - $$.c = abc_getlocal_0($$.c); - $$.c = abc_getsuper2($$.c, &m); - $$.t = slotinfo_gettype((slotinfo_t*)f); + typedcode_t v; + v.c = 0; + v.c = abc_getlocal_0(v.c); + v.c = abc_getsuper2(v.c, &m); + v.t = slotinfo_gettype((slotinfo_t*)f); + $$ = mkcodenode(v); } E : '@' T_IDENTIFIER { // attribute TODO - $$.c = abc_pushundefined(0); - $$.t = 0; + $$ = mkdummynode(); as3_warning("ignored @ operator"); } E : E '.' '@' T_IDENTIFIER { // child attribute TODO - $$.c = abc_pushundefined(0); - $$.t = 0; + $$ = mkdummynode(); as3_warning("ignored .@ operator"); } E : E '.' T_IDENTIFIER "::" T_IDENTIFIER { // namespace declaration TODO - $$.c = abc_pushundefined(0); - $$.t = 0; + $$ = mkdummynode(); as3_warning("ignored :: operator"); } E : E ".." T_IDENTIFIER { // descendants TODO - $$.c = abc_pushundefined(0); - $$.t = 0; + $$ = mkdummynode(); as3_warning("ignored .. operator"); } E : E '.' '(' E ')' { // filter TODO - $$.c = abc_pushundefined(0); - $$.t = 0; + $$ = mkdummynode(); as3_warning("ignored .() operator"); } @@ -3679,10 +3184,10 @@ E : E '.' '(' E ')' { // as3_warning("ignored ::[] operator"); // } - -E : E '.' T_IDENTIFIER { - $$.c = $1.c; - classinfo_t*t = $1.t; +MEMBER : E '.' T_IDENTIFIER { + typedcode_t v1 = node_read($1); + $$.c = v1.c; + classinfo_t*t = v1.t; char is_static = 0; if(TYPE_IS_CLASS(t) && t->data) { t = t->data; @@ -3700,9 +3205,8 @@ E : E '.' T_IDENTIFIER { $$.c = abc_getslot($$.c, f->slot); } else { if(!f) { - as3_warning("Access of undefined property '%s' in %s", $3, t->name); + as3_softwarning("Access of undefined property '%s' in %s", $3, t->name); } - MEMBER_MULTINAME(m, f, $3); $$.c = abc_getproperty2($$.c, &m); } @@ -3710,8 +3214,9 @@ E : E '.' T_IDENTIFIER { $$.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]; + + } else if(v1.c && v1.c->opcode == OPCODE___PUSHPACKAGE__) { + string_t*package = v1.c->data[0]; char*package2 = concat3(package->str, ".", $3); slotinfo_t*a = registry_find(package->str, $3); @@ -3719,7 +3224,7 @@ E : E '.' T_IDENTIFIER { $$ = push_class(a); } else if(dict_contains(state->import_toplevel_packages, package2) || registry_ispackage(package2)) { - $$.c = $1.c; + $$.c = v1.c; $$.c->data[0] = string_new4(package2); $$.t = 0; } else { @@ -3733,7 +3238,7 @@ E : E '.' T_IDENTIFIER { multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3}; $$.c = abc_getproperty2($$.c, &m); $$.c = abc_coerce_a($$.c); - $$.t = registry_getanytype(); + $$.t = TYPE_ANY; } } @@ -3755,8 +3260,11 @@ VAR_READ : T_IDENTIFIER { as3_schedule_class_noerror(state->package, $1); PASS2 - $$.t = 0; - $$.c = 0; + typedcode_t o; + o.t = 0; + o.c = 0; + $$ = 0; + slotinfo_t*a = 0; memberinfo_t*f = 0; @@ -3764,14 +3272,16 @@ VAR_READ : T_IDENTIFIER { /* look at variables */ if((v = find_variable(state, $1))) { // $1 is a local variable - $$.c = abc_getlocal($$.c, v->index); - $$.t = v->type; + o.c = abc_getlocal(o.c, v->index); + o.t = v->type; + $$ = mkcodenode(o); break; } if((v = find_slot(state, $1))) { - $$.c = abc_getscopeobject($$.c, 1); - $$.c = abc_getslot($$.c, v->index); - $$.t = v->type; + o.c = abc_getscopeobject(o.c, 1); + o.c = abc_getslot(o.c, v->index); + o.t = v->type; + $$ = mkcodenode(o); break; } @@ -3780,53 +3290,70 @@ VAR_READ : T_IDENTIFIER { /* look at current class' members */ if(!state->method->inner && state->cls && - (f = findmember_nsset(state->cls->info, $1, 1)) && - (f->flags&FLAG_STATIC) >= i_am_static) + (f = findmember_nsset(state->cls->info, $1, 1))) { - // $1 is a function in this class + // $1 is a member or attribute in this class int var_is_static = (f->flags&FLAG_STATIC); - if(f->kind == INFOTYPE_METHOD) { - $$.t = TYPE_FUNCTION(f); - } else { - $$.t = f->type; - } - if(var_is_static && !i_am_static) { - /* access to a static member from a non-static location. - do this via findpropstrict: - there doesn't seem to be any non-lookup way to access - static properties of a class */ - state->method->late_binding = 1; - $$.t = f->type; - namespace_t ns = {f->access, f->package}; - multiname_t m = {QNAME, &ns, 0, $1}; - $$.c = abc_findpropstrict2($$.c, &m); - $$.c = abc_getproperty2($$.c, &m); - break; - } else if(f->slot>0) { - $$.c = abc_getlocal_0($$.c); - $$.c = abc_getslot($$.c, f->slot); - break; - } else { - namespace_t ns = {f->access, f->package}; - multiname_t m = {QNAME, &ns, 0, $1}; - $$.c = abc_getlocal_0($$.c); - $$.c = abc_getproperty2($$.c, &m); - break; + if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) { + /* if the variable is a constant (and we know what is evaluates to), we + can just use the value itself */ + varinfo_t*v = (varinfo_t*)f; + if(v->value) { + $$ = mkconstnode(v->value); + break; + } + } + + if(var_is_static >= i_am_static) { + if(f->kind == INFOTYPE_METHOD) { + o.t = TYPE_FUNCTION(f); + } else { + o.t = f->type; + } + + if(var_is_static && !i_am_static) { + /* access to a static member from a non-static location. + do this via findpropstrict: + there doesn't seem to be any non-lookup way to access + static properties of a class */ + state->method->late_binding = 1; + o.t = f->type; + namespace_t ns = {f->access, f->package}; + multiname_t m = {QNAME, &ns, 0, $1}; + o.c = abc_findpropstrict2(o.c, &m); + o.c = abc_getproperty2(o.c, &m); + $$ = mkcodenode(o); + break; + } else if(f->slot>0) { + o.c = abc_getlocal_0(o.c); + o.c = abc_getslot(o.c, f->slot); + $$ = mkcodenode(o); + break; + } else { + namespace_t ns = {f->access, f->package}; + multiname_t m = {QNAME, &ns, 0, $1}; + o.c = abc_getlocal_0(o.c); + o.c = abc_getproperty2(o.c, &m); + $$ = mkcodenode(o); + break; + } } } /* look at actual classes, in the current package and imported */ if((a = find_class($1))) { - $$ = push_class(a); + o = push_class(a); + $$ = mkcodenode(o); break; } /* look through package prefixes */ if(dict_contains(state->import_toplevel_packages, $1) || registry_ispackage($1)) { - $$.c = abc___pushpackage__($$.c, $1); - $$.t = 0; + o.c = abc___pushpackage__(o.c, $1); + o.t = 0; + $$ = mkcodenode(o); //? break; } @@ -3838,9 +3365,11 @@ VAR_READ : T_IDENTIFIER { multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1}; - $$.t = 0; - $$.c = abc_findpropstrict2($$.c, &m); - $$.c = abc_getproperty2($$.c, &m); + o.t = 0; + o.c = abc_findpropstrict2(o.c, &m); + o.c = abc_getproperty2(o.c, &m); + $$ = mkcodenode(o); + break; } } @@ -3902,7 +3431,7 @@ USE_NAMESPACE : "use" "namespace" CLASS_SPEC { syntaxerror("Couldn't resolve namespace %s", $3->name); } - if(!s || s->kind != INFOTYPE_SLOT) + if(!s || s->kind != INFOTYPE_VAR) syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1); if(!s->value || !NS_TYPE(s->value->type)) syntaxerror("%s.%s is not a namespace", $3->package, $3->name);