X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=6b527653ce2778e286e33d6cbc479e51542eece5;hb=938c420724d7780bcd4754febc87e47d0d9a3b55;hp=0c8c6354db0e5d979b9af71e3079e026cf9bda92;hpb=b16e095437f2a9c6adcfadbe8116f413927a4ed5;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index 0c8c635..6b52765 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -59,13 +59,17 @@ constant_t*constant; for_start_t for_start; abc_exception_t *exception; - abc_exception_list_t *exception_list; + regexp_t regexp; + struct { + abc_exception_list_t *l; + code_t*finally; + } catch_list; } %token T_IDENTIFIER %token T_STRING -%token T_REGEXP +%token T_REGEXP %token T_EMPTY %token T_INT %token T_UINT @@ -78,17 +82,18 @@ %token T_DO "do" %token T_SWITCH "switch" -%token KW_IMPLEMENTS +%token KW_IMPLEMENTS "implements" %token KW_NAMESPACE "namespace" %token KW_PACKAGE "package" -%token KW_PROTECTED -%token KW_PUBLIC -%token KW_PRIVATE +%token KW_PROTECTED "protected" +%token KW_PUBLIC "public" +%token KW_PRIVATE "private" %token KW_USE "use" -%token KW_INTERNAL +%token KW_INTERNAL "internal" %token KW_NEW "new" -%token KW_NATIVE +%token KW_NATIVE "native" %token KW_FUNCTION "function" +%token KW_FINALLY "finally" %token KW_UNDEFINED "undefined" %token KW_CONTINUE "continue" %token KW_CLASS "class" @@ -98,7 +103,8 @@ %token KW_SET "set" %token KW_VOID "void" %token KW_THROW "throw" -%token KW_STATIC +%token KW_STATIC "static" +%token KW_WITH "with" %token KW_INSTANCEOF "instanceof" %token KW_IMPORT "import" %token KW_RETURN "return" @@ -107,13 +113,13 @@ %token KW_NULL "null" %token KW_VAR "var" %token KW_DYNAMIC "dynamic" -%token KW_OVERRIDE -%token KW_FINAL +%token KW_OVERRIDE "override" +%token KW_FINAL "final" %token KW_EACH "each" %token KW_GET "get" %token KW_TRY "try" %token KW_SUPER "super" -%token KW_EXTENDS +%token KW_EXTENDS "extends" %token KW_FALSE "false" %token KW_TRUE "true" %token KW_BOOLEAN "Boolean" @@ -136,6 +142,7 @@ %token T_NEE "!==" %token T_LE "<=" %token T_GE ">=" +%token T_ORBY "|=" %token T_DIVBY "/=" %token T_MODBY "%=" %token T_MULBY "*=" @@ -156,16 +163,16 @@ %token T_SHR ">>" %type FOR_START -%type X_IDENTIFIER PACKAGE FOR_IN_INIT +%type X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER %type VARCONST %type CODE %type CODEPIECE CODE_STATEMENT -%type CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH +%type CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH %type PACKAGE_DECLARATION SLOT_DECLARATION %type FUNCTION_DECLARATION PACKAGE_INITCODE %type VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW -%type CATCH -%type CATCH_LIST +%type CATCH FINALLY +%type CATCH_LIST CATCH_FINALLY_LIST %type CLASS_DECLARATION %type NAMESPACE_DECLARATION %type INTERFACE_DECLARATION @@ -174,7 +181,8 @@ %type MAYBEEXPRESSION %type E DELETE %type CONSTANT -%type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY +%type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY +%type INNERFUNCTION %type USE_NAMESPACE %type FOR_INIT %type IMPORT @@ -212,8 +220,8 @@ %right '?' ':' %left "||" %left "&&" -%nonassoc '|' -%nonassoc '^' +%left '|' +%left '^' %nonassoc '&' %nonassoc "==" "!=" "===" "!==" %nonassoc "is" "as" "in" @@ -235,7 +243,8 @@ // needed for "return" precedence: %nonassoc T_STRING T_REGEXP %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT -%nonassoc "false" "true" "null" "undefined" "super" +%nonassoc "false" "true" "null" "undefined" "super" "function" +%nonassoc above_function @@ -244,6 +253,7 @@ static int yyerror(char*s) { syntaxerror("%s", s); + return 0; //make gcc happy } static char* concat2(const char* t1, const char* t2) @@ -288,9 +298,6 @@ typedef struct _methodstate { /* method data */ memberinfo_t*info; char late_binding; - /* code that needs to be executed at the start of - a method (like initializing local registers) */ - code_t*initcode; char is_constructor; char has_super; char is_global; @@ -300,7 +307,7 @@ typedef struct _methodstate { typedef struct _state { struct _state*old; int level; - + char*package; import_list_t*wildcard_imports; dict_t*imports; @@ -335,8 +342,7 @@ DECLARE_LIST(state); multiname_t m;\ namespace_t m##_ns;\ if(f) { \ - m##_ns.access = flags2access(f->flags); \ - m##_ns.name = ""; \ + m##_ns = flags2namespace(f->flags, ""); \ m.type = QNAME; \ m.ns = &m##_ns; \ m.namespace_set = 0; \ @@ -419,30 +425,56 @@ static void old_state() if(!state || !state->old) syntaxerror("invalid nesting"); state_t*leaving = state; + state = state->old; - /*if(state->method->initcode) { - printf("residual initcode\n"); - code_dump(state->method->initcode, 0, 0, "", stdout); - }*/ state_destroy(leaving); } -void initialize_state() + +void initialize_parser() { global = rfx_calloc(sizeof(global_t)); - new_state(); - - state->package = current_filename; - global->file = abc_file_new(); global->file->flags &= ~ABCFILE_LAZY; global->variable_count = 1; - - global->init = abc_initscript(global->file, 0); + global->init = abc_initscript(global->file); code_t*c = global->init->method->body->code; - c = abc_getlocal_0(c); c = abc_pushscope(c); - + /*c = abc_findpropstrict(c, "[package]::trace"); + c = abc_pushstring(c, "[entering global init function]"); + c = abc_callpropvoid(c, "[package]::trace", 1);*/ + global->init->method->body->code = c; +} + +void initialize_file(char*filename) +{ + new_state(); + state->package = filename; + // needed for state->method->late_binding: + state->method = rfx_calloc(sizeof(methodstate_t)); +} +void finish_file() +{ + if(!state || state->level!=1) { + syntaxerror("unexpected end of file"); + } + state_destroy(state);state=0; +} + +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; + return global->file; +} + + +static void xx_scopetest() +{ /* findpropstrict doesn't just return a scope object- it also makes it "active" somehow. Push local_0 on the scope stack and read it back with findpropstrict, it'll @@ -468,29 +500,115 @@ void initialize_state() c = abc_getlocal_3(c); c = abc_kill(c, 3); c = abc_iftrue(c,xx);*/ +} - c = abc_findpropstrict(c, "[package]::trace"); - c = abc_pushstring(c, "[entering global init function]"); - c = abc_callpropvoid(c, "[package]::trace", 1); + +typedef struct _variable { + int index; + classinfo_t*type; + char init; +} variable_t; + +static variable_t* find_variable(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; + } + s = s->old; + } + return 0; +} +static variable_t* find_variable_safe(char*name) +{ + variable_t* v = find_variable(name); + if(!v) + syntaxerror("undefined variable: %s", name); + return v; +} +static char variable_exists(char*name) +{ + return dict_lookup(state->vars, name)!=0; +} +code_t*defaultvalue(code_t*c, classinfo_t*type); +static int new_variable(char*name, classinfo_t*type, char init) +{ + NEW(variable_t, v); + v->index = global->variable_count; + v->type = type; + v->init = init; - global->init->method->body->code = c; + dict_put(state->vars, name, v); + + return global->variable_count++; } -void* finalize_state() +#define TEMPVARNAME "__as3_temp__" +static int gettempvar() { - if(state->level!=1) { - syntaxerror("unexpected end of file"); + variable_t*v = find_variable(TEMPVARNAME); + if(v) + return v->index; + return new_variable(TEMPVARNAME, 0, 0); +} + +code_t* var_block(code_t*body) +{ + 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; + } } - abc_method_body_t*m = global->init->method->body; - //__ popscope(m); - - __ findpropstrict(m, "[package]::trace"); - __ pushstring(m, "[leaving global init function]"); - __ callpropvoid(m, "[package]::trace", 1); - __ returnvoid(m); - state_destroy(state); + if(k) { + code_t*x = body; + while(x) { + if(x->opcode== OPCODE___BREAK__ || + x->opcode== OPCODE___CONTINUE__) { + /* link kill code before break/continue */ + code_t*e = code_dup(k); + code_t*s = code_start(e); + s->prev = x->prev; + if(x->prev) { + x->prev->next = s; + } + e->next = x; + x->prev = e; + } + x = x->prev; + } + } + + c = code_append(c, body); + c = code_append(c, k); + return c; +} - return global->file; +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)); + /* append return if necessary */ + if(!c || (c->opcode != OPCODE_RETURNVOID && + c->opcode != OPCODE_RETURNVALUE)) { + c = abc_returnvoid(c); + } + return c; } @@ -511,7 +629,7 @@ static void endpackage() old_state(); } -char*globalclass=0; +char*as3_globalclass=0; static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface) { if(state->cls) { @@ -520,24 +638,15 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo new_state(); global->variable_count = 1; state->cls = rfx_calloc(sizeof(classstate_t)); + state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor token_list_t*t=0; classinfo_list_t*mlist=0; - /*printf("entering class %s\n", name); - printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n"); - if(extends) - printf(" extends: %s.%s\n", extends->package, extends->name); - printf(" implements (%d): ", list_length(implements)); - for(mlist=implements;mlist;mlist=mlist->next) { - printf("%s ", mlist->classinfo?mlist->classinfo->name:0); - } - printf("\n"); - */ - if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC)) + if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC)) syntaxerror("invalid modifier(s)"); - if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL)) + if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) syntaxerror("public and internal not supported at the same time."); /* create the class name, together with the proper attributes */ @@ -642,28 +751,16 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo __ setslot(m, slotindex); /* flash.display.MovieClip handling */ - if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) { + if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) { if(state->package && state->package[0]) { - globalclass = concat3(state->package, ".", classname); + as3_globalclass = concat3(state->package, ".", classname); } else { - globalclass = strdup(classname); + as3_globalclass = strdup(classname); } } multiname_destroy(extends2); } -static code_t* wrap_function(code_t*c,code_t*initcode, code_t*body) -{ - c = code_append(c, initcode); - c = code_append(c, body); - /* append return if necessary */ - if(!c || c->opcode != OPCODE_RETURNVOID && - c->opcode != OPCODE_RETURNVALUE) { - c = abc_returnvoid(c); - } - return c; -} - static void endclass() { if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) { @@ -672,6 +769,13 @@ static void endclass() c = abc_constructsuper(c, 0); state->cls->init = code_append(state->cls->init, c); } + 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); @@ -680,93 +784,13 @@ static void endclass() if(state->cls->static_init) { abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0); m->body->code = wrap_function(0, state->cls->static_init, m->body->code); - } else { - // handy for scope testing - /*code_t*c = 0; - c = abc_pop(c); - c = abc_pop(c); - abc_class_getstaticconstructor(state->cls->abc,0)->body->code = c;*/ } free(state->cls);state->cls=0; + free(state->method);state->method=0; old_state(); } -typedef struct _variable { - int index; - classinfo_t*type; -} variable_t; - -static variable_t* find_variable(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; - } - s = s->old; - } - return 0; -} -static variable_t* find_variable_safe(char*name) -{ - variable_t* v = find_variable(name); - if(!v) - syntaxerror("undefined variable: %s", name); - return v; -} -static char variable_exists(char*name) -{ - return dict_lookup(state->vars, name)!=0; -} -code_t*defaultvalue(code_t*c, classinfo_t*type); -static int new_variable(char*name, classinfo_t*type, char init) -{ - NEW(variable_t, v); - v->index = global->variable_count; - v->type = type; - - dict_put(state->vars, name, v); - - if(init && state->method && type) { - /* if this is a typed variable: - push default value for type on stack at the very beginning of the - method, so that it always has that type regardless of the control - path */ - state->method->initcode = defaultvalue(state->method->initcode, type); - state->method->initcode = abc_setlocal(state->method->initcode, v->index); - } - return global->variable_count++; -} -#define TEMPVARNAME "__as3_temp__" -static int gettempvar() -{ - variable_t*v = find_variable(TEMPVARNAME); - if(v) - return v->index; - return new_variable(TEMPVARNAME, 0, 0); -} - -code_t* killvars(code_t*c) -{ - int t; - for(t=0;tvars->hashsize;t++) { - dictentry_t*e =state->vars->slots[t]; - while(e) { - variable_t*v = (variable_t*)e->data; - //do this always, otherwise register types don't match - //in the verifier when doing nested loops - //if(!TYPE_IS_BUILTIN_SIMPLE(type)) { - c = abc_kill(c, v->index); - e = e->next; - } - } - return c; -} - void check_code_for_break(code_t*c) { while(c) { @@ -801,17 +825,21 @@ static void check_constant_against_type(classinfo_t*t, constant_t*c) } } + static int flags2access(int flags) { int access = 0; if(flags&FLAG_PUBLIC) { - if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); access = ACCESS_PACKAGE; } else if(flags&FLAG_PRIVATE) { - if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); access = ACCESS_PRIVATE; } else if(flags&FLAG_PROTECTED) { - if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); access = ACCESS_PROTECTED; } else { access = ACCESS_PACKAGEINTERNAL; @@ -819,6 +847,7 @@ static int flags2access(int flags) return access; } + static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot) { memberinfo_t*minfo = 0; @@ -875,7 +904,7 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC; if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE; if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED; - if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL; + if(flags&FLAG_PACKAGEINTERNAL) minfo->flags |= FLAG_PACKAGEINTERNAL; if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE; return minfo; } @@ -883,14 +912,14 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type) { - if(state->method) { + if(state->method && state->method->info) { syntaxerror("not able to start another method scope"); } new_state(); global->variable_count = 0; state->method = rfx_calloc(sizeof(methodstate_t)); - state->method->initcode = 0; state->method->has_super = 0; + if(state->cls) { state->method->is_constructor = !strcmp(state->cls->info->name,name); state->cls->has_constructor |= state->method->is_constructor; @@ -924,7 +953,7 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam if(state->method->is_constructor) { f = abc_class_getconstructor(state->cls->abc, type2); } else if(!state->method->is_global) { - namespace_t mname_ns = {flags2access(flags), ""}; + namespace_t mname_ns = flags2namespace(flags, ""); multiname_t mname = {QNAME, &mname_ns, 0, name}; if(flags&FLAG_STATIC) @@ -933,7 +962,7 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam f = abc_class_method(state->cls->abc, type2, &mname); slot = f->trait->slot_id; } else { - namespace_t mname_ns = {flags2access(flags), state->package}; + namespace_t mname_ns = flags2namespace(flags, state->package); multiname_t mname = {QNAME, &mname_ns, 0, name}; f = abc_method_new(global->file, type2, 1); @@ -1011,12 +1040,25 @@ void continuejumpsto(code_t*c, char*name, code_t*jump) } } +#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)) + 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(); @@ -1064,6 +1106,7 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to)) return c; syntaxerror("can't convert type %s to %s", from->name, to->name); + return 0; // make gcc happy } code_t*defaultvalue(code_t*c, classinfo_t*type) @@ -1076,8 +1119,12 @@ 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) { + //c = abc_pushundefined(c); } else { c = abc_pushnull(c); + MULTINAME(m, type); + c = abc_coerce2(c, &m); } return c; } @@ -1130,6 +1177,27 @@ static classinfo_t* find_class(char*name) return 0; } +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; +} + static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore) { /* converts this: @@ -1206,7 +1274,7 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r } else if(r->opcode == OPCODE_GETLOCAL_3) { write->opcode = OPCODE_SETLOCAL_3; } else { - code_dump(r, 0, 0, "", stdout); + code_dump(r); syntaxerror("illegal lvalue: can't assign a value to this expression"); } code_t* c = 0; @@ -1272,12 +1340,139 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r c = abc_kill(c, temp); } } + return c; +} + +char is_break_or_jump(code_t*c) +{ + if(!c) + return 0; + if(c->opcode == OPCODE_JUMP || + c->opcode == OPCODE___BREAK__ || + c->opcode == OPCODE___CONTINUE__ || + c->opcode == OPCODE_THROW || + c->opcode == OPCODE_RETURNVOID || + c->opcode == OPCODE_RETURNVALUE) { + return 1; + } + return 0; +} + + +#define IS_FINALLY_TARGET(op) \ + ((op) == OPCODE___CONTINUE__ || \ + (op) == OPCODE___BREAK__ || \ + (op) == OPCODE_RETURNVOID || \ + (op) == OPCODE_RETURNVALUE || \ + (op) == OPCODE___RETHROW__) + +static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar) +{ +#define NEED_EXTRA_STACK_ARG + code_t*finally_label = abc_nop(0); + NEW(lookupswitch_t, l); + //_lookupswitch + + code_t*i = c; + int count=0; + while(i) { + code_t*prev = i->prev; + if(IS_FINALLY_TARGET(i->opcode)) { + code_t*p = prev; + char needvalue=0; + if(i->opcode == OPCODE___RETHROW__ || + i->opcode == OPCODE_RETURNVALUE) { + if(i->opcode == OPCODE___RETHROW__) + i->opcode = OPCODE_THROW; + needvalue=1; + p = abc_coerce_a(p); + p = abc_setlocal(p, tempvar); + } + p = abc_pushbyte(p, count++); + p = abc_jump(p, finally_label); + code_t*target = p = abc_label(p); +#ifdef NEED_EXTRA_STACK_ARG + p = abc_pop(p); +#endif + if(needvalue) { + p = abc_getlocal(p, tempvar); + } + + p->next = i;i->prev = p; + list_append(l->targets, target); + } + i = prev; + } + + code_t*j,*f; + c = abc_pushbyte(c, -1); + c = code_append(c, finally_label); + c = code_append(c, finally); + +#ifdef NEED_EXTRA_STACK_ARG + c = abc_dup(c); +#endif + c = abc_lookupswitch(c, l); + c = l->def = abc_label(c); +#ifdef NEED_EXTRA_STACK_ARG + c = abc_pop(c); +#endif return c; } -#define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t)) -#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b)) +static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar) +{ + code_t*i = c; + while(i) { + code_t*prev = i->prev; + if(IS_FINALLY_TARGET(i->opcode)) { + if(i->opcode == OPCODE___RETHROW__) + i->opcode = OPCODE_THROW; + code_t*end = code_dup(finally); + code_t*start = code_start(end); + if(prev) prev->next = start; + start->prev = prev; + i->prev = end; + end->next = i; + } + i = prev; + } + return code_append(c, finally); +} + +code_t* insert_finally(code_t*c, code_t*finally, int tempvar) +{ + if(!finally) + return c; + code_t*i = c; + char cantdup=0; + int num_insertion_points=0; + while(i) { + if(IS_FINALLY_TARGET(i->opcode)) + num_insertion_points++; + i = i->prev; + } + i = finally; + int code_size=0; + while(i) { + code_size++; + if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) { + cantdup=1; + } + i = i->prev; + } + int simple_version_cost = (1+num_insertion_points)*code_size; + int lookup_version_cost = 4*num_insertion_points + 5; + + if(cantdup || simple_version_cost > lookup_version_cost) { + printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost); + return insert_finally_lookup(c, finally, tempvar); + } else { + printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost); + return insert_finally_simple(c, finally, tempvar); + } +} %} @@ -1319,19 +1514,20 @@ CODE: CODEPIECE {$$=$1;} // code which also may appear outside a method CODE_STATEMENT: IMPORT -CODE_STATEMENT: VOIDEXPRESSION CODE_STATEMENT: FOR CODE_STATEMENT: FOR_IN CODE_STATEMENT: WHILE CODE_STATEMENT: DO_WHILE CODE_STATEMENT: SWITCH CODE_STATEMENT: IF +CODE_STATEMENT: WITH CODE_STATEMENT: TRY +CODE_STATEMENT: VOIDEXPRESSION // code which may appear anywhere CODEPIECE: ';' {$$=0;} -CODEPIECE: VARIABLE_DECLARATION CODEPIECE: CODE_STATEMENT +CODEPIECE: VARIABLE_DECLARATION CODEPIECE: BREAK CODEPIECE: CONTINUE CODEPIECE: RETURN @@ -1348,7 +1544,8 @@ CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;} /* ------------ package init code ------------------- */ PACKAGE_INITCODE: CODE_STATEMENT { - if($1) warning("code ignored"); + code_t**cc = &global->init->method->body->code; + *cc = code_append(*cc, $1); } /* ------------ variables --------------------------- */ @@ -1410,6 +1607,7 @@ MAYBEELSE: "else" CODEBLOCK {$$=$2;} //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;} IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { + $$ = code_new(); $$ = code_append($$, $4.c); code_t*myjmp,*myif = $$ = abc_iffalse($$, 0); @@ -1423,13 +1621,15 @@ IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { $$ = code_append($$, $7); myjmp->branch = $$ = abc_nop($$); } - - $$ = killvars($$);old_state(); + $$ = var_block($$); + old_state(); } FOR_INIT : {$$=code_new();} FOR_INIT : VARIABLE_DECLARATION FOR_INIT : VOIDEXPRESSION + +// TODO: why doesn't an %prec above_identifier resolve the r-r conflict here? FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE { $$=$2;new_variable($2,$3,1); } @@ -1456,7 +1656,8 @@ FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { continuejumpsto($$, $1.name, cont); myif->branch = out; - $$ = killvars($$);old_state(); + $$ = var_block($$); + old_state(); } FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { @@ -1493,16 +1694,17 @@ FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { breakjumpsto($$, $1.name, out); continuejumpsto($$, $1.name, loopstart); - $$ = killvars($$); - myif->branch = out; + $$ = var_block($$); old_state(); + free(tmp1name); free(tmp2name); } WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { + $$ = code_new(); code_t*myjmp = $$ = abc_jump($$, 0); @@ -1516,7 +1718,7 @@ WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - $$ = killvars($$); + $$ = var_block($$); old_state(); } @@ -1530,7 +1732,8 @@ DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' { code_t*out = $$ = abc_nop($$); breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - $$ = killvars($$); + + $$ = var_block($$); old_state(); } @@ -1590,56 +1793,111 @@ SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' { } c=c->prev; } + + $$ = var_block($$); old_state(); } /* ------------ try / catch /finally ---------------- */ -FINALLY: "finally" '{' CODE '}' -MAYBE_FINALLY: | FINALLY - CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);} - '{' CODE '}' { - code_t*c = 0; - int i = find_variable_safe($3)->index; - c = abc_setlocal(c, i); - c = code_append(c, $8); - c = abc_kill(c, i); - + '{' MAYBECODE '}' { namespace_t name_ns = {ACCESS_PACKAGE, ""}; multiname_t name = {QNAME, &name_ns, 0, $3}; - + NEW(abc_exception_t, e) e->exc_type = sig2mname($4); e->var_name = multiname_clone(&name); - e->target = code_start(c); $$ = e; + + code_t*c = 0; + int i = find_variable_safe($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(); } +FINALLY: "finally" '{' {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 + e->var_name = 0; //no name + e->target = 0; + e->to = abc_nop(0); + e->to = code_append(e->to, $4); + old_state(); + $$ = e; + } +} -CATCH_LIST: CATCH {$$=list_new();list_append($$,$1);} -CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$,$2);} - -TRY : "try" '{' CODE '}' CATCH_LIST MAYBE_FINALLY { - code_t*start = code_start($3); - $$=$3; +CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);} +CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);} +CATCH_FINALLY_LIST: CATCH_LIST {$$=$1}; +CATCH_FINALLY_LIST: CATCH_LIST FINALLY { + $$ = $1; + $$.finally = 0; + if($2) { + list_append($$.l,$2); + $$.finally = $2->to;$2->to=0; + } +} +CATCH_FINALLY_LIST: FINALLY { + $$.l=list_new(); + $$.finally = 0; + if($1) { + list_append($$.l,$1); + $$.finally = $1->to;$1->to=0; + } +} +TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST { code_t*out = abc_nop(0); - code_t*jmp = $$ = abc_jump($$, out); - abc_exception_list_t*l = $5; + code_t*start = abc_nop(0); + $$ = code_append(start, $4); + if(!is_break_or_jump($4)) { + $$ = abc_jump($$, out); + } + code_t*end = $$ = abc_nop($$); + + int tmp; + if($6.finally) + tmp = new_variable("__finally__", 0, 0); + + abc_exception_list_t*l = $6.l; + int count=0; while(l) { abc_exception_t*e = l->abc_exception; + if(e->var_name) { + $$ = code_append($$, e->target); + $$ = abc_jump($$, out); + } else { + parserassert((ptroff_t)$6.finally); + // finally block + e->target = $$ = abc_nop($$); + $$ = abc___rethrow__($$); + } + e->from = start; - e->to = jmp; - $$ = code_append($$, e->target); - $$ = abc_jump($$, out); + e->to = end; + l = l->next; } $$ = code_append($$, out); - jmp->branch = out; + + $$ = insert_finally($$, $6.finally, tmp); - list_concat(state->method->exceptions, $5); + list_concat(state->method->exceptions, $6.l); + + $$ = var_block($$); + old_state(); } /* ------------ throw ------------------------------- */ @@ -1657,6 +1915,15 @@ THROW : "throw" %prec prec_none { $$=abc_throw($$); } +/* ------------ with -------------------------------- */ + +WITH : "with" '(' EXPRESSION ')' CODEBLOCK { + $$ = $3.c; + $$ = abc_pushscope($$); + $$ = code_append($$, $5); + $$ = abc_popscope($$); +} + /* ------------ packages and imports ---------------- */ X_IDENTIFIER: T_IDENTIFIER @@ -1686,7 +1953,7 @@ IMPORT : "import" PACKAGE '.' '*' { /* ------------ classes and interfaces (header) -------------- */ -MAYBE_MODIFIERS : {$$=0;} +MAYBE_MODIFIERS : %prec above_function {$$=0;} MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1} MODIFIER_LIST : MODIFIER {$$=$1;} MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;} @@ -1699,7 +1966,7 @@ MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;} | KW_FINAL {$$=FLAG_FINAL;} | KW_OVERRIDE {$$=FLAG_OVERRIDE;} | KW_NATIVE {$$=FLAG_NATIVE;} - | KW_INTERNAL {$$=FLAG_INTERNAL;} + | KW_INTERNAL {$$=FLAG_PACKAGEINTERNAL;} EXTENDS : {$$=registry_getobjectclass();} EXTENDS : KW_EXTENDS QNAME {$$=$2;} @@ -1748,7 +2015,7 @@ IDECLARATION : "var" T_IDENTIFIER { } IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE { $1 |= FLAG_PUBLIC; - if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) { + if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) { syntaxerror("invalid method modifiers: interface methods always need to be public"); } startfunction(0,$1,$3,$4,&$6,$8); @@ -1891,11 +2158,19 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P c = abc_getlocal_0(c); c = abc_constructsuper(c, 0); } - c = wrap_function(c, state->method->initcode, $11); + c = wrap_function(c, 0, $11); endfunction(0,$1,$3,$4,&$6,$8,c); $$=0; } +MAYBE_IDENTIFIER: T_IDENTIFIER +MAYBE_IDENTIFIER: {$$=0;} +INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE '{' MAYBECODE '}' +{ + syntaxerror("nested functions not supported yet"); +} + + /* ------------- package + class ids --------------- */ CLASS: T_IDENTIFIER { @@ -2083,13 +2358,32 @@ VOIDEXPRESSION : EXPRESSION %prec below_minus { // ----------------------- expression evaluation ------------------------------------- +E : INNERFUNCTION %prec prec_none {$$ = $1;} +//V : CONSTANT {$$ = 0;} E : CONSTANT +//V : VAR_READ %prec T_IDENTIFIER {$$ = 0;} E : VAR_READ %prec T_IDENTIFIER {$$ = $1;} +//V : NEW {$$ = $1.c;} E : NEW {$$ = $1;} +//V : DELETE {$$ = $1.c;} E : DELETE {$$ = $1;} -E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */ - $$.t = TYPE_ANY; - } + +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; +} CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1); //MULTINAME(m, registry_getintclass()); @@ -2204,15 +2498,6 @@ E : E '|' E {$$.c = code_append($1.c,$3.c); $$.t = TYPE_INT; } -E : E '-' E {$$.c = code_append($1.c,$3.c); - if(BOTH_INT($1,$3)) { - $$.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); $$.c = abc_rshift($$.c); $$.t = TYPE_INT; @@ -2230,16 +2515,30 @@ 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_add($$.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,$3)) { + if(BOTH_INT($1.t,$3.t)) { $$.c = abc_multiply_i($$.c); $$.t = TYPE_INT; } else { @@ -2296,7 +2595,7 @@ E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too E : '-' E { $$=$2; - if(IS_INT($2)) { + if(IS_INT($2.t)) { $$.c=abc_negate_i($$.c); $$.t = TYPE_INT; } else { @@ -2348,7 +2647,7 @@ E : '{' MAYBE_EXPRPAIR_LIST '}' { E : E "*=" E { code_t*c = $3.c; - if(BOTH_INT($1,$3)) { + if(BOTH_INT($1.t,$3.t)) { c=abc_multiply_i(c); } else { c=abc_multiply(c); @@ -2388,25 +2687,32 @@ E : E "/=" E { $$.c = toreadwrite($1.c, c, 0, 0); $$.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); + $$.t = $1.t; + } E : E "+=" E { code_t*c = $3.c; - if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) { + + 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=converttype(c, join_types($1.t, $3.t, '+'), $1.t); $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; } E : E "-=" E { code_t*c = $3.c; - if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) { + 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=converttype(c, join_types($1.t, $3.t, '-'), $1.t); $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; @@ -2419,30 +2725,45 @@ E : E '=' E { code_t*c = 0; } 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, $3.t, $$.t); $$.c = j2->branch = abc_label($$.c); - $$.t = join_types($3.t,$5.t,'?'); } -// TODO: use inclocal where appropriate E : E "++" { code_t*c = 0; classinfo_t*type = $1.t; - if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { - c=abc_increment_i(c); - type = TYPE_INT; + 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 { - c=abc_increment(c); - type = TYPE_NUMBER; + 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); + $$.t = $1.t; } - c=converttype(c, type, $1.t); - $$.c = toreadwrite($1.c, c, 0, 1); - $$.t = $1.t; } + +// 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)) { @@ -2492,7 +2813,7 @@ E : "super" '.' T_IDENTIFIER if(!t) t = TYPE_OBJECT; memberinfo_t*f = registry_findmember(t, $3, 1); - namespace_t ns = {flags2access(f->flags), ""}; + namespace_t ns = flags2namespace(f->flags, ""); MEMBER_MULTINAME(m, f, $3); $$.c = 0; $$.c = abc_getlocal_0($$.c); @@ -2584,7 +2905,11 @@ VAR_READ : T_IDENTIFIER { MULTINAME(m, a); $$.c = abc_findpropstrict2($$.c, &m); $$.c = abc_getproperty2($$.c, &m); - $$.t = TYPE_FUNCTION(a->function); + if(a->function->kind == MEMBER_METHOD) { + $$.t = TYPE_FUNCTION(a->function); + } else { + $$.t = a->function->type; + } } else { if(a->slot) { $$.c = abc_getglobalscope($$.c); @@ -2599,7 +2924,7 @@ VAR_READ : T_IDENTIFIER { /* unknown object, let the avm2 resolve it */ } else { if(strcmp($1,"trace")) - warning("Couldn't resolve '%s', doing late binding", $1); + as3_softwarning("Couldn't resolve '%s', doing late binding", $1); state->method->late_binding = 1; multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};