fixed ok/innerfunctionslots.as
[swftools.git] / lib / as3 / parser.y
index d6cf02a..3abeaa3 100644 (file)
@@ -332,6 +332,7 @@ struct _methodstate {
     char uses_parent_function;
     int uses_slots;
     dict_t*slots;
+    int activation_var;
 
     abc_method_t*abc;
     int var_index; // for inner methods
@@ -354,8 +355,9 @@ typedef struct _state {
     import_list_t*wildcard_imports;
     dict_t*import_toplevel_packages;
     dict_t*imports;
-    namespace_list_t*active_namespaces;
-    namespace_decl_list_t*new_namespaces;
+
+    namespace_list_t*active_namespace_urls;
+    
     char has_own_imports;
     char new_vars; // e.g. transition between two functions
   
@@ -441,10 +443,14 @@ static void new_state()
     state = s;
     state->level++;
     state->has_own_imports = 0;    
-    state->new_namespaces = 0;
     state->vars = dict_new(); 
     state->old = oldstate;
     state->new_vars = 0;
+
+    trie_remember(active_namespaces);
+   
+    if(oldstate)
+        state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
 }
 static void state_has_imports()
 {
@@ -486,23 +492,22 @@ static void state_destroy(state_t*state)
         dict_destroy(state->vars);state->vars=0;
     }
     
+    list_free(state->active_namespace_urls)
+    state->active_namespace_urls = 0;
+    
     free(state);
 }
 
 static void old_state()
 {
+    trie_rollback(active_namespaces);
+
     if(!state || !state->old)
         syntaxerror("invalid nesting");
     state_t*leaving = state;
     
     state = state->old;
 
-    namespace_decl_list_t*nl=leaving->new_namespaces;
-    while(nl) {
-        tokenizer_unregister_namespace(nl->namespace_decl->name);
-        nl = nl->next;
-    }
-    
     if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
         free(leaving->method);
         leaving->method=0;
@@ -511,7 +516,7 @@ static void old_state()
         free(leaving->cls);
         leaving->cls=0;
     }
-    
+
     state_destroy(leaving);
 }
 
@@ -526,6 +531,9 @@ void initialize_file(char*filename)
     if(state) {
         syntaxerror("invalid call to initialize_file during parsing of another file");
     }
+    
+    active_namespaces = trie_new();
+
     new_state();
     state->package = internal_filename_package = strdup(filename);
     
@@ -617,6 +625,7 @@ typedef struct _variable {
     int index;
     classinfo_t*type;
     char init;
+    char is_parameter;
     methodstate_t*is_inner_method;
 } variable_t;
 
@@ -748,17 +757,26 @@ static void parsererror(const char*file, int line, const char*f)
 }
 
    
-static code_t* add_scope_code(code_t*c, methodstate_t*m)
+static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
 {
-    if(m->uses_slots || (m->late_binding && !m->inner)) {
+    if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
         c = abc_getlocal_0(c);
         c = abc_pushscope(c);
     }
     if(m->uses_slots) {
-        /* FIXME: does this need to be the same activation object as
-                  in the function header? */
-        c = abc_newactivation(c);
-        c = abc_pushscope(c);
+        /* FIXME: this alloc_local() causes variable indexes to be
+           different in pass2 than in pass1 */
+        if(!m->activation_var)
+            m->activation_var = alloc_local();
+        if(init) {
+            c = abc_newactivation(c);
+            c = abc_dup(c);
+            c = abc_pushscope(c);
+            c = abc_setlocal(c, m->activation_var);
+        } else {
+            c = abc_getlocal(c, m->activation_var);
+            c = abc_pushscope(c);
+        }
     }
     return c;
 }
@@ -767,7 +785,7 @@ static code_t* method_header(methodstate_t*m)
 {
     code_t*c = 0;
 
-    c = add_scope_code(c, m);
+    c = add_scope_code(c, m, 1);
 
     methodstate_list_t*l = m->innerfunctions;
     while(l) {
@@ -794,6 +812,19 @@ static code_t* method_header(methodstate_t*m)
         c = abc_getlocal_0(c);
         c = abc_constructsuper(c, 0);
     }
+
+    if(m->slots) {
+        /* all parameters that are used by inner functions
+           need to be copied from local to slot */
+        parserassert(m->activation_var);
+        DICT_ITERATE_ITEMS(m->slots,char*,name,variable_t*,v) {
+            if(v->is_parameter) {
+                c = abc_getlocal(c, m->activation_var); 
+                c = abc_getlocal(c, v->index); 
+                c = abc_setslot(c, v->index); 
+            }
+        }
+    }
     list_free(m->innerfunctions);
     m->innerfunctions = 0;
     return c;
@@ -844,7 +875,15 @@ static namespace_t modifiers2access(modifiers_t*mod)
         if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) 
             syntaxerror("invalid combination of access levels and namespaces");
         ns.access = ACCESS_NAMESPACE;
-        ns.name = mod->ns;
+        state_t*s = state;
+        const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
+        if(!url) {
+            /* shouldn't happen- the tokenizer only reports something as a namespace
+               if it was already registered */
+            trie_dump(active_namespaces);
+            syntaxerror("unknown namespace: %s", mod->ns);
+        }
+        ns.name = url;
     } else if(mod->flags&FLAG_PUBLIC)  {
         if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) 
             syntaxerror("invalid combination of access levels");
@@ -864,6 +903,18 @@ static namespace_t modifiers2access(modifiers_t*mod)
 }
 static slotinfo_t* find_class(const char*name);
 
+static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
+{
+    return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
+}
+
+void add_active_url(const char*url)
+{
+    NEW(namespace_t,n);
+    n->name = url;
+    list_append(state->active_namespace_urls, n);
+}
+
 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
 {
     if(var0) {
@@ -890,15 +941,14 @@ static void function_initvars(methodstate_t*m, params_t*params, int flags, char
     if(params) {
         param_list_t*p=0;
         for(p=params->list;p;p=p->next) {
-            new_variable(p->param->name, p->param->type, 0, 1);
+            variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1);
+            v->is_parameter = 1;
         }
     }
-
-    if(as3_pass==2) {
-        m->scope_code = add_scope_code(m->scope_code, m);
+    if(m->uses_slots) {
+        dict_dump(m->slots, stdout, "");
     }
 
-    
     methodstate_list_t*l = m->innerfunctions;
     while(l) {
         methodstate_t*m = l->methodstate;
@@ -911,14 +961,19 @@ static void function_initvars(methodstate_t*m, params_t*params, int flags, char
         l = l->next;
     }
     
+    if(as3_pass==2) {
+        m->scope_code = add_scope_code(m->scope_code, m, 0);
+    }
+    
     if(as3_pass==2 && m->slots) {
         /* exchange unresolved identifiers with the actual objects */
         DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
             if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
-                v->type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
-                if(!v->type || v->type->kind != INFOTYPE_CLASS) {
-                    syntaxerror("Couldn't find class %s", v->type->name);
+                classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
+                if(!type || type->kind != INFOTYPE_CLASS) {
+                    syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name);
                 }
+                v->type = type;
             }
         }
     }
@@ -1163,6 +1218,7 @@ void check_code_for_break(code_t*c)
 
 static void check_constant_against_type(classinfo_t*t, constant_t*c)
 {
+    return;
 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
    if(TYPE_IS_NUMBER(t)) {
         xassert(c->type == CONSTANT_FLOAT
@@ -1373,8 +1429,6 @@ static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
                           params_t*params, classinfo_t*return_type, code_t*body)
 {
-    int flags = mod?mod->flags:0;
-
     if(as3_pass==1) {
         // store inner methods in variables
         function_initvars(state->method, 0, 0, 0);
@@ -1447,10 +1501,10 @@ static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*
         } else if(state->method->is_constructor) {
             f = abc_class_getconstructor(state->cls->abc, type2);
         } else if(!state->method->is_global) {
-            namespace_t mname_ns = {state->method->info->access, ""};
+            namespace_t mname_ns = modifiers2access(mod);
             multiname_t mname = {QNAME, &mname_ns, 0, name};
 
-            if(flags&FLAG_STATIC)
+            if(mod->flags&FLAG_STATIC)
                 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
             else
                 f = abc_class_method(state->cls->abc, type2, &mname);
@@ -1466,7 +1520,7 @@ static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*
         //flash doesn't seem to allow us to access function slots
         //state->method->info->slot = slot;
 
-        if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
+        if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
         if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
         if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
         if(params->varargs) f->flags |= METHOD_NEED_REST;
@@ -1940,7 +1994,6 @@ char is_break_or_jump(code_t*c)
     return 0;
 }
 
-
 #define IS_FINALLY_TARGET(op) \
         ((op) == OPCODE___CONTINUE__ || \
          (op) == OPCODE___BREAK__ || \
@@ -2580,6 +2633,7 @@ WITH : WITH_HEAD CODEBLOCK {
 
 X_IDENTIFIER: T_IDENTIFIER
             | "package" {PASS12 $$="package";}
+            | T_NAMESPACE {PASS12 $$=$1;}
 
 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
 PACKAGE: X_IDENTIFIER             {PASS12 $$=strdup($1);}
@@ -2595,8 +2649,6 @@ IMPORT : "import" PACKAGEANDCLASS {
        if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
            as3_schedule_class($2->package, $2->name);
        }
-
-       PASS2
        classinfo_t*c = $2;
        if(!c) 
             syntaxerror("Couldn't import class\n");
@@ -2611,7 +2663,6 @@ IMPORT : "import" PACKAGE '.' '*' {
            as3_schedule_package($2);
        }
 
-       PASS2
        NEW(import_t,i);
        i->package = $2;
        state_has_imports();
@@ -2632,7 +2683,6 @@ MODIFIER_LIST : MODIFIER_LIST MODIFIER {
     $$.ns=$1.ns?$1.ns:$2.ns;
 
 }
-
 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
          | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
          | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
@@ -2910,7 +2960,7 @@ INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
 
 /* ------------- package + class ids --------------- */
 
-CLASS: T_IDENTIFIER {
+CLASS: X_IDENTIFIER {
     PASS1 NEW(unresolvedinfo_t,c);
           memset(c, 0, sizeof(*c));
           c->kind = INFOTYPE_UNRESOLVED;
@@ -2920,7 +2970,7 @@ CLASS: T_IDENTIFIER {
               c->nsset = get_current_imports();
               /* make the compiler look for this class in the current directory,
                  just in case: */
-              as3_schedule_class_noerror(state->package, $1);
+              //as3_schedule_class_noerror(state->package, $1);
           }
           $$ = (classinfo_t*)c;
     PASS2
@@ -2929,7 +2979,7 @@ CLASS: T_IDENTIFIER {
     $$ = (classinfo_t*)s;
 }
 
-PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
+PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
     PASS1 NEW(unresolvedinfo_t,c);
           memset(c, 0, sizeof(*c));
           c->kind = INFOTYPE_UNRESOLVED;
@@ -3592,7 +3642,7 @@ E : "super" '.' T_IDENTIFIER
               classinfo_t*t = state->cls->info->superclass;
               if(!t) t = TYPE_OBJECT;
 
-              memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
+              memberinfo_t*f = findmember_nsset(t, $3, 1);
 
               MEMBER_MULTINAME(m, f, $3);
               $$.c = 0;
@@ -3652,7 +3702,7 @@ E : E '.' T_IDENTIFIER {
         if(t->subtype==INFOTYPE_UNRESOLVED) {
             syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
         }
-        memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
+        memberinfo_t*f = findmember_nsset(t, $3, 1);
         char noslot = 0;
         if(f && !is_static != !(f->flags&FLAG_STATIC))
            noslot=1;
@@ -3669,15 +3719,17 @@ E : E '.' T_IDENTIFIER {
     } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
         string_t*package = $1.c->data[0];
         char*package2 = concat3(package->str, ".", $3);
-        if(dict_contains(state->import_toplevel_packages, package2)) {
+
+        slotinfo_t*a = registry_find(package->str, $3);
+        if(a) {
+            $$ = push_class(a);
+        } else if(dict_contains(state->import_toplevel_packages, package2) ||
+                  registry_ispackage(package2)) {
             $$.c = $1.c;
             $$.c->data[0] = string_new4(package2);
             $$.t = 0;
         } else {
-            slotinfo_t*a = registry_find(package->str, $3);
-            if(!a) 
-                syntaxerror("couldn't resolve %s", package2);
-            $$ = push_class(a);
+            syntaxerror("couldn't resolve %s", package2);
         }
     } else {
         /* when resolving a property on an unknown type, we do know the
@@ -3703,9 +3755,9 @@ VAR_READ : T_IDENTIFIER {
         unknown_variable($1);
     }
    
-    /* let the compiler know that it might check the current directory/package
+    /* let the compiler know that it might want to check the current directory/package
        for this identifier- maybe there's a file $1.as defining $1. */
-    as3_schedule_class_noerror(state->package, $1);
+    //as3_schedule_class_noerror(state->package, $1);
     PASS2
 
     $$.t = 0;
@@ -3731,8 +3783,11 @@ VAR_READ : T_IDENTIFIER {
     int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
 
     /* look at current class' members */
-    if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) &&
-        (f->flags&FLAG_STATIC) >= i_am_static) {
+    if(!state->method->inner && 
+        state->cls && 
+        (f = findmember_nsset(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);
 
@@ -3773,7 +3828,8 @@ VAR_READ : T_IDENTIFIER {
     }
 
     /* look through package prefixes */
-    if(dict_contains(state->import_toplevel_packages, $1)) {
+    if(dict_contains(state->import_toplevel_packages, $1) || 
+       registry_ispackage($1)) {
         $$.c = abc___pushpackage__($$.c, $1);
         $$.t = 0;
         break;
@@ -3799,7 +3855,7 @@ NAMESPACE_ID : "namespace" T_IDENTIFIER {
     PASS12
     NEW(namespace_decl_t,n);
     n->name = $2;
-    n->url = 0;
+    n->url = $2;
     $$=n;
 }
 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
@@ -3818,20 +3874,31 @@ NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
 }
 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
     PASS12
-    list_append(state->new_namespaces, $2);
-    tokenizer_register_namespace($2->name);
+    trie_put(active_namespaces, $2->name, (void*)$2->url);
+
+    namespace_t access = modifiers2access(&$1);
+    varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
+    var->type = TYPE_NAMESPACE;
+    namespace_t ns;
+    ns.access = ACCESS_NAMESPACE;
+    ns.name = $2->url;
+    var->value = constant_new_namespace(&ns);
+
     $$=0;
 }
 
 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
-    PASS12
-    NEW(namespace_decl_t,n);
-    n->name = $3->name;
-    n->url = 0;
-    /* FIXME: for pass2, we should now try to figure out what the URL of 
-              this thing is */
-    list_append(state->new_namespaces, n);
-    tokenizer_register_namespace($3->name);
+    
+    const char*url = $3->name;
+    varinfo_t*s = (varinfo_t*)$3;
+    if(!s || s->kind != INFOTYPE_SLOT)
+        syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
+    if(!s->value || !NS_TYPE(s->value->type))
+        syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
+    url = s->value->ns->name;
+
+    trie_put(active_namespaces, $3->name, (void*)url);
+    add_active_url(url);
     $$=0;
 }