+typedef struct _variable {
+ int index;
+ classinfo_t*type;
+} variable_t;
+
+static int find_variable(char*name, classinfo_t**m)
+{
+ state_list_t* s = state_stack;
+ while(s) {
+ variable_t*v = dict_lookup(s->state->vars, name);
+ if(v) {
+ if(m) {
+ *m = v->type;
+ }
+ return v->index;
+ }
+ s = s->next;
+ }
+ return -1;
+}
+static int find_variable_safe(char*name, classinfo_t**m)
+{
+ int i = find_variable(name, m);
+ if(i<0)
+ syntaxerror("undefined variable: %s", name);
+ return i;
+}
+static char variable_exists(char*name)
+{
+ return dict_lookup(state->vars, name)!=0;
+}
+static int new_variable(char*name, classinfo_t*type)
+{
+ NEW(variable_t, v);
+ v->index = global->variable_count;
+ v->type = type;
+ dict_put(state->vars, name, v);
+ return global->variable_count++;
+}
+#define TEMPVARNAME "__as3_temp__"
+static int gettempvar()
+{
+ int i = find_variable(TEMPVARNAME, 0);
+ if(i<0) {
+ return new_variable(TEMPVARNAME, 0);
+ } else {
+ return i;
+ }
+}
+
+code_t* killvars(code_t*c)
+{
+ int t;
+ for(t=0;t<state->vars->hashsize;t++) {
+ dictentry_t*e =state->vars->slots[t];
+ while(e) {
+ variable_t*v = (variable_t*)e->data;
+ //do this always, otherwise register types don't match
+ //in the verifier when doing nested loops
+ //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
+ c = abc_kill(c, v->index);
+ e = e->next;
+ }
+ }
+ return c;
+}
+
+
+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 startfunction(token_t*ns, token_t*mod, token_t*getset, token_t*name,
+ params_t*params, classinfo_t*type)
+{
+ token_list_t*t;
+ new_state();
+ global->variable_count = 0;
+ state->function = name->text;
+
+ if(state->m) {
+ syntaxerror("not able to start another method scope");
+ }
+
+ multiname_t*type2 = sig2mname(type);
+ if(!strcmp(state->clsinfo->name,name->text)) {
+ state->m = abc_class_constructor(state->cls, type2, 0);
+ } else {
+ state->minfo = memberinfo_register(state->clsinfo, name->text, MEMBER_METHOD);
+ state->minfo->return_type = type;
+ state->m = abc_class_method(state->cls, type2, name->text, 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->type == KW_GET) {
+ state->m->method->trait->kind = TRAIT_GETTER;
+ }
+ if(getset->type == KW_SET) {
+ state->m->method->trait->kind = TRAIT_SETTER;
+ }
+ if(params->varargs) {
+ state->m->method->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(state->m->method->parameters, m);
+ if(p->param->value) {
+ check_constant_against_type(p->param->type, p->param->value);
+ opt=1;list_append(state->m->method->optional_parameters, p->param->value);
+ } else if(opt) {
+ syntaxerror("non-optional parameter not allowed after optional parameters");
+ }
+ }
+
+ /* state->vars is initialized by state_new */
+ if(new_variable("this", state->clsinfo)!=0) syntaxerror("Internal error");
+
+ for(p=params->list;p;p=p->next) {
+ new_variable(p->param->name, p->param->type);
+ }
+}
+static void endfunction(code_t*body)
+{
+ code_t*c = 0;
+ if(state->late_binding) {
+ c = abc_getlocal_0(c);
+ c = abc_pushscope(c);
+ }
+ c = code_append(c, state->initcode);
+ c = code_append(c, body);
+
+ /* append return if necessary */
+ if(!c || c->opcode != OPCODE_RETURNVOID &&
+ c->opcode != OPCODE_RETURNVALUE)
+ c = abc_returnvoid(c);
+
+ if(state->m->code) syntaxerror("internal error");
+ state->m->code = c;
+ old_state();
+}
+
+
+
+char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
+{
+ return 1; // FIXME
+}
+
+void breakjumpsto(code_t*c, code_t*jump)
+{
+ while(c->prev)
+ c=c->prev;
+ while(c) {
+ if(c->opcode == OPCODE___BREAK__) {
+ c->opcode = OPCODE_JUMP;
+ c->branch = jump;
+ }
+ c = c->next;
+ }
+}
+
+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(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) {
+ /*TODO: can omit this if from is zero? */
+ return abc_coerce_a(c);
+ }
+ if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
+ MULTINAME(m, TYPE_UINT);
+ return abc_coerce2(c, &m);
+ }
+ if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
+ MULTINAME(m, TYPE_INT);
+ return abc_coerce2(c, &m);
+ }
+ return c;
+}
+
+code_t*defaultvalue(code_t*c, classinfo_t*type)
+{
+ if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) {
+ c = abc_pushbyte(c, 0);
+ } else if(TYPE_IS_BOOLEAN(type)) {
+ c = abc_pushfalse(c);
+ } else {
+ c = abc_pushnull(c);
+ }
+ return c;
+}
+
+char is_pushundefined(code_t*c)
+{
+ return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
+}
+
+void parserassert(int b)
+{
+ if(!b) syntaxerror("internal error: assertion failed");
+}
+
+static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign)
+{
+ /* 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;
+ }
+
+ /* 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)
+ syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)");
+ if(!justassign) {
+ prefix = abc_dup(prefix); // we need the object, too
+ }
+ } 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
+ }
+ } 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, 0, 0, "", stdout);
+ syntaxerror("illegal lvalue: can't assign a value to this expression");
+ }
+ code_t* c = 0;
+
+ int temp = -1;
+ if(!justassign) {
+#if 1
+ /* 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);
+ 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);
+#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);
+#endif
+ } else {
+ /* even smaller version: overwrite the value without reading
+ it out first */
+ 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);
+ }
+
+ return c;
+}
+
+