added support for "Class.member" syntax
[swftools.git] / lib / as3 / parser.y
index cd7b1f4..08900d1 100644 (file)
@@ -297,6 +297,16 @@ DECLARE_LIST(state);
     m##_l.namespace = &m##_ns; \
     multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
 
+static namespace_t ns1 = {ACCESS_PRIVATE, ""};
+static namespace_t ns2 = {ACCESS_PROTECTED, ""};
+static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
+static namespace_t ns4 = {ACCESS_PACKAGE, ""};
+static namespace_list_t nl4 = {&ns4,0};
+static namespace_list_t nl3 = {&ns3,&nl4};
+static namespace_list_t nl2 = {&ns2,&nl3};
+static namespace_list_t nl1 = {&ns1,&nl2};
+static namespace_set_t nopackage_namespace_set = {&nl1};
+
 static state_list_t*state_stack=0;
     
 static void init_globals()
@@ -358,7 +368,7 @@ void initialize_state()
     state->file = abc_file_new();
     state->file->flags &= ~ABCFILE_LAZY;
     
-    state->init = abc_initscript(state->file, 0, 0);
+    state->init = abc_initscript(state->file, 0);
     code_t*c = state->init->method->body->code;
 
     c = abc_getlocal_0(c);
@@ -564,7 +574,7 @@ static void endclass()
 {
     if(state->cls_init) {
         if(!state->cls->constructor) {
-            abc_method_t*m = abc_class_constructor(state->cls, 0, 0);
+            abc_method_t*m = abc_class_constructor(state->cls, 0);
             m->body->code = code_append(m->body->code, state->cls_init);
             m->body->code = abc_returnvoid(m->body->code);
         } else {
@@ -576,8 +586,9 @@ static void endclass()
     }
     if(state->cls_static_init) {
         if(!state->cls->static_constructor) {
-            abc_method_t*m = abc_class_staticconstructor(state->cls, 0, 0);
-            m->body->code = state->cls_static_init;
+            abc_method_t*m = abc_class_staticconstructor(state->cls, 0);
+            m->body->code = code_append(m->body->code, state->cls_static_init);
+            m->body->code = abc_returnvoid(m->body->code);
         } else {
             state->cls->static_constructor->body->code = 
                 code_append(state->cls_static_init, state->cls->static_constructor->body->code);
@@ -673,8 +684,72 @@ static void check_constant_against_type(classinfo_t*t, constant_t*c)
    }
 }
 
+static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
+{
+    memberinfo_t*minfo = 0;
+    if(getset != KW_GET && getset != KW_SET) {
+        if(registry_findmember(state->clsinfo, name)) {
+            syntaxerror("class already contains a member/method called '%s'", name);
+        }
+        minfo = memberinfo_register(state->clsinfo, name, MEMBER_METHOD);
+        minfo->return_type = return_type;
+        // getslot on a member slot only returns "undefined", so no need
+        // to actually store these
+        //state->minfo->slot = state->m->method->trait->slot_id;
+    } else {
+        int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
+        classinfo_t*type=0;
+        if(getset == KW_GET)
+            type = return_type;
+        else if(params->list)
+            type = params->list->param->type;
+        if((minfo=registry_findmember(state->clsinfo, name))) {
+            if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
+                syntaxerror("class already contains a member or method called '%s'", name);
+            if(minfo->kind & gs)
+                syntaxerror("getter/setter for '%s' already defined", name);
+            /* make a setter or getter into a getset */
+            minfo->kind |= gs;
+            if(!minfo->type) 
+                minfo->type = type;
+            else
+                if(type && minfo->type != type)
+                    syntaxerror("different type in getter and setter");
+        } else {
+            minfo = memberinfo_register(state->clsinfo, name, gs);
+            minfo->type = type;
+        }
+        /* can't assign a slot as getter and setter might have different slots */
+        //minfo->slot = slot;
+    }
+    if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
+    if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
+    if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
+    if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
+    if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
+    return minfo;
+}
+
+static int flags2access(int flags)
+{
+    int access = 0;
+    if(flags&FLAG_PUBLIC)  {
+        if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+        access = ACCESS_PACKAGE;
+    } else if(flags&FLAG_PRIVATE) {
+        if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+        access = ACCESS_PRIVATE;
+    } else if(flags&FLAG_PROTECTED) {
+        if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+        access = ACCESS_PROTECTED;
+    } else {
+        access = ACCESS_PACKAGEINTERNAL;
+    }
+    return access;
+}
+
 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
-                          params_t*params, classinfo_t*type)
+                          params_t*params, classinfo_t*return_type)
 {
     token_list_t*t;
     new_state();
@@ -685,27 +760,26 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n
         syntaxerror("not able to start another method scope");
     }
 
-    multiname_t*type2 = sig2mname(type);
+    namespace_t mname_ns = {flags2access(flags), ""};
+    multiname_t mname = {QNAME, &mname_ns, 0, name};
+
+    multiname_t*type2 = sig2mname(return_type);
+    int slot = 0;
     if(!strcmp(state->clsinfo->name,name)) {
-        state->m = abc_class_constructor(state->cls, type2, 0);
+        state->m = abc_class_constructor(state->cls, type2);
+        name = "__as3_constructor__";
     } else {
-        state->minfo = memberinfo_register(state->clsinfo, name, MEMBER_METHOD);
-        state->minfo->return_type = type;
-
-        state->m = abc_class_method(state->cls, type2, name, 0);
-        // getslot on a member slot only returns "undefined", so no need
-        // to actually store these
-        //state->minfo->slot = state->m->method->trait->slot_id;
-    }
-    if(getset == KW_GET) {
-        state->m->trait->kind = TRAIT_GETTER;
-    }
-    if(getset == KW_SET) {
-        state->m->trait->kind = TRAIT_SETTER;
-    }
-    if(params->varargs) {
-        state->m->flags |= METHOD_NEED_REST;
+        if(flags&FLAG_STATIC)
+            state->m = abc_class_staticmethod(state->cls, type2, &mname);
+        else
+            state->m = abc_class_method(state->cls, type2, &mname);
+        slot = state->m->trait->slot_id;
     }
+    state->minfo = registerfunction(getset, flags, name, params, return_type, slot);
+
+    if(getset == KW_GET) state->m->trait->kind = TRAIT_GETTER;
+    if(getset == KW_SET) state->m->trait->kind = TRAIT_SETTER;
+    if(params->varargs) state->m->flags |= METHOD_NEED_REST;
 
     char opt=0;
     param_list_t*p=0;
@@ -724,7 +798,7 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n
     }
 
     /* state->vars is initialized by state_new */
-    if(new_variable("this", state->clsinfo)!=0) syntaxerror("Internal error");
+    if(new_variable((flags&FLAG_STATIC)?"class":"this", state->clsinfo)!=0) syntaxerror("Internal error");
 
     for(p=params->list;p;p=p->next) {
         new_variable(p->param->name, p->param->type);
@@ -803,6 +877,14 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
     if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
         return abc_coerce2(c, &m);
     }
+    /* these are subject to overflow */
+    if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) {
+        return abc_coerce2(c, &m);
+    }
+    if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) {
+        return abc_coerce2(c, &m);
+    }
+
     classinfo_t*supertype = from;
     while(supertype) {
         if(supertype == to) {
@@ -819,6 +901,8 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
         }
         supertype = supertype->superclass;
     }
+    if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
+        return c;
     syntaxerror("can't convert type %s to %s", from->name, to->name);
 }
 
@@ -879,19 +963,19 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r
         write->opcode = OPCODE_SETPROPERTY;
         multiname_t*m = (multiname_t*)r->data[0];
         write->data[0] = multiname_clone(m);
-        if(m->type != QNAME)
+        if(m->type != QNAME && m->type != MULTINAME)
             syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)");
         if(!justassign) {
-            use_temp_var = 1;
             prefix = abc_dup(prefix); // we need the object, too
         }
+        use_temp_var = 1;
     } else if(r->opcode == OPCODE_GETSLOT) {
         write->opcode = OPCODE_SETSLOT;
         write->data[0] = r->data[0];
         if(!justassign) {
-            use_temp_var = 1;
             prefix = abc_dup(prefix); // we need the object, too
         }
+        use_temp_var = 1;
     } else if(r->opcode == OPCODE_GETLOCAL) { 
         write->opcode = OPCODE_SETLOCAL;
         write->data[0] = r->data[0];
@@ -949,13 +1033,26 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r
     } else {
         /* even smaller version: overwrite the value without reading
            it out first */
-        if(prefix) {
-            c = code_append(c, prefix);
+        if(!use_temp_var) {
+            if(prefix) {
+                c = code_append(c, prefix);
+                c = abc_dup(c);
+            }
+            c = code_append(c, middlepart);
+            c = code_append(c, write);
+            c = code_append(c, r);
+        } else {
+            temp = gettempvar();
+            if(prefix) {
+                c = code_append(c, prefix);
+                c = abc_dup(c);
+            }
+            c = code_append(c, middlepart);
             c = abc_dup(c);
+            c = abc_setlocal(c, temp);
+            c = code_append(c, write);
+            c = abc_getlocal(c, temp);
         }
-        c = code_append(c, middlepart);
-        c = code_append(c, write);
-        c = code_append(c, r);
     }
 
     return c;
@@ -1205,8 +1302,9 @@ IDECLARATION : "var" T_IDENTIFIER {
     syntaxerror("variable declarations not allowed in interfaces");
 }
 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
-    if($1&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
-        syntaxerror("invalid method modifiers: interface methods are always public");
+    $1 |= FLAG_PUBLIC;
+    if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
+        syntaxerror("invalid method modifiers: interface methods always need to be public");
     }
     startfunction(0,$1,$3,$4,&$6,$8);
     endfunction(0);
@@ -1215,30 +1313,47 @@ IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LI
 /* ------------ classes and interfaces (body, slots ) ------- */
 
 VARCONST: "var" | "const"
-SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
 
+SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
+    int flags = $1;
     memberinfo_t* info = memberinfo_register(state->clsinfo, $3, MEMBER_SLOT);
     info->type = $4;
-
+    info->flags = flags;
     trait_t*t=0;
-    if($4) {
-        MULTINAME(m, $4);
-        t=abc_class_slot(state->cls, $3, &m);
+
+    namespace_t mname_ns = {flags2access(flags), ""};
+    multiname_t mname = {QNAME, &mname_ns, 0, $3};
+
+    if(!(flags&FLAG_STATIC)) {
+        if($4) {
+            MULTINAME(m, $4);
+            t=abc_class_slot(state->cls, &mname, &m);
+        } else {
+            t=abc_class_slot(state->cls, &mname, 0);
+        }
+        info->slot = t->slot_id;
     } else {
-        t=abc_class_slot(state->cls, $3, 0);
-    }
-    if($2==KW_CONST) {
-        t->kind= TRAIT_CONST;
+        if($4) {
+            MULTINAME(m, $4);
+            t=abc_class_staticslot(state->cls, &mname, &m);
+        } else {
+            t=abc_class_staticslot(state->cls, &mname, 0);
+        }
+        info->slot = t->slot_id;
     }
-    info->slot = t->slot_id;
     if($5.c && !is_pushundefined($5.c)) {
         code_t*c = 0;
         c = abc_getlocal_0(c);
         c = code_append(c, $5.c);
         c = converttype(c, $5.t, $4);
         c = abc_setslot(c, t->slot_id);
-        //c = abc_setproperty(c, $3); 
-        state->cls_init = code_append(state->cls_init, c);
+        if(!(flags&FLAG_STATIC))
+            state->cls_init = code_append(state->cls_init, c);
+        else
+            state->cls_static_init = code_append(state->cls_static_init, c);
+    }
+    if($2==KW_CONST) {
+        t->kind= TRAIT_CONST;
     }
 }
 
@@ -1675,6 +1790,7 @@ E : E "++" { code_t*c = 0;
              classinfo_t*type = $1.t;
              if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
                  c=abc_increment_i(c);
+                 type = TYPE_INT;
              } else {
                  c=abc_increment(c);
                  type = TYPE_NUMBER;
@@ -1687,6 +1803,7 @@ E : E "--" { code_t*c = 0;
              classinfo_t*type = $1.t;
              if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
                  c=abc_decrement_i(c);
+                 type = TYPE_INT;
              } else {
                  c=abc_decrement(c);
                  type = TYPE_NUMBER;
@@ -1700,6 +1817,7 @@ E : "++" E { code_t*c = 0;
              classinfo_t*type = $2.t;
              if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
                  c=abc_increment_i(c);
+                 type = TYPE_INT;
              } else {
                  c=abc_increment(c);
                  type = TYPE_NUMBER;
@@ -1713,6 +1831,7 @@ E : "--" E { code_t*c = 0;
              classinfo_t*type = $2.t;
              if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
                  c=abc_decrement_i(c);
+                 type = TYPE_INT;
              } else {
                  c=abc_decrement(c);
                  type = TYPE_NUMBER;
@@ -1730,9 +1849,14 @@ E : E '.' T_IDENTIFIER
                  if(f && f->slot) {
                      $$.c = abc_getslot($$.c, f->slot);
                  } else {
-                     namespace_t ns = {$$.t->access, ""}; // needs to be "", not $$.t->package
-                     multiname_t m = {QNAME, &ns, 0, $3};
-                     $$.c = abc_getproperty2($$.c, &m);
+                     if(f) {
+                         namespace_t ns = {flags2access(f->flags), ""}; // needs to be "", not $$.t->package (!)
+                         multiname_t m = {QNAME, &ns, 0, $3};
+                         $$.c = abc_getproperty2($$.c, &m);
+                     } else {
+                         multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+                         $$.c = abc_getproperty2($$.c, &m);
+                     }
                  }
                  /* determine type */
                  if(f) {
@@ -1746,8 +1870,10 @@ E : E '.' T_IDENTIFIER
                     $$.t = registry_getanytype();
                  }
              } else {
-                 namespace_t ns = {ACCESS_PACKAGE, ""};
-                 multiname_t m = {QNAME, &ns, 0, $3};
+                 /* when resolving a property on an unknown type, we do know the
+                    name of the property (and don't seem to need the package), but
+                    we do need to make avm2 try out all access modes */
+                 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
                  $$.c = abc_getproperty2($$.c, &m);
                  $$.c = abc_coerce_a($$.c);
                  $$.t = registry_getanytype();
@@ -1758,35 +1884,69 @@ VAR_READ : T_IDENTIFIER {
     $$.t = 0;
     $$.c = 0;
     int i;
+    classinfo_t*a = 0;
     memberinfo_t*f = 0;
+
+    /* look at variables */
     if((i = find_variable($1, &$$.t)) >= 0) {
         // $1 is a local variable
         $$.c = abc_getlocal($$.c, i);
+
+    /* look at current class' members */
     } else if((f = registry_findmember(state->clsinfo, $1))) {
         // $1 is a function in this class
+        int var_is_static = (f->flags&FLAG_STATIC);
+        int i_am_static = (state->minfo?(state->minfo->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 */
+            state->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);
+        } 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;
         }
-        if(f->slot>0) {
-            $$.c = abc_getlocal_0($$.c);
-            $$.c = abc_getslot($$.c, f->slot);
+    
+    /* look at classes in the current package */
+    } else if((a = registry_findclass(state->package, $1))) {
+        if(a->slot) {
+            $$.c = abc_getglobalscope($$.c);
+            $$.c = abc_getslot($$.c, a->slot);
         } else {
-            namespace_t ns = {state->clsinfo->access, ""};
-            multiname_t m = {QNAME, &ns, 0, $1};
-            $$.c = abc_getlocal_0($$.c);
-            $$.c = abc_getproperty2($$.c, &m);
+            MULTINAME(m, a);
+            $$.c = abc_getlex2($$.c, &m);
         }
+        /* this is not entirely correct (this is the class itself,
+           not an object of this class) */
+        $$.t = a;
+
+    /* unknown object, let the avm2 resolve it */
     } else {
-        // let the avm2 resolve $1 
         if(strcmp($1,"trace"))
-        warning("Couldn't resolve %s, doing late binding", $1);
+            warning("Couldn't resolve %s, doing late binding", $1);
         state->late_binding = 1;
+                
+        multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
 
         $$.t = 0;
-        $$.c = abc_findpropstrict($$.c, $1);
-        $$.c = abc_getproperty($$.c, $1);
+        $$.c = abc_findpropstrict2($$.c, &m);
+        $$.c = abc_getproperty2($$.c, &m);
     }
 }