%nonassoc "false" "true" "null" "undefined" "super" "function"
%left above_function
-
%{
methodstate_list_t*innerfunctions;
};
+void methodstate_destroy(methodstate_t*m)
+{
+ dict_destroy(m->unresolved_variables);
+ m->unresolved_variables = 0;
+ list_free(m->innerfunctions);m->innerfunctions=0;
+}
+
typedef struct _state {
struct _state*old;
int level;
state = state->old;
if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
- free(leaving->method);
- leaving->method=0;
+ methodstate_destroy(leaving->method);leaving->method=0;
}
if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
free(leaving->cls);
methodstate_t*is_inner_method;
} variable_t;
-static variable_t* find_variable(state_t*s, char*name)
+static variable_t* find_variable(state_t*s, const char*name)
{
if(s->method->no_variable_scoping) {
return dict_lookup(s->allvars, name);
return 0;
}
}
-static variable_t* find_slot(state_t*s, const char*name)
+static variable_t* find_slot(methodstate_t*m, const char*name)
{
- if(s->method && s->method->slots)
- return dict_lookup(s->method->slots, name);
+ if(m && m->slots)
+ return dict_lookup(m->slots, name);
return 0;
}
return state->method->variable_count++;
}
-static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
+static variable_t* new_variable2(methodstate_t*method, const char*name, classinfo_t*type, char init, char maybeslot)
{
if(maybeslot) {
- variable_t*v = find_slot(state, name);
+ variable_t*v = find_slot(method, name);
if(v) {
alloc_local();
return v;
v->init = v->kill = init;
if(name) {
- if(!state->method->no_variable_scoping)
+ if(!method->no_variable_scoping)
{
- if(dict_contains(state->vars, name))
+ if(dict_contains(state->vars, name)) {
+ *(int*)0=0;
syntaxerror("variable %s already defined", name);
+ }
dict_put(state->vars, name, v);
}
- if(state->method->no_variable_scoping &&
+ if(method->no_variable_scoping &&
as3_pass==2 &&
dict_contains(state->allvars, name))
{
return v;
}
-static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
+static int new_variable(methodstate_t*method, const char*name, classinfo_t*type, char init, char maybeslot)
{
- return new_variable2(name, type, init, maybeslot)->index;
+ return new_variable2(method, name, type, init, maybeslot)->index;
}
#define TEMPVARNAME "__as3_temp__"
if(v)
i = v->index;
else
- i = new_variable(TEMPVARNAME, 0, 0, 0);
+ i = new_variable(state->method, TEMPVARNAME, 0, 0, 0);
parserassert(i);
return i;
}
static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
{
- if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
+ if(m->uses_slots || m->innerfunctions || (m->late_binding && !m->inner)) {
c = abc_getlocal_0(c);
c = abc_pushscope(c);
}
syntaxerror("invalid combination of access levels and namespaces");
ns.access = ACCESS_NAMESPACE;
state_t*s = state;
- const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
+ const char*url = (const char*)trie_lookup(active_namespaces, (unsigned char*)mod->ns);
if(!url) {
/* shouldn't happen- the tokenizer only reports something as a namespace
if it was already registered */
}
static slotinfo_t* find_class(const char*name);
-static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
+static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse, char is_static)
{
- return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
+ return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse, is_static);
}
static void innerfunctions2vars(methodstate_t*m)
while(l) {
methodstate_t*m = l->methodstate;
- variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 0);
+ variable_t* v = new_variable2(state->method, m->info->name, TYPE_FUNCTION(m->info), 0, 0);
m->var_index = v->index;
if(m->is_a_slot)
m->slot_index = m->is_a_slot;
if(var0) {
int index = -1;
if(m->inner)
- index = new_variable("this", 0, 0, 0);
+ index = new_variable(m, "this", 0, 0, 0);
else if(!m->is_global)
- index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
+ index = new_variable(m, (flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
else
- index = new_variable("globalscope", 0, 0, 0);
+ index = new_variable(m, "globalscope", 0, 0, 0);
+ if(index) {
+ DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
+ printf("%s %d\n", name, v->index);
+ }
+ }
parserassert(!index);
}
if(has_params) {
param_list_t*p=0;
for(p=params->list;p;p=p->next) {
- variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1);
+ variable_t*v = new_variable2(m, p->param->name, p->param->type, 0, 1);
v->is_parameter = 1;
}
if(as3_pass==2 && m->need_arguments) {
/* arguments can never be used by an innerfunction (the inner functions
have their own arguments var), so it's ok to not initialize this until
pass 2. (We don't know whether we need it before, anyway) */
- variable_t*v = new_variable2("arguments", TYPE_ARRAY, 0, 0);
+ variable_t*v = new_variable2(m, "arguments", TYPE_ARRAY, 0, 0);
m->need_arguments = v->index;
}
}
state->cls->init = rfx_calloc(sizeof(methodstate_t));
state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
state->cls->static_init->is_static=FLAG_STATIC;
- state->cls->static_init->variable_count=1;
/* notice: we make no effort to initialize the top variable (local0) here,
even though it has special meaning. We just rely on the fact
that pass 1 won't do anything with variables */
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);
+ state->method = state->cls->static_init;
+
function_initvars(state->cls->init, 0, 0, 0, 1);
+ state->cls->static_init->variable_count=1;
function_initvars(state->cls->static_init, 0, 0, 0, 0);
if(extends && (extends->flags & FLAG_FINAL))
{
if(!m)
return;
- if(m->parent == state->cls->info)
+ if(m->parent == state->cls->info && !((flags^m->flags)&FLAG_STATIC))
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);
minfo->return_type = return_type;
} else if(getset != KW_GET && getset != KW_SET) {
//class method
- memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
+ memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0, mod->flags&FLAG_STATIC);
if(m) {
syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
}
- minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
+ minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name, mod->flags&FLAG_STATIC);
minfo->return_type = return_type;
// getslot on a member slot only returns "undefined", so no need
// to actually store these
} else
syntaxerror("setter function needs to take exactly one argument");
// not sure wether to look into superclasses here, too
- minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
+ minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1, mod->flags&FLAG_STATIC);
if(minfo) {
if(minfo->kind!=INFOTYPE_VAR)
syntaxerror("class already contains a method called '%s'", name);
type?type->name:"*");
}*/
} else {
- minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
+ minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name, mod->flags&FLAG_STATIC);
minfo->kind = INFOTYPE_VAR; //hack
minfo->subtype = gs;
minfo->return_type = type;
//parserassert(state->method && state->method->info);
methodstate_t*parent_method = state->method;
+ variable_t*v = 0;
if(as3_pass==1) {
return_type = 0; // not valid in pass 1
+ if(name) {
+ v = new_variable2(parent_method, name, 0, 0, 0);
+ }
}
new_state();
state->method->is_static = parent_method->is_static;
state->method->variable_count = 0;
state->method->abc = rfx_calloc(sizeof(abc_method_t));
+ if(v) {
+ v->is_inner_method = state->method;
+ }
NEW(methodinfo_t,minfo);
minfo->kind = INFOTYPE_METHOD;
parserassert(state->method);
if(state->cls) {
- memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
+ memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2, mod->flags&FLAG_STATIC);
check_override(m, mod->flags);
}
}
}
+static void insert_unresolved(methodstate_t*m, dict_t*xvars, dict_t*allvars)
+{
+ parserassert(m->inner);
+ if(m->unresolved_variables) {
+ dict_t*d = m->unresolved_variables;
+ int t;
+ DICT_ITERATE_KEY(d, char*, id) {
+ /* check parent method's variables */
+ variable_t*v;
+ if(dict_contains(allvars, id)) {
+ m->uses_parent_function = 1;
+ state->method->uses_slots = 1;
+ dict_put(xvars, id, 0);
+ }
+ }
+ }
+ methodstate_list_t*ml = m->innerfunctions;
+ while(ml) {
+ insert_unresolved(ml->methodstate, xvars, allvars);
+ ml = ml->next;
+ }
+}
+
static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
params_t*params, classinfo_t*return_type, code_t*body)
{
if(as3_pass==1) {
- innerfunctions2vars(state->method);
-
- methodstate_list_t*ml = state->method->innerfunctions;
-
dict_t*xvars = dict_new();
if(state->method->unresolved_variables) {
DICT_ITERATE_KEY(state->method->unresolved_variables, char*, vname) {
- if(dict_contains(state->allvars, vname)) {
- state->method->no_variable_scoping = 1;
- as3_warning("function %s uses forward or outer block variable references (%s): switching into compatiblity mode", name, vname);
- break;
+ if(!state->method->no_variable_scoping && dict_contains(state->allvars, vname)) {
+ variable_t*v = dict_lookup(state->allvars, vname);
+ if(!v->is_inner_method) {
+ state->method->no_variable_scoping = 1;
+ as3_warning("function %s uses forward or outer block variable references (%s): switching into compatiblity mode", name, vname);
+ }
}
}
}
+ 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;
- DICT_ITERATE_KEY(d, char*, id) {
- /* check parent method's variables */
- variable_t*v;
- if((v=find_variable(state, id))) {
- m->uses_parent_function = 1;
- state->method->uses_slots = 1;
- dict_put(xvars, id, 0);
- }
- }
- dict_destroy(m->unresolved_variables);
- m->unresolved_variables = 0;
- }
+ insert_unresolved(ml->methodstate, xvars, state->allvars);
ml = ml->next;
}
if(state->method->uses_slots) {
state->method->slots = dict_new();
int i = 1;
- DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
+ DICT_ITERATE_ITEMS(state->allvars, char*, name, variable_t*, v) {
if(!name) syntaxerror("internal error");
if(v->index && dict_contains(xvars, name)) {
v->init = v->kill = 0;
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[0]?".":"", from->name,
to->package, to->package[0]?".":"", to->name);
ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
{
PASS12
- if(variable_exists($1))
+ if(variable_exists($1))
syntaxerror("Variable %s already defined", $1);
PASS1
- new_variable($1, 0, 1, 0);
+ new_variable(state->method, $1, 0, 1, 0);
PASS2
char slot = 0;
int index = 0;
variable_t*v = 0;
if(state->method->uses_slots) {
- v = find_slot(state, $1);
+ v = find_slot(state->method, $1);
if(v && !v->init) {
// this variable is stored in a slot
v->init = 1;
}
}
if(!v) {
- v = new_variable2($1, $2, 1, 0);
+ v = new_variable2(state->method, $1, $2, 1, 0);
}
$$ = slot?abc_getscopeobject(0, 1):0;
// (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 {
- PASS1 $$=$2;new_variable($2,0,1,0);
- PASS2 $$=$2;new_variable($2,$3,1,0);
+ PASS1 $$=$2;new_variable(state->method, $2,0,1,0);
+ PASS2 $$=$2;new_variable(state->method, $2,$3,1,0);
}
FOR_IN_INIT : T_IDENTIFIER {
PASS12
}
FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' IF_CODEBLOCK {
- variable_t*var = find_variable(state, $2);
- if(!var) {
- syntaxerror("variable %s not known in this scope", $2);
- }
+ node_t*n = resolve_identifier($2);
+ typedcode_t w = node_write(n);
+
int it = alloc_local();
int array = alloc_local();
$$ = abc_nextname($$);
else
$$ = abc_nextvalue($$);
- $$ = converttype($$, 0, var->type);
- $$ = abc_setlocal($$, var->index);
+
+ $$ = converttype($$, 0, w.t);
+ $$ = code_append($$, w.c);
$$ = code_append($$, $6);
$$ = abc_jump($$, loopstart);
$$ = abc_kill($$, it);
$$ = abc_kill($$, array);
+ $$ = var_block($$, state->vars);
PASS12 old_state();
}
CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
state->exception_name=$3;
- PASS1 new_variable($3, 0, 0, 0);
- PASS2 new_variable($3, $4, 0, 0);
+ PASS1 new_variable(state->method, $3, 0, 0, 0);
+ PASS2 new_variable(state->method, $3, $4, 0, 0);
}
'{' MAYBECODE '}' {
namespace_t name_ns = {ACCESS_PACKAGE, ""};
varinfo_t* info = 0;
if(state->cls) {
- memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
+ memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1, slotstate_flags->flags&FLAG_STATIC);
if(i) {
check_override(i, flags);
}
- info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
+ info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1, slotstate_flags->flags&FLAG_STATIC);
} else {
slotinfo_t*i = registry_find(state->package, $1);
if(i) {
$$.c = code_append($$.c, paramcode);
$$.c = abc_constructprop2($$.c, name, $4.number);
multiname_destroy(name);
+ } else if(is_getlocal($$.c)) {
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_construct($$.c, $4.number);
} else if(TYPE_IS_CLASS(v.t) && v.t->data) {
code_free($$.c);
classinfo_t*c = v.t->data;
syntaxerror("super keyword not allowed outside a class");
classinfo_t*t = state->cls->info->superclass;
if(!t) t = TYPE_OBJECT;
- memberinfo_t*f = findmember_nsset(t, $3, 1);
+ memberinfo_t*f = findmember_nsset(t, $3, 1, 0);
MEMBER_MULTINAME(m, f, $3);
typedcode_t v;
v.c = 0;
SUBNODE: X_IDENTIFIER
| '*' {$$="*";}
-/*
-MAYBE_NS: T_IDENTIFIER "::" {$$=$1;}
- | T_NAMESPACE "::" {$$=(char*)$1;}
- | '*' "::" {$$="*";}
- | {$$=0;}*/
+%code {
+ node_t* resolve_identifier(const char*name);
+ node_t* get_descendants(node_t*e,const char*ns,const char*subnode,char multi, char attr)
+ {
+ typedcode_t v = node_read(e);
+ typedcode_t w;
+
+ multiname_t m = {0,0,0,subnode};
+ namespace_t zero = {ZERONAMESPACE,"*"};
+ if(!strcmp(ns,"*")) {
+ m.ns = &zero;
+ m.type = attr?QNAMEA:QNAME;
+ } else {
+ typedcode_t w = node_read(resolve_identifier(ns));
+ if(!TYPE_IS_NAMESPACE(w.t)) {
+ as3_softwarning("%s might not be a namespace", ns);
+ }
+ v.c = code_append(v.c, w.c);
+ v.c = converttype(v.c, w.t, TYPE_NAMESPACE);
+ m.type = attr?RTQNAMEA:RTQNAME;
+ }
+
+ if(!multi) {
+ v.c = abc_getproperty2(v.c, &m);
+ } else {
+ v.c = abc_getdescendants2(v.c, &m);
+ }
+
+ if(TYPE_IS_XML(v.t)) {
+ v.t = TYPE_XMLLIST;
+ } else {
+ v.c = abc_coerce_a(v.c);
+ v.t = TYPE_ANY;
+ }
+ return mkcodenode(v);
+ }
+};
E : E '.' ID_OR_NS "::" SUBNODE {
- typedcode_t v = node_read($1);
- typedcode_t w = node_read(resolve_identifier($3));
- v.c = code_append(v.c, w.c);
- if(!TYPE_IS_NAMESPACE(w.t)) {
- as3_softwarning("%s might not be a namespace", $3);
- }
- v.c = converttype(v.c, w.t, TYPE_NAMESPACE);
- multiname_t m = {RTQNAME, 0, 0, $5};
- v.c = abc_getproperty2(v.c, &m);
- if(TYPE_IS_XML(v.t)) {
- v.t = TYPE_XMLLIST;
- } else {
- v.c = abc_coerce_a(v.c);
- v.t = TYPE_ANY;
- }
- $$ = mkcodenode(v);
+ $$ = get_descendants($1, $3, $5, 0, 0);
}
E : E ".." SUBNODE {
typedcode_t v = node_read($1);
v.t = TYPE_XMLLIST;
$$ = mkcodenode(v);
}
+E : E ".." ID_OR_NS "::" SUBNODE {
+ $$ = get_descendants($1, $3, $5, 1, 0);
+}
E : E '.' '[' E ']' {
typedcode_t v = node_read($1);
typedcode_t w = node_read($4);
v.t = TYPE_STRING;
$$ = mkcodenode(v);
}
+
+E : E '.' '@' ID_OR_NS "::" SUBNODE {
+ $$ = get_descendants($1, $4, $6, 0, 1);
+}
+
E : E ".." '@' SUBNODE {
typedcode_t v = node_read($1);
multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
v.t = TYPE_STRING;
$$ = mkcodenode(v);
}
+E : E ".." '@' ID_OR_NS "::" SUBNODE {
+ $$ = get_descendants($1, $4, $6, 1, 1);
+}
+
E : E '.' '@' '[' E ']' {
typedcode_t v = node_read($1);
typedcode_t w = node_read($5);
t = t->data;
is_static = 1;
}
- if(TYPE_IS_XML(t)) {
+ if(TYPE_IS_XML(t) && !findmember_nsset(t, $3, 1, is_static)) {
multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
$$.c = abc_getproperty2($$.c, &m);
$$.c = abc_coerce_a($$.c);
if(t->subtype==INFOTYPE_UNRESOLVED) {
syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
}
- memberinfo_t*f = findmember_nsset(t, $3, 1);
+ memberinfo_t*f = findmember_nsset(t, $3, 1, is_static);
char noslot = 0;
if(f && !is_static != !(f->flags&FLAG_STATIC))
noslot=1;
}
%code {
- node_t* resolve_identifier(char*name)
+ node_t* var_read(variable_t*v)
+ {
+ typedcode_t o;
+ o.c = abc_getlocal(0, v->index);
+ o.t = v->type;
+ return mkcodenode(o);
+ }
+
+ node_t* resolve_identifier(const char*name)
{
typedcode_t o;
o.t = 0;
/* look at variables */
if((v = find_variable(state, name))) {
// name is a local variable
- o.c = abc_getlocal(o.c, v->index);
- o.t = v->type;
- return mkcodenode(o);
+ return var_read(v);
}
- if((v = find_slot(state, name))) {
+ if((v = find_slot(state->method, name))) {
o.c = abc_getscopeobject(o.c, 1);
o.c = abc_getslot(o.c, v->index);
o.t = v->type;
if(!state->method->inner &&
!state->xmlfilter &&
state->cls &&
- (f = findmember_nsset(state->cls->info, name, 1)))
+ (f = findmember_nsset(state->cls->info, name, 1, i_am_static)))
{
// name is a member or attribute in this class
int var_is_static = (f->flags&FLAG_STATIC);
if(!state->xmlfilter &&
(dict_contains(state->import_toplevel_packages, name) ||
registry_ispackage(name))) {
- o.c = abc___pushpackage__(o.c, name);
+ o.c = abc___pushpackage__(o.c, (char*)name);
o.t = 0;
return mkcodenode(o); //?
}
}
};
+/* TODO: causes 16 r/r conflicts */
+VAR_READ : T_NAMESPACE {
+ PASS2
+ $$ = resolve_identifier($1);
+}
VAR_READ : T_IDENTIFIER {
PASS1
/* Queue unresolved identifiers for checking against the parent
}
NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
PASS12
- trie_put(active_namespaces, $2->name, (void*)$2->url);
+ trie_put(active_namespaces, (unsigned char*)$2->name, (void*)$2->url);
namespace_t access = modifiers2access(&$1);
varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
DEFAULT_NAMESPACE : "default xml" "namespace" '=' E
{
- as3_warning("default xml namespaces not supported yet");
$$ = 0;
+ $$ = code_append($$, node_read($4).c);
+ $$ = abc_dxnslate($$);
}
USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
url = s->value->ns->name;
- trie_put(active_namespaces, $3->name, (void*)url);
+ trie_put(active_namespaces, (unsigned char*)$3->name, (void*)url);
add_active_url(url);
$$=0;
}