#include "opcodes.h"
#include "compiler.h"
#include "expr.h"
+#include "initcode.h"
extern int a3_lex();
%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
+%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> MAYBEEXPRESSION
%type <value> DELETE
%type <node> E COMMA_EXPRESSION
+%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> IMPLEMENTS_LIST
%type <classinfo> EXTENDS CLASS_SPEC
%type <classinfo_list> EXTENDS_LIST
-
%type <classinfo> CLASS PACKAGEANDCLASS
%type <classinfo_list> CLASS_SPEC_LIST
-
+%type <node> XMLEXPR1 XMLEXPR2 XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT XML_ID_OR_EXPR XML
%type <classinfo> TYPE
//%type <token> VARIABLE
-%type <value> VAR_READ MEMBER
+%type <value> MEMBER
%type <value> NEW
//%type <token> T_IDENTIFIER
%type <value> FUNCTIONCALL
-%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD
+%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES
+%type <value_list> MAYBE_DICT_EXPRPAIR_LIST DICT_EXPRPAIR_LIST WITH_HEAD
+%type <code> DICTLH
// precedence: from low to high
%nonassoc '&'
%nonassoc "==" "!=" "===" "!=="
%nonassoc "is" "as" "in"
+%left below_lt
%nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
%left "<<" ">>" ">>>"
%left below_minus
%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
+%left T_NAMESPACE
%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;
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;
};
+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;
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 {
abc_file_t*file;
- abc_script_t*init;
+
+ parsedclass_list_t*classes;
+ abc_script_t*classinit;
+
+ abc_script_t*init; //package-level code
+
dict_t*token2info;
dict_t*file2token2info;
} global_t;
DECLARE_LIST(state);
-#define MULTINAME(m,x) \
- multiname_t m;\
- namespace_t m##_ns;\
- (x)->package; \
- registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
-
+/* 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};
dict_destroy(state->imports);state->imports=0;
}
if(state->vars) {
- int t;
- for(t=0;t<state->vars->hashsize;t++) {
- dictentry_t*e =state->vars->slots[t];
- while(e) {
- free(e->data);e->data=0;
- e = e->next;
- }
- }
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;
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;
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->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
} 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 = 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
global->file->flags &= ~ABCFILE_LAZY;
global->file2token2info = dict_new();
global->token2info = 0;
+ global->classinit = abc_initscript(global->file);
}
void* finish_parser()
{
dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
global->token2info=0;
+
+ initcode_add_classlist(global->classinit, global->classes);
+
return global->file;
}
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)
{
- 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->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 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;
}
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);
+ if(name) {
+ if(!method->no_variable_scoping)
+ {
+ if(dict_contains(state->vars, name)) {
+ *(int*)0=0;
+ syntaxerror("variable %s already defined", name);
+ }
+ dict_put(state->vars, name, v);
+ }
+ if(method->no_variable_scoping &&
+ as3_pass==2 &&
+ dict_contains(state->allvars, name))
+ {
+ variable_t*v = dict_lookup(state->allvars, name);
+ if(v->type != type)
+ syntaxerror("variable %s already defined.", name);
+ return v;
+ }
+ dict_put(state->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->allvars:state->vars));
/* append return if necessary */
if(!c || (c->opcode != OPCODE_RETURNVOID &&
c->opcode != OPCODE_RETURNVALUE)) {
return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
}
-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;
}
+ 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;
+ }
}
-
- 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;
- }
+
+ 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*, 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);
+ }
+ v->type = type;
}
- v->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
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->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);
int num_interfaces = (list_length(implements));
state->cls->info = classinfo_register(access, package, classname, num_interfaces);
state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
+ state->cls->info->superclass = extends;
int pos = 0;
classinfo_list_t*l = implements;
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);
pos++;
}
- /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
- state->cls->info->superclass = extends;
-
/* generate the abc code for this class */
MULTINAME(classname2,state->cls->info);
multiname_t*extends2 = sig2mname(extends);
- state->cls->abc = abc_class_new(global->file, &classname2, extends2);
+ /* don't add the class to the class index just yet- that will be done later
+ by initscript */
+ state->cls->abc = abc_class_new(0, &classname2, extends2);
+ state->cls->abc->file = global->file;
+
+ multiname_destroy(extends2);
if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
if(state->cls->info->flags&FLAG_INTERFACE) {
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);
}
- /* write the construction code for this class to the global init
- function */
- int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
-
- abc_method_body_t*m = global->init->method->body;
- __ getglobalscope(m);
- classinfo_t*s = extends;
-
- int count=0;
-
- while(s) {
- //TODO: take a look at the current scope stack, maybe
- // we can re-use something
- s = s->superclass;
- if(!s)
- break;
-
- multiname_t*s2 = sig2mname(s);
- __ getlex2(m, s2);
- multiname_destroy(s2);
-
- __ pushscope(m); count++;
- m->code = m->code->prev->prev; // invert
- }
- /* continue appending after last op end */
- while(m->code && m->code->next) m->code = m->code->next;
-
- /* TODO: if this is one of *our* classes, we can also
- do a getglobalscope/getslot <nr> (which references
- the init function's slots) */
- if(extends2) {
- __ getlex2(m, extends2);
- __ dup(m);
- /* notice: we get a Verify Error #1107 if the top elemnt on the scope
- stack is not the superclass */
- __ pushscope(m);count++;
- } else {
- __ pushnull(m);
- /* notice: we get a verify error #1107 if the top element on the scope
- stack is not the global object */
- __ getlocal_0(m);
- __ pushscope(m);count++;
- }
- __ newclass(m,state->cls->abc);
- while(count--) {
- __ popscope(m);
- }
- __ setslot(m, slotindex);
- multiname_destroy(extends2);
+ 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)) {
if(state->package && state->package[0]) {
as3_globalclass = concat3(state->package, ".", classname);
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(!state->cls) {
//package method
minfo = methodinfo_register_global(ns.access, state->package, name);
- minfo->return_type = 0; // save this for pass 2
+ 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);
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->return_type = 0; // save this for pass 2
+ minfo->return_type = return_type;
// getslot on a member slot only returns "undefined", so no need
// to actually store these
//state->minfo->slot = state->method->abc->method->trait->slot_id;
// not sure wether to look into superclasses here, too
minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
if(minfo) {
- if(minfo->kind!=INFOTYPE_SLOT)
+ if(minfo->kind!=INFOTYPE_VAR)
syntaxerror("class already contains a method called '%s'", name);
if(!(minfo->subtype & (SUBTYPE_GETSET)))
syntaxerror("class already contains a field called '%s'", name);
}*/
} else {
minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
- minfo->kind = INFOTYPE_SLOT; //hack
+ minfo->kind = INFOTYPE_VAR; //hack
minfo->subtype = gs;
- minfo->return_type = 0;
+ minfo->return_type = type;
}
+
/* can't assign a slot as getter and setter might have different slots */
//minfo->slot = slot;
}
//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->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->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);
}
state->cls->has_constructor |= state->method->is_constructor;
}
- state->method->info->return_type = return_type;
- 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->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);
}
- 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->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 = 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;
} else if(state->method->is_constructor) {
f = abc_class_getconstructor(state->cls->abc, type2);
} else if(!state->method->is_global) {
- namespace_t mname_ns = modifiers2access(mod);
- multiname_t mname = {QNAME, &mname_ns, 0, name};
-
+ namespace_t ns = modifiers2access(mod);
+ 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;
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;}
MAYBECODE: {$$=code_new();}
-CODE: CODE CODEPIECE {$$=code_append($1,$2);}
+CODE: CODE CODEPIECE {
+ $$=code_append($1,$2);
+}
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
CODEPIECE: CONTINUE
CODEPIECE: RETURN
CODEPIECE: THROW
-CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {PASS_ALWAYS as3_pass=$1;}
+CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {
+ PASS_ALWAYS
+ if(as3_pass) {
+ $$ = $3;
+ } else {
+ $$ = 0;
+ }
+ as3_pass=$1;
+}
//CODEBLOCK : '{' CODE '}' {$$=$2;}
//CODEBLOCK : '{' '}' {$$=0;}
/* ------------ 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, 0, 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 {
+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);
}
-
- 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);
+ int it = alloc_local();
+ int array = alloc_local();
$$ = code_new();
$$ = code_append($$, $4.c);
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, 0, 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";}
+ | "namespace" {PASS12 $$="namespace";}
+ | "NaN" {PASS12 $$="NaN";}
| T_NAMESPACE {PASS12 $$=$1;}
PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
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)) {
as3_schedule_class($2->package, $2->name);
}
- classinfo_t*c = $2;
- if(!c)
- syntaxerror("Couldn't import class\n");
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 '.' '*' {
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);
}
}
+ static trait_t* add_abc_slot(modifiers_t* modifiers, const char*name, multiname_t*m, code_t***c)
+ {
+ int flags = modifiers->flags;
+ namespace_t ns = modifiers2access(modifiers);
+
+ /* slot name */
+ multiname_t mname = {QNAME, &ns, 0, name};
+
+ trait_list_t**traits;
+ 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;
+ } else if(flags&FLAG_STATIC) {
+ // static variable
+ traits = &state->cls->abc->static_traits;
+ code = &state->cls->static_init->header;
+ } else {
+ // 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)
+ *m = *multiname_clone(&mname);
+
+ return trait_new_member(traits, 0, multiname_clone(&mname), 0);
+ }
};
VARCONST: "var" | "const"
if(as3_pass == 2) {
varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
- /* slot name */
- multiname_t mname = {QNAME, &ns, 0, $1};
-
- trait_list_t**traits;
+ multiname_t mname;
code_t**code;
- if(!state->cls) {
- // global variable
- ns.name = state->package;
- traits = &global->init->traits;
- code = &global->init->method->body->code;
- } else if(flags&FLAG_STATIC) {
- // static variable
- traits = &state->cls->abc->static_traits;
- code = &state->cls->static_init->header;
- } else {
- // instance variable
- traits = &state->cls->abc->traits;
- code = &state->cls->init->header;
- }
-
- trait_t*t=0;
+ trait_t*t = add_abc_slot(slotstate_flags, $1, &mname, &code);
+
if($2) {
MULTINAME(m, $2);
- t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
- } else {
- t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
+ t->type_name = multiname_clone(&m);
}
info->slot = t->slot_id;
+
+ /* workaround for "VerifyError: Error #1053: Illegal override of ::test2 in C1"
+ FIXME: is there a way to use slots and still don't have conflicting overrides?
+ */
+ info->slot = t->slot_id = 0;
constant_t cval = $3->type->eval($3);
if(cval.type!=CONSTANT_UNKNOWN) {
/* compile time constant */
t->value = malloc(sizeof(constant_t));
memcpy(t->value, &cval, sizeof(constant_t));
+ info->value = constant_clone(t->value);
} else {
typedcode_t v = node_read($3);
/* initalization code (if needed) */
c = abc_getlocal_0(c);
c = code_append(c, v.c);
c = converttype(c, v.t, $2);
- c = abc_setslot(c, t->slot_id);
+ if(!t->slot_id) {
+ c = abc_initproperty2(c, &mname);
+ } else {
+ c = abc_setslot(c, t->slot_id);
+ }
}
*code = code_append(*code, c);
}
if(slotstate_varconst==KW_CONST) {
t->kind= TRAIT_CONST;
+ info->flags |= FLAG_CONST;
}
}
/* ------------ constants -------------------------------------- */
MAYBECONSTANT: {$$=0;}
-MAYBECONSTANT: '=' CONSTANT {$$=$2;}
+MAYBECONSTANT: '=' E {
+ $$ = malloc(sizeof(constant_t));
+ *$$ = node_eval($2);
+ if($$->type == CONSTANT_UNKNOWN) {
+ syntaxerror("can't evaluate default parameter value (needs to be a compile-time constant)");
+ }
+}
//CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
CONSTANT : T_INT {$$ = constant_new_int($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);
- }
+/*CONSTANT : T_NAMESPACE {
+ // TODO
+ $$ = constant_new_namespace(namespace_new_namespace($1.url));
}*/
+/* ---------------------------xml ------------------------------ */
+
+%code {
+ static int xml_level = 0;
+};
+
+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 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();}
+
+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;
+}
+
+MAYBE_XMLATTRIBUTES: {
+ $$ = mkstringnode("");
+}
+MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {
+ $$ = mkaddnode(mkstringnode(" "),$1);
+}
+
+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 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 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(">"));
+}
+
+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);
+ $$=mkstringnode(allocprintf("%s=%s", $1,str));
+ free(str);
+ free($1);free((char*)$3.str);
+}
+
/* ------------ classes and interfaces (body, functions) ------- */
// non-vararg version
TYPE : CLASS_SPEC {PASS12 $$=$1;}
| '*' {PASS12 $$=TYPE_ANY;}
- | "void" {PASS12 $$=TYPE_ANY;}
+ | "void" {PASS12 $$=TYPE_VOID;}
/*
| "String" {$$=registry_getstringclass();}
| "int" {$$=registry_getintclass();}
$$.c = code_append($$.c, paramcode);
$$.c = abc_constructprop2($$.c, name, $4.number);
multiname_destroy(name);
- } else if($$.c->opcode == OPCODE_GETSLOT) {
+ } 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);
if(TYPE_IS_FUNCTION(v.t) && v.t->data) {
$$.t = ((methodinfo_t*)(v.t->data))->return_type;
+ } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
+ // 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);
}
}
// ----------------------- expression types -------------------------------------
-NONCOMMAEXPRESSION : E %prec below_minus {
+NONCOMMAEXPRESSION : E %prec below_lt {
$$ = node_read($1);
}
EXPRESSION : COMMA_EXPRESSION {
$$ = node_read($1);
}
-COMMA_EXPRESSION : E %prec below_minus {
+COMMA_EXPRESSION : E %prec below_lt {
$$ = mkmultinode(&node_comma, $1);
}
-COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_minus {
+COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_lt {
$$ = multinode_extend($1, $3);
}
VOIDEXPRESSION : E %prec below_minus {
$$ = node_exec($1);
}
-VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_minus {
+VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_lt {
$$ = $1;
$$ = code_append($$, node_exec($3));
}
-MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
-MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
+MAYBE_DICT_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
+MAYBE_DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST {$$=$1;}
-EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+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;
- $$.cc = code_append($$.cc, $1.c);
+ $$.cc = code_append($$.cc, $1);
$$.cc = code_append($$.cc, $3.c);
$$.number = 2;
}
-EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST ',' DICTLH ':' NONCOMMAEXPRESSION {
$$.cc = $1.cc;
$$.number = $1.number+2;
- $$.cc = code_append($$.cc, $3.c);
+ $$.cc = code_append($$.cc, $3);
$$.cc = code_append($$.cc, $5.c);
}
// ----------------------- expression evaluation -------------------------------------
E : INNERFUNCTION %prec prec_none {$$ = mkcodenode($1);}
-E : VAR_READ %prec T_IDENTIFIER {$$ = mkcodenode($1);}
E : MEMBER %prec '.' {$$ = mkcodenode($1);}
E : NEW {$$ = mkcodenode($1);}
E : DELETE {$$ = mkcodenode($1);}
E : FUNCTIONCALL {$$ = mkcodenode($1);}
+E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
E : CONSTANT {
$$ = mkconstnode($1);
}
+E : XML {
+ $$ = $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;
}
/* dictionary */
-E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
+E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' {
typedcode_t v;
v.c = code_new();
v.c = code_append(v.c, $2.cc);
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;}
}
E : '@' T_IDENTIFIER {
- // attribute 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 '.' '@' T_IDENTIFIER {
- // child attribute TODO
- $$ = 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);
+}
-E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
- // namespace declaration TODO
- $$ = mkdummynode();
- as3_warning("ignored :: operator");
- }
+ID_OR_NS : T_IDENTIFIER {$$=$1;}
+ID_OR_NS : '*' {$$="*";}
+ID_OR_NS : T_NAMESPACE {$$=(char*)$1;}
+SUBNODE: X_IDENTIFIER
+ | '*' {$$="*";}
-E : E ".." T_IDENTIFIER {
- // descendants TODO
- $$ = mkdummynode();
- as3_warning("ignored .. operator");
- }
+/*
+MAYBE_NS: T_IDENTIFIER "::" {$$=$1;}
+ | T_NAMESPACE "::" {$$=(char*)$1;}
+ | '*' "::" {$$="*";}
+ | {$$=0;}*/
-E : E '.' '(' E ')' {
- // filter TODO
- $$ = mkdummynode();
- as3_warning("ignored .() operator");
- }
+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);
+}
+E : E ".." SUBNODE {
+ typedcode_t v = node_read($1);
+ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+ v.c = abc_getdescendants2(v.c, &m);
+ v.t = TYPE_XMLLIST;
+ $$ = mkcodenode(v);
+}
+E : E '.' '[' E ']' {
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read($4);
+ multiname_t m = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
+ v.c = code_append(v.c, w.c);
+ v.c = converttype(w.c, w.t, TYPE_STRING);
+ v.c = abc_getproperty2(v.c, &m);
+ v.t = TYPE_XMLLIST;
+ $$ = mkcodenode(v);
+}
-//E : E "::" '[' E ']' {
-// // qualified expression TODO
-// $$.c = abc_pushundefined(0);
-// $$.t = 0;
-// as3_warning("ignored ::[] operator");
-// }
+E : E '.' '@' SUBNODE {
+ typedcode_t v = node_read($1);
+ multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
+ v.c = abc_getproperty2(v.c, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
+E : E ".." '@' SUBNODE {
+ typedcode_t v = node_read($1);
+ multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
+ v.c = abc_getdescendants2(v.c, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
+E : E '.' '@' '[' E ']' {
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read($5);
+ multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
+ v.c = code_append(v.c, w.c);
+ v.c = converttype(w.c, w.t, TYPE_STRING);
+ v.c = abc_getproperty2(v.c, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
+E : E ".." '@' '[' E ']' {
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read($5);
+ multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
+ v.c = code_append(v.c, w.c);
+ v.c = converttype(w.c, w.t, TYPE_STRING);
+ v.c = abc_getdescendants2(v.c, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
-MEMBER : E '.' T_IDENTIFIER {
+MEMBER : E '.' SUBNODE {
typedcode_t v1 = node_read($1);
$$.c = v1.c;
classinfo_t*t = v1.t;
t = t->data;
is_static = 1;
}
- if(t) {
+ if(TYPE_IS_XML(t)) {
+ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+ $$.c = abc_getproperty2($$.c, &m);
+ $$.c = abc_coerce_a($$.c);
+ $$.t = TYPE_XMLLIST;
+ } else if(t) {
if(t->subtype==INFOTYPE_UNRESOLVED) {
syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
}
$$.c = abc_getslot($$.c, f->slot);
} else {
if(!f) {
- as3_warning("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);
}
$$.t = slotinfo_gettype((slotinfo_t*)f);
if(!$$.t)
$$.c = abc_coerce_a($$.c);
+
} else if(v1.c && v1.c->opcode == OPCODE___PUSHPACKAGE__) {
string_t*package = v1.c->data[0];
char*package2 = concat3(package->str, ".", $3);
/* 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)
+ {
+ typedcode_t o;
+ o.t = 0;
+ o.c = 0;
+
+ slotinfo_t*a = 0;
+ memberinfo_t*f = 0;
+
+ variable_t*v;
+ /* 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);
+ }
+ 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->is_static;
+
+ /* look at current class' members */
+ if(!state->method->inner &&
+ !state->xmlfilter &&
+ 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 {
+ MEMBER_MULTINAME(m, f, name);
+ o.c = abc_getlocal_0(o.c);
+ o.c = abc_getproperty2(o.c, &m);
+ return mkcodenode(o);
+ }
+ }
+ }
+
+ /* look at actual classes, in the current package and imported */
+ if(!state->xmlfilter && (a = find_class(name))) {
+ 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(!state->xmlfilter &&
+ (dict_contains(state->import_toplevel_packages, name) ||
+ registry_ispackage(name))) {
+ o.c = abc___pushpackage__(o.c, name);
+ o.t = 0;
+ return mkcodenode(o); //?
+ }
+
+ /* unknown object, let the avm2 resolve it */
+ if(1) {
+ 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};
+
+ o.t = 0;
+ o.c = abc_findpropstrict2(o.c, &m);
+ o.c = abc_getproperty2(o.c, &m);
+ return mkcodenode(o);
+ }
+ }
+};
+
VAR_READ : T_IDENTIFIER {
PASS1
/* Queue unresolved identifiers for checking against the parent
etc. which is *correct* because local variables of the parent function
would shadow those.
*/
- if(state->method->inner && !find_variable(state, $1)) {
+
+ if(!find_variable(state, $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);
}
- /* 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);
+ $$ = 0;
PASS2
- $$.t = 0;
- $$.c = 0;
- slotinfo_t*a = 0;
- memberinfo_t*f = 0;
-
- variable_t*v;
- /* look at variables */
- if((v = find_variable(state, $1))) {
- // $1 is a local variable
- $$.c = abc_getlocal($$.c, v->index);
- $$.t = v->type;
- break;
- }
- if((v = find_slot(state, $1))) {
- $$.c = abc_getscopeobject($$.c, 1);
- $$.c = abc_getslot($$.c, v->index);
- $$.t = v->type;
- break;
- }
-
- 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, $1, 1)) &&
- (f->flags&FLAG_STATIC) >= i_am_static)
- {
- // $1 is a function in this class
- int var_is_static = (f->flags&FLAG_STATIC);
-
- if(f->kind == INFOTYPE_METHOD) {
- $$.t = TYPE_FUNCTION(f);
- } else {
- $$.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;
- $$.t = f->type;
- namespace_t ns = {f->access, f->package};
- multiname_t m = {QNAME, &ns, 0, $1};
- $$.c = abc_findpropstrict2($$.c, &m);
- $$.c = abc_getproperty2($$.c, &m);
- break;
- } else if(f->slot>0) {
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getslot($$.c, f->slot);
- break;
- } else {
- namespace_t ns = {f->access, f->package};
- multiname_t m = {QNAME, &ns, 0, $1};
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getproperty2($$.c, &m);
- break;
- }
- }
-
- /* look at actual classes, in the current package and imported */
- if((a = find_class($1))) {
- $$ = push_class(a);
- break;
- }
-
- /* look through package prefixes */
- if(dict_contains(state->import_toplevel_packages, $1) ||
- registry_ispackage($1)) {
- $$.c = abc___pushpackage__($$.c, $1);
- $$.t = 0;
- break;
- }
-
- /* unknown object, let the avm2 resolve it */
- if(1) {
- //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
- as3_warning("Couldn't resolve '%s', doing late binding", $1);
- state->method->late_binding = 1;
-
- multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
-
- $$.t = 0;
- $$.c = abc_findpropstrict2($$.c, &m);
- $$.c = abc_getproperty2($$.c, &m);
- }
+ $$ = resolve_identifier($1);
}
// ----------------- namespaces -------------------------------------------------
+%code {
+ void add_active_url(const char*url)
+ {
+ NEW(namespace_t,n);
+ n->name = url;
+ list_append(state->active_namespace_urls, n);
+ }
+};
+
NAMESPACE_ID : "namespace" T_IDENTIFIER {
PASS12
NEW(namespace_decl_t,n);
ns.access = ACCESS_NAMESPACE;
ns.name = $2->url;
var->value = constant_new_namespace(&ns);
+
+ if(as3_pass==2) {
+ MULTINAME(m, TYPE_NAMESPACE);
+ trait_t*t = add_abc_slot(&$1, $2->name, 0, 0);
+ t->value = var->value;
+ t->type_name = multiname_clone(&m);
+ }
$$=0;
}
-%code {
- void add_active_url(const char*url)
- {
- NEW(namespace_t,n);
- n->name = url;
- list_append(state->active_namespace_urls, n);
- }
-};
+DEFAULT_NAMESPACE : "default xml" "namespace" '=' E
+{
+ as3_warning("default xml namespaces not supported yet");
+ $$ = 0;
+}
USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
PASS12
syntaxerror("Couldn't resolve namespace %s", $3->name);
}
- if(!s || s->kind != INFOTYPE_SLOT)
+ if(!s || s->kind != INFOTYPE_VAR)
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);