X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=3783b297b4929d9ca590f3c84b069debe03d9bc6;hb=ff7bd81590dc66a135f8ac513d0fb9d5c4c95bf9;hp=f26a444befd9da6eb2d896173a9957c2bdfb4602;hpb=15983c1c1825073cd732196122da30c1157cf215;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index f26a444..3783b29 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -31,6 +31,9 @@ #include "registry.h" #include "code.h" #include "opcodes.h" +#include "compiler.h" + +extern int a3_lex(); %} @@ -197,8 +200,8 @@ %type IMPLEMENTS_LIST %type EXTENDS %type EXTENDS_LIST -%type CLASS PACKAGEANDCLASS QNAME -%type QNAME_LIST +%type CLASS PACKAGEANDCLASS CLASS_SPEC +%type CLASS_SPEC_LIST %type TYPE //%type VARIABLE %type VAR_READ @@ -233,29 +236,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" "function" -%nonassoc above_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); @@ -294,6 +301,8 @@ typedef struct _classstate { char has_constructor; } classstate_t; +DECLARE_LIST(methodstate); + typedef struct _methodstate { /* method data */ memberinfo_t*info; @@ -301,7 +310,15 @@ typedef struct _methodstate { 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 { @@ -312,6 +329,7 @@ typedef struct _state { 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; @@ -324,8 +342,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; @@ -427,40 +444,57 @@ static void old_state() state_t*leaving = state; state = state->old; + + 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_parser() -{ - global = rfx_calloc(sizeof(global_t)); - global->file = abc_file_new(); - global->file->flags &= ~ABCFILE_LAZY; - 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) { + if(state) { + syntaxerror("invalid call to initialize_file during parsing of another file"); + } new_state(); - state->package = filename; - // needed for state->method->late_binding: + state->package = strdup(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"); + syntaxerror("unexpected end of file in pass %d", as3_pass); } + free(state->method);state->method=0; + + //free(state->package);state->package=0; // used in registry + 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); + 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; @@ -469,6 +503,7 @@ void* finish_parser() 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; } @@ -519,6 +554,8 @@ static variable_t* find_variable(char*name) if(v) { return v; } + if(s->new_vars) + break; s = s->old; } return 0; @@ -535,16 +572,16 @@ 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) +static int new_variable(const char*name, classinfo_t*type, char init) { NEW(variable_t, v); - v->index = global->variable_count; + v->index = state->method->variable_count; v->type = type; v->init = init; dict_put(state->vars, name, v); - return global->variable_count++; + return state->method->variable_count++; } #define TEMPVARNAME "__as3_temp__" static int gettempvar() @@ -599,6 +636,44 @@ code_t* var_block(code_t*body) return c; } +#define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);} + +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); @@ -617,7 +692,6 @@ static void startpackage(char*name) new_state(); /*printf("entering package \"%s\"\n", name);*/ state->package = strdup(name); - global->variable_count = 1; } static void endpackage() { @@ -629,6 +703,7 @@ static void endpackage() old_state(); } + char*as3_globalclass=0; static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface) { @@ -636,9 +711,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo 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; @@ -654,7 +727,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo char*package=0; if(!(flags&FLAG_PUBLIC) && !state->package) { - access = ACCESS_PRIVATE; package = current_filename; + access = ACCESS_PRIVATE; package = strdup(current_filename_short); } else if(!(flags&FLAG_PUBLIC) && state->package) { access = ACCESS_PACKAGEINTERNAL; package = state->package; } else if(state->package) { @@ -663,131 +736,141 @@ 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); - } - + if(as3_pass==1) { + state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor + state->method->variable_count = 1; + dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method); - /* 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(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); + if(as3_pass == 2) { + state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount); + + 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; + } - MULTINAME(classname2,state->cls->info); + /* generate the abc code for this class */ + MULTINAME(classname2,state->cls->info); + multiname_t*extends2 = sig2mname(extends); - /*if(extends) { - state->cls_init = abc_getlocal_0(state->cls_init); - state->cls_init = abc_constructsuper(state->cls_init, 0); - }*/ + 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); + } - 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); - } + abc_class_protectedNS(state->cls->abc, classname); - abc_class_protectedNS(state->cls->abc, classname); + for(mlist=implements;mlist;mlist=mlist->next) { + MULTINAME(m, mlist->classinfo); + abc_class_add_interface(state->cls->abc, &m); + } - for(mlist=implements;mlist;mlist=mlist->next) { - MULTINAME(m, mlist->classinfo); - abc_class_add_interface(state->cls->abc, &m); - } + /* write the construction code for this class to the global init + function */ + int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc); - /* now write the construction code for this class */ - 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; - 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(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) { - if(state->package && state->package[0]) { - as3_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 { - as3_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++; + } + __ newclass(m,state->cls->abc); + while(count--) { + __ popscope(m); + } + __ setslot(m, slotindex); + multiname_destroy(extends2); + + /* flash.display.MovieClip handling */ + + 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); + } } } - multiname_destroy(extends2); } 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->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(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); + } - 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); + 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); + } } - free(state->cls);state->cls=0; - free(state->method);state->method=0; old_state(); } @@ -878,7 +961,7 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na 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))) { @@ -909,6 +992,67 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na return minfo; } +static void function_initvars(params_t*params, int flags) +{ + 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); + } +} + static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type) { @@ -916,41 +1060,56 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n syntaxerror("not able to start another method scope"); } new_state(); - global->variable_count = 0; - state->method = rfx_calloc(sizeof(methodstate_t)); - state->method->has_super = 0; + + if(as3_pass == 1) { + state->method = rfx_calloc(sizeof(methodstate_t)); + state->method->has_super = 0; + state->method->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 + 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__"; - new_variable("globalscope", 0, 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); } - /* state->vars is initialized by state_new */ - - param_list_t*p=0; - for(p=params->list;p;p=p->next) { - new_variable(p->param->name, p->param->type, 0); - } - 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) { + 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 if(!state->method->is_global) { namespace_t mname_ns = flags2namespace(flags, ""); @@ -1002,12 +1161,10 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam 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 @@ -1134,11 +1291,6 @@ 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; @@ -1169,9 +1321,9 @@ static classinfo_t* find_class(char*name) /* try global package */ c = registry_findclass("", name); if(c) return c; - + /* try local "filename" package */ - c = registry_findclass(current_filename, name); + c = registry_findclass(current_filename_short, name); if(c) return c; return 0; @@ -1208,7 +1360,6 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar] */ - if(in && in->opcode == OPCODE_COERCE_A) { in = code_cutlast(in); } @@ -1328,6 +1479,7 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r c = code_append(c, write); c = code_append(c, r); } else { + code_free(r);r=0; temp = gettempvar(); if(prefix) { c = code_append(c, prefix); @@ -1474,8 +1626,13 @@ code_t* insert_finally(code_t*c, code_t*finally, int 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) {{ +%} %% @@ -1493,6 +1650,7 @@ PROGRAM_CODE: PACKAGE_DECLARATION | FUNCTION_DECLARATION | SLOT_DECLARATION | PACKAGE_INITCODE + | T_IDENTIFIER "::" T_IDENTIFIER '{' PROGRAM_CODE_LIST '}' // conditional compilation | ';' MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST @@ -1504,6 +1662,7 @@ INPACKAGE_CODE: INTERFACE_DECLARATION | FUNCTION_DECLARATION | SLOT_DECLARATION | PACKAGE_INITCODE + | T_IDENTIFIER "::" T_IDENTIFIER '{' INPACKAGE_CODE_LIST '}' // conditional compilation | ';' MAYBECODE: CODE {$$=$1;} @@ -1579,6 +1738,7 @@ ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION $$ = converttype($$, $3.t, $2); $$ = abc_setlocal($$, index); } else { + code_free($3.c); $$ = defaultvalue(0, $2); $$ = abc_setlocal($$, index); } @@ -1588,6 +1748,7 @@ ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION $$ = abc_coerce_a($$); $$ = abc_setlocal($$, index); } else { + code_free($3.c); $$ = code_new(); } } @@ -1754,7 +1915,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 { @@ -1839,7 +2000,7 @@ FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' { 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 {$$=$1;} CATCH_FINALLY_LIST: CATCH_LIST FINALLY { $$ = $1; $$.finally = 0; @@ -1927,15 +2088,23 @@ WITH : "with" '(' EXPRESSION ')' CODEBLOCK { /* ------------ packages and imports ---------------- */ X_IDENTIFIER: T_IDENTIFIER - | "package" {$$="package";} + | "package" {PASS12 $$="package";} + +PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;} +PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);} -PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;} -PACKAGE: X_IDENTIFIER {$$=strdup($1);} +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;} -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" PACKAGEANDCLASS { + PASS1 + if(!registry_findclass($2->package, $2->name)) { + as3_schedule_class($2->package, $2->name); + } -IMPORT : "import" QNAME { + PASS2 classinfo_t*c = $2; if(!c) syntaxerror("Couldn't import class\n"); @@ -1944,6 +2113,12 @@ IMPORT : "import" QNAME { $$=0; } IMPORT : "import" PACKAGE '.' '*' { + PASS1 + if(strncmp("flash.", $2, 6)) { + as3_schedule_package($2); + } + + PASS2 NEW(import_t,i); i->package = $2; state_has_imports(); @@ -1953,41 +2128,41 @@ IMPORT : "import" PACKAGE '.' '*' { /* ------------ classes and interfaces (header) -------------- */ -MAYBE_MODIFIERS : %prec above_function {$$=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_PACKAGEINTERNAL;} +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 : KW_EXTENDS CLASS_SPEC {$$=$2;} -EXTENDS_LIST : {$$=list_new();} -EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;} +EXTENDS_LIST : {PASS12 $$=list_new();} +EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;} -IMPLEMENTS_LIST : {$$=list_new();} -IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;} +IMPLEMENTS_LIST : {PASS12 $$=list_new();} +IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;} CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER EXTENDS IMPLEMENTS_LIST - '{' {startclass($1,$3,$4,$5, 0);} + '{' {PASS12 startclass($1,$3,$4,$5, 0);} MAYBE_CLASS_BODY - '}' {endclass();$$=0;} + '}' {PASS12 endclass();$$=0;} INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER EXTENDS_LIST - '{' {startclass($1,$3,0,$4,1);} + '{' {PASS12 startclass($1,$3,0,$4,1);} MAYBE_INTERFACE_BODY - '}' {endclass();$$=0;} + '}' {PASS12 endclass();$$=0;} /* ------------ classes and interfaces (body) -------------- */ @@ -2014,12 +2189,14 @@ 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_PACKAGEINTERNAL|FLAG_PROTECTED)) { syntaxerror("invalid method modifiers: interface methods always need to be public"); } startfunction(0,$1,$3,$4,&$6,$8); endfunction(0,$1,$3,$4,&$6,$8, 0); + list_deep_free($6.list); } /* ------------ classes and interfaces (body, slots ) ------- */ @@ -2043,6 +2220,7 @@ SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSIO 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) { @@ -2091,7 +2269,7 @@ STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);} STATICCONSTANT : T_INT {$$ = constant_new_int($1);} STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);} STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);} -STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);} +STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);} //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);} STATICCONSTANT : "true" {$$ = constant_new_true($1);} STATICCONSTANT : "false" {$$ = constant_new_false($1);} @@ -2101,19 +2279,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); @@ -2121,21 +2303,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; @@ -2146,53 +2334,71 @@ 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); - } + PASS1 old_state();list_deep_free($6.list); + 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); + list_deep_free($6.list); $$=0; } MAYBE_IDENTIFIER: T_IDENTIFIER -MAYBE_IDENTIFIER: {$$=0;} -INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE '{' MAYBECODE '}' +MAYBE_IDENTIFIER: {PASS12 $$=0;} +INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE + '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}' { - syntaxerror("nested functions not supported yet"); + PASS1 old_state();list_deep_free($4.list); + 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); + list_deep_free($4.list); + + $$.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 static classinfo_t c; + memset(&c, 0, sizeof(c)); + c.package = $1; + c.name = $3; + $$=&c; + PASS2 $$ = registry_findclass($1, $3); if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3); free($1);$1=0; } -QNAME: PACKAGEANDCLASS - | CLASS +CLASS_SPEC: PACKAGEANDCLASS + | CLASS -QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);} -QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);} +CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);} +CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);} -TYPE : QNAME {$$=$1;} +TYPE : CLASS_SPEC {$$=$1;} | '*' {$$=registry_getanytype();} | "void" {$$=registry_getanytype();} /* @@ -2209,7 +2415,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 @@ -2220,25 +2426,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), @@ -2278,21 +2497,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"); @@ -2308,6 +2525,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; @@ -2368,6 +2586,8 @@ E : NEW {$$ = $1;} //V : DELETE {$$ = $1.c;} E : DELETE {$$ = $1;} +E : FUNCTIONCALL + E : T_REGEXP { $$.c = 0; namespace_t ns = {ACCESS_PACKAGE, ""}; @@ -2402,7 +2622,7 @@ CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1); CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1); $$.t = TYPE_FLOAT; } -CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1); +CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str); $$.t = TYPE_STRING; } CONSTANT : "undefined" {$$.c = abc_pushundefined(0); @@ -2418,7 +2638,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; } @@ -2607,7 +2826,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 @@ -2621,7 +2840,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; @@ -2934,8 +3153,7 @@ VAR_READ : T_IDENTIFIER { /* unknown object, let the avm2 resolve it */ if(1) { - if(strcmp($1,"trace")) - as3_softwarning("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};