%left new2
%left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
-%nonassoc T_IDENTIFIER
+%left T_IDENTIFIER
%left above_identifier
%left below_else
%nonassoc "else"
char is_global;
int variable_count;
+ dict_t*unresolved_variables;
+
char inner;
+ char uses_parent_function;
abc_method_t*abc;
int var_index; // for inner methods
static code_t* method_header(methodstate_t*m);
static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
-static void function_initvars(methodstate_t*m, params_t*params, int flags);
+static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
static char* internal_filename_package = 0;
dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
} else {
state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
- function_initvars(state->method, 0, 0);
+ function_initvars(state->method, 0, 0, 1);
global->init = abc_initscript(global->file);
state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
}
c = abc_iftrue(c,xx);*/
}
-
typedef struct _variable {
int index;
classinfo_t*type;
char init;
+ methodstate_t*method;
} variable_t;
-static variable_t* find_variable(char*name)
+static variable_t* find_variable(state_t*s, char*name)
{
- state_t* s = state;
while(s) {
variable_t*v = 0;
if(s->method)
}
return 0;
}
-static variable_t* find_variable_safe(char*name)
+static variable_t* find_variable_safe(state_t*s, char*name)
{
- variable_t* v = find_variable(name);
+ variable_t* v = find_variable(s, name);
if(!v)
syntaxerror("undefined variable: %s", name);
return v;
}
static char variable_exists(char*name)
{
- return dict_lookup(state->vars, name)!=0;
+ return dict_contains(state->vars, name);
}
code_t*defaultvalue(code_t*c, classinfo_t*type);
static int new_variable(const char*name, classinfo_t*type, char init)
v->index = state->method->variable_count;
v->type = type;
v->init = init;
+ v->method = state->method;
dict_put(state->vars, name, v);
#define TEMPVARNAME "__as3_temp__"
static int gettempvar()
{
- variable_t*v = find_variable(TEMPVARNAME);
+ variable_t*v = find_variable(state, TEMPVARNAME);
if(v)
return v->index;
return new_variable(TEMPVARNAME, 0, 0);
return c;
}
+void unknown_variable(char*name)
+{
+ if(!state->method->unresolved_variables)
+ state->method->unresolved_variables = dict_new();
+ if(!dict_contains(state->method->unresolved_variables, name))
+ dict_put(state->method->unresolved_variables, name, 0);
+}
+
#define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
static void parsererror(const char*file, int line, const char*f)
return access;
}
-static void function_initvars(methodstate_t*m, params_t*params, int flags)
+static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
{
- if(m->inner)
- new_variable("this", 0, 0);
- else if(!m->is_global)
- new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0);
- else
- new_variable("globalscope", 0, 0);
+ if(var0) {
+ int index = -1;
+ if(m->inner)
+ index = new_variable("this", 0, 0);
+ else if(!m->is_global)
+ index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0);
+ else
+ index = new_variable("globalscope", 0, 0);
+ if(index)
+ *(int*)0=0;
+ parserassert(!index);
+ }
if(params) {
param_list_t*p=0;
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);
+
+ function_initvars(state->cls->init, 0, 0, 1);
+ function_initvars(state->cls->static_init, 0, 0, 0);
if(extends && (extends->flags & FLAG_FINAL))
syntaxerror("Can't extend final class '%s'", extends->name);
|| c->type == CONSTANT_UINT);
} else if(TYPE_IS_UINT(t)) {
xassert(c->type == CONSTANT_UINT ||
- (c->type == CONSTANT_INT && c->i>0));
+ (c->type == CONSTANT_INT && c->i>=0));
} else if(TYPE_IS_INT(t)) {
xassert(c->type == CONSTANT_INT);
} else if(TYPE_IS_BOOLEAN(t)) {
methodstate_t*parent_method = state->method;
if(as3_pass==1) {
- // not valid yet
- params = 0;
- return_type = 0;
+ return_type = 0; // not valid in pass 1
}
new_state();
list_append(parent_method->innerfunctions, state->method);
dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
+
+ function_initvars(state->method, params, 0, 1);
}
if(as3_pass == 2) {
state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+ state->method->variable_count = 0;
parserassert(state->method);
state->method->info->return_type = return_type;
- function_initvars(state->method, params, 0);
+ function_initvars(state->method, params, 0, 1);
}
}
return_type = 0;
state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
+
+ function_initvars(state->method, params, flags, 1);
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);
+ state->method->variable_count = 0;
parserassert(state->method);
-
+
if(state->cls) {
memberinfo_t*m = registry_findmember(state->cls->info, name, 2);
check_override(m, flags);
}
state->method->info->return_type = return_type;
- function_initvars(state->method, params, flags);
+ function_initvars(state->method, params, flags, 1);
}
}
params_t*params, classinfo_t*return_type, code_t*body)
{
if(as3_pass==1) {
+ // store inner methods in variables
+ function_initvars(state->method, 0, 0, 0);
+
+ methodstate_list_t*ml = state->method->innerfunctions;
+ while(ml) {
+ methodstate_t*m = ml->methodstate;
+ parserassert(m->inner);
+ if(m->unresolved_variables) {
+ dict_t*d = m->unresolved_variables;
+ int t;
+ for(t=0;t<d->hashsize;t++) {
+ dictentry_t*l = d->slots[t];
+ while(l) {
+ /* check parent method's variables */
+ if(find_variable(state, l->key)) {
+ m->uses_parent_function = 1;
+ break;
+ }
+ l = l->next;
+ }
+ if(l) break;
+ }
+
+ dict_destroy(m->unresolved_variables);
+ m->unresolved_variables = 0;
+ }
+ ml = ml->next;
+ }
old_state();
return 0;
}
- abc_method_t*f = 0;
+ if(as3_pass==2) {
+ if(state->method->uses_parent_function){
+ syntaxerror("accessing variables of parent function from inner functions not supported yet");
+ }
- 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};
+ 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};
- 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
+ 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;
}
- 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");
+ //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");
}
- }
- 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;
}
-
- old_state();
- return f;
+
+ return 0;
}
char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
return c;
if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
return c;
- syntaxerror("can't convert type %s to %s", from->name, to->name);
- return 0; // make gcc happy
+
+ 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)
ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
{
+PASS12
if(variable_exists($1))
syntaxerror("Variable %s already defined", $1);
+PASS1
+ new_variable($1, $2, 1);
+PASS2
if(!is_subtype_of($3.t, $2)) {
syntaxerror("Can't convert %s to %s", $3.t->name,
$2->name);
}
-
int index = new_variable($1, $2, 1);
if($2) {
MAYBEELSE: "else" CODEBLOCK {$$=$2;}
//MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
-IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
+IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
$$ = code_new();
$$ = code_append($$, $4.c);
myjmp->branch = $$ = abc_nop($$);
}
$$ = var_block($$);
- old_state();
+ PASS12 old_state();
}
FOR_INIT : {$$=code_new();}
FOR_INIT : VOIDEXPRESSION
// TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
+// (I don't see any easy way to revolve this conflict otherwise, as we
+// can't touch VAR_READ without upsetting the precedence about "return")
FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
+ PASS12
$$=$2;new_variable($2,$3,1);
}
FOR_IN_INIT : T_IDENTIFIER {
+ PASS12
$$=$1;
}
-FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
-FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
+FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
+FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
myif->branch = out;
$$ = var_block($$);
- old_state();
+ PASS12 old_state();
}
FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
- variable_t*var = find_variable($2);
+ variable_t*var = find_variable(state, $2);
char*tmp1name = concat2($2, "__tmp1__");
int it = new_variable(tmp1name, TYPE_INT, 0);
char*tmp2name = concat2($2, "__array__");
myif->branch = out;
$$ = var_block($$);
- old_state();
free(tmp1name);
free(tmp2name);
+
+ PASS12 old_state();
}
-WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
+WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
$$ = code_new();
continuejumpsto($$, $1, cont);
$$ = var_block($$);
- old_state();
+ PASS12 old_state();
}
-DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
+DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
$$ = code_new();
code_t*loopstart = $$ = abc_label($$);
$$ = code_append($$, $3);
continuejumpsto($$, $1, cont);
$$ = var_block($$);
- old_state();
+ PASS12 old_state();
}
BREAK : "break" %prec prec_none {
DEFAULT: "default" ':' MAYBECODE {
$$ = $3;
}
-SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
+SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
$$=$4.c;
$$ = code_append($$, $7);
code_t*out = $$ = abc_pop($$);
}
$$ = var_block($$);
- old_state();
+ PASS12 old_state();
}
/* ------------ try / catch /finally ---------------- */
-CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);}
+CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();state->exception_name=$3;new_variable($3, $4, 0);}
'{' MAYBECODE '}' {
namespace_t name_ns = {ACCESS_PACKAGE, ""};
multiname_t name = {QNAME, &name_ns, 0, $3};
$$ = e;
code_t*c = 0;
- int i = find_variable_safe($3)->index;
+ int i = find_variable_safe(state, $3)->index;
e->target = c = abc_nop(0);
c = abc_setlocal(c, i);
c = code_append(c, $8);
c = abc_kill(c, i);
c = var_block(c);
- old_state();
+ PASS12 old_state();
}
-FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
+FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
$4 = var_block($4);
if(!$4) {
$$=0;
- old_state();
} else {
NEW(abc_exception_t, e)
e->exc_type = 0; //all exceptions
e->target = 0;
e->to = abc_nop(0);
e->to = code_append(e->to, $4);
- old_state();
$$ = e;
}
+ PASS12 old_state();
}
CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
}
}
-TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
+TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
code_t*out = abc_nop(0);
code_t*start = abc_nop(0);
list_concat(state->method->exceptions, $6.l);
$$ = var_block($$);
- old_state();
+ PASS12 old_state();
}
/* ------------ throw ------------------------------- */
THROW : "throw" %prec prec_none {
if(!state->exception_name)
syntaxerror("re-throw only possible within a catch block");
- variable_t*v = find_variable(state->exception_name);
+ variable_t*v = find_variable(state, state->exception_name);
$$=code_new();
$$=abc_getlocal($$, v->index);
$$=abc_throw($$);
MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
IMPORT : "import" PACKAGEANDCLASS {
- PASS1
- if(!registry_find($2->package, $2->name)) {
+ PASS12
+ slotinfo_t*s = registry_find($2->package, $2->name);
+ if(!s) {// || !(s->flags&FLAG_BUILTIN)) {
as3_schedule_class($2->package, $2->name);
}
$$=0;
}
IMPORT : "import" PACKAGE '.' '*' {
- PASS1
+ PASS12
if(strncmp("flash.", $2, 6)) {
as3_schedule_package($2);
}
}
PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
- PASS1 $$=0;
- PASS2
- $$ = malloc(sizeof(param_t));
+ PASS12
+ $$ = rfx_calloc(sizeof(param_t));
$$->name=$1;
$$->type = $3;
+ PASS2
$$->value = $4;
}
PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
- PASS1 $$=0;
- PASS2
- $$ = malloc(sizeof(param_t));
+ PASS12
+ $$ = rfx_calloc(sizeof(param_t));
$$->name=$1;
$$->type = TYPE_ANY;
+ PASS2
$$->value = $2;
}
GETSET : "get" {$$=$1;}
FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
{
- PASS1 old_state();list_deep_free($6.list);
+ PASS1
+ endfunction(0,$1,$3,$4,&$6,0,0);
PASS2
if(!state->method->info) syntaxerror("internal error");
c = wrap_function(c, 0, $11);
endfunction(0,$1,$3,$4,&$6,$8,c);
+ PASS12
list_deep_free($6.list);
$$=0;
}
INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
'{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
{
- PASS1 old_state();list_deep_free($4.list);
+ PASS1
+ endfunction(0,0,0,$2,&$4,0,0);
PASS2
methodinfo_t*f = state->method->info;
if(!f || !f->kind) syntaxerror("internal error");
int index = state->method->var_index;
endfunction(0,0,0,$2,&$4,$6,c);
- list_deep_free($4.list);
$$.c = abc_getlocal(0, index);
$$.t = TYPE_FUNCTION(f);
+
+ PASS12 list_deep_free($4.list);
}
CLASS: T_IDENTIFIER {
PASS1 $$=0;
PASS2
- /* try current package */
slotinfo_t*s = find_class($1);
- if(!s) syntaxerror("Could not find class/method %s\n", $1);
+ if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
$$ = (classinfo_t*)s;
}
}
VAR_READ : T_IDENTIFIER {
+ PASS1
+ /* Queue unresolved identifiers for checking against the parent
+ function's variables.
+ We consider everything which is not a local variable "unresolved".
+ This encompasses class names, members of the surrounding class
+ etc. which *correct* because local variables of the parent function
+ would shadow those.
+ */
+ if(state->method->inner && !find_variable(state, $1)) {
+ unknown_variable($1);
+ }
+ PASS2
+
$$.t = 0;
$$.c = 0;
slotinfo_t*a = 0;
variable_t*v;
/* look at variables */
- if((v = find_variable($1))) {
+ if((v = find_variable(state, $1))) {
// $1 is a local variable
$$.c = abc_getlocal($$.c, v->index);
$$.t = v->type;