%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 XML_ID_OR_EXPR XMLEXPR1 XMLEXPR2
+%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
// 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
methodstate_t*static_init;
//code_t*init;
//code_t*static_init;
+ parsedclass_t*dependencies;
char has_constructor;
} classstate_t;
char inner;
char uses_parent_function;
+ char no_variable_scoping;
int uses_slots;
dict_t*slots;
int activation_var;
methodstate_list_t*innerfunctions;
};
+void methodstate_destroy(methodstate_t*m)
+{
+ dict_destroy(m->unresolved_variables);
+ m->unresolved_variables = 0;
+ list_free(m->innerfunctions);m->innerfunctions=0;
+}
+
typedef struct _state {
struct _state*old;
int level;
state = state->old;
if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
- free(leaving->method);
- leaving->method=0;
+ methodstate_destroy(leaving->method);leaving->method=0;
}
if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
free(leaving->cls);
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, 0, 1);
- global->init = abc_initscript(global->file);
+ 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)
{
- 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->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;
}
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(!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);
}
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 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(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;
}
- variable_t*v = new_variable2("arguments", TYPE_ARRAY, 0, 0);
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;
}
}
- 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;
- }
-
-
- 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;
}
}
}
state->cls->init = rfx_calloc(sizeof(methodstate_t));
state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
state->cls->static_init->is_static=FLAG_STATIC;
- state->cls->static_init->variable_count=1;
/* notice: we make no effort to initialize the top variable (local0) here,
even though it has special meaning. We just rely on the fact
that pass 1 won't do anything with variables */
if(as3_pass == 2) {
state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
- state->method = state->cls->init;
parserassert(state->cls && state->cls->info);
+ state->method = state->cls->static_init;
+
function_initvars(state->cls->init, 0, 0, 0, 1);
+ state->cls->static_init->variable_count=1;
function_initvars(state->cls->static_init, 0, 0, 0, 0);
if(extends && (extends->flags & FLAG_FINAL))
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)) {
//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();
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;
}
}
+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, 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);
}
}
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;
}
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) {
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(0) { //Error #1026: Slot 1 exceeds slotCount=0 of global
| 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, 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 '.' '*' {
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;
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 ------------------------------ */
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 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();} '}' {
- $$=strdup("{...}");
- as3_warning("xml string substitution not yet supported");
+ $$ = $2;
}
XMLEXPR2 : '{' E {PASS_ALWAYS tokenizer_begin_xml();} '}' {
- $$=strdup("{...}");
- as3_warning("xml string substitution not yet supported");
+ $$ = $2;
}
-XMLTEXT : {$$="";}
+XMLTEXT : {$$=mkstringnode("");}
XMLTEXT : XMLTEXT XMLEXPR1 {
- $$ = concat2($1, "{...}");
+ $$ = 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;
}
-XMLTEXT : XMLTEXT T_STRING {$$=concat2($1, string_cstr(&$2));}
-XMLTEXT : XMLTEXT '>' {$$=concat2($1, ">");}
-
-XML2 : XMLNODE XMLTEXT {$$=concat2($1,$2);}
-XML2 : XML2 XMLNODE XMLTEXT {$$=concat3($1,$2,$3);free($1);free($2);free($3);}
-
-XML_ID_OR_EXPR: T_IDENTIFIER {$$=$1;}
-XML_ID_OR_EXPR: XMLEXPR2 {$$=$1;}
-XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
- $$ = allocprintf("<%s%s>%s</%s>", $2, $3, $5, $8);
- free($2);free($3);free($5);free($8);
+MAYBE_XMLATTRIBUTES: {
+ $$ = mkstringnode("");
+}
+MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {
+ $$ = mkaddnode(mkstringnode(" "),$1);
}
+
XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' {
- $$ = allocprintf("<%s%s/>", $2, $3);
+ //$$ = 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);
- free($2);free($3);free($5);free($6);free($9);
+ //$$ = 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 {
- $$ = strdup("{...}");
+ $$ = $1;
}
XMLATTRIBUTE: XMLEXPR2 '=' T_STRING {
char* str = string_cstr(&$3);
- $$ = concat2("{...}=",str);
+ $$ = mkaddnode($1, mkstringnode(concat2("=",str)));
+ free(str);
}
XMLATTRIBUTE: XMLEXPR2 '=' XMLEXPR2 {
- $$ = strdup("{...}={...}");
+ $$ = mkaddnode($1, mkaddnode(mkstringnode("=\""), mkaddnode($3, mkstringnode("\""))));
}
XMLATTRIBUTE: T_IDENTIFIER '=' XMLEXPR2 {
- $$ = concat2($1,"={...}");
+ $$ = 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);
}
$$.c = code_append($$.c, paramcode);
$$.c = abc_constructprop2($$.c, name, $4.number);
multiname_destroy(name);
+ } else if(is_getlocal($$.c)) {
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_construct($$.c, $4.number);
} else if(TYPE_IS_CLASS(v.t) && v.t->data) {
code_free($$.c);
classinfo_t*c = v.t->data;
// 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);
}
}
}
E : XML {
- typedcode_t v;
- v.c = 0;
- multiname_t m = {QNAME, &stdns, 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 : 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;}
c = abc_kill(c, result);
c = abc_kill(c, index);
- c = var_block(c);
+ c = var_block(c, state->vars);
old_state();
typedcode_t r;
r.c = c;
}
ID_OR_NS : T_IDENTIFIER {$$=$1;}
+ID_OR_NS : '*' {$$="*";}
ID_OR_NS : T_NAMESPACE {$$=(char*)$1;}
-SUBNODE: T_IDENTIFIER
+SUBNODE: X_IDENTIFIER
| '*' {$$="*";}
+/*
+MAYBE_NS: T_IDENTIFIER "::" {$$=$1;}
+ | T_NAMESPACE "::" {$$=(char*)$1;}
+ | '*' "::" {$$="*";}
+ | {$$=0;}*/
+
E : E '.' ID_OR_NS "::" SUBNODE {
typedcode_t v = node_read($1);
typedcode_t w = node_read(resolve_identifier($3));
$$.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);
o.t = v->type;
return mkcodenode(o);
}
- if((v = find_slot(state, name))) {
+ if((v = find_slot(state->method, name))) {
o.c = abc_getscopeobject(o.c, 1);
o.c = abc_getslot(o.c, v->index);
o.t = v->type;
*/
if(!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);
$$=0;
}
+DEFAULT_NAMESPACE : "default xml" "namespace" '=' E
+{
+ as3_warning("default xml namespaces not supported yet");
+ $$ = 0;
+}
+
USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
PASS12
const char*url = $3->name;