%token<token> KW_NATIVE
%token<token> KW_FUNCTION "function"
%token<token> KW_UNDEFINED "undefined"
+%token<token> KW_CONTINUE "continue"
%token<token> KW_FOR "for"
%token<token> KW_CLASS "class"
%token<token> KW_CONST "const"
%token<token> KW_SET "set"
%token<token> KW_VOID "void"
%token<token> KW_STATIC
+%token<token> KW_INSTANCEOF "instanceof"
%token<token> KW_IMPORT "import"
%token<token> KW_RETURN "return"
%token<token> KW_TYPEOF "typeof"
%token<token> KW_BREAK "break"
%token<token> KW_IS "is"
%token<token> KW_AS "as"
+%token<token> KW_DO "do"
%token<token> T_EQEQ "=="
%token<token> T_EQEQEQ "==="
%token<token> T_USHR ">>>"
%token<token> T_SHR ">>"
-%type <id> X_IDENTIFIER PACKAGE
+%type <id> X_IDENTIFIER PACKAGE MAYBELABEL
%type <token> VARCONST
%type <code> CODE
%type <code> CODEPIECE
%type <value> MAYBEEXPRESSION
%type <value> E DELETE
%type <value> CONSTANT
-%type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
+%type <code> FOR IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE
%type <token> USE_NAMESPACE
%type <code> FOR_INIT
%type <token> IMPORT
abc_class_t*abc;
code_t*init;
code_t*static_init;
+ char has_constructor;
} classstate_t;
typedef struct _methodstate {
/* method data */
memberinfo_t*info;
- dict_t*vars;
char late_binding;
/* code that needs to be executed at the start of
a method (like initializing local registers) */
classstate_t*cls;
methodstate_t*method;
+
+ dict_t*vars;
} state_t;
typedef struct _global {
namespace_t m##_ns;\
registry_fill_multiname(&m, &m##_ns, x);
-#define MEMBER_MULTINAME(m,f) \
+#define MEMBER_MULTINAME(m,f,n) \
multiname_t m;\
namespace_t m##_ns;\
if(f) { \
m.type = MULTINAME; \
m.ns =0; \
m.namespace_set = &nopackage_namespace_set; \
- m.name = f->name; \
+ m.name = n; \
}
/* warning: list length of namespace set is undefined */
state = s;
state->level++;
state->has_own_imports = 0;
+ state->vars = dict_new();
}
static void state_has_imports()
{
list_free(oldstate->wildcard_imports);
dict_destroy(oldstate->imports);oldstate->imports=0;
}
- if(state->method)
- state->method->initcode =
- code_append(state->method->initcode,
- oldstate->method->initcode);
}
void initialize_state()
{
state->cls->abc = abc_class_new(global->file, &classname2, extends2);
if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
- if(flags&FLAG_DYNAMIC) abc_class_sealed(state->cls->abc);
- if(interface) abc_class_interface(state->cls->abc);
+ if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
+ if(interface) {
+ state->cls->info->flags |= CLASS_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);
multiname_destroy(extends2);
}
+static code_t* wrap_function(code_t*c,code_t*initcode, code_t*body)
+{
+ c = code_append(c, initcode);
+ c = code_append(c, body);
+ /* append return if necessary */
+ if(!c || c->opcode != OPCODE_RETURNVOID &&
+ c->opcode != OPCODE_RETURNVALUE) {
+ c = abc_returnvoid(c);
+ }
+ return c;
+}
+
static void endclass()
{
- if(state->cls->init) {
- if(!state->cls->abc->constructor) {
- abc_method_t*m = abc_class_constructor(state->cls->abc, 0);
- m->body->code = code_append(m->body->code, state->cls->init);
- m->body->code = abc_returnvoid(m->body->code);
- } else {
- code_t*c = state->cls->abc->constructor->body->code;
- c = code_append(state->cls->init, c);
- state->cls->abc->constructor->body->code = c;
+ if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
+ code_t*c = 0;
+ c = abc_getlocal_0(c);
+ c = abc_constructsuper(c, 0);
+ state->cls->init = code_append(state->cls->init, c);
+ }
- }
+ if(state->cls->init) {
+ abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
+ m->body->code = wrap_function(0, state->cls->init, m->body->code);
}
if(state->cls->static_init) {
- if(!state->cls->abc->static_constructor) {
- abc_method_t*m = abc_class_staticconstructor(state->cls->abc, 0);
- m->body->code = code_append(m->body->code, state->cls->static_init);
- m->body->code = abc_returnvoid(m->body->code);
- } else {
- state->cls->abc->static_constructor->body->code =
- code_append(state->cls->static_init, state->cls->abc->static_constructor->body->code);
- }
+ abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
+ m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
+ } else {
+ // handy for scope testing
+ /*code_t*c = 0;
+ c = abc_pop(c);
+ c = abc_pop(c);
+ abc_class_getstaticconstructor(state->cls->abc,0)->body->code = c;*/
}
old_state();
while(s) {
variable_t*v = 0;
if(s->state->method)
- v = dict_lookup(s->state->method->vars, name);
+ v = dict_lookup(s->state->vars, name);
if(v) {
if(m) {
*m = v->type;
}
static char variable_exists(char*name)
{
- return dict_lookup(state->method->vars, name)!=0;
+ return dict_lookup(state->vars, name)!=0;
}
static int new_variable(char*name, classinfo_t*type)
{
NEW(variable_t, v);
v->index = global->variable_count;
v->type = type;
- dict_put(state->method->vars, name, v);
+ dict_put(state->vars, name, v);
return global->variable_count++;
}
#define TEMPVARNAME "__as3_temp__"
{
int i = find_variable(TEMPVARNAME, 0);
if(i<0) {
- return new_variable(TEMPVARNAME, 0);
- } else {
- return i;
+ i = new_variable(TEMPVARNAME, 0);
}
+ return i;
}
code_t* killvars(code_t*c)
{
int t;
- for(t=0;t<state->method->vars->hashsize;t++) {
- dictentry_t*e =state->method->vars->slots[t];
+ for(t=0;t<state->vars->hashsize;t++) {
+ dictentry_t*e =state->vars->slots[t];
while(e) {
variable_t*v = (variable_t*)e->data;
//do this always, otherwise register types don't match
return c;
}
+void check_code_for_break(code_t*c)
+{
+ while(c) {
+ if(c->opcode == OPCODE___BREAK__) {
+ char*name = string_cstr(c->data[0]);
+ syntaxerror("Unresolved \"break %s\"", name);
+ }
+ if(c->opcode == OPCODE___CONTINUE__) {
+ char*name = string_cstr(c->data[0]);
+ syntaxerror("Unresolved \"continue %s\"", name);
+ }
+ c=c->prev;
+ }
+}
+
static void check_constant_against_type(classinfo_t*t, constant_t*c)
{
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->a bc->method->trait->slot_id;
+ //state->minfo->slot = state->method->abc->method->trait->slot_id;
} else {
int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
classinfo_t*type=0;
state->method->initcode = 0;
state->method->is_constructor = !strcmp(state->cls->info->name,name);
state->method->has_super = 0;
+
+ state->cls->has_constructor |= state->method->is_constructor;
global->variable_count = 0;
- state->method->vars = dict_new();
+
/* state->vars is initialized by state_new */
if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info)!=0) syntaxerror("Internal error");
param_list_t*p=0;
for(p=params->list;p;p=p->next) {
new_variable(p->param->name, p->param->type);
}
+ if(state->method->is_constructor)
+ name = "__as3_constructor__";
state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
}
multiname_t*type2 = sig2mname(return_type);
int slot = 0;
if(state->method->is_constructor) {
- f = abc_class_constructor(state->cls->abc, type2);
- name = "__as3_constructor__";
+ f = abc_class_getconstructor(state->cls->abc, type2);
} else {
if(flags&FLAG_STATIC)
f = abc_class_staticmethod(state->cls->abc, type2, &mname);
f = abc_class_method(state->cls->abc, type2, &mname);
slot = f->trait->slot_id;
}
- state->method->info->slot = slot;
+ //flash doesn't seem to allow us to access function slots
+ //state->method->info->slot = slot;
if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
syntaxerror("non-optional parameter not allowed after optional parameters");
}
}
- f->body->code = body;
+ check_code_for_break(body);
+
+ if(f->body)
+ f->body->code = body;
+ else //interface
+ if(body)
+ syntaxerror("interface methods can't have a method body");
old_state();
}
return 1; // FIXME
}
-void breakjumpsto(code_t*c, code_t*jump)
+void breakjumpsto(code_t*c, char*name, code_t*jump)
{
- while(c->prev)
- c=c->prev;
while(c) {
if(c->opcode == OPCODE___BREAK__) {
- c->opcode = OPCODE_JUMP;
- c->branch = jump;
+ string_t*name2 = c->data[0];
+ if(!name2->len || !strncmp(name2->str, name, name2->len)) {
+ c->opcode = OPCODE_JUMP;
+ c->branch = jump;
+ }
+ }
+ c=c->prev;
+ }
+}
+void continuejumpsto(code_t*c, char*name, code_t*jump)
+{
+ while(c) {
+ if(c->opcode == OPCODE___CONTINUE__) {
+ string_t*name2 = c->data[0];
+ if(!name2->len || !strncmp(name2->str, name, name2->len)) {
+ c->opcode = OPCODE_JUMP;
+ c->branch = jump;
+ }
}
- c = c->next;
+ c = c->prev;
}
}
code_t*defaultvalue(code_t*c, classinfo_t*type)
{
- if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) {
+ if(TYPE_IS_INT(type)) {
c = abc_pushbyte(c, 0);
+ } else if(TYPE_IS_UINT(type)) {
+ c = abc_pushuint(c, 0);
+ } else if(TYPE_IS_FLOAT(type)) {
+ c = abc_pushnan(c);
} else if(TYPE_IS_BOOLEAN(type)) {
c = abc_pushfalse(c);
} else {
MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/}
MAYBECODE: {$$=code_new();}
-CODE: CODE CODEPIECE {$$=code_append($1,$2);}
-CODE: CODEPIECE {$$=$1;}
+CODE: CODE CODEPIECE {
+ $$=code_append($1,$2);
+}
+CODE: CODEPIECE {
+ $$=$1;
+}
CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
CODEPIECE: VOIDEXPRESSION {$$=$1}
CODEPIECE: FOR {$$=$1}
CODEPIECE: WHILE {$$=$1}
+CODEPIECE: DO_WHILE {$$=$1}
CODEPIECE: BREAK {$$=$1}
+CODEPIECE: CONTINUE {$$=$1}
CODEPIECE: RETURN {$$=$1}
CODEPIECE: IF {$$=$1}
CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
//MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
- $$ = state->method->initcode;state->method->initcode=0;
-
+ $$ = code_new();
$$ = code_append($$, $4.c);
code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
if($7) {
myjmp = $$ = abc_jump($$, 0);
}
- myif->branch = $$ = abc_label($$);
+ myif->branch = $$ = abc_nop($$);
if($7) {
$$ = code_append($$, $7);
- myjmp->branch = $$ = abc_label($$);
+ myjmp->branch = $$ = abc_nop($$);
}
$$ = killvars($$);old_state();
}
+MAYBELABEL : T_IDENTIFIER ':' {$$=$1;}
+MAYBELABEL : {$$="";}
+
FOR_INIT : {$$=code_new();}
FOR_INIT : VARIABLE_DECLARATION
FOR_INIT : VOIDEXPRESSION
-FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
- $$ = state->method->initcode;state->method->initcode=0;
-
- $$ = code_append($$, $4);
+FOR : MAYBELABEL "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
+ $$ = code_new();
+ $$ = code_append($$, $5);
code_t*loopstart = $$ = abc_label($$);
- $$ = code_append($$, $6.c);
+ $$ = code_append($$, $7.c);
code_t*myif = $$ = abc_iffalse($$, 0);
- $$ = code_append($$, $10);
- $$ = code_append($$, $8);
+ $$ = code_append($$, $11);
+ code_t*cont = $$ = abc_nop($$);
+ $$ = code_append($$, $9);
$$ = abc_jump($$, loopstart);
- code_t*out = $$ = abc_label($$);
- breakjumpsto($$, out);
+ code_t*out = $$ = abc_nop($$);
+ breakjumpsto($$, $1, out);
+ continuejumpsto($$, $1, cont);
myif->branch = out;
$$ = killvars($$);old_state();
}
-WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
- $$ = state->method->initcode;state->method->initcode=0;
+WHILE : MAYBELABEL "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
+ $$ = code_new();
code_t*myjmp = $$ = abc_jump($$, 0);
code_t*loopstart = $$ = abc_label($$);
- $$ = code_append($$, $6);
- myjmp->branch = $$ = abc_label($$);
- $$ = code_append($$, $4.c);
+ $$ = code_append($$, $7);
+ myjmp->branch = $$ = abc_nop($$);
+ $$ = code_append($$, $5.c);
$$ = abc_iftrue($$, loopstart);
- code_t*out = $$ = abc_label($$);
- breakjumpsto($$, out);
+ code_t*out = $$ = abc_nop($$);
+ breakjumpsto($$, $1, out);
+ continuejumpsto($$, $1, loopstart);
- $$ = killvars($$);old_state();
+ $$ = killvars($$);
+ old_state();
+}
+
+DO_WHILE : MAYBELABEL "do" {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
+ $$ = code_new();
+ code_t*loopstart = $$ = abc_label($$);
+ $$ = code_append($$, $4);
+ code_t*cont = $$ = abc_nop($$);
+ $$ = code_append($$, $7.c);
+ $$ = abc_iftrue($$, loopstart);
+ code_t*out = $$ = abc_nop($$);
+ breakjumpsto($$, $1, out);
+ continuejumpsto($$, $1, cont);
+ $$ = killvars($$);
+ old_state();
}
-BREAK : "break" {
- $$ = abc___break__(0);
+BREAK : "break" %prec prec_none {
+ $$ = abc___break__(0, "");
+}
+BREAK : "break" T_IDENTIFIER {
+ $$ = abc___break__(0, $2);
+}
+CONTINUE : "continue" %prec prec_none {
+ $$ = abc___continue__(0, "");
+}
+CONTINUE : "continue" T_IDENTIFIER {
+ $$ = abc___continue__(0, $2);
}
/* ------------ packages and imports ---------------- */
c = abc_pushscope(c);
}
if(state->method->is_constructor && !state->method->has_super) {
- // generate default constructor
+ // call default constructor
c = abc_getlocal_0(c);
c = abc_constructsuper(c, 0);
}
-
- c = code_append(c, state->method->initcode);
- c = code_append(c, $11);
-
- /* append return if necessary */
- if(!c || c->opcode != OPCODE_RETURNVOID &&
- c->opcode != OPCODE_RETURNVALUE) {
- c = abc_returnvoid(c);
- }
+ c = wrap_function(c, state->method->initcode, $11);
endfunction(0,$1,$3,$4,&$6,$8,c);
}
int slot = (int)(ptroff_t)$$.c->data[0];
trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
if(t->kind!=TRAIT_METHOD) {
- //flash allows to assign closures to members.
- //syntaxerror("not a function");
+ //ok: flash allows to assign closures to members.
}
name = t->name;
$$.c = code_cutlast($$.c);
for(l=$3;l;l=l->next) {
$$.c = code_append($$.c, l->typedcode->c);len++;
}
+ /*
+ this is dependent on the control path, check this somewhere else
+ if(state->method->has_super)
+ syntaxerror("constructor may call super() only once");
+ */
state->method->has_super = 1;
$$.c = abc_constructsuper($$.c, len);
$$.c = abc_pushundefined($$.c);
}
}
+E : E "instanceof" E
+ {$$.c = code_append($1.c, $3.c);
+ $$.c = abc_instanceof($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+
E : E "is" E {$$.c = code_append($1.c, $3.c);
$$.c = abc_istypelate($$.c);
$$.t = TYPE_BOOLEAN;
$$.t = 0; // array elements have unknown type
}
+E : '[' MAYBE_EXPRESSION_LIST ']' {
+ $$.c = code_new();
+ typedcode_list_t*l = 0;
+ int len = 0;
+ for(l=$2;l;l=l->next) {
+ $$.c = code_append($$.c, l->typedcode->c);len++;
+ }
+ $$.c = abc_newarray($$.c, len);
+ $$.t = registry_getarrayclass();
+}
+
E : E "*=" E {
code_t*c = $3.c;
if(BOTH_INT($1,$3)) {
memberinfo_t*f = registry_findmember(t, $3);
namespace_t ns = {flags2access(f->flags), ""};
- MEMBER_MULTINAME(m, f);
+ MEMBER_MULTINAME(m, f, $3);
$$.c = 0;
$$.c = abc_getlocal_0($$.c);
$$.c = abc_getsuper2($$.c, &m);
if(f && f->slot && !noslot) {
$$.c = abc_getslot($$.c, f->slot);
} else {
- MEMBER_MULTINAME(m, f);
+ MEMBER_MULTINAME(m, f, $3);
$$.c = abc_getproperty2($$.c, &m);
}
/* determine type */
/* look at classes in the current package and imported classes */
} else if((a = find_class($1))) {
- if(a->slot) {
- $$.c = abc_getglobalscope($$.c);
- $$.c = abc_getslot($$.c, a->slot);
- } else {
+ if(a->flags & FLAG_METHOD) {
MULTINAME(m, a);
- $$.c = abc_getlex2($$.c, &m);
+ $$.c = abc_findpropstrict2($$.c, &m);
+ $$.c = abc_getproperty2($$.c, &m);
+ $$.t = TYPE_FUNCTION(a->function);
+ } else {
+ if(a->slot) {
+ $$.c = abc_getglobalscope($$.c);
+ $$.c = abc_getslot($$.c, a->slot);
+ } else {
+ MULTINAME(m, a);
+ $$.c = abc_getlex2($$.c, &m);
+ }
+ $$.t = TYPE_CLASS(a);
}
- $$.t = TYPE_CLASS(a);
/* unknown object, let the avm2 resolve it */
} else {