+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 = flags2namespace(flags, "");
+ 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 = flags2namespace(flags, 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;
+ 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)
+{
+ 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 classinfo_t* find_class(char*name)
+{
+ classinfo_t*c=0;
+
+ c = registry_findclass(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 = (classinfo_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_findclass(l->import->package, name);
+ if(c) return c;
+ l = l->next;
+ }
+
+ /* try global package */
+ c = registry_findclass("", name);
+ if(c) return c;
+
+ /* try local "filename" package */
+ c = registry_findclass(current_filename_short, 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;
+
+ /* generate the write instruction, and maybe append a dup to the prefix code */
+ code_t* write = abc_nop(0);
+ if(r->opcode == OPCODE_GETPROPERTY) {
+ write->opcode = OPCODE_SETPROPERTY;
+ multiname_t*m = (multiname_t*)r->data[0];
+ write->data[0] = multiname_clone(m);
+ if(m->type == QNAME || m->type == MULTINAME) {
+ if(!justassign) {
+ prefix = abc_dup(prefix); // we need the object, too
+ }
+ use_temp_var = 1;
+ } else if(m->type == MULTINAMEL) {
+ if(!justassign) {
+ /* dupping two values on the stack requires 5 operations and one register-
+ couldn't adobe just have given us a dup2? */
+ int temp = gettempvar();
+ prefix = abc_setlocal(prefix, temp);
+ prefix = abc_dup(prefix);
+ prefix = abc_getlocal(prefix, temp);
+ prefix = abc_swap(prefix);
+ prefix = abc_getlocal(prefix, temp);
+ if(!use_temp_var);
+ prefix = abc_kill(prefix, temp);
+ }
+ use_temp_var = 1;
+ } else {
+ syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
+ }
+ } else if(r->opcode == OPCODE_GETSLOT) {
+ write->opcode = OPCODE_SETSLOT;
+ write->data[0] = r->data[0];
+ if(!justassign) {
+ 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];
+ } else if(r->opcode == OPCODE_GETLOCAL_0) {
+ write->opcode = OPCODE_SETLOCAL_0;
+ } else if(r->opcode == OPCODE_GETLOCAL_1) {
+ write->opcode = OPCODE_SETLOCAL_1;
+ } else if(r->opcode == OPCODE_GETLOCAL_2) {
+ write->opcode = OPCODE_SETLOCAL_2;
+ } else if(r->opcode == OPCODE_GETLOCAL_3) {
+ write->opcode = OPCODE_SETLOCAL_3;
+ } else {
+ code_dump(r);
+ syntaxerror("illegal lvalue: can't assign a value to this expression");
+ }
+ code_t* c = 0;
+
+ int temp = -1;
+ if(!justassign) {
+ if(use_temp_var) {
+ /* with getproperty/getslot, we have to be extra careful not
+ to execute the read code twice, as it might have side-effects
+ (e.g. if the property is in fact a setter/getter combination)
+
+ So read the value, modify it, and write it again,
+ using prefix only once and making sure (by using a temporary
+ register) that the return value is what we just wrote */
+ temp = gettempvar();
+ c = code_append(c, prefix);
+ c = code_append(c, r);
+ if(readbefore) {
+ c = abc_dup(c);
+ c = abc_setlocal(c, temp);
+ }
+ c = code_append(c, middlepart);
+ if(!readbefore) {
+ c = abc_dup(c);
+ c = abc_setlocal(c, temp);
+ }
+ c = code_append(c, write);
+ c = abc_getlocal(c, temp);
+ c = abc_kill(c, temp);
+ } else {
+ /* if we're allowed to execute the read code twice *and*
+ the middlepart doesn't modify the code, things are easier.
+ */
+ code_t* r2 = code_dup(r);
+ //c = code_append(c, prefix);
+ parserassert(!prefix);
+ c = code_append(c, r);
+ c = code_append(c, middlepart);
+ c = code_append(c, write);
+ c = code_append(c, r2);
+ }
+ } else {
+ /* even smaller version: overwrite the value without reading
+ it out first */
+ 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 {
+ code_free(r);r=0;
+ temp = gettempvar();
+ if(prefix) {
+ c = code_append(c, prefix);
+ }
+ 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 = abc_kill(c, temp);
+ }
+ }
+ return c;
+}
+
+char is_break_or_jump(code_t*c)
+{
+ if(!c)
+ return 0;
+ if(c->opcode == OPCODE_JUMP ||
+ c->opcode == OPCODE___BREAK__ ||
+ c->opcode == OPCODE___CONTINUE__ ||
+ c->opcode == OPCODE_THROW ||
+ c->opcode == OPCODE_RETURNVOID ||
+ c->opcode == OPCODE_RETURNVALUE) {
+ return 1;
+ }
+ return 0;
+}