X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=b6bb97eb8d87669d8018f569425d65d2cc9ad475;hb=618a86d162d32d7b64cdf872d3033a56d548b526;hp=3cdf1fe4870755e265ae91c7eefdd7fd23b58ccc;hpb=16901658118e16e75f0ec606fcf54cc09774fe87;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index 3cdf1fe..b6bb97e 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -32,6 +32,8 @@ #include "code.h" #include "opcodes.h" +extern int a3_lex(); + %} //%glr-parser @@ -58,12 +60,18 @@ char*id; constant_t*constant; for_start_t for_start; + abc_exception_t *exception; + 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 @@ -76,17 +84,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" @@ -95,7 +104,9 @@ %token KW_CASE "case" %token KW_SET "set" %token KW_VOID "void" -%token KW_STATIC +%token KW_THROW "throw" +%token KW_STATIC "static" +%token KW_WITH "with" %token KW_INSTANCEOF "instanceof" %token KW_IMPORT "import" %token KW_RETURN "return" @@ -104,13 +115,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" @@ -133,6 +144,7 @@ %token T_NEE "!==" %token T_LE "<=" %token T_GE ">=" +%token T_ORBY "|=" %token T_DIVBY "/=" %token T_MODBY "%=" %token T_MULBY "*=" @@ -153,26 +165,29 @@ %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 -%type CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH -%type PACKAGE_DECLARATION -%type FUNCTION_DECLARATION -%type VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST -%type CLASS_DECLARATION -%type NAMESPACE_DECLARATION -%type INTERFACE_DECLARATION +%type CODEPIECE CODE_STATEMENT +%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 FINALLY +%type CATCH_LIST CATCH_FINALLY_LIST +%type CLASS_DECLARATION +%type NAMESPACE_DECLARATION +%type INTERFACE_DECLARATION %type VOIDEXPRESSION %type EXPRESSION NONCOMMAEXPRESSION %type MAYBEEXPRESSION %type E DELETE %type CONSTANT -%type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE +%type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY +%type INNERFUNCTION %type USE_NAMESPACE %type FOR_INIT -%type IMPORT +%type IMPORT %type MAYBETYPE %type GETSET %type PARAM @@ -207,8 +222,8 @@ %right '?' ':' %left "||" %left "&&" -%nonassoc '|' -%nonassoc '^' +%left '|' +%left '^' %nonassoc '&' %nonassoc "==" "!=" "===" "!==" %nonassoc "is" "as" "in" @@ -220,27 +235,33 @@ %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too %left "--" "++" %nonassoc below_curly -%left '[' ']' '{' "new" '.' ".." "::" + +%left '(' +%left new2 +%left '[' ']' "new" '{' '.' ".." "::" + %nonassoc T_IDENTIFIER %left above_identifier %left below_else %nonassoc "else" -%left '(' // 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" +%left above_function %{ -static int yyerror(char*s) +static int a3_error(char*s) { syntaxerror("%s", s); + return 0; //make gcc happy } + static char* concat2(const char* t1, const char* t2) { int l1 = strlen(t1); @@ -279,28 +300,40 @@ typedef struct _classstate { char has_constructor; } classstate_t; +DECLARE_LIST(methodstate); + 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; + int variable_count; + + char inner; + abc_method_t*abc; + int var_index; // for inner methods + + abc_exception_list_t*exceptions; + + methodstate_list_t*innerfunctions; } methodstate_t; typedef struct _state { struct _state*old; int level; - + char*package; import_list_t*wildcard_imports; dict_t*imports; char has_own_imports; + char new_vars; // e.g. transition between two functions classstate_t*cls; methodstate_t*method; + + char*exception_name; dict_t*vars; } state_t; @@ -308,8 +341,7 @@ typedef struct _state { typedef struct _global { abc_file_t*file; abc_script_t*init; - - int variable_count; + dict_t*token2info; } global_t; static global_t*global = 0; @@ -326,8 +358,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; \ @@ -359,11 +390,6 @@ static namespace_list_t nl2 = {&ns2,&nl3}; static namespace_list_t nl1 = {&ns1,&nl2}; static namespace_set_t nopackage_namespace_set = {&nl1}; -static void init_globals() -{ - global = rfx_calloc(sizeof(global_t)); -} - static void new_state() { NEW(state_t, s); @@ -376,7 +402,7 @@ static void new_state() state = s; state->level++; state->has_own_imports = 0; - state->vars = dict_new(); + state->vars = dict_new(); state->old = oldstate; } static void state_has_imports() @@ -415,27 +441,67 @@ 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); - }*/ + + if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) { + free(leaving->method); + leaving->method=0; + } + if(leaving->cls && leaving->cls != state->cls) { + free(leaving->cls); + leaving->cls=0; + } + state_destroy(leaving); } -void initialize_state() + +void initialize_file(char*filename) { - init_globals(); new_state(); + state->package = filename; + + state->method = rfx_calloc(sizeof(methodstate_t)); + state->method->variable_count = 1; +} +void finish_file() +{ + if(!state || state->level!=1) { + syntaxerror("unexpected end of file in pass %d", as3_pass); + } + state_destroy(state);state=0; +} + +void initialize_parser() +{ + global = rfx_calloc(sizeof(global_t)); global->file = abc_file_new(); global->file->flags &= ~ABCFILE_LAZY; + global->token2info = dict_new2(&ptr_type); - 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); - + global->init->method->body->code = c; +} + +void* finish_parser() +{ + code_t*c = global->init->method->body->code; + /*c = abc_findpropstrict(c, "[package]::trace"); + c = abc_pushstring(c, "[leaving global init function]"); + c = abc_callpropvoid(c, "[package]::trace", 1);*/ + c = abc_returnvoid(c); + global->init->method->body->code = c; + dict_destroy(global->token2info);global->token2info=0; + 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 @@ -461,37 +527,160 @@ 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; + } + if(s->new_vars) + break; + 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(const char*name, classinfo_t*type, char init) +{ + NEW(variable_t, v); + v->index = state->method->variable_count; + v->type = type; + v->init = init; - global->init->method->body->code = c; + dict_put(state->vars, name, v); + + return state->method->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); } -void* finalize_state() + +code_t* var_block(code_t*body) { - if(state->level!=1) { - syntaxerror("unexpected end of file"); + 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; + } + } + + 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; + } } - 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); + c = code_append(c, body); + c = code_append(c, k); + return c; +} - state_destroy(state); +#define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);} - return global->file; +static void parsererror(const char*file, int line, const char*f) +{ + syntaxerror("internal error in %s, %s:%d", f, file, line); +} + + +code_t* method_header() +{ + code_t*c = 0; + if(state->method->late_binding && !state->method->inner) { + c = abc_getlocal_0(c); + c = abc_pushscope(c); + } + /*if(state->method->innerfunctions) { + c = abc_newactivation(c); + c = abc_pushscope(c); + }*/ + if(state->method->is_constructor && !state->method->has_super) { + // call default constructor + c = abc_getlocal_0(c); + c = abc_constructsuper(c, 0); + } + methodstate_list_t*l = state->method->innerfunctions; + while(l) { + parserassert(l->methodstate->abc); + c = abc_newfunction(c, l->methodstate->abc); + c = abc_setlocal(c, l->methodstate->var_index); + free(l->methodstate);l->methodstate=0; + l = l->next; + } + list_free(state->method->innerfunctions); + state->method->innerfunctions = 0; + return c; +} + + +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; } static void startpackage(char*name) { - if(state->package) { - syntaxerror("Packages can not be nested."); - } new_state(); /*printf("entering package \"%s\"\n", name);*/ state->package = strdup(name); @@ -506,7 +695,8 @@ 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) { @@ -514,24 +704,16 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo } new_state(); state->cls = rfx_calloc(sizeof(classstate_t)); + state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor + state->method->variable_count = 1; 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 */ @@ -548,216 +730,136 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo syntaxerror("public classes only allowed inside a package"); } - if(registry_findclass(package, classname)) { - syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname); - } - - - /* build info struct */ - int num_interfaces = (list_length(implements)); - state->cls->info = classinfo_register(access, package, classname, num_interfaces); - state->cls->info->superclass = extends?extends:TYPE_OBJECT; - int pos = 0; - classinfo_list_t*l = implements; - for(l=implements;l;l=l->next) { - state->cls->info->interfaces[pos++] = l->classinfo; + if(as3_pass==1) { + if(registry_findclass(package, classname)) { + syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname); + } + /* build info struct */ + int num_interfaces = (list_length(implements)); + state->cls->info = classinfo_register(access, package, classname, num_interfaces); } - multiname_t*extends2 = sig2mname(extends); - - MULTINAME(classname2,state->cls->info); - - /*if(extends) { - state->cls_init = abc_getlocal_0(state->cls_init); - state->cls_init = abc_constructsuper(state->cls_init, 0); - }*/ + if(as3_pass == 2) { + state->cls->info = registry_findclass(package, classname); + parserassert((int)state->cls->info); + + /* fill out interfaces and extends (we couldn't resolve those during the first pass) */ + state->cls->info->superclass = extends?extends:TYPE_OBJECT; + int pos = 0; + classinfo_list_t*l = implements; + for(l=implements;l;l=l->next) { + state->cls->info->interfaces[pos++] = l->classinfo; + } - state->cls->abc = abc_class_new(global->file, &classname2, extends2); - if(flags&FLAG_FINAL) abc_class_final(state->cls->abc); - if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc); - if(interface) { - state->cls->info->flags |= CLASS_INTERFACE; - abc_class_interface(state->cls->abc); - } + /* generate the abc code for this class */ + MULTINAME(classname2,state->cls->info); + multiname_t*extends2 = sig2mname(extends); - abc_class_protectedNS(state->cls->abc, classname); + state->cls->abc = abc_class_new(global->file, &classname2, extends2); + if(flags&FLAG_FINAL) abc_class_final(state->cls->abc); + if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc); + if(interface) { + state->cls->info->flags |= CLASS_INTERFACE; + abc_class_interface(state->cls->abc); + } - for(mlist=implements;mlist;mlist=mlist->next) { - MULTINAME(m, mlist->classinfo); - abc_class_add_interface(state->cls->abc, &m); - } + abc_class_protectedNS(state->cls->abc, classname); - /* now write the construction code for this class */ - int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc); + for(mlist=implements;mlist;mlist=mlist->next) { + MULTINAME(m, mlist->classinfo); + abc_class_add_interface(state->cls->abc, &m); + } - abc_method_body_t*m = global->init->method->body; - __ getglobalscope(m); - classinfo_t*s = extends; + /* 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); - 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); - - /* flash.display.MovieClip handling */ - if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) { - if(state->package && state->package[0]) { - globalclass = concat3(state->package, ".", classname); + __ 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 { - globalclass = strdup(classname); + __ 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++; } - } - 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)) { - code_t*c = 0; - c = abc_getlocal_0(c); - c = abc_constructsuper(c, 0); - state->cls->init = code_append(state->cls->init, c); - } - - if(state->cls->init) { - abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0); - m->body->code = wrap_function(0, state->cls->init, m->body->code); - } - if(state->cls->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; - old_state(); -} + __ newclass(m,state->cls->abc); + while(count--) { + __ popscope(m); + } + __ setslot(m, slotindex); + multiname_destroy(extends2); -typedef struct _variable { - int index; - classinfo_t*type; -} variable_t; + /* flash.display.MovieClip handling */ -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; + if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) { + if(state->package && state->package[0]) { + as3_globalclass = concat3(state->package, ".", classname); + } else { + as3_globalclass = strdup(classname); + } } - 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() +static void endclass() { - variable_t*v = find_variable(TEMPVARNAME); - if(v) - return v->index; - return new_variable(TEMPVARNAME, 0, 0); -} + if(as3_pass == 2) { + if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) { + code_t*c = 0; + c = abc_getlocal_0(c); + 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); + } -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; + if(state->cls->init) { + abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0); + m->body->code = wrap_function(0, state->cls->init, m->body->code); + } + if(state->cls->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); } } - return c; + + old_state(); } void check_code_for_break(code_t*c) @@ -794,10 +896,38 @@ 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_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); + access = ACCESS_PACKAGE; + } else if(flags&FLAG_PRIVATE) { + 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_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); + access = ACCESS_PROTECTED; + } else { + access = ACCESS_PACKAGEINTERNAL; + } + 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; - if(getset != KW_GET && getset != KW_SET) { + if(!state->cls) { + //package method + minfo = memberinfo_register_global(flags2access(flags), state->package, name, MEMBER_METHOD); + minfo->return_type = return_type; + } else if(getset != KW_GET && getset != KW_SET) { + //class method if((minfo = registry_findmember(state->cls->info, name, 0))) { if(minfo->parent == state->cls->info) { syntaxerror("class already contains a member/method called '%s'", name); @@ -814,11 +944,12 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na // to actually store these //state->minfo->slot = state->method->abc->method->trait->slot_id; } else { + //class getter/setter int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET; classinfo_t*type=0; if(getset == KW_GET) type = return_type; - else if(params->list) + else if(params->list && params->list->param) type = params->list->param->type; // not sure wether to look into superclasses here, too if((minfo=registry_findmember(state->cls->info, name, 0))) { @@ -844,74 +975,146 @@ 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; } -static int flags2access(int flags) +static void function_initvars(params_t*params, int flags) { - int access = 0; - if(flags&FLAG_PUBLIC) { - if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) 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"); - access = ACCESS_PRIVATE; - } else if(flags&FLAG_PROTECTED) { - if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PROTECTED; - } else { - access = ACCESS_PACKAGEINTERNAL; + if(state->method->inner) + new_variable("this", 0, 0); + else if(!state->method->is_global) + new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0); + else + new_variable("globalscope", 0, 0); + + param_list_t*p=0; + for(p=params->list;p;p=p->next) { + new_variable(p->param->name, p->param->type, 0); + } + + methodstate_list_t*l = state->method->innerfunctions; + while(l) { + methodstate_t*m = l->methodstate; + m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0); + l = l->next; + } +} + +static void innerfunction(char*name, params_t*params, classinfo_t*return_type) +{ + parserassert(state->method && state->method->info); + + methodstate_t*parent_method = state->method; + + if(as3_pass==1) { + // not valid yet + params = 0; + return_type = 0; + } + + new_state(); + state->new_vars = 1; + + if(as3_pass == 1) { + state->method = rfx_calloc(sizeof(methodstate_t)); + state->method->inner = 1; + state->method->variable_count = 0; + state->method->abc = rfx_calloc(sizeof(abc_method_t)); + + NEW(memberinfo_t,minfo); + minfo->name = name; + state->method->info = minfo; + + list_append(parent_method->innerfunctions, state->method); + + dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method); + } + + if(as3_pass == 2) { + state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); + parserassert(state->method); + + state->method->info->return_type = return_type; + function_initvars(params, 0); } - return access; } 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(); - state->method = rfx_calloc(sizeof(methodstate_t)); - state->method->initcode = 0; - state->method->is_constructor = !strcmp(state->cls->info->name,name); - state->method->has_super = 0; - state->cls->has_constructor |= state->method->is_constructor; + if(as3_pass == 1) { + state->method = rfx_calloc(sizeof(methodstate_t)); + state->method->has_super = 0; + state->method->variable_count = 0; - global->variable_count = 0; + if(state->cls) { + state->method->is_constructor = !strcmp(state->cls->info->name,name); + } else { + state->method->is_global = 1; + state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack + } + if(state->method->is_constructor) + name = "__as3_constructor__"; - /* state->vars is initialized by state_new */ - if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0)!=0) syntaxerror("Internal error"); - param_list_t*p=0; - for(p=params->list;p;p=p->next) { - new_variable(p->param->name, p->param->type, 0); + return_type = 0; + state->method->info = registerfunction(getset, flags, name, params, return_type, 0); + + dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method); } - if(state->method->is_constructor) - name = "__as3_constructor__"; - state->method->info = registerfunction(getset, flags, name, params, return_type, 0); + + if(as3_pass == 2) { + state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); + parserassert(state->method); + + if(state->cls) { + state->cls->has_constructor |= state->method->is_constructor; + } + + state->method->info->return_type = return_type; + function_initvars(params, flags); + } } -static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name, +static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type, code_t*body) { - namespace_t mname_ns = {flags2access(flags), ""}; - multiname_t mname = {QNAME, &mname_ns, 0, name}; + if(as3_pass==1) { + old_state(); + return 0; + } abc_method_t*f = 0; multiname_t*type2 = sig2mname(return_type); int slot = 0; - if(state->method->is_constructor) { + if(state->method->inner) { + f = state->method->abc; + abc_method_init(f, global->file, type2, 1); + } else if(state->method->is_constructor) { f = abc_class_getconstructor(state->cls->abc, type2); - } else { + } else if(!state->method->is_global) { + namespace_t mname_ns = flags2namespace(flags, ""); + multiname_t mname = {QNAME, &mname_ns, 0, name}; + if(flags&FLAG_STATIC) f = abc_class_staticmethod(state->cls->abc, type2, &mname); else f = abc_class_method(state->cls->abc, type2, &mname); slot = f->trait->slot_id; + } else { + namespace_t mname_ns = flags2namespace(flags, state->package); + multiname_t mname = {QNAME, &mname_ns, 0, name}; + + f = abc_method_new(global->file, type2, 1); + trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f); + //abc_code_t*c = global->init->method->body->code; } //flash doesn't seem to allow us to access function slots //state->method->info->slot = slot; @@ -938,18 +1141,18 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam } check_code_for_break(body); - if(f->body) + if(f->body) { f->body->code = body; - else //interface + f->body->exceptions = state->method->exceptions; + } else { //interface if(body) syntaxerror("interface methods can't have a method body"); + } - free(state->method);state->method=0; old_state(); + return f; } - - char is_subtype_of(classinfo_t*type, classinfo_t*supertype) { return 1; // FIXME @@ -982,12 +1185,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(); @@ -1035,6 +1251,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) @@ -1047,8 +1264,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; } @@ -1058,24 +1279,20 @@ char is_pushundefined(code_t*c) return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED); } -void parserassert(int b) -{ - if(!b) syntaxerror("internal error: assertion failed"); -} - static classinfo_t* find_class(char*name) { classinfo_t*c=0; c = registry_findclass(state->package, name); + if(c) return c; /* try explicit imports */ dictentry_t* e = dict_get_slot(state->imports, name); + if(c) return c; while(e) { - if(c) - break; if(!strcmp(e->key, name)) { c = (classinfo_t*)e->data; + if(c) return c; } e = e->next; } @@ -1083,18 +1300,42 @@ static classinfo_t* find_class(char*name) /* try package.* imports */ import_list_t*l = state->wildcard_imports; while(l) { - if(c) - break; //printf("does package %s contain a class %s?\n", l->import->package, name); c = registry_findclass(l->import->package, name); + if(c) return c; l = l->next; } /* try global package */ - if(!c) { - c = registry_findclass("", name); - } - return c; + c = registry_findclass("", name); + if(c) return c; + + /* try local "filename" package */ + c = registry_findclass(current_filename_short, name); + if(c) return c; + + 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) @@ -1147,6 +1388,8 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r 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 { @@ -1171,7 +1414,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; @@ -1237,61 +1480,219 @@ 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); + } +} +#define PASS1 }} if(as3_pass == 1) {{ +#define PASS1END }} if(as3_pass == 2) {{ +#define PASS2 }} if(as3_pass == 2) {{ +#define PASS12 }} {{ +#define PASS12END }} if(as3_pass == 2) {{ + +%} %% /* ------------ code blocks / statements ---------------- */ -PROGRAM: MAYBECODE { - /* todo: do something with this code if we're outside a function */ - if($1) - warning("ignored code"); -} +PROGRAM: MAYBE_PROGRAM_CODE_LIST + +MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST +PROGRAM_CODE_LIST: PROGRAM_CODE + | PROGRAM_CODE_LIST PROGRAM_CODE + +PROGRAM_CODE: PACKAGE_DECLARATION + | INTERFACE_DECLARATION + | CLASS_DECLARATION + | FUNCTION_DECLARATION + | SLOT_DECLARATION + | PACKAGE_INITCODE + | ';' + +MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST +INPACKAGE_CODE_LIST: INPACKAGE_CODE + | INPACKAGE_CODE_LIST INPACKAGE_CODE + +INPACKAGE_CODE: INTERFACE_DECLARATION + | CLASS_DECLARATION + | FUNCTION_DECLARATION + | SLOT_DECLARATION + | PACKAGE_INITCODE + | ';' MAYBECODE: CODE {$$=$1;} MAYBECODE: {$$=code_new();} -CODE: CODE CODEPIECE { - $$=code_append($1,$2); -} -CODE: CODEPIECE { - $$=$1; -} - -CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/} -CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/} -CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/} -CODEPIECE: INTERFACE_DECLARATION {$$=code_new();} -CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/} -CODEPIECE: ';' {$$=code_new();} -CODEPIECE: VARIABLE_DECLARATION {$$=$1} -CODEPIECE: VOIDEXPRESSION {$$=$1} -CODEPIECE: FOR {$$=$1} -CODEPIECE: FOR_IN {$$=$1} -CODEPIECE: WHILE {$$=$1} -CODEPIECE: DO_WHILE {$$=$1} -CODEPIECE: SWITCH {$$=$1} -CODEPIECE: BREAK {$$=$1} -CODEPIECE: CONTINUE {$$=$1} -CODEPIECE: RETURN {$$=$1} -CODEPIECE: IF {$$=$1} -CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();} -CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();} +CODE: CODE CODEPIECE {$$=code_append($1,$2);} +CODE: CODEPIECE {$$=$1;} + +// code which also may appear outside a method +CODE_STATEMENT: IMPORT +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: CODE_STATEMENT +CODEPIECE: VARIABLE_DECLARATION +CODEPIECE: BREAK +CODEPIECE: CONTINUE +CODEPIECE: RETURN +CODEPIECE: THROW + +CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;} +CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;} CODEBLOCK : '{' CODE '}' {$$=$2;} CODEBLOCK : '{' '}' {$$=0;} CODEBLOCK : CODEPIECE ';' {$$=$1;} 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); +} + /* ------------ variables --------------------------- */ MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;} @@ -1351,6 +1752,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); @@ -1364,13 +1766,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); } @@ -1397,7 +1801,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 { @@ -1434,16 +1839,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); @@ -1457,7 +1863,7 @@ WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - $$ = killvars($$); + $$ = var_block($$); old_state(); } @@ -1471,7 +1877,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(); } @@ -1492,7 +1899,7 @@ MAYBE_CASE_LIST : {$$=0;} MAYBE_CASE_LIST : CASE_LIST {$$=$1;} MAYBE_CASE_LIST : DEFAULT {$$=$1;} MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);} -CASE_LIST: CASE {$$=$1} +CASE_LIST: CASE {$$=$1;} CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);} CASE: "case" E ':' MAYBECODE { @@ -1531,19 +1938,149 @@ SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' { } c=c->prev; } + + $$ = var_block($$); + old_state(); +} + +/* ------------ try / catch /finally ---------------- */ + +CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);} + '{' 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; + + 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 {$$.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*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 = end; + + l = l->next; + } + $$ = code_append($$, out); + + $$ = insert_finally($$, $6.finally, tmp); + + list_concat(state->method->exceptions, $6.l); + + $$ = var_block($$); old_state(); } +/* ------------ throw ------------------------------- */ + +THROW : "throw" EXPRESSION { + $$=$2.c; + $$=abc_throw($$); +} +THROW : "throw" %prec prec_none { + if(!state->exception_name) + syntaxerror("re-throw only possible within a catch block"); + variable_t*v = find_variable(state->exception_name); + $$=code_new(); + $$=abc_getlocal($$, v->index); + $$=abc_throw($$); +} + +/* ------------ with -------------------------------- */ + +WITH : "with" '(' EXPRESSION ')' CODEBLOCK { + $$ = $3.c; + $$ = abc_pushscope($$); + $$ = code_append($$, $5); + $$ = abc_popscope($$); +} + /* ------------ packages and imports ---------------- */ X_IDENTIFIER: T_IDENTIFIER - | "package" {$$="package";} + | "package" {PASS12 $$="package";} -PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;} -PACKAGE: X_IDENTIFIER {$$=strdup($1);} +PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;} +PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);} -PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBECODE '}' {endpackage()} -PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()} +PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;} + MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;} +PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");} + MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;} IMPORT : "import" QNAME { classinfo_t*c = $2; @@ -1563,63 +2100,70 @@ IMPORT : "import" PACKAGE '.' '*' { /* ------------ classes and interfaces (header) -------------- */ -MAYBE_MODIFIERS : {$$=0;} -MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1} -MODIFIER_LIST : MODIFIER {$$=$1;} -MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;} - -MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;} - | KW_PRIVATE {$$=FLAG_PRIVATE;} - | KW_PROTECTED {$$=FLAG_PROTECTED;} - | KW_STATIC {$$=FLAG_STATIC;} - | KW_DYNAMIC {$$=FLAG_DYNAMIC;} - | KW_FINAL {$$=FLAG_FINAL;} - | KW_OVERRIDE {$$=FLAG_OVERRIDE;} - | KW_NATIVE {$$=FLAG_NATIVE;} - | KW_INTERNAL {$$=FLAG_INTERNAL;} +MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;} +MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;} +MODIFIER_LIST : MODIFIER {PASS12 $$=$1;} +MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;} + +MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;} + | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;} + | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;} + | KW_STATIC {PASS12 $$=FLAG_STATIC;} + | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;} + | KW_FINAL {PASS12 $$=FLAG_FINAL;} + | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;} + | KW_NATIVE {PASS12 $$=FLAG_NATIVE;} + | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;} EXTENDS : {$$=registry_getobjectclass();} EXTENDS : KW_EXTENDS QNAME {$$=$2;} -EXTENDS_LIST : {$$=list_new();} -EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;} +EXTENDS_LIST : {PASS12 $$=list_new();} +EXTENDS_LIST : KW_EXTENDS QNAME_LIST {PASS12 $$=$2;} -IMPLEMENTS_LIST : {$$=list_new();} -IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;} +IMPLEMENTS_LIST : {PASS12 $$=list_new();} +IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {PASS12 $$=$2;} CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER EXTENDS IMPLEMENTS_LIST - '{' {startclass($1,$3,$4,$5, 0);} - MAYBE_DECLARATION_LIST - '}' {endclass();} + '{' {PASS12 startclass($1,$3,$4,$5, 0);} + MAYBE_CLASS_BODY + '}' {PASS12 endclass();$$=0;} INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER EXTENDS_LIST - '{' {startclass($1,$3,0,$4,1);} - MAYBE_IDECLARATION_LIST - '}' {endclass();} + '{' {PASS12 startclass($1,$3,0,$4,1);} + MAYBE_INTERFACE_BODY + '}' {PASS12 endclass();$$=0;} /* ------------ classes and interfaces (body) -------------- */ -MAYBE_DECLARATION_LIST : -MAYBE_DECLARATION_LIST : DECLARATION_LIST -DECLARATION_LIST : DECLARATION -DECLARATION_LIST : DECLARATION_LIST DECLARATION -DECLARATION : ';' -DECLARATION : SLOT_DECLARATION -DECLARATION : FUNCTION_DECLARATION - -MAYBE_IDECLARATION_LIST : -MAYBE_IDECLARATION_LIST : IDECLARATION_LIST -IDECLARATION_LIST : IDECLARATION -IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION +MAYBE_CLASS_BODY : +MAYBE_CLASS_BODY : CLASS_BODY +CLASS_BODY : CLASS_BODY_ITEM +CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM +CLASS_BODY_ITEM : ';' +CLASS_BODY_ITEM : SLOT_DECLARATION +CLASS_BODY_ITEM : FUNCTION_DECLARATION + +CLASS_BODY_ITEM : CODE_STATEMENT { + code_t*c = state->cls->static_init; + c = code_append(c, $1); + state->cls->static_init = c; +} + +MAYBE_INTERFACE_BODY : +MAYBE_INTERFACE_BODY : INTERFACE_BODY +INTERFACE_BODY : IDECLARATION +INTERFACE_BODY : INTERFACE_BODY IDECLARATION IDECLARATION : ';' IDECLARATION : "var" T_IDENTIFIER { syntaxerror("variable declarations not allowed in interfaces"); } IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE { + PASS12 $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); @@ -1632,45 +2176,59 @@ VARCONST: "var" | "const" SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION { int flags = $1; - memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT); + memberinfo_t* info = state->cls? + memberinfo_register(state->cls->info, $3, MEMBER_SLOT): + memberinfo_register_global(flags2access($1), state->package, $3, MEMBER_SLOT); + info->type = $4; info->flags = flags; - trait_t*t=0; + /* slot name */ namespace_t mname_ns = {flags2access(flags), ""}; multiname_t mname = {QNAME, &mname_ns, 0, $3}; - - if(!(flags&FLAG_STATIC)) { - if($4) { - MULTINAME(m, $4); - t=abc_class_slot(state->cls->abc, &mname, &m); - } else { - t=abc_class_slot(state->cls->abc, &mname, 0); - } - info->slot = t->slot_id; + + trait_list_t**traits; + code_t**code; + if(!state->cls) { + // global variable + mname_ns.name = state->package; + traits = &global->init->traits; + code = &global->init->method->body->code; + } else if(flags&FLAG_STATIC) { + // static variable + traits = &state->cls->abc->static_traits; + code = &state->cls->static_init; } else { - if($4) { - MULTINAME(m, $4); - t=abc_class_staticslot(state->cls->abc, &mname, &m); - } else { - t=abc_class_staticslot(state->cls->abc, &mname, 0); - } - info->slot = t->slot_id; + // instance variable + traits = &state->cls->abc->traits; + code = &state->cls->init; + } + + trait_t*t=0; + if($4) { + MULTINAME(m, $4); + t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0); + } else { + t = trait_new_member(traits, 0, multiname_clone(&mname), 0); } + info->slot = t->slot_id; + + /* initalization code (if needed) */ + code_t*c = 0; if($5.c && !is_pushundefined($5.c)) { - code_t*c = 0; c = abc_getlocal_0(c); c = code_append(c, $5.c); c = converttype(c, $5.t, $4); c = abc_setslot(c, t->slot_id); - if(!(flags&FLAG_STATIC)) - state->cls->init = code_append(state->cls->init, c); - else - state->cls->static_init = code_append(state->cls->static_init, c); } + + *code = code_append(*code, c); + if($2==KW_CONST) { t->kind= TRAIT_CONST; } + + $$=0; } /* ------------ constants -------------------------------------- */ @@ -1692,19 +2250,23 @@ STATICCONSTANT : "null" {$$ = constant_new_null($1);} // non-vararg version MAYBE_PARAM_LIST: { + PASS12 memset(&$$,0,sizeof($$)); } MAYBE_PARAM_LIST: PARAM_LIST { + PASS12 $$=$1; } // vararg version MAYBE_PARAM_LIST: "..." PARAM { + PASS12 memset(&$$,0,sizeof($$)); $$.varargs=1; list_append($$.list, $2); } MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM { + PASS12 $$ =$1; $$.varargs=1; list_append($$.list, $4); @@ -1712,21 +2274,27 @@ MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM { // non empty PARAM_LIST: PARAM_LIST ',' PARAM { + PASS12 $$ = $1; list_append($$.list, $3); } PARAM_LIST: PARAM { + PASS12 memset(&$$,0,sizeof($$)); list_append($$.list, $1); } PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT { + PASS1 $$=0; + PASS2 $$ = malloc(sizeof(param_t)); $$->name=$1; $$->type = $3; $$->value = $4; } PARAM: T_IDENTIFIER MAYBESTATICCONSTANT { + PASS1 $$=0; + PASS2 $$ = malloc(sizeof(param_t)); $$->name=$1; $$->type = TYPE_ANY; @@ -1737,32 +2305,53 @@ GETSET : "get" {$$=$1;} | {$$=0;} FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' - MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}' + MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}' { - code_t*c = 0; - if(state->method->late_binding) { - c = abc_getlocal_0(c); - c = abc_pushscope(c); - } - if(state->method->is_constructor && !state->method->has_super) { - // call default constructor - c = abc_getlocal_0(c); - c = abc_constructsuper(c, 0); - } - c = wrap_function(c, state->method->initcode, $11); + PASS1 old_state(); + PASS2 + if(!state->method->info) syntaxerror("internal error"); + + code_t*c = method_header(); + c = wrap_function(c, 0, $11); + endfunction(0,$1,$3,$4,&$6,$8,c); + $$=0; +} + +MAYBE_IDENTIFIER: T_IDENTIFIER +MAYBE_IDENTIFIER: {PASS12 $$=0;} +INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE + '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}' +{ + PASS1 old_state(); + PASS2 + memberinfo_t*f = state->method->info; + if(!f) syntaxerror("internal error"); + + code_t*c = method_header(); + c = wrap_function(c, 0, $9); + + int index = state->method->var_index; + endfunction(0,0,0,$2,&$4,$6,c); + + $$.c = abc_getlocal(0, index); + $$.t = TYPE_FUNCTION(f); } + /* ------------- package + class ids --------------- */ CLASS: T_IDENTIFIER { - + PASS1 $$=0; + PASS2 /* try current package */ $$ = find_class($1); if(!$$) syntaxerror("Could not find class %s\n", $1); } PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER { + PASS1 $$=0; + PASS2 $$ = registry_findclass($1, $3); if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3); free($1);$1=0; @@ -1771,8 +2360,8 @@ PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER { QNAME: PACKAGEANDCLASS | CLASS -QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);} -QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);} +QNAME_LIST : QNAME {PASS12 $$=list_new();list_append($$, $1);} +QNAME_LIST : QNAME_LIST ',' QNAME {PASS12 $$=$1;list_append($$,$3);} TYPE : QNAME {$$=$1;} | '*' {$$=registry_getanytype();} @@ -1791,7 +2380,7 @@ MAYBETYPE: {$$=0;} /* ----------function calls, delete, constructor calls ------ */ MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;} -MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2} +MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;} MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;} MAYBE_EXPRESSION_LIST : EXPRESSION_LIST @@ -1802,25 +2391,38 @@ EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION { $$.len= $1.len+1; $$.cc = code_append($1.cc, $3.c); } - -NEW : "new" CLASS MAYBE_PARAM_VALUES { - MULTINAME(m, $2); - $$.c = code_new(); - - if($2->slot) { - $$.c = abc_getglobalscope($$.c); - $$.c = abc_getslot($$.c, $2->slot); + +XX : %prec new2 +NEW : "new" E XX MAYBE_PARAM_VALUES { + $$.c = $2.c; + if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c); + + code_t*paramcode = $4.cc; + if($$.c->opcode == OPCODE_GETPROPERTY) { + multiname_t*name = $$.c->data[0];$$.c->data[0]=0; + $$.c = code_cutlast($$.c); + $$.c = code_append($$.c, paramcode); + $$.c = abc_constructprop2($$.c, name, $4.len); + multiname_destroy(name); + } else if($$.c->opcode == OPCODE_GETSLOT) { + int slot = (int)(ptroff_t)$$.c->data[0]; + trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME + multiname_t*name = t->name; + $$.c = code_cutlast($$.c); + $$.c = code_append($$.c, paramcode); + $$.c = abc_constructprop2($$.c, name, $4.len); } else { - $$.c = abc_findpropstrict2($$.c, &m); + $$.c = code_append($$.c, paramcode); + $$.c = abc_construct($$.c, $4.len); + } + + $$.t = TYPE_ANY; + if(TYPE_IS_CLASS($2.t) && $2.t->cls) { + $$.t = $2.t->cls; + } else { + $$.c = abc_coerce_a($$.c); + $$.t = TYPE_ANY; } - - $$.c = code_append($$.c, $3.cc); - - if($2->slot) - $$.c = abc_construct($$.c, $3.len); - else - $$.c = abc_constructprop2($$.c, &m, $3.len); - $$.t = $2; } /* TODO: use abc_call (for calling local variables), @@ -1860,21 +2462,19 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { $$.c = abc_callsuper2($$.c, name, $3.len); multiname_destroy(name); } else { - $$.c = abc_getlocal_0($$.c); + $$.c = abc_getglobalscope($$.c); $$.c = code_append($$.c, paramcode); $$.c = abc_call($$.c, $3.len); } - memberinfo_t*f = 0; - if(TYPE_IS_FUNCTION($1.t) && $1.t->function) { $$.t = $1.t->function->return_type; } else { $$.c = abc_coerce_a($$.c); $$.t = TYPE_ANY; } - } + FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' { if(!state->cls) syntaxerror("super() not allowed outside of a class"); if(!state->method) syntaxerror("super() not allowed outside of a function"); @@ -1890,6 +2490,7 @@ FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' { syntaxerror("constructor may call super() only once"); */ state->method->has_super = 1; + $$.c = abc_constructsuper($$.c, $3.len); $$.c = abc_pushundefined($$.c); $$.t = TYPE_ANY; @@ -1940,13 +2541,34 @@ 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 : FUNCTIONCALL + +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()); @@ -1981,7 +2603,6 @@ CONSTANT : "null" {$$.c = abc_pushnull(0); $$.t = TYPE_NULL; } -E : FUNCTIONCALL E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c); $$.t = TYPE_BOOLEAN; } @@ -2061,15 +2682,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; @@ -2087,16 +2699,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 { @@ -2153,7 +2779,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 { @@ -2165,7 +2791,7 @@ E : '-' E { 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 @@ -2179,7 +2805,7 @@ E : '[' MAYBE_EXPRESSION_LIST ']' { } MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;} -MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1}; +MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;} EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION { $$.cc = 0; @@ -2205,7 +2831,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); @@ -2245,25 +2871,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; @@ -2276,30 +2909,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)) { @@ -2349,7 +2997,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); @@ -2403,45 +3051,58 @@ VAR_READ : T_IDENTIFIER { // $1 is a local variable $$.c = abc_getlocal($$.c, v->index); $$.t = v->type; + break; + } + + int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC; /* look at current class' members */ - } else if((f = registry_findmember(state->cls->info, $1, 1))) { + if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) && + (f->flags&FLAG_STATIC) >= i_am_static) { // $1 is a function in this class int var_is_static = (f->flags&FLAG_STATIC); - int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC); - if(var_is_static != i_am_static) { - /* there doesn't seem to be any "static" way to access - static properties of a class */ + + if(f->kind == MEMBER_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 = {flags2access(f->flags), ""}; 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 { - if(f->slot>0) { - $$.c = abc_getlocal_0($$.c); - $$.c = abc_getslot($$.c, f->slot); - } else { - namespace_t ns = {flags2access(f->flags), ""}; - multiname_t m = {QNAME, &ns, 0, $1}; - $$.c = abc_getlocal_0($$.c); - $$.c = abc_getproperty2($$.c, &m); - } - } - if(f->kind == MEMBER_METHOD) { - $$.t = TYPE_FUNCTION(f); - } else { - $$.t = f->type; + namespace_t ns = {flags2access(f->flags), ""}; + multiname_t m = {QNAME, &ns, 0, $1}; + $$.c = abc_getlocal_0($$.c); + $$.c = abc_getproperty2($$.c, &m); + break; } + } /* look at actual classes, in the current package and imported */ - } else if((a = find_class($1))) { + if((a = find_class($1))) { if(a->flags & FLAG_METHOD) { 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); @@ -2452,11 +3113,12 @@ VAR_READ : T_IDENTIFIER { } $$.t = TYPE_CLASS(a); } + break; + } /* unknown object, let the avm2 resolve it */ - } else { - if(strcmp($1,"trace")) - warning("Couldn't resolve '%s', doing late binding", $1); + if(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}; @@ -2474,9 +3136,9 @@ VAR_READ : T_IDENTIFIER { // ----------------- namespaces ------------------------------------------------- -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;} -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;} -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;} -USE_NAMESPACE : "use" "namespace" T_IDENTIFIER +USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {$$=0;}