new function dumpclasses()
[swftools.git] / lib / as3 / parser.y
index 847386d..f26a444 100644 (file)
 static int yyerror(char*s)
 {
    syntaxerror("%s", s); 
+   return 0; //make gcc happy
 }
 
 static char* concat2(const char* t1, const char* t2)
@@ -449,6 +450,8 @@ 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()
 {
@@ -601,8 +604,8 @@ 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) {
+    if(!c || (c->opcode != OPCODE_RETURNVOID && 
+              c->opcode != OPCODE_RETURNVALUE)) {
         c = abc_returnvoid(c);
     }
     return c;
@@ -1103,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)
@@ -1191,6 +1195,7 @@ static int getlocalnr(code_t*c)
     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)
@@ -1353,9 +1358,17 @@ char is_break_or_jump(code_t*c)
     return 0;
 }
 
-#define NEED_EXTRA_STACK_ARG
-static code_t* insert_finally(code_t*c, code_t*finally)
+
+#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
@@ -1364,21 +1377,27 @@ static code_t* insert_finally(code_t*c, code_t*finally)
     int count=0;
     while(i) {
         code_t*prev = i->prev;
-        if(i->opcode == OPCODE___CONTINUE__ ||
-           i->opcode == OPCODE___BREAK__ ||
-           i->opcode == OPCODE_RETURNVOID ||
-           i->opcode == OPCODE_RETURNVALUE ||
-           i->opcode == OPCODE___RETHROW__) 
-        {
-           if(i->opcode == OPCODE___RETHROW__)
-                i->opcode = OPCODE_NOP;
+        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);
         }
@@ -1402,6 +1421,59 @@ static code_t* insert_finally(code_t*c, code_t*finally)
     return c;
 }
 
+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);
+    }
+}
+
 %}
 
 
@@ -1472,7 +1544,8 @@ CODEBLOCK :  CODEPIECE %prec below_semicolon {$$=$1;}
 /* ------------ package init code ------------------- */
 
 PACKAGE_INITCODE: CODE_STATEMENT {
-    if($1) as3_warning("code ignored");
+    code_t**cc = &global->init->method->body->code;
+    *cc = code_append(*cc, $1);
 }
 
 /* ------------ variables --------------------------- */
@@ -1739,7 +1812,8 @@ CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name
 
     code_t*c = 0;
     int i = find_variable_safe($3)->index;
-    e->target = c = abc_setlocal(0, i);
+    e->target = c = abc_nop(0);
+    c = abc_setlocal(c, i);
     c = code_append(c, $8);
     c = abc_kill(c, i);
 
@@ -1747,35 +1821,55 @@ CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name
     old_state();
 }
 FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
-    NEW(abc_exception_t, e)
-    e->exc_type = 0; //all exceptions
-    e->var_name = 0; //no name
-    e->target = 0;
-    $$ = e;
-    e->to = var_block($4);
-    old_state();
+    $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;list_append($$.l,$2);
-    $$.finally = $2->to;$2->to=0;
+    $$ = $1;
+    $$.finally = 0;
+    if($2) {
+        list_append($$.l,$2);
+        $$.finally = $2->to;$2->to=0;
+    }
 }
 CATCH_FINALLY_LIST: FINALLY {
-    $$.l=list_new();list_append($$.l,$1);
-    $$.finally = $1->to;$1->to=0;
+    $$.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*start = abc_nop(0);
-    code_t*end = $$ = code_append(start, $4);
     code_t*out = abc_nop(0);
-   
+
+    code_t*start = abc_nop(0);
+    $$ = code_append(start, $4);
     if(!is_break_or_jump($4)) {
-        end = $$ = abc_jump($$, out);
+        $$ = 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;
@@ -1785,24 +1879,20 @@ TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
             $$ = code_append($$, e->target);
             $$ = abc_jump($$, out);
         } else {
+            parserassert((ptroff_t)$6.finally);
             // finally block
-            int tmp = new_variable("__finally__", 0, 0);
-            e->target = 
-            $$ = abc_coerce_a($$);
-            $$ = abc_setlocal($$, tmp);
+            e->target = $$ = abc_nop($$);
             $$ = abc___rethrow__($$);
-            $$ = abc_getlocal($$, tmp);
-            $$ = abc_throw($$);
         }
         
         e->from = start;
-        e->to = end->next;
+        e->to = end;
 
         l = l->next;
     }
     $$ = code_append($$, out);
 
-    $$ = insert_finally($$, $6.finally);
+    $$ = insert_finally($$, $6.finally, tmp);
         
     list_concat(state->method->exceptions, $6.l);
    
@@ -2649,7 +2739,7 @@ E : E '?' E ':' E %prec below_assignment {
 
 E : E "++" { code_t*c = 0;
              classinfo_t*type = $1.t;
-             if(is_getlocal($1.c) && TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t)) {
+             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)) {
@@ -2777,40 +2867,49 @@ VAR_READ : T_IDENTIFIER {
         // $1 is a local variable
         $$.c = abc_getlocal($$.c, v->index);
         $$.t = v->type;
+        break;
+    }
+
+    int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
 
     /* look at current class' members */
-    } else if(state->cls && (f = registry_findmember(state->cls->info, $1, 1))) {
+    if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
+        (f->flags&FLAG_STATIC) >= i_am_static) {
         // $1 is a function in this class
         int var_is_static = (f->flags&FLAG_STATIC);
-        int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
-        if(var_is_static != i_am_static) {
-            /* there doesn't seem to be any "static" way to access
-               static properties of a class */
+
+        if(f->kind == MEMBER_METHOD) {
+            $$.t = TYPE_FUNCTION(f);
+        } else {
+            $$.t = f->type;
+        }
+        if(var_is_static && !i_am_static) {
+        /* access to a static member from a non-static location.
+           do this via findpropstrict:
+           there doesn't seem to be any non-lookup way to access
+           static properties of a class */
             state->method->late_binding = 1;
             $$.t = f->type;
             namespace_t ns = {flags2access(f->flags), ""};
             multiname_t m = {QNAME, &ns, 0, $1};
             $$.c = abc_findpropstrict2($$.c, &m);
             $$.c = abc_getproperty2($$.c, &m);
+            break;
+        } else if(f->slot>0) {
+            $$.c = abc_getlocal_0($$.c);
+            $$.c = abc_getslot($$.c, f->slot);
+            break;
         } else {
-            if(f->slot>0) {
-                $$.c = abc_getlocal_0($$.c);
-                $$.c = abc_getslot($$.c, f->slot);
-            } else {
-                namespace_t ns = {flags2access(f->flags), ""};
-                multiname_t m = {QNAME, &ns, 0, $1};
-                $$.c = abc_getlocal_0($$.c);
-                $$.c = abc_getproperty2($$.c, &m);
-            }
-        }
-        if(f->kind == MEMBER_METHOD) {
-            $$.t = TYPE_FUNCTION(f);
-        } else {
-            $$.t = f->type;
+            namespace_t ns = {flags2access(f->flags), ""};
+            multiname_t m = {QNAME, &ns, 0, $1};
+            $$.c = abc_getlocal_0($$.c);
+            $$.c = abc_getproperty2($$.c, &m);
+            break;
         }
+    } 
     
     /* look at actual classes, in the current package and imported */
-    } else if((a = find_class($1))) {
+    if((a = find_class($1))) {
         if(a->flags & FLAG_METHOD) {
             MULTINAME(m, a);
             $$.c = abc_findpropstrict2($$.c, &m);
@@ -2830,9 +2929,11 @@ VAR_READ : T_IDENTIFIER {
             }
             $$.t = TYPE_CLASS(a);
         }
+        break;
+    }
 
     /* unknown object, let the avm2 resolve it */
-    } else {
+    if(1) {
         if(strcmp($1,"trace"))
             as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
         state->method->late_binding = 1;