-/* parser.lex
+/* parser.y
Routines for compiling Flash2 AVM2 ABC Actionscript
}
-%token<id> T_IDENTIFIER T_NAMESPACE
+%token<id> T_IDENTIFIER
%token<str> T_STRING
%token<regexp> T_REGEXP
%token<token> T_EMPTY
%token<token> KW_NAMESPACE "namespace"
%token<token> KW_PACKAGE "package"
%token<token> KW_PROTECTED "protected"
+%token<token> KW_ARGUMENTS "arguments"
%token<token> KW_PUBLIC "public"
%token<token> KW_PRIVATE "private"
%token<token> KW_USE "use"
%token<token> KW_NUMBER "Number"
%token<token> KW_STRING "String"
%token<token> KW_DEFAULT "default"
+%token<token> KW_DEFAULT_XML "default xml"
%token<token> KW_DELETE "delete"
%token<token> KW_IF "if"
%token<token> KW_ELSE "else"
%token<token> T_USHR ">>>"
%token<token> T_SHR ">>"
-%type <number_int> CONDITIONAL_COMPILATION
+%type <number_int> CONDITIONAL_COMPILATION EMBED_START
%type <for_start> FOR_START
%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER ID_OR_NS SUBNODE
%type <namespace_decl> NAMESPACE_ID
%type <token> VARCONST
%type <code> CODE
%type <code> CODEPIECE CODE_STATEMENT
-%type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
+%type <code> CODEBLOCK IF_CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
%type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
%type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
%type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
%type <node> VAR_READ
%type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
%type <value> INNERFUNCTION
-%type <code> USE_NAMESPACE
+%type <code> USE_NAMESPACE DEFAULT_NAMESPACE
%type <code> FOR_INIT
%type <code> IMPORT
%type <classinfo> MAYBETYPE
%type <classinfo_list> EXTENDS_LIST
%type <classinfo> CLASS PACKAGEANDCLASS
%type <classinfo_list> CLASS_SPEC_LIST
-%type <id> XML XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT
+%type <node> XMLEXPR1 XMLEXPR2 XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT XML_ID_OR_EXPR XML
%type <classinfo> TYPE
//%type <token> VARIABLE
%type <value> MEMBER
%left new2
%left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
-%left T_IDENTIFIER
+%left T_IDENTIFIER "arguments"
%left above_identifier
%left below_else
%nonassoc "else"
// needed for "return" precedence:
%nonassoc T_STRING T_REGEXP
-%nonassoc T_INT T_UINT T_FLOAT KW_NAN
+%nonassoc T_INT T_UINT T_FLOAT KW_NAN
%nonassoc "false" "true" "null" "undefined" "super" "function"
%left above_function
-
%{
/* class data */
classinfo_t*info;
abc_class_t*abc;
-
+
methodstate_t*init;
methodstate_t*static_init;
//code_t*init;
//code_t*static_init;
+ parsedclass_t*dependencies;
char has_constructor;
} classstate_t;
char is_constructor;
char has_super;
char is_global;
+ char is_static;
int variable_count;
dict_t*unresolved_variables;
+ dict_t*allvars; // all variables (in all sublevels, but not for inner functions)
char inner;
char uses_parent_function;
+ char no_variable_scoping;
int uses_slots;
dict_t*slots;
int activation_var;
+ int need_arguments;
+
abc_method_t*abc;
int var_index; // for inner methods
int slot_index; // for inner methods
methodstate_list_t*innerfunctions;
};
+methodstate_t*methodstate_new()
+{
+ NEW(methodstate_t,m);
+ m->allvars = dict_new();
+ return m;
+}
+void methodstate_destroy(methodstate_t*m)
+{
+ dict_destroy(m->unresolved_variables); m->unresolved_variables = 0;
+ list_free(m->innerfunctions);m->innerfunctions=0;
+
+ if(m->allvars) {
+ DICT_ITERATE_DATA(m->allvars, void*, data) {free(data);}
+ m->allvars = 0;
+ }
+}
+
typedef struct _state {
struct _state*old;
int level;
dict_t*import_toplevel_packages;
dict_t*imports;
+ dict_t*namespaces;
namespace_list_t*active_namespace_urls;
char has_own_imports;
char new_vars; // e.g. transition between two functions
+ char xmlfilter; // are we inside a xmlobj..() filter?
classstate_t*cls;
methodstate_t*method;
int switch_var;
dict_t*vars;
- dict_t*allvars; // also contains variables from sublevels
} state_t;
typedef struct _global {
DECLARE_LIST(state);
+/* protected handling here is a big hack: we just assume the protectedns
+ is package:class. the correct approach would be to add the proper
+ namespace to all protected members in the registry, even though that
+ would slow down searching */
#define MEMBER_MULTINAME(m,f,n) \
multiname_t m;\
namespace_t m##_ns;\
if(f) { \
- if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
+ m##_ns.access = ((slotinfo_t*)(f))->access; \
+ if(m##_ns.access == ACCESS_NAMESPACE) \
m##_ns.name = ((slotinfo_t*)(f))->package; \
+ else if(m##_ns.access == ACCESS_PROTECTED && (f)->parent) \
+ m##_ns.name = concat3((f)->parent->package,":",(f)->parent->name); \
else \
m##_ns.name = ""; \
m.type = QNAME; \
static namespace_t ns1 = {ACCESS_PRIVATE, ""};
static namespace_t ns2 = {ACCESS_PROTECTED, ""};
static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
-static namespace_t ns4 = {ACCESS_PACKAGE, ""};
-static namespace_list_t nl4 = {&ns4,0};
+static namespace_t stdns = {ACCESS_PACKAGE, ""};
+static namespace_list_t nl4 = {&stdns,0};
static namespace_list_t nl3 = {&ns3,&nl4};
static namespace_list_t nl2 = {&ns2,&nl3};
static namespace_list_t nl1 = {&ns1,&nl2};
state->old = oldstate;
state->new_vars = 0;
- trie_remember(active_namespaces);
+ state->namespaces = dict_new();
if(oldstate)
state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
if(state->vars) {
dict_destroy(state->vars);state->vars=0;
}
- if(state->new_vars && state->allvars) {
- parserassert(!state->old || state->old->allvars != state->allvars);
- DICT_ITERATE_DATA(state->allvars, void*, data) {
- free(data);
- }
- dict_destroy(state->allvars);
- }
list_free(state->active_namespace_urls)
state->active_namespace_urls = 0;
static void old_state()
{
- trie_rollback(active_namespaces);
-
if(!state || !state->old)
syntaxerror("invalid nesting");
state_t*leaving = state;
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);
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, char var0);
+static void function_initvars(methodstate_t*m, char has_params, params_t*params, int flags, char var0);
static char* internal_filename_package = 0;
syntaxerror("invalid call to initialize_file during parsing of another file");
}
- active_namespaces = trie_new();
-
new_state();
state->package = internal_filename_package = strdup(filename);
- state->allvars = dict_new();
global->token2info = dict_lookup(global->file2token2info,
current_filename // use long version
state->method = rfx_calloc(sizeof(methodstate_t));
dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
+ state->method->allvars = dict_new();
} else {
state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+ state->method->variable_count = 0;
if(!state->method)
syntaxerror("internal error: skewed tokencount");
- function_initvars(state->method, 0, 0, 1);
- global->init = abc_initscript(global->file);
+ function_initvars(state->method, 0, 0, 0, 1);
+ global->init = 0;
}
}
if(as3_pass==2) {
dict_del(global->file2token2info, current_filename);
code_t*header = method_header(state->method);
- code_t*c = wrap_function(header, 0, global->init->method->body->code);
- global->init->method->body->code = abc_returnvoid(c);
- free(state->method);state->method=0;
+ //if(global->init->method->body->code || global->init->traits) {
+ if(global->init) {
+ code_t*c = wrap_function(header, 0, global->init->method->body->code);
+ global->init->method->body->code = abc_returnvoid(c);
+ free(state->method);state->method=0;
+ }
}
//free(state->package);state->package=0; // used in registry
int index;
classinfo_t*type;
char init;
+ char kill;
char is_parameter;
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)
{
- state_t*top = s;
- while(s) {
- variable_t*v = 0;
- v = dict_lookup(s->vars, name);
- if(v) return v;
- if(s->new_vars) break;
- s = s->old;
+ if(s->method->no_variable_scoping) {
+ return dict_lookup(s->method->allvars, name);
+ } else {
+ state_t*top = s;
+ while(s) {
+ variable_t*v = 0;
+ v = dict_lookup(s->vars, name);
+ if(v) return v;
+ if(s->new_vars) break;
+ s = s->old;
+ }
+ return 0;
}
- return dict_lookup(top->allvars, name);
}
-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;
}
static code_t*defaultvalue(code_t*c, classinfo_t*type)
{
+ parserassert(!type || type->kind!=INFOTYPE_UNRESOLVED);
if(TYPE_IS_INT(type)) {
c = abc_pushbyte(c, 0);
} else if(TYPE_IS_UINT(type)) {
c = abc_pushnan(c);
} else if(TYPE_IS_BOOLEAN(type)) {
c = abc_pushfalse(c);
+ } else if(TYPE_IS_STRING(type)) {
+ c = abc_pushnull(c);
+ c = abc_coerce_s(c);
} else if(!type) {
//c = abc_pushundefined(c);
syntaxerror("internal error: can't generate default value for * type");
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);
- if(v)
+ variable_t*v = find_slot(method, name);
+ if(v) {
+ alloc_local();
return v;
+ }
}
NEW(variable_t, v);
v->index = alloc_local();
v->type = type;
- v->init = init;
+ v->init = v->kill = init;
if(name) {
- dict_put(state->vars, name, v);
- dict_put(state->allvars, name, v);
+ if(!method->no_variable_scoping)
+ {
+ if(dict_contains(state->vars, name)) {
+ syntaxerror("variable %s already defined", name);
+ }
+ dict_put(state->vars, name, v);
+ }
+ if(method->no_variable_scoping &&
+ as3_pass==2 &&
+ dict_contains(state->method->allvars, name))
+ {
+ variable_t*v = dict_lookup(state->method->allvars, name);
+ if(v->type != type && (!v->type || v->type->kind!=INFOTYPE_UNRESOLVED)) {
+ syntaxerror("variable %s already defined.", name);
+ }
+ return v;
+ }
+ dict_put(state->method->allvars, name, v);
}
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* var_block(code_t*body)
+static code_t* var_block(code_t*body, dict_t*vars)
{
code_t*c = 0;
code_t*k = 0;
int t;
- int num=0;
- for(t=0;t<state->vars->hashsize;t++) {
- dictentry_t*e = state->vars->slots[t];
- while(e) {
- variable_t*v = (variable_t*)e->data;
- if(v->type && v->init) {
- c = defaultvalue(c, v->type);
- c = abc_setlocal(c, v->index);
- k = abc_kill(k, v->index);
- num++;
- }
- e = e->next;
+ DICT_ITERATE_DATA(vars, variable_t*, v) {
+ if(v->type && v->init) {
+ c = defaultvalue(c, v->type);
+ c = abc_setlocal(c, v->index);
+ }
+ if(v->type && v->kill) {
+ k = abc_kill(k, v->index);
}
}
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);
}
if(m->uses_slots) {
/* FIXME: this alloc_local() causes variable indexes to be
different in pass2 than in pass1 */
- if(!m->activation_var)
+ if(!m->activation_var) {
m->activation_var = alloc_local();
+ }
if(init) {
c = abc_newactivation(c);
c = abc_dup(c);
static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
{
c = code_append(c, header);
- c = code_append(c, var_block(body));
+ c = code_append(c, var_block(body, state->method->no_variable_scoping?state->method->allvars:state->vars));
/* append return if necessary */
if(!c || (c->opcode != OPCODE_RETURNVOID &&
c->opcode != OPCODE_RETURNVALUE)) {
#define FLAG_PACKAGEINTERNAL 2048
#define FLAG_NAMESPACE 4096
+static slotinfo_t* find_class(const char*name);
+
+const char* lookup_namespace(const char*name)
+{
+ state_t*s = state;
+ while(s) {
+ const char*url = dict_lookup(s->namespaces, name);
+ if(url)
+ return url;
+ s = s->old;
+ }
+ varinfo_t*a;
+ registry_find(state->package, name);
+ if(( a = (varinfo_t*)find_class(name) )) {
+ if(a->kind == INFOTYPE_VAR) {
+ if(!a->value || !NS_TYPE(a->value->type))
+ syntaxerror("%s.%s is not a namespace", a->package, a->name);
+ return a->value->ns->name;
+ }
+ }
+ return 0;
+}
+
static namespace_t modifiers2access(modifiers_t*mod)
{
namespace_t ns;
if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
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);
- if(!url) {
- /* shouldn't happen- the tokenizer only reports something as a namespace
- if it was already registered */
- trie_dump(active_namespaces);
- syntaxerror("unknown namespace: %s", mod->ns);
- }
+ const char*url = lookup_namespace(mod->ns);
+ if(!url) {
+ if(as3_pass>1) {
+ syntaxerror("unknown namespace: %s (pass %d)", mod->ns, as3_pass);
+ } else {
+ url = mod->ns;
+ }
+ }
ns.name = url;
} else if(mod->flags&FLAG_PUBLIC) {
if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
}
return ns;
}
-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 function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
+static void innerfunctions2vars(methodstate_t*m)
+{
+ methodstate_list_t*l = m->innerfunctions;
+ while(l) {
+ methodstate_t*m = l->methodstate;
+
+ 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;
+ v->is_inner_method = m;
+ l = l->next;
+ }
+}
+
+static void function_initvars(methodstate_t*m, char has_params, params_t*params, int flags, char var0)
{
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(m->uses_slots) {
- /* as variables and slots share the same number, make sure
- that those variable indices are reserved. It's up to the
- optimizer to later shuffle the variables down to lower
- indices */
- m->variable_count = m->uses_slots;
- }
-
- if(params) {
+ 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;
}
- }
-
- methodstate_list_t*l = m->innerfunctions;
- while(l) {
- methodstate_t*m = l->methodstate;
-
- variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
- m->var_index = v->index;
- m->slot_index = v->index;
- v->is_inner_method = m;
-
- l = l->next;
+ 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(m, "arguments", TYPE_ARRAY, 0, 0);
+ m->need_arguments = v->index;
+ }
}
+ innerfunctions2vars(m);
+
if(as3_pass==2) {
m->scope_code = add_scope_code(m->scope_code, m, 0);
- }
-
- if(as3_pass==2 && m->slots) {
- /* exchange unresolved identifiers with the actual objects */
- DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
- if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
- classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
- if(!type || type->kind != INFOTYPE_CLASS) {
- syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name);
+ if(m->slots) {
+ /* exchange unresolved identifiers with the actual objects */
+ DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v1) {
+ if(v1->type && v1->type->kind == INFOTYPE_UNRESOLVED) {
+ classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v1->type);
+ if(!type || type->kind != INFOTYPE_CLASS) {
+ syntaxerror("Couldn't find class %s::%s (%s)", v1->type->package, v1->type->name, name);
+ }
+ v1->type = type;
}
- v->type = type;
}
+ }
+ if(m->allvars) {
+ DICT_ITERATE_ITEMS(m->allvars, char*, name2, variable_t*, v2) {
+ if(v2->type && v2->type->kind == INFOTYPE_UNRESOLVED) {
+ classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v2->type);
+ if(!type || type->kind != INFOTYPE_CLASS) {
+ syntaxerror("Couldn't find class %s::%s (%s)", v2->type->package, v2->type->name, name2);
+ }
+ v2->type = type;
+ }
+ }
}
}
}
if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
syntaxerror("public and internal not supported at the same time.");
+ if((mod->flags&(FLAG_PROTECTED|FLAG_STATIC)) == (FLAG_PROTECTED|FLAG_STATIC))
+ syntaxerror("protected and static not supported at the same time.");
+
//if(!(mod->flags&FLAG_INTERFACE) && !extends) {
if(!(mod->flags&FLAG_INTERFACE) && !extends) {
// all classes extend object
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));
- state->cls->static_init->variable_count=1;
+ state->cls->init = methodstate_new();
+ state->cls->static_init = methodstate_new();
+ state->cls->static_init->is_static=FLAG_STATIC;
/* notice: we make no effort to initialize the top variable (local0) here,
- even though it has special meaning. We just rely on the facat
+ even though it has special meaning. We just rely on the fact
that pass 1 won't do anything with variables */
dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
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, 1);
- function_initvars(state->cls->static_init, 0, 0, 0);
+ 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))
syntaxerror("Can't extend final class '%s'", extends->name);
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);
}
- NEW(parsedclass_t,p);
- p->cls = state->cls->info;
- p->abc = state->cls->abc;
- list_append(global->classes, p);
+ state->cls->dependencies = parsedclass_new(state->cls->info, state->cls->abc);
+ list_append(global->classes, state->cls->dependencies);
/* flash.display.MovieClip handling */
if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
code_t*c = method_header(state->cls->static_init);
m->body->code = wrap_function(c, 0, m->body->code);
}
+
+ trait_list_t*trait = state->cls->abc->traits;
+ /* switch all protected members to the protected ns of this class */
+ while(trait) {
+ trait_t*t = trait->trait;
+ if(t->name->ns->access == ACCESS_PROTECTED) {
+ if(!state->cls->abc->protectedNS) {
+ char*n = concat3(state->cls->info->package, ":", state->cls->info->name);
+ state->cls->abc->protectedNS = namespace_new_protected(n);
+ state->cls->abc->flags |= CLASS_PROTECTED_NS;
+ }
+ t->name->ns->name = strdup(state->cls->abc->protectedNS->name);
+ }
+ trait = trait->next;
+ }
}
old_state();
{
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->new_vars = 1;
- state->allvars = dict_new();
if(as3_pass == 1) {
- state->method = rfx_calloc(sizeof(methodstate_t));
+ state->method = methodstate_new();
state->method->inner = 1;
+ 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;
dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
- function_initvars(state->method, params, 0, 1);
+ function_initvars(state->method, 1, params, 0, 1);
}
if(as3_pass == 2) {
parserassert(state->method);
state->method->info->return_type = return_type;
- function_initvars(state->method, params, 0, 1);
+ function_initvars(state->method, 1, params, 0, 1);
}
}
}
new_state();
state->new_vars = 1;
- state->allvars = dict_new();
if(as3_pass == 1) {
- state->method = rfx_calloc(sizeof(methodstate_t));
+ state->method = methodstate_new();
state->method->has_super = 0;
+ state->method->is_static = mod->flags&FLAG_STATIC;
if(state->cls) {
state->method->is_constructor = !strcmp(state->cls->info->name,name);
state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
- function_initvars(state->method, params, mod->flags, 1);
+ function_initvars(state->method, 1, params, mod->flags, 1);
dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->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);
}
state->cls->has_constructor |= state->method->is_constructor;
}
- function_initvars(state->method, params, mod->flags, 1);
+ function_initvars(state->method, 1, params, mod->flags, 1);
}
}
+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) {
- // store inner methods in variables
- function_initvars(state->method, 0, 0, 0);
-
- methodstate_list_t*ml = state->method->innerfunctions;
-
dict_t*xvars = dict_new();
-
- 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 */
- variable_t*v;
- if((v=find_variable(state, l->key))) {
- m->uses_parent_function = 1;
- state->method->uses_slots = 1;
- dict_put(xvars, l->key, 0);
- }
- l = l->next;
+
+ if(state->method->unresolved_variables) {
+ DICT_ITERATE_KEY(state->method->unresolved_variables, char*, vname) {
+ if(!state->method->no_variable_scoping && dict_contains(state->method->allvars, vname)) {
+ variable_t*v = dict_lookup(state->method->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 compatibility mode", name, vname);
}
- if(l) break;
}
-
- dict_destroy(m->unresolved_variables);
- m->unresolved_variables = 0;
}
+ }
+
+ methodstate_list_t*ml = state->method->innerfunctions;
+ while(ml) {
+ insert_unresolved(ml->methodstate, xvars, state->method->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->method->allvars, char*, name, variable_t*, v) {
if(!name) syntaxerror("internal error");
if(v->index && dict_contains(xvars, name)) {
- v->init = 0;
- v->index = i++;
+ v->init = v->kill = 0;
+ v->index = i;
if(v->is_inner_method) {
- v->is_inner_method->is_a_slot = 1;
+ v->is_inner_method->is_a_slot = i;
}
- //v->type = 0;
+ i++;
dict_put(state->method->slots, name, v);
}
}
state->method->uses_slots = i;
dict_destroy(state->vars);state->vars = 0;
parserassert(state->new_vars);
- dict_destroy(state->allvars);state->allvars = 0;
}
old_state();
return 0;
f = abc_class_getconstructor(state->cls->abc, type2);
} else if(!state->method->is_global) {
namespace_t ns = modifiers2access(mod);
-
- /* deal with protected */
- if(ns.access == ACCESS_PROTECTED && state->cls)
- ns.name = state->cls->info->name;
-
multiname_t mname = {QNAME, &ns, 0, name};
-
if(mod->flags&FLAG_STATIC)
f = abc_class_staticmethod(state->cls->abc, type2, &mname);
else
multiname_t mname = {QNAME, &mname_ns, 0, name};
f = abc_method_new(global->file, type2, 1);
+ if(!global->init) global->init = abc_initscript(global->file);
trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
//abc_code_t*c = global->init->method->body->code;
}
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;
+ if(state->method->need_arguments) f->flags |= METHOD_NEED_ARGUMENTS;
char opt=0;
param_list_t*p=0;
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");
+ syntaxerror("function %s: non-optional parameter not allowed after optional parameters", name);
}
}
if(state->method->slots) {
}
}
-#define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
-
code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
{
if(from==to)
return abc_coerce2(c, &m);
}
+ if(TYPE_IS_XMLLIST(to) && TYPE_IS_XML(from))
+ return c;
+
if(TYPE_IS_BOOLEAN(to))
return abc_convert_b(c);
if(TYPE_IS_STRING(to))
return abc_convert_s(c);
if(TYPE_IS_OBJECT(to))
- return abc_convert_o(c);
+ return abc_coerce2(c, &m);
+ if(TYPE_IS_OBJECT(from) && TYPE_IS_XMLLIST(to))
+ return abc_coerce2(c, &m);
+ if(TYPE_IS_OBJECT(from) && TYPE_IS_ARRAY(to))
+ return abc_coerce2(c, &m);
classinfo_t*supertype = from;
while(supertype) {
if(supertype == to) {
- // target type is one of from's superclasses
+ /* target type is one of from's superclasses.
+ (not sure we need this coerce - as far as the verifier
+ is concerned, object==object (i think) */
return abc_coerce2(c, &m);
}
int t=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);
return c;
}
-/* move to ast.c todo end */
+code_t* coerce_to_type(code_t*c, classinfo_t*t)
+{
+ if(!t) {
+ return abc_coerce_a(c);
+ } else if(TYPE_IS_STRING(t)) {
+ return abc_coerce_s(c);
+ } else {
+ MULTINAME(m, t);
+ return abc_coerce2(c, &m);
+ }
+}
char is_pushundefined(code_t*c)
{
infotypename(a), a->name, a->package, state->package);
}
+
if(a->kind != INFOTYPE_CLASS) {
MULTINAME(m, a);
x.c = abc_findpropstrict2(x.c, &m);
varinfo_t*v = (varinfo_t*)a;
x.t = v->type;
}
+ return x;
} else {
+ if(state->cls && state->method == state->cls->static_init) {
+ /* we're in the static initializer.
+ record the fact that we're using this class here */
+ parsedclass_add_dependency(state->cls->dependencies, (classinfo_t*)a);
+ }
classinfo_t*c = (classinfo_t*)a;
- if(c->slot) {
+ //if(c->slot) {
+ if(0) { //Error #1026: Slot 1 exceeds slotCount=0 of global
x.c = abc_getglobalscope(x.c);
x.c = abc_getslot(x.c, c->slot);
} else {
| SLOT_DECLARATION
| PACKAGE_INITCODE
| CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
+ | '[' EMBED_START E ']' {PASS_ALWAYS as3_pass=$2;PASS1 as3_warning("embed command ignored");}
| ';'
MAYBECODE: CODE {$$=$1;}
CODE: CODEPIECE {$$=$1;}
// code which may appear outside of methods
+CODE_STATEMENT: DEFAULT_NAMESPACE
CODE_STATEMENT: IMPORT
CODE_STATEMENT: FOR
CODE_STATEMENT: FOR_IN
CODE_STATEMENT: '{' CODE '}' {$$=$2;}
CODE_STATEMENT: '{' '}' {$$=0;}
-// code which may appear in methods
+// code which may appear in methods (includes the above)
CODEPIECE: ';' {$$=0;}
CODEPIECE: CODE_STATEMENT
CODEPIECE: VARIABLE_DECLARATION
/* ------------ package init code ------------------- */
PACKAGE_INITCODE: CODE_STATEMENT {
- code_t**cc = &global->init->method->body->code;
- *cc = code_append(*cc, $1);
+ if($1) {
+ if(!global->init)
+ global->init = abc_initscript(global->file);
+ code_t**cc = &global->init->method->body->code;
+ *cc = code_append(*cc, $1);
+ }
+}
+
+/* ------------ embed code ------------- */
+
+EMBED_START: %prec above_function {
+ PASS_ALWAYS
+ $$ = as3_pass;
+ as3_pass=0;
}
/* ------------ conditional compilation ------------- */
{
return 1; // FIXME
}
+ char do_init_variable(char*name)
+ {
+ if(!state->method->no_variable_scoping)
+ return 0;
+ if(!state->new_vars)
+ return 1;
+ return 1;
+ }
};
MAYBEEXPRESSION : '=' E {$$=$2;}
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, $2, 1, 0);
PASS2
char slot = 0;
int index = 0;
+ variable_t*v = 0;
if(state->method->uses_slots) {
- variable_t* 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;
v->type = $2;
slot = 1;
- index = v->index;
}
}
- if(!index) {
- index = new_variable($1, $2, 1, 0);
+ if(!v) {
+ v = new_variable2(state->method, $1, $2, 1, 0);
}
$$ = slot?abc_getscopeobject(0, 1):0;
- typedcode_t v = node_read($3);
- if(!is_subtype_of(v.t, $2)) {
- syntaxerror("Can't convert %s to %s", v.t->name, $2->name);
+ typedcode_t val = node_read($3);
+ if(!is_subtype_of(val.t, $2)) {
+ syntaxerror("Can't convert %s to %s", val.t->name, $2->name);
}
if($2) {
- if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
- $$ = code_append($$, v.c);
- $$ = converttype($$, v.t, $2);
+ if(val.c->prev || val.c->opcode != OPCODE_PUSHUNDEFINED) {
+ $$ = code_append($$, val.c);
+ $$ = converttype($$, val.t, $2);
} else {
- code_free(v.c);
+ code_free(val.c);
$$ = defaultvalue($$, $2);
}
} else {
- if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
- $$ = code_append($$, v.c);
+ if(val.c->prev || val.c->opcode != OPCODE_PUSHUNDEFINED) {
+ $$ = code_append($$, val.c);
$$ = abc_coerce_a($$);
} else {
// don't do anything
- code_free(v.c);
+ code_free(val.c);
code_free($$);
$$ = 0;
break;
}
}
if(slot) {
- $$ = abc_setslot($$, index);
+ $$ = abc_setslot($$, v->index);
} else {
- $$ = abc_setlocal($$, index);
+ $$ = abc_setlocal($$, v->index);
+ v->init = do_init_variable($1);
}
}
/* ------------ control flow ------------------------- */
+IF_CODEBLOCK: {PASS12 new_state();} CODEBLOCK {
+ $$ = var_block($2, state->vars);
+ PASS12 old_state();
+}
MAYBEELSE: %prec below_else {$$ = code_new();}
-MAYBEELSE: "else" CODEBLOCK {$$=$2;}
+MAYBEELSE: "else" IF_CODEBLOCK {$$=$2;}
//MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
-IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
-
+IF : "if" '(' EXPRESSION ')' IF_CODEBLOCK MAYBEELSE {
$$ = code_new();
- $$ = code_append($$, $4.c);
+ $$ = code_append($$, $3.c);
code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
- $$ = code_append($$, $6);
- if($7) {
+ $$ = code_append($$, $5);
+ if($6) {
myjmp = $$ = abc_jump($$, 0);
}
myif->branch = $$ = abc_nop($$);
- if($7) {
- $$ = code_append($$, $7);
+ if($6) {
+ $$ = code_append($$, $6);
myjmp->branch = $$ = abc_nop($$);
}
- $$ = var_block($$);
- PASS12 old_state();
}
FOR_INIT : {$$=code_new();}
// (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_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 {
+FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' IF_CODEBLOCK {
if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
$$ = code_new();
$$ = code_append($$, $2);
continuejumpsto($$, $1.name, cont);
myif->branch = out;
- $$ = var_block($$);
+ $$ = var_block($$, state->vars);
PASS12 old_state();
}
-FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
- variable_t*var = find_variable(state, $2);
- if(!var) {
- syntaxerror("variable %s not known in this scope", $2);
- }
-
- char*tmp1name = concat2($2, "__tmp1__");
- int it = new_variable(tmp1name, TYPE_INT, 0, 0);
- char*tmp2name = concat2($2, "__array__");
- int array = new_variable(tmp1name, 0, 0, 0);
+FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' IF_CODEBLOCK {
+ node_t*n = resolve_identifier($2);
+ typedcode_t w = node_write(n);
+
+ int it = alloc_local();
+ int array = alloc_local();
$$ = code_new();
$$ = code_append($$, $4.c);
$$ = 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);
myif->branch = out;
- $$ = var_block($$);
-
- free(tmp1name);
- free(tmp2name);
+ $$ = abc_kill($$, it);
+ $$ = abc_kill($$, array);
+ $$ = var_block($$, state->vars);
PASS12 old_state();
}
-WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
+WHILE : T_WHILE '(' EXPRESSION ')' IF_CODEBLOCK {
$$ = code_new();
code_t*myjmp = $$ = abc_jump($$, 0);
code_t*loopstart = $$ = abc_label($$);
- $$ = code_append($$, $6);
+ $$ = code_append($$, $5);
code_t*cont = $$ = abc_nop($$);
myjmp->branch = cont;
- $$ = code_append($$, $4.c);
+ $$ = code_append($$, $3.c);
$$ = abc_iftrue($$, loopstart);
code_t*out = $$ = abc_nop($$);
breakjumpsto($$, $1, out);
continuejumpsto($$, $1, cont);
-
- $$ = var_block($$);
- PASS12 old_state();
}
-DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
+DO_WHILE : T_DO IF_CODEBLOCK "while" '(' EXPRESSION ')' {
$$ = code_new();
code_t*loopstart = $$ = abc_label($$);
- $$ = code_append($$, $3);
+ $$ = code_append($$, $2);
code_t*cont = $$ = abc_nop($$);
- $$ = code_append($$, $6.c);
+ $$ = code_append($$, $5.c);
$$ = abc_iftrue($$, loopstart);
code_t*out = $$ = abc_nop($$);
breakjumpsto($$, $1, out);
continuejumpsto($$, $1, cont);
-
- $$ = var_block($$);
- PASS12 old_state();
}
BREAK : "break" %prec prec_none {
c=c->prev;
}
- $$ = var_block($$);
+ $$ = 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, $4, 0, 0);
+ PASS2 new_variable(state->method, $3, $4, 0, 0);
}
'{' MAYBECODE '}' {
namespace_t name_ns = {ACCESS_PACKAGE, ""};
c = code_append(c, $8);
c = abc_kill(c, i);
- c = var_block(c);
+ c = var_block(c, state->vars);
PASS12 old_state();
}
FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
- $4 = var_block($4);
+ $4 = var_block($4, state->vars);
if(!$4) {
$$=0;
} else {
int tmp;
if($6.finally)
- tmp = new_variable("__finally__", 0, 0, 0);
+ tmp = alloc_local();
abc_exception_list_t*l = $6.l;
int count=0;
list_concat(state->method->exceptions, $6.l);
- $$ = var_block($$);
+ $$ = var_block($$, state->vars);
PASS12 old_state();
}
X_IDENTIFIER: T_IDENTIFIER
| "package" {PASS12 $$="package";}
- | T_NAMESPACE {PASS12 $$=$1;}
+ | "namespace" {PASS12 $$="namespace";}
+ | "NaN" {PASS12 $$="NaN";}
PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
free(s);
}
};
+
+IMPORT : "import" T_IDENTIFIER {
+ PASS12
+ slotinfo_t*s = registry_find(state->package, $2);
+ if(!s && as3_pass==1) {as3_schedule_class(state->package, $2);}
+ state_has_imports();
+ dict_put(state->imports, state->package, $2);
+ $$=0;
+}
IMPORT : "import" PACKAGEANDCLASS {
PASS12
slotinfo_t*s = registry_find($2->package, $2->name);
- if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
+ if(!s && as3_pass==1) {
as3_schedule_class($2->package, $2->name);
}
- classinfo_t*c = $2;
- if(!c)
- syntaxerror("Couldn't import class\n");
+ /*if(s && s->kind == INFOTYPE_VAR && TYPE_IS_NAMESPACE(s->type)) {
+ trie_put(active_namespaces, (unsigned char*)$2->name, 0);
+ }*/
state_has_imports();
- dict_put(state->imports, c->name, c);
- import_toplevel(c->package);
+ dict_put(state->imports, $2->name, $2);
+ import_toplevel($2->package);
$$=0;
}
IMPORT : "import" PACKAGE '.' '*' {
| KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
| KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
| KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
- | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
+ | T_IDENTIFIER {PASS12 $$.flags=FLAG_NAMESPACE;
$$.ns=$1;
}
CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}' {PASS_ALWAYS as3_pass=$1;}
CLASS_BODY_ITEM : SLOT_DECLARATION
CLASS_BODY_ITEM : FUNCTION_DECLARATION
+CLASS_BODY_ITEM : '[' EMBED_START E ']' {PASS_ALWAYS as3_pass=$2;PASS1 as3_warning("embed command ignored");}
CLASS_BODY_ITEM : CODE_STATEMENT {
code_t*c = state->cls->static_init->header;
slotstate_varconst = varconst;
slotstate_flags = flags;
if(state->cls) {
- if(flags && flags->flags&FLAG_STATIC) {
- state->method = state->cls->static_init;
+ if(flags) {
+ if(flags->flags&FLAG_STATIC) {
+ state->method = state->cls->static_init;
+ } else {
+ state->method = state->cls->init;
+ }
} else {
- state->method = state->cls->init;
+ // reset to "default" state (all in class code is static by default) */
+ state->method = state->cls->static_init;
}
} else {
parserassert(state->method);
{
int flags = modifiers->flags;
namespace_t ns = modifiers2access(modifiers);
- /* deal with protected */
- if(ns.access == ACCESS_PROTECTED && state->cls)
- ns.name = state->cls->info->name;
/* slot name */
multiname_t mname = {QNAME, &ns, 0, name};
code_t**code=0;
if(!state->cls) {
// global variable
+ if(!global->init) global->init = abc_initscript(global->file);
ns.name = state->package;
traits = &global->init->traits;
code = &global->init->method->body->code;
// instance variable
traits = &state->cls->abc->traits;
code = &state->cls->init->header;
+
+ if(ns.access == ACCESS_PROTECTED) {
+ ns.name = concat3(state->cls->info->package,":",state->cls->info->name);
+ }
}
if(c)
*c = code;
if(m)
- memcpy(m, &mname, sizeof(multiname_t));
+ *m = *multiname_clone(&mname);
return trait_new_member(traits, 0, multiname_clone(&mname), 0);
}
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, v.c);
c = converttype(c, v.t, $2);
if(!t->slot_id) {
- c = abc_setproperty2(c, &mname);
+ c = abc_initproperty2(c, &mname);
} else {
c = abc_setslot(c, t->slot_id);
}
}
}
-//CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
CONSTANT : T_INT {$$ = constant_new_int($1);}
CONSTANT : T_UINT {
$$ = constant_new_uint($1);
CONSTANT : "undefined" {$$ = constant_new_undefined($1);}
CONSTANT : KW_NAN {$$ = constant_new_float(__builtin_nan(""));}
-/*
-CONSTANT : T_IDENTIFIER {
- if(!strcmp($1, "NaN")) {
- $$ = constant_new_float(__builtin_nan(""));
- } else {
- as3_warning("Couldn't evaluate constant value of %s", $1);
- $$ = constant_new_null($1);
- }
-}*/
-
/* ---------------------------xml ------------------------------ */
%code {
static int xml_level = 0;
};
-XML: XMLNODE
+XML: XMLNODE {
+ multiname_t m = {QNAME, &stdns, 0, "XML"};
+ typedcode_t v;
+ v.c = 0;
+ v.c = abc_getlex2(v.c, &m);
+ v.c = code_append(v.c, node_read($1).c);
+ v.c = abc_construct(v.c, 1);
+ v.t = TYPE_XML;
+ $$ = mkcodenode(v);
+}
-OPEN : '<' {PASS_ALWAYS tokenizer_begin_xml();xml_level++;}
+OPEN : '<' {PASS_ALWAYS if(!xml_level++) tokenizer_begin_xml();}
CLOSE : '>' {PASS_ALWAYS tokenizer_begin_xmltext();}
CLOSE2 : {PASS_ALWAYS if(!--xml_level) tokenizer_end_xml(); else tokenizer_begin_xmltext();}
-XMLTEXT : {$$="";}
-XMLTEXT : XMLTEXT T_STRING {$$=concat2($1, string_cstr(&$2));}
-XMLTEXT : XMLTEXT '>' {$$=concat2($1, ">");}
+XMLEXPR1 : '{' E {PASS_ALWAYS tokenizer_begin_xmltext();} '}' {
+ $$ = $2;
+}
+XMLEXPR2 : '{' E {PASS_ALWAYS tokenizer_begin_xml();} '}' {
+ $$ = $2;
+}
+XMLTEXT : {$$=mkstringnode("");}
+XMLTEXT : XMLTEXT XMLEXPR1 {
+ $$ = mkaddnode($1,$2);
+}
+XMLTEXT : XMLTEXT T_STRING {
+ char* str = string_cstr(&$2);
+ $$ = mkaddnode($1,mkstringnode(str));
+ free(str);
+}
+XMLTEXT : XMLTEXT '>' {
+ $$ = mkaddnode($1, mkstringnode(">"));
+}
+XML2 : XMLNODE XMLTEXT {
+ $$ = mkaddnode($1,$2);
+}
+XML2 : XML2 XMLNODE XMLTEXT {
+ $$ = mkaddnode($1, mkaddnode($2,$3));
+}
+XML_ID_OR_EXPR: T_IDENTIFIER {
+ $$ = mkstringnode($1);
+}
+XML_ID_OR_EXPR: XMLEXPR2 {
+ $$ = $1;
+}
-XML2 : XMLNODE XMLTEXT {$$=concat2($1,$2);}
-XML2 : XML2 XMLNODE XMLTEXT {$$=concat3($1,$2,$3);free($1);free($2);free($3);}
+MAYBE_XMLATTRIBUTES: {
+ $$ = mkstringnode("");
+}
+MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {
+ $$ = mkaddnode(mkstringnode(" "),$1);
+}
-XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' T_IDENTIFIER CLOSE2 '>' {
- $$ = allocprintf("<%s%s>%s</%s>", $2, $3, $5, $8);
- free($2);free($3);free($5);free($8);
+XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' {
+ //$$ = allocprintf("<%s%s/>", $2, $3, $5, $8);
+ $$ = mkaddnode(mkaddnode(mkaddnode(mkstringnode("<"),$2),$3),mkstringnode("/>"));
}
-XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' {
- $$ = allocprintf("<%s%s/>", $2, $3);
+XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
+ //$$ = allocprintf("<%s%s>%s</%s>", $2, $3, $5, $8);
+ $$ = mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(
+ mkstringnode("<"),$2),$3),mkstringnode(">")),$5),mkstringnode("</")),$8),mkstringnode(">"));
}
-XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES CLOSE XMLTEXT XML2 '<' '/' T_IDENTIFIER CLOSE2 '>' {
- $$ = allocprintf("<%s%s>%s%s</%s>", $2, $3, $5, $6, $9);
- free($2);free($3);free($5);free($6);free($6);free($9);
+XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT XML2 '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
+ //$$ = allocprintf("<%s%s>%s%s</%s>", $2, $3, $5, $6, $9);
+ $$ = mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(mkaddnode(
+ mkstringnode("<"),$2),$3),mkstringnode(">")),$5),$6),mkstringnode("</")),$9),mkstringnode(">"));
}
-MAYBE_XMLATTRIBUTES: {$$=strdup("");}
-MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {$$=concat2(" ",$1);}
-XMLATTRIBUTES: XMLATTRIBUTE {$$=$1;}
-XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE {$$=concat3($1," ",$2);free($1);free($2);}
+XMLATTRIBUTES: XMLATTRIBUTE {
+ $$ = $1;
+}
+XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE {
+ $$ = mkaddnode($1, mkaddnode(mkstringnode(" "),$2));
+}
+XMLATTRIBUTE: XMLEXPR2 {
+ $$ = $1;
+}
+XMLATTRIBUTE: XMLEXPR2 '=' T_STRING {
+ char* str = string_cstr(&$3);
+ $$ = mkaddnode($1, mkstringnode(concat2("=",str)));
+ free(str);
+}
+XMLATTRIBUTE: XMLEXPR2 '=' XMLEXPR2 {
+ $$ = mkaddnode($1, mkaddnode(mkstringnode("=\""), mkaddnode($3, mkstringnode("\""))));
+}
+XMLATTRIBUTE: T_IDENTIFIER '=' XMLEXPR2 {
+ $$ = mkaddnode(mkaddnode(mkstringnode(concat2($1,"=\"")), $3), mkstringnode("\""));
+}
XMLATTRIBUTE: T_IDENTIFIER '=' T_STRING {
char* str = string_cstr(&$3);
- $$=allocprintf("%s=\"%s\"", $1,str);
+ $$=mkstringnode(allocprintf("%s=%s", $1,str));
free(str);
free($1);free((char*)$3.str);
}
slotinfo_t*s = find_class($1);
if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
$$ = (classinfo_t*)s;
+ registry_use(s);
}
PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
free($1);$1=0;
$$ = (classinfo_t*)s;
+ registry_use(s);
}
CLASS_SPEC: PACKAGEANDCLASS
$$.c = code_append($$.c, paramcode);
$$.c = abc_constructprop2($$.c, name, $4.number);
multiname_destroy(name);
- } else if($$.c->opcode == OPCODE_GETSLOT) {
+ } 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;
+ MULTINAME(m, c);
+ $$.c = abc_findpropstrict2(0, &m);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_constructprop2($$.c, &m, $4.number);
+ /*} else if($$.c->opcode == OPCODE_GETSLOT) {
int slot = (int)(ptroff_t)$$.c->data[0];
trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
multiname_t*name = t->name;
$$.c = code_cutlast($$.c);
$$.c = code_append($$.c, paramcode);
- $$.c = abc_constructprop2($$.c, name, $4.number);
+ $$.c = abc_constructprop2($$.c, name, $4.number);*/
} else {
$$.c = code_append($$.c, paramcode);
$$.c = abc_construct($$.c, $4.number);
$$.c = code_append($$.c, paramcode);
$$.c = abc_callproperty2($$.c, name, $3.number);
multiname_destroy(name);
- } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
+/* } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
int slot = (int)(ptroff_t)$$.c->data[0];
trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
if(t->kind!=TRAIT_METHOD) {
$$.c = code_cutlast($$.c);
$$.c = code_append($$.c, paramcode);
//$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
- $$.c = abc_callproperty2($$.c, name, $3.number);
+ $$.c = abc_callproperty2($$.c, name, $3.number);*/
} else if($$.c->opcode == OPCODE_GETSUPER) {
multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
$$.c = code_cutlast($$.c);
// calling a class is like a typecast
$$.t = (classinfo_t*)v.t->data;
} else {
- $$.c = abc_coerce_a($$.c);
$$.t = TYPE_ANY;
+ $$.c = abc_coerce_a($$.c);
}
}
DICTLH: T_IDENTIFIER {$$=abc_pushstring(0,$1);}
DICTLH: T_STRING {$$=abc_pushstring2(0,&$1);}
+DICTLH: T_INT {syntaxerror("dictionary keys must be strings");}
+DICTLH: T_UINT {syntaxerror("dictionary keys must be strings");}
+DICTLH: T_FLOAT {syntaxerror("dictionary keys must be strings");}
DICT_EXPRPAIR_LIST : DICTLH ':' NONCOMMAEXPRESSION {
$$.cc = 0;
}
E : XML {
- typedcode_t v;
- v.c = 0;
- namespace_t ns = {ACCESS_PACKAGE, ""};
- multiname_t m = {QNAME, &ns, 0, "XML"};
- v.c = abc_getlex2(v.c, &m);
- v.c = abc_pushstring(v.c, $1);
- v.c = abc_construct(v.c, 1);
- v.t = TYPE_XML;
- $$ = mkcodenode(v);
+ $$ = $1;
}
/* regexp */
E : T_REGEXP {
typedcode_t v;
v.c = 0;
- namespace_t ns = {ACCESS_PACKAGE, ""};
- multiname_t m = {QNAME, &ns, 0, "RegExp"};
+ multiname_t m = {QNAME, &stdns, 0, "RegExp"};
if(!$1.options) {
v.c = abc_getlex2(v.c, &m);
v.c = abc_pushstring(v.c, $1.pattern);
$$ = mkcodenode(v);
}
+E : KW_ARGUMENTS {
+ PASS1
+ state->method->need_arguments = 1;
+ PASS2
+ typedcode_t v;
+ v.c = abc_getlocal(0, state->method->need_arguments);
+ v.t = TYPE_ARRAY;
+ $$ = mkcodenode(v);
+}
+
/* array */
E : '[' MAYBE_EXPRESSION_LIST ']' {
typedcode_t v;
E : E "as" E {$$ = mknode2(&node_as, $1, $3);}
E : E "instanceof" E {$$ = mknode2(&node_instanceof, $1, $3);}
E : E "is" E {$$ = mknode2(&node_is, $1, $3);}
-E : "typeof" '(' E ')' {$$ = mknode1(&node_typeof, $3);}
+E : "typeof" E {$$ = mknode1(&node_typeof, $2);}
E : "void" E {$$ = mknode1(&node_void, $2);}
E : "void" { $$ = mkconstnode(constant_new_undefined());}
E : '(' COMMA_EXPRESSION ')' { $$=$2;}
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;
}
E : '@' T_IDENTIFIER {
- // attribute occuring in .() loops
- // TODO
- $$ = mkdummynode();
- as3_warning("ignored @ operator");
+ typedcode_t v;
+ multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $2};
+ v.c = abc_getlex2(0, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
}
-E : E '.' '(' E ')' {
- // filter
- // TODO: this needs to be implemented using a loop
- $$ = mkdummynode();
- as3_warning("ignored .() operator");
+E : E '.' '(' {PASS12 new_state();state->xmlfilter=1;} E ')' {
+ PASS1 old_state();
+ PASS2
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read($5);
+ code_t*c = 0;
+ int index = alloc_local();
+ int result = alloc_local();
+ int tmp = alloc_local();
+ int xml = alloc_local();
+
+ c = code_append(c, v.c);
+ c = abc_checkfilter(c);
+ c = abc_coerce_a(c); //hasnext2 converts to *
+ c = abc_setlocal(c, xml);
+ multiname_t m = {QNAME, &stdns, 0, "XMLList"};
+ c = abc_getlex2(c, &m);
+ c = abc_construct(c, 0);
+ c = abc_setlocal(c, result);
+ c = abc_pushbyte(c, 0);
+ c = abc_setlocal(c, index);
+ code_t*jmp = c = abc_jump(c, 0);
+ code_t*loop = c = abc_label(c);
+ c = abc_getlocal(c, xml);
+ c = abc_getlocal(c, index);
+ c = abc_nextvalue(c);
+ c = abc_dup(c);
+ c = abc_setlocal(c, tmp);
+ c = abc_pushwith(c);
+ c = code_append(c, w.c);
+ c = abc_popscope(c);
+ code_t*b = c = abc_iffalse(c, 0);
+ c = abc_getlocal(c, result);
+ c = abc_getlocal(c, index);
+ c = abc_getlocal(c, tmp);
+ multiname_t m2 = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
+ c = abc_setproperty2(c, &m2);
+ c = b->branch = jmp->branch = abc_nop(c);
+ c = abc_kill(c, tmp);
+ c = abc_hasnext2(c, xml, index);
+ c = abc_iftrue(c, loop);
+ c = abc_getlocal(c, result);
+ c = abc_kill(c, xml);
+ c = abc_kill(c, result);
+ c = abc_kill(c, index);
+
+ c = var_block(c, state->vars);
+ old_state();
+ typedcode_t r;
+ r.c = c;
+ r.t = TYPE_XMLLIST;
+ $$ = mkcodenode(r);
}
ID_OR_NS : T_IDENTIFIER {$$=$1;}
-ID_OR_NS : T_NAMESPACE {$$=(char*)$1;}
-SUBNODE: T_IDENTIFIER
+ID_OR_NS : '*' {$$="*";}
+SUBNODE: X_IDENTIFIER
| '*' {$$="*";}
-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;
+%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);
}
- $$ = mkcodenode(v);
+};
+
+E : E '.' ID_OR_NS "::" SUBNODE {
+ $$ = 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;
$$.c = abc_getslot($$.c, f->slot);
} else {
if(!f) {
- as3_softwarning("Access of undefined property '%s' in %s", $3, t->name);
+ if(!TYPE_IS_XMLLIST(t)) {
+ as3_softwarning("Access of undefined property '%s' in %s", $3, t->name);
+ }
}
MEMBER_MULTINAME(m, f, $3);
$$.c = abc_getproperty2($$.c, &m);
/* when resolving a property on an unknown type, we do know the
name of the property (and don't seem to need the package), but
we need to make avm2 try out all access modes */
- as3_warning("Resolving %s on unknown type", $3);
+ as3_softwarning("Resolving %s on unknown type", $3);
multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
$$.c = abc_getproperty2($$.c, &m);
$$.c = abc_coerce_a($$.c);
}
%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;
return mkcodenode(o);
}
- int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
-
- /* look at current class' members */
- if(!state->method->inner &&
- state->cls &&
- (f = findmember_nsset(state->cls->info, name, 1)))
- {
- // name is a member or attribute in this class
- int var_is_static = (f->flags&FLAG_STATIC);
-
- if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
- /* if the variable is a constant (and we know what is evaluates to), we
- can just use the value itself */
- varinfo_t*v = (varinfo_t*)f;
- if(v->value) {
- return mkconstnode(v->value);
- }
- }
-
- if(var_is_static >= i_am_static) {
- if(f->kind == INFOTYPE_METHOD) {
- o.t = TYPE_FUNCTION(f);
- } else {
- o.t = f->type;
- }
-
- if(var_is_static && !i_am_static) {
- /* access to a static member from a non-static location.
- do this via findpropstrict:
- there doesn't seem to be any non-lookup way to access
- static properties of a class */
- state->method->late_binding = 1;
- o.t = f->type;
- namespace_t ns = {f->access, f->package};
- multiname_t m = {QNAME, &ns, 0, name};
- o.c = abc_findpropstrict2(o.c, &m);
- o.c = abc_getproperty2(o.c, &m);
- return mkcodenode(o);
- } else if(f->slot>0) {
- o.c = abc_getlocal_0(o.c);
- o.c = abc_getslot(o.c, f->slot);
- return mkcodenode(o);
- } else {
- namespace_t ns = {f->access, f->package};
- multiname_t m = {QNAME, &ns, 0, name};
- o.c = abc_getlocal_0(o.c);
- o.c = abc_getproperty2(o.c, &m);
- return mkcodenode(o);
- }
- }
- }
+ int i_am_static = state->method->is_static;
+
+ if(!state->method->inner && !state->xmlfilter && state->cls)
+ {
+ /* look at current class' members */
+ if((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(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
+ /* if the variable is a constant (and we know what is evaluates to), we
+ can just use the value itself */
+ varinfo_t*v = (varinfo_t*)f;
+ if(v->value) {
+ return mkconstnode(v->value);
+ }
+ }
+
+ if(var_is_static >= i_am_static) {
+ if(f->kind == INFOTYPE_METHOD) {
+ o.t = TYPE_FUNCTION(f);
+ } else {
+ o.t = f->type;
+ }
+
+ if(var_is_static && !i_am_static) {
+ /* access to a static member from a non-static location.
+ do this via findpropstrict:
+ there doesn't seem to be any non-lookup way to access
+ static properties of a class */
+ state->method->late_binding = 1;
+ o.t = f->type;
+ namespace_t ns = {f->access, f->package};
+ multiname_t m = {QNAME, &ns, 0, name};
+ o.c = abc_findpropstrict2(o.c, &m);
+ o.c = abc_getproperty2(o.c, &m);
+ return mkcodenode(o);
+ } else if(f->slot>0) {
+ o.c = abc_getlocal_0(o.c);
+ o.c = abc_getslot(o.c, f->slot);
+ return mkcodenode(o);
+ } else {
+ MEMBER_MULTINAME(m, f, name);
+ o.c = abc_getlocal_0(o.c);
+ o.c = abc_getproperty2(o.c, &m);
+ return mkcodenode(o);
+ }
+ }
+ }
+ /* special case: it's allowed to access non-static constants
+ from a static context */
+ if(i_am_static && (f=findmember_nsset(state->cls->info, name, 1, 0))) {
+ if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
+ varinfo_t*v = (varinfo_t*)f;
+ if(v->value) {
+ return mkconstnode(v->value);
+ }
+ }
+ }
+ }
/* look at actual classes, in the current package and imported */
- if((a = find_class(name))) {
- o = push_class(a);
+ if(!state->xmlfilter && (a = find_class(name))) {
+ registry_use(a);
+ if(state->cls && state->cls->info == (classinfo_t*)a && i_am_static) {
+ o.c = abc_getlocal_0(0);
+ o.t = TYPE_CLASS((classinfo_t*)a);
+ } else {
+ o = push_class(a);
+ }
return mkcodenode(o);
}
/* look through package prefixes */
- if(dict_contains(state->import_toplevel_packages, name) ||
- registry_ispackage(name)) {
- o.c = abc___pushpackage__(o.c, name);
+ if(!state->xmlfilter &&
+ (dict_contains(state->import_toplevel_packages, name) ||
+ registry_ispackage(name))) {
+ o.c = abc___pushpackage__(o.c, (char*)name);
o.t = 0;
return mkcodenode(o); //?
}
/* unknown object, let the avm2 resolve it */
if(1) {
- //as3_softwarning("Couldn't resolve '%s', doing late binding", name);
- as3_warning("Couldn't resolve '%s', doing late binding", name);
+ if(!state->method->inner && !state->xmlfilter) {
+ /* we really should make inner functions aware of the class context */
+ as3_warning("Couldn't resolve '%s', doing late binding", name);
+ }
state->method->late_binding = 1;
multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, name};
etc. which is *correct* because local variables of the parent function
would shadow those.
*/
+
if(!find_variable(state, $1)) {
- if(state->method->inner) {
- unknown_variable($1);
- }
+ unknown_variable($1);
/* let the compiler know that it might want to check the current directory/package
for this identifier- maybe there's a file $1.as defining $1. */
as3_schedule_class_noerror(state->package, $1);
}
NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
PASS12
- trie_put(active_namespaces, $2->name, (void*)$2->url);
+ dict_put(state->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);
$$=0;
}
+DEFAULT_NAMESPACE : "default xml" "namespace" '=' E
+{
+ $$ = 0;
+ $$ = code_append($$, node_read($4).c);
+ $$ = abc_dxnslate($$);
+}
+
USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
PASS12
- const char*url = $3->name;
varinfo_t*s = (varinfo_t*)$3;
if(s->kind == INFOTYPE_UNRESOLVED) {
syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
if(!s->value || !NS_TYPE(s->value->type))
syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
- url = s->value->ns->name;
- trie_put(active_namespaces, $3->name, (void*)url);
+ const char*url = s->value->ns->name;
+ dict_put(state->namespaces, (unsigned char*)$3->name, (void*)url);
add_active_url(url);
$$=0;
}