X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=9940fa56fac18165d477389f5ced95545a0556f5;hb=d730cd0a68888a4bee1484276735db5185ed7c5b;hp=cd7b1f47fabd6ee1a0776986576bd4d095a66959;hpb=2e94d57b5a3d15718bad6c1c1b0342acb9362770;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index cd7b1f4..9940fa5 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -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); } @@ -844,6 +928,40 @@ void parserassert(int b) if(!b) syntaxerror("internal error: assertion failed"); } +static classinfo_t* find_class(char*name) +{ + classinfo_t*c=0; + + c = registry_findclass(state->package, name); + + /* try explicit imports */ + dictentry_t* e = dict_get_slot(state->imports, name); + while(e) { + if(c) + break; + if(!strcmp(e->key, name)) { + c = (classinfo_t*)e->data; + } + e = e->next; + } + + /* try package.* imports */ + import_list_t*l = state->wildcard_imports; + while(l) { + if(c) + break; + //printf("does package %s contain a class %s?\n", l->import->package, name); + c = registry_findclass(l->import->package, name); + l = l->next; + } + + /* try global package */ + if(!c) { + c = registry_findclass("", name); + } + return c; +} + static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore) { /* converts this: @@ -879,19 +997,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 +1067,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 +1336,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 +1347,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; } } @@ -1317,34 +1466,7 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P CLASS: T_IDENTIFIER { /* try current package */ - $$ = registry_findclass(state->package, $1); - - /* try explicit imports */ - dictentry_t* e = dict_get_slot(state->imports, $1); - while(e) { - if($$) - break; - if(!strcmp(e->key, $1)) { - $$ = (classinfo_t*)e->data; - } - e = e->next; - } - - /* try package.* imports */ - import_list_t*l = state->wildcard_imports; - while(l) { - if($$) - break; - //printf("does package %s contain a class %s?\n", l->import->package, $1); - $$ = registry_findclass(l->import->package, $1); - l = l->next; - } - - /* try global package */ - if(!$$) { - $$ = registry_findclass("", $1); - } - + $$ = find_class($1); if(!$$) syntaxerror("Could not find class %s\n", $1); } @@ -1390,8 +1512,12 @@ NEW : "new" CLASS MAYBE_PARAM_VALUES { MULTINAME(m, $2); $$.c = code_new(); - /* TODO: why do we have to *find* our own classes? */ - $$.c = abc_findpropstrict2($$.c, &m); + if($2->slot) { + $$.c = abc_getglobalscope($$.c); + $$.c = abc_getslot($$.c, $2->slot); + } else { + $$.c = abc_findpropstrict2($$.c, &m); + } typedcode_list_t*l = $3; int len = 0; @@ -1400,7 +1526,10 @@ NEW : "new" CLASS MAYBE_PARAM_VALUES { l = l->next; len ++; } - $$.c = abc_constructprop2($$.c, &m, len); + if($2->slot) + $$.c = abc_construct($$.c, len); + else + $$.c = abc_constructprop2($$.c, &m, len); $$.t = $2; } @@ -1675,6 +1804,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 +1817,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 +1831,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 +1845,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; @@ -1724,15 +1857,31 @@ E : "--" E { code_t*c = 0; E : E '.' T_IDENTIFIER {$$.c = $1.c; - if($$.t) { - memberinfo_t*f = registry_findmember($$.t, $3); + classinfo_t*t = $1.t; + char is_static = 0; + if(TYPE_IS_CLASS(t)) { + memberinfo_t*m = registry_findmember($1.t, "prototype"); + if(!m) syntaxerror("identifier '%s' not found in anonymous class", $3); + t = m->type; + is_static = 1; + } + if(t) { + memberinfo_t*f = registry_findmember(t, $3); + char noslot = 0; + if(f && !is_static != !(f->flags&FLAG_STATIC)) + noslot=1; - if(f && f->slot) { + if(f && f->slot && !noslot) { $$.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 +1895,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 +1909,67 @@ 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 and imported classes */ + } else if((a = find_class($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); } + $$.t = TYPE_CLASS(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); } }