fixed nested_function, started fixing new
[swftools.git] / lib / as3 / parser.y
index 0b35fc9..b6bb97e 100644 (file)
@@ -32,6 +32,8 @@
 #include "code.h"
 #include "opcodes.h"
 
+extern int a3_lex();
+
 %}
 
 //%glr-parser
 %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
 
 
      
@@ -295,6 +300,8 @@ typedef struct _classstate {
     char has_constructor;
 } classstate_t;
 
+DECLARE_LIST(methodstate);
+
 typedef struct _methodstate {
     /* method data */
     memberinfo_t*info;
@@ -302,9 +309,15 @@ typedef struct _methodstate {
     char is_constructor;
     char has_super;
     char is_global;
-    char inner;
     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 {
@@ -315,6 +328,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;
@@ -327,6 +341,7 @@ typedef struct _state {
 typedef struct _global {
     abc_file_t*file;
     abc_script_t*init;
+    dict_t*token2info;
 } global_t;
 
 static global_t*global = 0;
@@ -429,7 +444,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,19 +456,6 @@ 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->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 initialize_file(char*filename)
 {
     new_state();
@@ -466,11 +468,25 @@ void initialize_file(char*filename)
 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;
@@ -479,6 +495,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;
 }
 
@@ -529,6 +546,8 @@ static variable_t* find_variable(char*name)
         if(v) {
             return v;
         }
+        if(s->new_vars)
+            break;
         s = s->old;
     }
     return 0;
@@ -545,7 +564,7 @@ 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 = state->method->variable_count;
@@ -609,6 +628,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);
@@ -638,11 +695,6 @@ static void endpackage()
     old_state();
 }
 
-void parserassert(int b)
-{
-    if(!b) syntaxerror("internal error: assertion failed");
-}
-
 
 char*as3_globalclass=0;
 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
@@ -897,7 +949,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))) {
@@ -928,10 +980,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
@@ -940,39 +1015,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;
-    state->method->variable_count = 0;
+    state->new_vars = 1;
    
-    memberinfo_t*minfo = 0;
-
-    /* TODO: we need some better way to pass things from pass1 to pass2 */
-    char myname[200];
-    sprintf(myname, "as3-innerfunction-%d-%d", current_line, current_column);
-
     if(as3_pass == 1) {
-        minfo = rfx_calloc(sizeof(memberinfo_t));
+        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;
-        if(!parent_method->subfunctions) 
-            parent_method->subfunctions = dict_new();
-        if(name)
-            dict_put(parent_method->subfunctions, name, minfo);
-        dict_put(parent_method->subfunctions, myname, minfo);
+        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) {
-        minfo = dict_lookup(parent_method->subfunctions, myname);
-        parserassert(minfo);
-
-        minfo->return_type = return_type;
+        state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+        parserassert(state->method);
 
-        new_variable("FIXME", 0, 0); //FIXME: is local_0 "this"?
-        param_list_t*p=0;
-        for(p=params->list;p;p=p->next) {
-            new_variable(p->param->name, p->param->type, 0);
-        }
+        state->method->info->return_type = return_type;
+        function_initvars(params, 0);
     }
-    state->method->info = minfo;
 }
 
 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
@@ -982,41 +1048,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;
-    state->method->variable_count = 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;
-
-        /* 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);
     } 
 }
 
@@ -1033,7 +1095,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) {
@@ -2187,19 +2250,23 @@ STATICCONSTANT : "null" {$$ = constant_new_null($1);}
 
 // non-vararg version
 MAYBE_PARAM_LIST: {
+    PASS12
     memset(&$$,0,sizeof($$));
 }
 MAYBE_PARAM_LIST: PARAM_LIST {
+    PASS12
     $$=$1;
 }
 
 // vararg version
 MAYBE_PARAM_LIST: "..." PARAM {
+    PASS12
     memset(&$$,0,sizeof($$));
     $$.varargs=1;
     list_append($$.list, $2);
 }
 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
+    PASS12
     $$ =$1;
     $$.varargs=1;
     list_append($$.list, $4);
@@ -2207,21 +2274,27 @@ MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
 
 // non empty
 PARAM_LIST: PARAM_LIST ',' PARAM {
+    PASS12
     $$ = $1;
     list_append($$.list, $3);
 }
 PARAM_LIST: PARAM {
+    PASS12
     memset(&$$,0,sizeof($$));
     list_append($$.list, $1);
 }
 
 PARAM:  T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
+     PASS1 $$=0;
+     PASS2
      $$ = malloc(sizeof(param_t));
      $$->name=$1;
      $$->type = $3;
      $$->value = $4;
 }
 PARAM:  T_IDENTIFIER MAYBESTATICCONSTANT {
+     PASS1 $$=0;
+     PASS2
      $$ = malloc(sizeof(param_t));
      $$->name=$1;
      $$->type = TYPE_ANY;
@@ -2237,17 +2310,8 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P
     PASS1 old_state();
     PASS2
     if(!state->method->info) syntaxerror("internal error");
-    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);
-    }
-
+    
+    code_t*c = method_header();
     c = wrap_function(c, 0, $11);
 
     endfunction(0,$1,$3,$4,&$6,$8,c);
@@ -2263,13 +2327,14 @@ INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
     PASS2
     memberinfo_t*f = state->method->info;
     if(!f) syntaxerror("internal error");
-
-    code_t*c = 0;
+    
+    code_t*c = method_header();
     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);
 }
 
@@ -2326,25 +2391,38 @@ EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
                                                   $$.len= $1.len+1;
                                                   $$.cc = code_append($1.cc, $3.c);
                                                   }
-
-NEW : "new" CLASS MAYBE_PARAM_VALUES {
-    MULTINAME(m, $2);
-    $$.c = code_new();
-
-    if($2->slot) {
-        $$.c = abc_getglobalscope($$.c);
-        $$.c = abc_getslot($$.c, $2->slot);
+               
+XX : %prec new2
+NEW : "new" E XX MAYBE_PARAM_VALUES {
+    $$.c = $2.c;
+    if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
+    
+    code_t*paramcode = $4.cc;
+    if($$.c->opcode == OPCODE_GETPROPERTY) {
+        multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
+        $$.c = code_cutlast($$.c);
+        $$.c = code_append($$.c, paramcode);
+        $$.c = abc_constructprop2($$.c, name, $4.len);
+        multiname_destroy(name);
+    } else if($$.c->opcode == OPCODE_GETSLOT) {
+        int slot = (int)(ptroff_t)$$.c->data[0];
+        trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
+        multiname_t*name = t->name;
+        $$.c = code_cutlast($$.c);
+        $$.c = code_append($$.c, paramcode);
+        $$.c = abc_constructprop2($$.c, name, $4.len);
     } else {
-        $$.c = abc_findpropstrict2($$.c, &m);
+        $$.c = code_append($$.c, paramcode);
+        $$.c = abc_construct($$.c, $4.len);
+    }
+   
+    $$.t = TYPE_ANY;
+    if(TYPE_IS_CLASS($2.t) && $2.t->cls) {
+        $$.t = $2.t->cls;
+    } else {
+        $$.c = abc_coerce_a($$.c);
+        $$.t = TYPE_ANY;
     }
-
-    $$.c = code_append($$.c, $3.cc);
-
-    if($2->slot)
-        $$.c = abc_construct($$.c, $3.len);
-    else
-        $$.c = abc_constructprop2($$.c, &m, $3.len);
-    $$.t = $2;
 }
 
 /* TODO: use abc_call (for calling local variables),
@@ -2384,21 +2462,19 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
         $$.c = abc_callsuper2($$.c, name, $3.len);
         multiname_destroy(name);
     } else {
-        $$.c = abc_getlocal_0($$.c);
+        $$.c = abc_getglobalscope($$.c);
         $$.c = code_append($$.c, paramcode);
         $$.c = abc_call($$.c, $3.len);
     }
    
-    memberinfo_t*f = 0;
-   
     if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
         $$.t = $1.t->function->return_type;
     } else {
         $$.c = abc_coerce_a($$.c);
         $$.t = TYPE_ANY;
     }
-
 }
+
 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
     if(!state->cls) syntaxerror("super() not allowed outside of a class");
     if(!state->method) syntaxerror("super() not allowed outside of a function");
@@ -2414,6 +2490,7 @@ FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
         syntaxerror("constructor may call super() only once");
     */
     state->method->has_super = 1;
+
     $$.c = abc_constructsuper($$.c, $3.len);
     $$.c = abc_pushundefined($$.c);
     $$.t = TYPE_ANY;
@@ -2474,6 +2551,8 @@ E : NEW                         {$$ = $1;}
 //V : DELETE                      {$$ = $1.c;}
 E : DELETE                      {$$ = $1;}
 
+E : FUNCTIONCALL
+
 E : T_REGEXP {
     $$.c = 0;
     namespace_t ns = {ACCESS_PACKAGE, ""};
@@ -2524,7 +2603,6 @@ CONSTANT : "null" {$$.c = abc_pushnull(0);
                     $$.t = TYPE_NULL;
                    }
 
-E : FUNCTIONCALL
 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
              $$.t = TYPE_BOOLEAN;
             }
@@ -3040,8 +3118,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};