+ if(as3_pass==1) {
+ state->cls = rfx_calloc(sizeof(classstate_t));
+ state->cls->init = rfx_calloc(sizeof(methodstate_t));
+ state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
+ /* notice: we make no effort to initialize the top variable (local0) here,
+ even though it has special meaning. We just rely on the facat
+ that pass 1 won't do anything with variables */
+
+ dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
+
+ /* set current method to constructor- all code within the class-level (except
+ static variable initializations) will be executed during construction time */
+ state->method = state->cls->init;
+
+ if(registry_find(package, classname)) {
+ syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
+ }
+ /* build info struct */
+ int num_interfaces = (list_length(implements));
+ state->cls->info = classinfo_register(access, package, classname, num_interfaces);
+ state->cls->info->flags |= flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
+ }
+
+ if(as3_pass == 2) {
+ state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+
+ state->method = state->cls->init;
+ parserassert(state->cls && state->cls->info);
+
+ function_initvars(state->cls->init, 0, 0);
+ function_initvars(state->cls->static_init, 0, 0);
+
+ if(extends && (extends->flags & FLAG_FINAL))
+ syntaxerror("Can't extend final class '%s'", extends->name);
+
+ /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
+ state->cls->info->superclass = extends?extends:TYPE_OBJECT;
+ int pos = 0;
+ classinfo_list_t*l = implements;
+ for(l=implements;l;l=l->next) {
+ if(!(l->classinfo->flags & FLAG_INTERFACE))
+ syntaxerror("'%s' is not an interface", l->classinfo->name);
+ state->cls->info->interfaces[pos++] = l->classinfo;
+ }
+
+ /* generate the abc code for this class */
+ MULTINAME(classname2,state->cls->info);
+ multiname_t*extends2 = sig2mname(extends);
+
+ state->cls->abc = abc_class_new(global->file, &classname2, extends2);
+ if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
+ if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
+ if(state->cls->info->flags&FLAG_INTERFACE) {
+ abc_class_interface(state->cls->abc);
+ }
+
+ abc_class_protectedNS(state->cls->abc, classname);
+
+ for(mlist=implements;mlist;mlist=mlist->next) {
+ MULTINAME(m, mlist->classinfo);
+ abc_class_add_interface(state->cls->abc, &m);
+ }
+
+ /* write the construction code for this class to the global init
+ function */
+ int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
+
+ abc_method_body_t*m = global->init->method->body;
+ __ getglobalscope(m);
+ classinfo_t*s = extends;
+
+ int count=0;
+
+ while(s) {
+ //TODO: take a look at the current scope stack, maybe
+ // we can re-use something
+ s = s->superclass;
+ if(!s)
+ break;
+
+ multiname_t*s2 = sig2mname(s);
+ __ getlex2(m, s2);
+ multiname_destroy(s2);
+
+ __ pushscope(m); count++;
+ m->code = m->code->prev->prev; // invert
+ }
+ /* continue appending after last op end */
+ while(m->code && m->code->next) m->code = m->code->next;
+
+ /* TODO: if this is one of *our* classes, we can also
+ do a getglobalscope/getslot <nr> (which references
+ the init function's slots) */
+ if(extends2) {
+ __ getlex2(m, extends2);
+ __ dup(m);
+ /* notice: we get a Verify Error #1107 if the top elemnt on the scope
+ stack is not the superclass */
+ __ pushscope(m);count++;
+ } else {
+ __ pushnull(m);
+ /* notice: we get a verify error #1107 if the top element on the scope
+ stack is not the global object */
+ __ getlocal_0(m);
+ __ pushscope(m);count++;
+ }
+ __ newclass(m,state->cls->abc);
+ while(count--) {
+ __ popscope(m);
+ }
+ __ setslot(m, slotindex);
+ multiname_destroy(extends2);
+
+ /* flash.display.MovieClip handling */
+
+ if(!as3_globalclass && (flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
+ if(state->package && state->package[0]) {
+ as3_globalclass = concat3(state->package, ".", classname);
+ } else {
+ as3_globalclass = strdup(classname);
+ }
+ }
+ }
+}
+
+static void setstaticfunction(int x)
+{
+ if(state->cls) {
+ if(x&FLAG_STATIC) {
+ state->method = state->cls->static_init;
+ } else {
+ state->method = state->cls->init;
+ }
+ } else {
+ parserassert(state->method);
+ }
+}
+
+static void endclass()
+{
+ if(as3_pass == 2) {
+ if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
+ code_t*c = 0;
+ c = abc_getlocal_0(c);
+ c = abc_constructsuper(c, 0);
+ state->cls->init->header = code_append(state->cls->init->header, c);
+ state->cls->has_constructor=1;
+ }
+ if(state->cls->init) {
+ if(state->cls->info->flags&FLAG_INTERFACE) {
+ if(state->cls->init->header)
+ syntaxerror("interface can not have class-level code");
+ } else {
+ abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
+ code_t*c = method_header(state->cls->init);
+ m->body->code = wrap_function(c, 0, m->body->code);
+ }
+ }
+ if(state->cls->static_init) {
+ abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
+ code_t*c = method_header(state->cls->static_init);
+ m->body->code = wrap_function(c, 0, m->body->code);
+ }
+ }
+
+ old_state();
+}
+
+void check_code_for_break(code_t*c)
+{
+ while(c) {
+ if(c->opcode == OPCODE___BREAK__) {
+ char*name = string_cstr(c->data[0]);
+ syntaxerror("Unresolved \"break %s\"", name);
+ }
+ if(c->opcode == OPCODE___CONTINUE__) {
+ char*name = string_cstr(c->data[0]);
+ syntaxerror("Unresolved \"continue %s\"", name);
+ }
+ c=c->prev;
+ }
+}
+
+
+static void check_constant_against_type(classinfo_t*t, constant_t*c)
+{
+#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
+ || c->type == CONSTANT_INT
+ || c->type == CONSTANT_UINT);
+ } else if(TYPE_IS_UINT(t)) {
+ xassert(c->type == CONSTANT_UINT ||
+ (c->type == CONSTANT_INT && c->i>=0));
+ } else if(TYPE_IS_INT(t)) {
+ xassert(c->type == CONSTANT_INT);
+ } else if(TYPE_IS_BOOLEAN(t)) {
+ xassert(c->type == CONSTANT_TRUE
+ || c->type == CONSTANT_FALSE);
+ }
+}
+
+static void check_override(memberinfo_t*m, int flags)
+{
+ if(!m)
+ return;
+ if(m->parent == state->cls->info)
+ syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
+ if(!m->parent)
+ syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
+ if(m->access==ACCESS_PRIVATE)
+ return;
+ if(m->flags & FLAG_FINAL)
+ syntaxerror("can't override final member %s", m->name);
+ if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
+ syntaxerror("can't override static member %s", m->name);
+ if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
+ syntaxerror("can't override non-static member %s with static declaration", m->name);
+
+ if(!(flags&FLAG_OVERRIDE)) {
+ if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
+ if(m->kind == INFOTYPE_METHOD)
+ syntaxerror("can't override without explicit 'override' declaration");
+ else
+ syntaxerror("can't override '%s'", m->name);
+ }
+ }
+}
+
+static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
+{
+ methodinfo_t*minfo = 0;
+ U8 access = flags2access(flags);
+ if(!state->cls) {
+ //package method
+ minfo = methodinfo_register_global(access, state->package, name);
+ minfo->return_type = return_type;
+ } else if(getset != KW_GET && getset != KW_SET) {
+ //class method
+ memberinfo_t* m = registry_findmember(state->cls->info, name, 0);
+ if(m) {
+ syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
+ }
+ minfo = methodinfo_register_onclass(state->cls->info, access, name);
+ minfo->return_type = return_type;
+ // getslot on a member slot only returns "undefined", so no need
+ // to actually store these
+ //state->minfo->slot = state->method->abc->method->trait->slot_id;
+ } else {
+ //class getter/setter
+ int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
+ classinfo_t*type=0;
+ if(getset == KW_GET)
+ type = return_type;
+ else if(params->list && params->list->param)
+ type = params->list->param->type;
+ // not sure wether to look into superclasses here, too
+ minfo = (methodinfo_t*)registry_findmember(state->cls->info, name, 1);
+ if(minfo) {
+ if(minfo->kind!=INFOTYPE_SLOT)
+ syntaxerror("class already contains a method called '%s'", name);
+ if(!(minfo->subtype & (SUBTYPE_GETSET)))
+ syntaxerror("class already contains a field called '%s'", name);
+ if(minfo->subtype & gs)
+ syntaxerror("getter/setter for '%s' already defined", name);
+ /* make a setter or getter into a getset */
+ minfo->subtype |= gs;
+ if(!minfo->return_type) {
+ minfo->return_type = type;
+ } else {
+ if(minfo && minfo->return_type != type)
+ syntaxerror("different type in getter and setter");
+ }
+ } else {
+ minfo = methodinfo_register_onclass(state->cls->info, access, name);
+ minfo->kind = INFOTYPE_SLOT; //hack
+ minfo->subtype = gs;
+ minfo->return_type = type;
+ }
+ /* can't assign a slot as getter and setter might have different slots */
+ //minfo->slot = slot;
+ }
+ if(flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
+ if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
+ if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
+ return minfo;
+}
+
+static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
+{
+ //parserassert(state->method && state->method->info);
+
+ methodstate_t*parent_method = state->method;
+
+ if(as3_pass==1) {
+ // not valid yet
+ params = 0;
+ return_type = 0;
+ }
+
+ new_state();
+ state->new_vars = 1;
+
+ if(as3_pass == 1) {
+ 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(methodinfo_t,minfo);
+ minfo->kind = INFOTYPE_METHOD;
+ minfo->access = ACCESS_PACKAGEINTERNAL;
+ minfo->name = name;
+ state->method->info = minfo;
+
+ if(parent_method)
+ list_append(parent_method->innerfunctions, state->method);
+
+ dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
+ }
+
+ if(as3_pass == 2) {
+ state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+ parserassert(state->method);
+
+ state->method->info->return_type = return_type;
+ function_initvars(state->method, params, 0);
+ }
+}
+
+static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
+ params_t*params, classinfo_t*return_type)
+{
+ if(state->method && state->method->info) {
+ syntaxerror("not able to start another method scope");
+ }
+ new_state();
+
+ if(as3_pass == 1) {
+ state->method = rfx_calloc(sizeof(methodstate_t));
+ state->method->has_super = 0;
+
+ 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__";
+
+ 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) {
+ state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+ parserassert(state->method);
+
+ if(state->cls) {
+ memberinfo_t*m = registry_findmember(state->cls->info, name, 2);
+ check_override(m, flags);
+ }
+
+ if(state->cls) {
+ state->cls->has_constructor |= state->method->is_constructor;
+ }
+
+ state->method->info->return_type = return_type;
+ function_initvars(state->method, params, flags);
+ }
+}
+
+static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
+ params_t*params, classinfo_t*return_type, code_t*body)
+{
+ if(as3_pass==1) {
+ old_state();
+ return 0;
+ }
+
+ abc_method_t*f = 0;
+
+ multiname_t*type2 = sig2mname(return_type);
+ int slot = 0;
+ if(state->method->inner) {
+ 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) {
+ namespace_t mname_ns = {state->method->info->access, ""};
+ multiname_t mname = {QNAME, &mname_ns, 0, name};
+
+ if(flags&FLAG_STATIC)
+ f = abc_class_staticmethod(state->cls->abc, type2, &mname);
+ else
+ f = abc_class_method(state->cls->abc, type2, &mname);
+ slot = f->trait->slot_id;
+ } else {
+ namespace_t mname_ns = {state->method->info->access, state->package};
+ multiname_t mname = {QNAME, &mname_ns, 0, name};
+
+ f = abc_method_new(global->file, type2, 1);
+ trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
+ //abc_code_t*c = global->init->method->body->code;
+ }
+ //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(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;
+
+ char opt=0;
+ param_list_t*p=0;
+ for(p=params->list;p;p=p->next) {
+ if(params->varargs && !p->next) {
+ break; //varargs: omit last parameter in function signature
+ }
+ multiname_t*m = sig2mname(p->param->type);
+ list_append(f->parameters, m);
+ if(p->param->value) {
+ check_constant_against_type(p->param->type, p->param->value);
+ opt=1;list_append(f->optional_parameters, p->param->value);
+ } else if(opt) {
+ syntaxerror("non-optional parameter not allowed after optional parameters");
+ }
+ }
+ check_code_for_break(body);
+
+ if(f->body) {
+ f->body->code = body;
+ f->body->exceptions = state->method->exceptions;
+ } else { //interface
+ if(body)
+ syntaxerror("interface methods can't have a method body");
+ }
+
+ old_state();
+ return f;
+}
+
+char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
+{
+ return 1; // FIXME
+}
+
+void breakjumpsto(code_t*c, char*name, code_t*jump)
+{
+ while(c) {
+ if(c->opcode == OPCODE___BREAK__) {
+ string_t*name2 = c->data[0];
+ if(!name2->len || !strncmp(name2->str, name, name2->len)) {
+ c->opcode = OPCODE_JUMP;
+ c->branch = jump;
+ }
+ }
+ c=c->prev;
+ }
+}
+void continuejumpsto(code_t*c, char*name, code_t*jump)
+{
+ while(c) {
+ if(c->opcode == OPCODE___CONTINUE__) {
+ string_t*name2 = c->data[0];
+ if(!name2->len || !strncmp(name2->str, name, name2->len)) {
+ c->opcode = OPCODE_JUMP;
+ c->branch = jump;
+ }
+ }
+ c = c->prev;
+ }
+}
+
+#define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
+#define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
+#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
+
+classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
+{
+ if(!type1 || !type2)
+ return registry_getanytype();
+ if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
+ return registry_getanytype();
+
+ if(op=='+') {
+ if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
+ return TYPE_NUMBER;
+ } else {
+ return TYPE_ANY;
+ }
+ }
+
+ if(type1 == type2)
+ return type1;
+ return registry_getanytype();
+}
+code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
+{
+ if(from==to)
+ return c;
+ if(!to) {
+ return abc_coerce_a(c);
+ }
+ MULTINAME(m, to);
+ if(!from) {
+ // cast an "any" type to a specific type. subject to
+ // runtime exceptions
+ return abc_coerce2(c, &m);
+ }
+
+ if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
+ (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
+ // allow conversion between number types
+ return abc_coerce2(c, &m);
+ }
+ //printf("%s.%s\n", from.package, from.name);
+ //printf("%s.%s\n", to.package, to.name);
+
+ classinfo_t*supertype = from;
+ while(supertype) {
+ if(supertype == to) {
+ // target type is one of from's superclasses
+ return abc_coerce2(c, &m);
+ }
+ int t=0;
+ while(supertype->interfaces[t]) {
+ if(supertype->interfaces[t]==to) {
+ // target type is one of from's interfaces
+ return abc_coerce2(c, &m);
+ }
+ t++;
+ }
+ supertype = supertype->superclass;
+ }
+ if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
+ return c;
+ if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
+ return c;
+ if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
+ return c;
+
+ as3_error("can't convert type %s%s%s to %s%s%s",
+ from->package, from->package?".":"", from->name,
+ to->package, to->package?".":"", to->name);
+ return c;
+}
+
+code_t*defaultvalue(code_t*c, classinfo_t*type)
+{
+ if(TYPE_IS_INT(type)) {
+ c = abc_pushbyte(c, 0);
+ } else if(TYPE_IS_UINT(type)) {
+ c = abc_pushuint(c, 0);
+ } else if(TYPE_IS_FLOAT(type)) {
+ c = abc_pushnan(c);
+ } else if(TYPE_IS_BOOLEAN(type)) {
+ c = abc_pushfalse(c);
+ } else if(!type) {
+ //c = abc_pushundefined(c);
+ } else {
+ c = abc_pushnull(c);
+ MULTINAME(m, type);
+ c = abc_coerce2(c, &m);
+ }
+ return c;
+}
+
+char is_pushundefined(code_t*c)
+{
+ return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
+}
+
+static slotinfo_t* find_class(char*name)
+{
+ slotinfo_t*c=0;
+
+ c = registry_find(state->package, name);
+ if(c) return c;
+
+ /* try explicit imports */
+ dictentry_t* e = dict_get_slot(state->imports, name);
+ if(c) return c;
+ while(e) {
+ if(!strcmp(e->key, name)) {
+ c = (slotinfo_t*)e->data;
+ if(c) return c;
+ }
+ e = e->next;
+ }
+
+ /* try package.* imports */
+ import_list_t*l = state->wildcard_imports;
+ while(l) {
+ //printf("does package %s contain a class %s?\n", l->import->package, name);
+ c = registry_find(l->import->package, name);
+ if(c) return c;
+ l = l->next;
+ }
+
+ /* try global package */
+ c = registry_find("", name);
+ if(c) return c;
+
+ /* try local "filename" package */
+ c = registry_find(internal_filename_package, name);
+ if(c) return c;
+
+ return 0;
+}
+
+static char is_getlocal(code_t*c)
+{
+ if(!c || c->prev || c->next)
+ return 0;
+ return(c->opcode == OPCODE_GETLOCAL
+ || c->opcode == OPCODE_GETLOCAL_0
+ || c->opcode == OPCODE_GETLOCAL_1
+ || c->opcode == OPCODE_GETLOCAL_2
+ || c->opcode == OPCODE_GETLOCAL_3);
+}
+static int getlocalnr(code_t*c)
+{
+ if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
+ else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
+ else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
+ 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)
+{
+ /* converts this:
+
+ [prefix code] [read instruction]
+
+ to this:
+
+ [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
+ */
+ if(in && in->opcode == OPCODE_COERCE_A) {
+ in = code_cutlast(in);
+ }
+ if(in->next)
+ syntaxerror("internal error");
+
+ /* chop off read instruction */
+ code_t*prefix = in;
+ code_t*r = in;
+ if(r->prev) {
+ prefix = r->prev;r->prev = 0;
+ prefix->next=0;
+ } else {
+ prefix = 0;
+ }
+
+ char use_temp_var = readbefore;
+