X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=8da6dd0a67e1796a73da9c60d5249816b0d30a51;hb=1ae7fbe0e9e5eb492ae3c1fd3b26267aa6b34fcb;hp=a8cb4a74098c249ba394db4718cc344e42b311e1;hpb=422d33e62fa625ebbd8da6080e83dce3274ebbf3;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index a8cb4a7..8da6dd0 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 @@ -250,12 +252,13 @@ %{ -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 +297,8 @@ typedef struct _classstate { char has_constructor; } classstate_t; +DECLARE_LIST(methodstate); + typedef struct _methodstate { /* method data */ memberinfo_t*info; @@ -301,8 +306,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 { @@ -313,6 +325,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; @@ -325,8 +338,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; @@ -429,7 +441,7 @@ static void old_state() state = state->old; - if(leaving->method && leaving->method != state->method) { + if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) { free(leaving->method); leaving->method=0; } @@ -441,37 +453,37 @@ static void old_state() 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) { new_state(); state->package = filename; - // needed for state->method->late_binding: + 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); } 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; @@ -480,6 +492,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; } @@ -530,6 +543,8 @@ static variable_t* find_variable(char*name) if(v) { return v; } + if(s->new_vars) + break; s = s->old; } return 0; @@ -546,16 +561,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() @@ -628,7 +643,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() { @@ -640,9 +654,12 @@ static void endpackage() old_state(); } -void parserassert(int b) +#define _TRACE_ {printf("vfw: %s: %d (%s)\n",__FILE__,__LINE__,__func__);fflush(stdout);} +#define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);} + +static void parsererror(const char*file, int line, const char*f) { - if(!b) syntaxerror("internal error: assertion failed"); + syntaxerror("internal error in %s, %s:%d", f, file, line); } @@ -653,9 +670,9 @@ 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 + state->method->variable_count = 1; token_list_t*t=0; classinfo_list_t*mlist=0; @@ -899,7 +916,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))) { @@ -930,10 +947,33 @@ 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); - memberinfo_t*parent_method = state->method->info; + + methodstate_t*parent_method = state->method; if(as3_pass==1) { // not valid yet @@ -942,18 +982,30 @@ static void innerfunction(char*name, params_t*params, classinfo_t*return_type) } new_state(); - state->method = rfx_calloc(sizeof(methodstate_t)); - state->method->inner = 1; - - NEW(memberinfo_t,minfo); - minfo->return_type = return_type; - minfo->name = name; + 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; - if(!parent_method->subfunctions) - parent_method->subfunctions = dict_new(); + list_append(parent_method->innerfunctions, state->method); - dict_put(parent_method->subfunctions, name, minfo); - state->method->info = minfo; + 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, @@ -963,41 +1015,37 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n syntaxerror("not able to start another method scope"); } new_state(); - 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; - } 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__"; + 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__"; - if(as3_pass == 1) { 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(as3_pass == 2) { - /* retrieve the member info that we stored in the first pass. - TODO: better getter/setter support? */ - if(!state->cls) state->method->info = registry_findclass(state->package, name)->function; - else state->method->info = registry_findmember(state->cls->info, name, 0); - state->method->info->return_type = return_type; - - global->variable_count = 0; - /* state->vars is initialized by state_new */ - 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); + 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); } } @@ -1014,7 +1062,8 @@ static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, multiname_t*type2 = sig2mname(return_type); int slot = 0; if(state->method->inner) { - f = abc_method_new(global->file, type2, 1); + 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) { @@ -2168,19 +2217,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); @@ -2188,21 +2241,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; @@ -2223,11 +2282,26 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P 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; + c = wrap_function(c, 0, $11); endfunction(0,$1,$3,$4,&$6,$8,c); @@ -2235,7 +2309,7 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P } MAYBE_IDENTIFIER: T_IDENTIFIER -MAYBE_IDENTIFIER: {$$=0;} +MAYBE_IDENTIFIER: {PASS12 $$=0;} INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}' { @@ -2247,9 +2321,10 @@ INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE code_t*c = 0; c = wrap_function(c, 0, $9); - abc_method_t*abc = endfunction(0,0,0,$2,&$4,$6,c); + int index = state->method->var_index; + endfunction(0,0,0,$2,&$4,$6,c); - $$.c = abc_newfunction(0, abc); + $$.c = abc_getlocal(0, index); $$.t = TYPE_FUNCTION(f); } @@ -2307,6 +2382,16 @@ EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION { $$.cc = code_append($1.cc, $3.c); } +/*NEW : "new" E { + $$ = $2; + if($2.c->opcode == OPCODE_CALL) + $2.c->opcode = OPCODE_CONSTRUCT; + else if($2.c->opcode == OPCODE_CALLPROPERTY) + $2.c->opcode = OPCODE_CONSTRUCTPROP; + else + as3_error("invalid argument to 'new'"); +}*/ + NEW : "new" CLASS MAYBE_PARAM_VALUES { MULTINAME(m, $2); $$.c = code_new(); @@ -2364,7 +2449,7 @@ 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); } @@ -2377,8 +2462,8 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { $$.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"); @@ -2394,6 +2479,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; @@ -2454,6 +2540,8 @@ E : NEW {$$ = $1;} //V : DELETE {$$ = $1.c;} E : DELETE {$$ = $1;} +E : FUNCTIONCALL + E : T_REGEXP { $$.c = 0; namespace_t ns = {ACCESS_PACKAGE, ""}; @@ -2504,7 +2592,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; } @@ -3020,8 +3107,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};