X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=6b527653ce2778e286e33d6cbc479e51542eece5;hb=938c420724d7780bcd4754febc87e47d0d9a3b55;hp=a1edea63ad5be770cd6b03691cd2d64594070e9c;hpb=c159e3f70b282f0de056aeddc8797edf727843bc;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index a1edea6..6b52765 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -58,12 +58,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 +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" @@ -95,7 +102,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 +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" @@ -133,6 +142,7 @@ %token T_NEE "!==" %token T_LE "<=" %token T_GE ">=" +%token T_ORBY "|=" %token T_DIVBY "/=" %token T_MODBY "%=" %token T_MULBY "*=" @@ -153,26 +163,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 +220,8 @@ %right '?' ':' %left "||" %left "&&" -%nonassoc '|' -%nonassoc '^' +%left '|' +%left '^' %nonassoc '&' %nonassoc "==" "!=" "===" "!==" %nonassoc "is" "as" "in" @@ -230,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 @@ -239,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) @@ -283,17 +298,16 @@ 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; + abc_exception_list_t*exceptions; } methodstate_t; typedef struct _state { struct _state*old; int level; - + char*package; import_list_t*wildcard_imports; dict_t*imports; @@ -301,6 +315,8 @@ typedef struct _state { classstate_t*cls; methodstate_t*method; + + char*exception_name; dict_t*vars; } state_t; @@ -326,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; \ @@ -359,11 +374,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 +386,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 +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() -{ - init_globals(); - new_state(); +void initialize_parser() +{ + global = rfx_calloc(sizeof(global_t)); global->file = abc_file_new(); global->file->flags &= ~ABCFILE_LAZY; - - global->init = abc_initscript(global->file, 0); + global->variable_count = 1; + 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 @@ -461,40 +500,124 @@ 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; } 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); + global->variable_count = 1; } static void endpackage() { @@ -506,32 +629,24 @@ 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) { syntaxerror("inner classes now allowed"); } 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 */ @@ -636,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)) { @@ -666,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); @@ -674,92 +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) { @@ -794,10 +825,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,6 +873,7 @@ 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) @@ -844,47 +904,36 @@ 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) -{ - 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; - } - 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(); + global->variable_count = 0; 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; - global->variable_count = 0; + if(state->cls) { + state->method->is_constructor = !strcmp(state->cls->info->name,name); + state->cls->has_constructor |= state->method->is_constructor; + + new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0); + } else { + state->method->is_global = 1; + state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack + + new_variable("globalscope", 0, 0); + } /* 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); @@ -897,21 +946,28 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n static void 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}; - abc_method_t*f = 0; multiname_t*type2 = sig2mname(return_type); int slot = 0; 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,11 +994,13 @@ 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(); @@ -982,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(); @@ -1035,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) @@ -1047,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; } @@ -1068,14 +1144,15 @@ 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 +1160,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, 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) @@ -1173,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; @@ -1239,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); + } +} %} @@ -1253,47 +1481,73 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r /* ------------ 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;} @@ -1353,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); @@ -1366,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); } @@ -1399,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 { @@ -1436,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); @@ -1459,7 +1718,7 @@ WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - $$ = killvars($$); + $$ = var_block($$); old_state(); } @@ -1473,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(); } @@ -1533,9 +1793,137 @@ 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 @@ -1544,8 +1932,8 @@ X_IDENTIFIER: T_IDENTIFIER PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;} PACKAGE: X_IDENTIFIER {$$=strdup($1);} -PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBECODE '}' {endpackage()} -PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()} +PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;} +PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;} IMPORT : "import" QNAME { classinfo_t*c = $2; @@ -1565,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;} @@ -1578,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;} @@ -1592,36 +1980,42 @@ IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;} CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER EXTENDS IMPLEMENTS_LIST '{' {startclass($1,$3,$4,$5, 0);} - MAYBE_DECLARATION_LIST - '}' {endclass();} + MAYBE_CLASS_BODY + '}' {endclass();$$=0;} INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER EXTENDS_LIST '{' {startclass($1,$3,0,$4,1);} - MAYBE_IDECLARATION_LIST - '}' {endclass();} + MAYBE_INTERFACE_BODY + '}' {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 { $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); @@ -1634,45 +2028,58 @@ 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 + 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 -------------------------------------- */ @@ -1751,10 +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 { @@ -1942,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()); @@ -2063,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; @@ -2089,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 { @@ -2155,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 { @@ -2207,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); @@ -2247,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; @@ -2278,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)) { @@ -2351,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); @@ -2407,7 +2869,7 @@ VAR_READ : T_IDENTIFIER { $$.t = v->type; /* look at current class' members */ - } else if((f = registry_findmember(state->cls->info, $1, 1))) { + } else if(state->cls && (f = registry_findmember(state->cls->info, $1, 1))) { // $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); @@ -2443,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); @@ -2458,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}; @@ -2476,9 +2942,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;}