#include "registry.h"
#include "code.h"
#include "opcodes.h"
+#include "compiler.h"
+
+extern int a3_lex();
%}
%type <classinfo_list> IMPLEMENTS_LIST
%type <classinfo> EXTENDS
%type <classinfo_list> EXTENDS_LIST
-%type <classinfo> CLASS PACKAGEANDCLASS QNAME
-%type <classinfo_list> QNAME_LIST
+%type <classinfo> CLASS PACKAGEANDCLASS CLASS_SPEC
+%type <classinfo_list> CLASS_SPEC_LIST
%type <classinfo> TYPE
//%type <token> VARIABLE
%type <value> VAR_READ
%left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
%left "--" "++"
%nonassoc below_curly
-%left '[' ']' '{' "new" '.' ".." "::"
+
+%left '('
+%left new2
+%left '[' ']' "new" '{' '.' ".." "::"
+
%nonassoc T_IDENTIFIER
%left above_identifier
%left below_else
%nonassoc "else"
-%left '('
// needed for "return" precedence:
%nonassoc T_STRING T_REGEXP
%nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
%nonassoc "false" "true" "null" "undefined" "super" "function"
-%nonassoc above_function
+%left above_function
%{
-static int yyerror(char*s)
+static int a3_error(char*s)
{
syntaxerror("%s", s);
return 0; //make gcc happy
}
+
static char* concat2(const char* t1, const char* t2)
{
int l1 = strlen(t1);
char has_constructor;
} classstate_t;
+DECLARE_LIST(methodstate);
+
typedef struct _methodstate {
/* method data */
memberinfo_t*info;
char is_constructor;
char has_super;
char is_global;
+ int variable_count;
+
+ char inner;
+ abc_method_t*abc;
+ int var_index; // for inner methods
+
abc_exception_list_t*exceptions;
+
+ methodstate_list_t*innerfunctions;
} methodstate_t;
typedef struct _state {
import_list_t*wildcard_imports;
dict_t*imports;
char has_own_imports;
+ char new_vars; // e.g. transition between two functions
classstate_t*cls;
methodstate_t*method;
typedef struct _global {
abc_file_t*file;
abc_script_t*init;
-
- int variable_count;
+ dict_t*token2info;
} global_t;
static global_t*global = 0;
state_t*leaving = state;
state = state->old;
+
+ if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
+ free(leaving->method);
+ leaving->method=0;
+ }
+ if(leaving->cls && leaving->cls != state->cls) {
+ free(leaving->cls);
+ leaving->cls=0;
+ }
+
state_destroy(leaving);
}
-void initialize_parser()
-{
- global = rfx_calloc(sizeof(global_t));
- global->file = abc_file_new();
- global->file->flags &= ~ABCFILE_LAZY;
- global->variable_count = 1;
- global->init = abc_initscript(global->file);
- code_t*c = global->init->method->body->code;
- c = abc_getlocal_0(c);
- c = abc_pushscope(c);
- /*c = abc_findpropstrict(c, "[package]::trace");
- c = abc_pushstring(c, "[entering global init function]");
- c = abc_callpropvoid(c, "[package]::trace", 1);*/
- global->init->method->body->code = c;
-}
-
void initialize_file(char*filename)
{
+ if(state) {
+ syntaxerror("invalid call to initialize_file during parsing of another file");
+ }
new_state();
- state->package = filename;
- // needed for state->method->late_binding:
+ state->package = strdup(filename);
+
state->method = rfx_calloc(sizeof(methodstate_t));
+ state->method->variable_count = 1;
}
+
void finish_file()
{
if(!state || state->level!=1) {
- syntaxerror("unexpected end of file");
+ syntaxerror("unexpected end of file in pass %d", as3_pass);
}
+ free(state->method);state->method=0;
+
+ //free(state->package);state->package=0; // used in registry
+
state_destroy(state);state=0;
}
+void initialize_parser()
+{
+ global = rfx_calloc(sizeof(global_t));
+ global->file = abc_file_new();
+ global->file->flags &= ~ABCFILE_LAZY;
+ global->token2info = dict_new2(&ptr_type);
+
+ global->init = abc_initscript(global->file);
+ code_t*c = global->init->method->body->code;
+ c = abc_getlocal_0(c);
+ c = abc_pushscope(c);
+ global->init->method->body->code = c;
+}
+
void* finish_parser()
{
code_t*c = global->init->method->body->code;
c = abc_callpropvoid(c, "[package]::trace", 1);*/
c = abc_returnvoid(c);
global->init->method->body->code = c;
+ dict_destroy(global->token2info);global->token2info=0;
return global->file;
}
if(v) {
return v;
}
+ if(s->new_vars)
+ break;
s = s->old;
}
return 0;
return dict_lookup(state->vars, name)!=0;
}
code_t*defaultvalue(code_t*c, classinfo_t*type);
-static int new_variable(char*name, classinfo_t*type, char init)
+static int new_variable(const char*name, classinfo_t*type, char init)
{
NEW(variable_t, v);
- v->index = global->variable_count;
+ v->index = state->method->variable_count;
v->type = type;
v->init = init;
dict_put(state->vars, name, v);
- return global->variable_count++;
+ return state->method->variable_count++;
}
#define TEMPVARNAME "__as3_temp__"
static int gettempvar()
return c;
}
+#define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
+
+static void parsererror(const char*file, int line, const char*f)
+{
+ syntaxerror("internal error in %s, %s:%d", f, file, line);
+}
+
+
+code_t* method_header()
+{
+ code_t*c = 0;
+ if(state->method->late_binding && !state->method->inner) {
+ c = abc_getlocal_0(c);
+ c = abc_pushscope(c);
+ }
+ /*if(state->method->innerfunctions) {
+ c = abc_newactivation(c);
+ c = abc_pushscope(c);
+ }*/
+ if(state->method->is_constructor && !state->method->has_super) {
+ // call default constructor
+ c = abc_getlocal_0(c);
+ c = abc_constructsuper(c, 0);
+ }
+ methodstate_list_t*l = state->method->innerfunctions;
+ while(l) {
+ parserassert(l->methodstate->abc);
+ c = abc_newfunction(c, l->methodstate->abc);
+ c = abc_setlocal(c, l->methodstate->var_index);
+ free(l->methodstate);l->methodstate=0;
+ l = l->next;
+ }
+ list_free(state->method->innerfunctions);
+ state->method->innerfunctions = 0;
+ return c;
+}
+
+
static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
{
c = code_append(c, header);
new_state();
/*printf("entering package \"%s\"\n", name);*/
state->package = strdup(name);
- global->variable_count = 1;
}
static void endpackage()
{
old_state();
}
+
char*as3_globalclass=0;
static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
{
syntaxerror("inner classes now allowed");
}
new_state();
- global->variable_count = 1;
state->cls = rfx_calloc(sizeof(classstate_t));
- state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor
token_list_t*t=0;
classinfo_list_t*mlist=0;
char*package=0;
if(!(flags&FLAG_PUBLIC) && !state->package) {
- access = ACCESS_PRIVATE; package = current_filename;
+ access = ACCESS_PRIVATE; package = strdup(current_filename_short);
} else if(!(flags&FLAG_PUBLIC) && state->package) {
access = ACCESS_PACKAGEINTERNAL; package = state->package;
} else if(state->package) {
syntaxerror("public classes only allowed inside a package");
}
- if(registry_findclass(package, classname)) {
- syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
- }
-
+ if(as3_pass==1) {
+ state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor
+ state->method->variable_count = 1;
+ dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
- /* build info struct */
- int num_interfaces = (list_length(implements));
- state->cls->info = classinfo_register(access, package, classname, num_interfaces);
- state->cls->info->superclass = extends?extends:TYPE_OBJECT;
- int pos = 0;
- classinfo_list_t*l = implements;
- for(l=implements;l;l=l->next) {
- state->cls->info->interfaces[pos++] = l->classinfo;
+ if(registry_findclass(package, classname)) {
+ syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
+ }
+ /* build info struct */
+ int num_interfaces = (list_length(implements));
+ state->cls->info = classinfo_register(access, package, classname, num_interfaces);
}
- multiname_t*extends2 = sig2mname(extends);
-
- MULTINAME(classname2,state->cls->info);
+ if(as3_pass == 2) {
+ state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+
+ state->cls->info = registry_findclass(package, classname);
+ parserassert((int)state->cls->info);
+
+ /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
+ state->cls->info->superclass = extends?extends:TYPE_OBJECT;
+ int pos = 0;
+ classinfo_list_t*l = implements;
+ for(l=implements;l;l=l->next) {
+ state->cls->info->interfaces[pos++] = l->classinfo;
+ }
- /*if(extends) {
- state->cls_init = abc_getlocal_0(state->cls_init);
- state->cls_init = abc_constructsuper(state->cls_init, 0);
- }*/
+ /* 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);
- if(flags&FLAG_FINAL) abc_class_final(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);
- }
+ 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) {
+ state->cls->info->flags |= CLASS_INTERFACE;
+ abc_class_interface(state->cls->abc);
+ }
- abc_class_protectedNS(state->cls->abc, classname);
+ 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);
- }
+ for(mlist=implements;mlist;mlist=mlist->next) {
+ MULTINAME(m, mlist->classinfo);
+ abc_class_add_interface(state->cls->abc, &m);
+ }
- /* now write the construction code for this class */
- int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
+ /* 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;
+ 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);
-
- /* flash.display.MovieClip handling */
- if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
- if(state->package && state->package[0]) {
- as3_globalclass = concat3(state->package, ".", classname);
+ 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 {
- as3_globalclass = strdup(classname);
+ __ 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);
+
+ /* flash.display.MovieClip handling */
+
+ if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
+ if(state->package && state->package[0]) {
+ as3_globalclass = concat3(state->package, ".", classname);
+ } else {
+ as3_globalclass = strdup(classname);
+ }
}
}
- multiname_destroy(extends2);
}
static void endclass()
{
- 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->method->late_binding) {
- // class initialization code uses late binding
- code_t*c = 0;
- c = abc_getlocal_0(c);
- c = abc_pushscope(c);
- state->cls->static_init = code_append(c, state->cls->static_init);
- }
+ if(as3_pass == 2) {
+ 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->method->late_binding) {
+ // class initialization code uses late binding
+ code_t*c = 0;
+ c = abc_getlocal_0(c);
+ c = abc_pushscope(c);
+ state->cls->static_init = code_append(c, state->cls->static_init);
+ }
- 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) {
- abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
- m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
+ 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) {
+ abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
+ m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
+ }
}
- free(state->cls);state->cls=0;
- free(state->method);state->method=0;
old_state();
}
classinfo_t*type=0;
if(getset == KW_GET)
type = return_type;
- else if(params->list)
+ else if(params->list && params->list->param)
type = params->list->param->type;
// not sure wether to look into superclasses here, too
if((minfo=registry_findmember(state->cls->info, name, 0))) {
return minfo;
}
+static void function_initvars(params_t*params, int flags)
+{
+ if(state->method->inner)
+ new_variable("this", 0, 0);
+ else if(!state->method->is_global)
+ new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
+ else
+ new_variable("globalscope", 0, 0);
+
+ param_list_t*p=0;
+ for(p=params->list;p;p=p->next) {
+ new_variable(p->param->name, p->param->type, 0);
+ }
+
+ methodstate_list_t*l = state->method->innerfunctions;
+ while(l) {
+ methodstate_t*m = l->methodstate;
+ m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0);
+ l = l->next;
+ }
+}
+
+static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
+{
+ parserassert(state->method && state->method->info);
+
+ methodstate_t*parent_method = state->method;
+
+ if(as3_pass==1) {
+ // not valid yet
+ params = 0;
+ return_type = 0;
+ }
+
+ new_state();
+ state->new_vars = 1;
+
+ if(as3_pass == 1) {
+ state->method = rfx_calloc(sizeof(methodstate_t));
+ state->method->inner = 1;
+ state->method->variable_count = 0;
+ state->method->abc = rfx_calloc(sizeof(abc_method_t));
+
+ NEW(memberinfo_t,minfo);
+ minfo->name = name;
+ state->method->info = minfo;
+
+ list_append(parent_method->innerfunctions, state->method);
+
+ dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
+ }
+
+ if(as3_pass == 2) {
+ state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+ parserassert(state->method);
+
+ state->method->info->return_type = return_type;
+ function_initvars(params, 0);
+ }
+}
+
static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
params_t*params, classinfo_t*return_type)
{
syntaxerror("not able to start another method scope");
}
new_state();
- global->variable_count = 0;
- state->method = rfx_calloc(sizeof(methodstate_t));
- state->method->has_super = 0;
+
+ if(as3_pass == 1) {
+ state->method = rfx_calloc(sizeof(methodstate_t));
+ state->method->has_super = 0;
+ state->method->variable_count = 0;
- if(state->cls) {
- state->method->is_constructor = !strcmp(state->cls->info->name,name);
- state->cls->has_constructor |= state->method->is_constructor;
-
- new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
- } else {
- state->method->is_global = 1;
- state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
+ if(state->cls) {
+ state->method->is_constructor = !strcmp(state->cls->info->name,name);
+ } else {
+ state->method->is_global = 1;
+ state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
+ }
+ if(state->method->is_constructor)
+ name = "__as3_constructor__";
- new_variable("globalscope", 0, 0);
+ return_type = 0;
+ state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
+
+ dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
}
- /* state->vars is initialized by state_new */
-
- param_list_t*p=0;
- for(p=params->list;p;p=p->next) {
- new_variable(p->param->name, p->param->type, 0);
- }
- if(state->method->is_constructor)
- name = "__as3_constructor__";
- state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
+ if(as3_pass == 2) {
+ state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+ parserassert(state->method);
+
+ if(state->cls) {
+ state->cls->has_constructor |= state->method->is_constructor;
+ }
+
+ state->method->info->return_type = return_type;
+ function_initvars(params, flags);
+ }
}
-static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
+static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
params_t*params, classinfo_t*return_type, code_t*body)
{
+ if(as3_pass==1) {
+ old_state();
+ return 0;
+ }
+
abc_method_t*f = 0;
multiname_t*type2 = sig2mname(return_type);
int slot = 0;
- if(state->method->is_constructor) {
+ if(state->method->inner) {
+ f = state->method->abc;
+ abc_method_init(f, global->file, type2, 1);
+ } else if(state->method->is_constructor) {
f = abc_class_getconstructor(state->cls->abc, type2);
} else if(!state->method->is_global) {
namespace_t mname_ns = flags2namespace(flags, "");
syntaxerror("interface methods can't have a method body");
}
- free(state->method);state->method=0;
old_state();
+ return f;
}
-
-
char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
{
return 1; // FIXME
return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
}
-void parserassert(int b)
-{
- if(!b) syntaxerror("internal error: assertion failed");
-}
-
static classinfo_t* find_class(char*name)
{
classinfo_t*c=0;
/* try global package */
c = registry_findclass("", name);
if(c) return c;
-
+
/* try local "filename" package */
- c = registry_findclass(current_filename, name);
+ c = registry_findclass(current_filename_short, name);
if(c) return c;
return 0;
[prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
*/
-
if(in && in->opcode == OPCODE_COERCE_A) {
in = code_cutlast(in);
}
c = code_append(c, write);
c = code_append(c, r);
} else {
+ code_free(r);r=0;
temp = gettempvar();
if(prefix) {
c = code_append(c, prefix);
}
}
-%}
+#define PASS1 }} if(as3_pass == 1) {{
+#define PASS1END }} if(as3_pass == 2) {{
+#define PASS2 }} if(as3_pass == 2) {{
+#define PASS12 }} {{
+#define PASS12END }} if(as3_pass == 2) {{
+%}
%%
| FUNCTION_DECLARATION
| SLOT_DECLARATION
| PACKAGE_INITCODE
+ | T_IDENTIFIER "::" T_IDENTIFIER '{' PROGRAM_CODE_LIST '}' // conditional compilation
| ';'
MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
| FUNCTION_DECLARATION
| SLOT_DECLARATION
| PACKAGE_INITCODE
+ | T_IDENTIFIER "::" T_IDENTIFIER '{' INPACKAGE_CODE_LIST '}' // conditional compilation
| ';'
MAYBECODE: CODE {$$=$1;}
/* ------------ package init code ------------------- */
PACKAGE_INITCODE: CODE_STATEMENT {
- if($1) as3_warning("code ignored");
+ code_t**cc = &global->init->method->body->code;
+ *cc = code_append(*cc, $1);
}
/* ------------ variables --------------------------- */
$$ = converttype($$, $3.t, $2);
$$ = abc_setlocal($$, index);
} else {
+ code_free($3.c);
$$ = defaultvalue(0, $2);
$$ = abc_setlocal($$, index);
}
$$ = abc_coerce_a($$);
$$ = abc_setlocal($$, index);
} else {
+ code_free($3.c);
$$ = code_new();
}
}
MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
MAYBE_CASE_LIST : DEFAULT {$$=$1;}
MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
-CASE_LIST: CASE {$$=$1}
+CASE_LIST: CASE {$$=$1;}
CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
CASE: "case" E ':' MAYBECODE {
CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
-CATCH_FINALLY_LIST: CATCH_LIST {$$=$1};
+CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
$$ = $1;
$$.finally = 0;
/* ------------ packages and imports ---------------- */
X_IDENTIFIER: T_IDENTIFIER
- | "package" {$$="package";}
+ | "package" {PASS12 $$="package";}
-PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;}
-PACKAGE: X_IDENTIFIER {$$=strdup($1);}
+PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
+PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
-PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;}
-PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;}
+PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
+ MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
+PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
+ MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
-IMPORT : "import" QNAME {
+IMPORT : "import" PACKAGEANDCLASS {
+ PASS1
+ if(!registry_findclass($2->package, $2->name)) {
+ as3_schedule_class($2->package, $2->name);
+ }
+
+ PASS2
classinfo_t*c = $2;
if(!c)
syntaxerror("Couldn't import class\n");
$$=0;
}
IMPORT : "import" PACKAGE '.' '*' {
+ PASS1
+ if(strncmp("flash.", $2, 6)) {
+ as3_schedule_package($2);
+ }
+
+ PASS2
NEW(import_t,i);
i->package = $2;
state_has_imports();
/* ------------ classes and interfaces (header) -------------- */
-MAYBE_MODIFIERS : %prec above_function {$$=0;}
-MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
-MODIFIER_LIST : MODIFIER {$$=$1;}
-MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
-
-MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
- | KW_PRIVATE {$$=FLAG_PRIVATE;}
- | KW_PROTECTED {$$=FLAG_PROTECTED;}
- | KW_STATIC {$$=FLAG_STATIC;}
- | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
- | KW_FINAL {$$=FLAG_FINAL;}
- | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
- | KW_NATIVE {$$=FLAG_NATIVE;}
- | KW_INTERNAL {$$=FLAG_PACKAGEINTERNAL;}
+MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
+MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
+MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
+MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
+
+MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
+ | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
+ | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
+ | KW_STATIC {PASS12 $$=FLAG_STATIC;}
+ | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
+ | KW_FINAL {PASS12 $$=FLAG_FINAL;}
+ | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
+ | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
+ | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
EXTENDS : {$$=registry_getobjectclass();}
-EXTENDS : KW_EXTENDS QNAME {$$=$2;}
+EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
-EXTENDS_LIST : {$$=list_new();}
-EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
+EXTENDS_LIST : {PASS12 $$=list_new();}
+EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
-IMPLEMENTS_LIST : {$$=list_new();}
-IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
+IMPLEMENTS_LIST : {PASS12 $$=list_new();}
+IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
EXTENDS IMPLEMENTS_LIST
- '{' {startclass($1,$3,$4,$5, 0);}
+ '{' {PASS12 startclass($1,$3,$4,$5, 0);}
MAYBE_CLASS_BODY
- '}' {endclass();$$=0;}
+ '}' {PASS12 endclass();$$=0;}
INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
EXTENDS_LIST
- '{' {startclass($1,$3,0,$4,1);}
+ '{' {PASS12 startclass($1,$3,0,$4,1);}
MAYBE_INTERFACE_BODY
- '}' {endclass();$$=0;}
+ '}' {PASS12 endclass();$$=0;}
/* ------------ classes and interfaces (body) -------------- */
syntaxerror("variable declarations not allowed in interfaces");
}
IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
+ PASS12
$1 |= FLAG_PUBLIC;
if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
syntaxerror("invalid method modifiers: interface methods always need to be public");
}
startfunction(0,$1,$3,$4,&$6,$8);
endfunction(0,$1,$3,$4,&$6,$8, 0);
+ list_deep_free($6.list);
}
/* ------------ classes and interfaces (body, slots ) ------- */
code_t**code;
if(!state->cls) {
// global variable
+ mname_ns.name = state->package;
traits = &global->init->traits;
code = &global->init->method->body->code;
} else if(flags&FLAG_STATIC) {
STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
-STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
+STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
//STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
STATICCONSTANT : "true" {$$ = constant_new_true($1);}
STATICCONSTANT : "false" {$$ = constant_new_false($1);}
// non-vararg version
MAYBE_PARAM_LIST: {
+ PASS12
memset(&$$,0,sizeof($$));
}
MAYBE_PARAM_LIST: PARAM_LIST {
+ PASS12
$$=$1;
}
// vararg version
MAYBE_PARAM_LIST: "..." PARAM {
+ PASS12
memset(&$$,0,sizeof($$));
$$.varargs=1;
list_append($$.list, $2);
}
MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
+ PASS12
$$ =$1;
$$.varargs=1;
list_append($$.list, $4);
// non empty
PARAM_LIST: PARAM_LIST ',' PARAM {
+ PASS12
$$ = $1;
list_append($$.list, $3);
}
PARAM_LIST: PARAM {
+ PASS12
memset(&$$,0,sizeof($$));
list_append($$.list, $1);
}
PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
+ PASS1 $$=0;
+ PASS2
$$ = malloc(sizeof(param_t));
$$->name=$1;
$$->type = $3;
$$->value = $4;
}
PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
+ PASS1 $$=0;
+ PASS2
$$ = malloc(sizeof(param_t));
$$->name=$1;
$$->type = TYPE_ANY;
| {$$=0;}
FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
- MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
+ MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
{
- code_t*c = 0;
- if(state->method->late_binding) {
- c = abc_getlocal_0(c);
- c = abc_pushscope(c);
- }
- if(state->method->is_constructor && !state->method->has_super) {
- // call default constructor
- c = abc_getlocal_0(c);
- c = abc_constructsuper(c, 0);
- }
+ PASS1 old_state();list_deep_free($6.list);
+ PASS2
+ if(!state->method->info) syntaxerror("internal error");
+
+ code_t*c = method_header();
c = wrap_function(c, 0, $11);
+
endfunction(0,$1,$3,$4,&$6,$8,c);
+ list_deep_free($6.list);
$$=0;
}
MAYBE_IDENTIFIER: T_IDENTIFIER
-MAYBE_IDENTIFIER: {$$=0;}
-INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE '{' MAYBECODE '}'
+MAYBE_IDENTIFIER: {PASS12 $$=0;}
+INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
+ '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
{
- syntaxerror("nested functions not supported yet");
+ PASS1 old_state();list_deep_free($4.list);
+ PASS2
+ memberinfo_t*f = state->method->info;
+ if(!f) syntaxerror("internal error");
+
+ code_t*c = method_header();
+ c = wrap_function(c, 0, $9);
+
+ int index = state->method->var_index;
+ endfunction(0,0,0,$2,&$4,$6,c);
+ list_deep_free($4.list);
+
+ $$.c = abc_getlocal(0, index);
+ $$.t = TYPE_FUNCTION(f);
}
/* ------------- package + class ids --------------- */
CLASS: T_IDENTIFIER {
-
+ PASS1 $$=0;
+ PASS2
/* try current package */
$$ = find_class($1);
if(!$$) syntaxerror("Could not find class %s\n", $1);
}
PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
+ PASS1 static classinfo_t c;
+ memset(&c, 0, sizeof(c));
+ c.package = $1;
+ c.name = $3;
+ $$=&c;
+ PASS2
$$ = registry_findclass($1, $3);
if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
free($1);$1=0;
}
-QNAME: PACKAGEANDCLASS
- | CLASS
+CLASS_SPEC: PACKAGEANDCLASS
+ | CLASS
-QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
-QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
+CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
+CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
-TYPE : QNAME {$$=$1;}
+TYPE : CLASS_SPEC {$$=$1;}
| '*' {$$=registry_getanytype();}
| "void" {$$=registry_getanytype();}
/*
/* ----------function calls, delete, constructor calls ------ */
MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
-MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
+MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
$$.len= $1.len+1;
$$.cc = code_append($1.cc, $3.c);
}
-
-NEW : "new" CLASS MAYBE_PARAM_VALUES {
- MULTINAME(m, $2);
- $$.c = code_new();
-
- if($2->slot) {
- $$.c = abc_getglobalscope($$.c);
- $$.c = abc_getslot($$.c, $2->slot);
+
+XX : %prec new2
+NEW : "new" E XX MAYBE_PARAM_VALUES {
+ $$.c = $2.c;
+ if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
+
+ code_t*paramcode = $4.cc;
+ if($$.c->opcode == OPCODE_GETPROPERTY) {
+ multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
+ $$.c = code_cutlast($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_constructprop2($$.c, name, $4.len);
+ multiname_destroy(name);
+ } else if($$.c->opcode == OPCODE_GETSLOT) {
+ int slot = (int)(ptroff_t)$$.c->data[0];
+ trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
+ multiname_t*name = t->name;
+ $$.c = code_cutlast($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_constructprop2($$.c, name, $4.len);
} else {
- $$.c = abc_findpropstrict2($$.c, &m);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_construct($$.c, $4.len);
+ }
+
+ $$.t = TYPE_ANY;
+ if(TYPE_IS_CLASS($2.t) && $2.t->cls) {
+ $$.t = $2.t->cls;
+ } else {
+ $$.c = abc_coerce_a($$.c);
+ $$.t = TYPE_ANY;
}
-
- $$.c = code_append($$.c, $3.cc);
-
- if($2->slot)
- $$.c = abc_construct($$.c, $3.len);
- else
- $$.c = abc_constructprop2($$.c, &m, $3.len);
- $$.t = $2;
}
/* TODO: use abc_call (for calling local variables),
$$.c = abc_callsuper2($$.c, name, $3.len);
multiname_destroy(name);
} else {
- $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getglobalscope($$.c);
$$.c = code_append($$.c, paramcode);
$$.c = abc_call($$.c, $3.len);
}
- memberinfo_t*f = 0;
-
if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
$$.t = $1.t->function->return_type;
} else {
$$.c = abc_coerce_a($$.c);
$$.t = TYPE_ANY;
}
-
}
+
FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
if(!state->cls) syntaxerror("super() not allowed outside of a class");
if(!state->method) syntaxerror("super() not allowed outside of a function");
syntaxerror("constructor may call super() only once");
*/
state->method->has_super = 1;
+
$$.c = abc_constructsuper($$.c, $3.len);
$$.c = abc_pushundefined($$.c);
$$.t = TYPE_ANY;
//V : DELETE {$$ = $1.c;}
E : DELETE {$$ = $1;}
+E : FUNCTIONCALL
+
E : T_REGEXP {
$$.c = 0;
namespace_t ns = {ACCESS_PACKAGE, ""};
CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
$$.t = TYPE_FLOAT;
}
-CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
+CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
$$.t = TYPE_STRING;
}
CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
$$.t = TYPE_NULL;
}
-E : FUNCTIONCALL
E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
$$.t = TYPE_BOOLEAN;
}
E : E '[' E ']' {
$$.c = $1.c;
$$.c = code_append($$.c, $3.c);
-
+
MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
$$.c = abc_getproperty2($$.c, &m);
$$.t = 0; // array elements have unknown type
}
MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
-MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1};
+MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
$$.cc = 0;
// $1 is a local variable
$$.c = abc_getlocal($$.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 */
- } else if(state->cls && (f = registry_findmember(state->cls->info, $1, 1))) {
+ if(state->cls && (f = registry_findmember(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);
- int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
- if(var_is_static != i_am_static) {
- /* there doesn't seem to be any "static" way to access
- static properties of a class */
+
+ if(f->kind == MEMBER_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 = {flags2access(f->flags), ""};
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 {
- if(f->slot>0) {
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getslot($$.c, f->slot);
- } else {
- namespace_t ns = {flags2access(f->flags), ""};
- multiname_t m = {QNAME, &ns, 0, $1};
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getproperty2($$.c, &m);
- }
- }
- if(f->kind == MEMBER_METHOD) {
- $$.t = TYPE_FUNCTION(f);
- } else {
- $$.t = f->type;
+ namespace_t ns = {flags2access(f->flags), ""};
+ 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 */
- } else if((a = find_class($1))) {
+ if((a = find_class($1))) {
if(a->flags & FLAG_METHOD) {
MULTINAME(m, a);
$$.c = abc_findpropstrict2($$.c, &m);
}
$$.t = TYPE_CLASS(a);
}
+ break;
+ }
/* unknown object, let the avm2 resolve it */
- } else {
- if(strcmp($1,"trace"))
- as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
+ if(1) {
+ as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
state->method->late_binding = 1;
multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};