%token<token> KW_NEW "new"
%token<token> KW_NATIVE
%token<token> KW_FUNCTION "function"
+%token<token> KW_UNDEFINED "undefined"
%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_INTERFACE "interface"
%token<token> KW_NULL "null"
%token<token> KW_VAR "var"
%token<token> KW_OVERRIDE
%token<token> KW_FINAL
%token<token> KW_GET "get"
+%token<token> KW_SUPER "super"
%token<token> KW_EXTENDS
%token<token> KW_FALSE "false"
%token<token> KW_TRUE "true"
%token<token> KW_WHILE "while"
%token<token> KW_NUMBER "Number"
%token<token> KW_STRING "String"
+%token<token> KW_DELETE "delete"
%token<token> KW_IF "if"
%token<token> KW_ELSE "else"
%token<token> KW_BREAK "break"
%token<token> T_EQEQ "=="
%token<token> T_EQEQEQ "==="
%token<token> T_NE "!="
+%token<token> T_NEE "!=="
%token<token> T_LE "<="
%token<token> T_GE ">="
%token<token> T_DIVBY "/="
%token<token> T_SHL "<<"
%token<token> T_USHR ">>>"
%token<token> T_SHR ">>"
-%token<token> T_SEMICOLON ';'
-%token<token> T_STAR '*'
-%token<token> T_DOT '.'
%type <id> X_IDENTIFIER PACKAGE
%type <token> VARCONST
%type <code> VOIDEXPRESSION
%type <value> EXPRESSION NONCOMMAEXPRESSION
%type <value> MAYBEEXPRESSION
-%type <value> E
+%type <value> E DELETE
%type <value> CONSTANT
%type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
%type <token> USE_NAMESPACE
%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
// precedence: from low to high
-// http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000012.html
%left prec_none
+
+%left below_semicolon
+%left ';'
+%left ','
+%nonassoc below_assignment // for ?:, contrary to spec
+%right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
%right '?' ':'
-%right '=' "/=" "%=" "*=" "+=" "-=" ">>=" "<<=" ">>>="
%left "||"
%left "&&"
%nonassoc '|'
%nonassoc '^'
%nonassoc '&'
-%nonassoc "!=" "==" "===" "<=" '<' ">=" '>' // TODO: support "a < b < c" syntax?
-%nonassoc "is"
-%left prec_belowminus
-%left '-'
-%left '+'
-%left "<<"
-%left ">>>"
-%left ">>"
-%left '%'
-%left '/'
-%left '*'
-%left '!'
-%left '~'
-%left "--" "++"
-%left '['
-%nonassoc "as"
-%left '.' ".." "::"
+%nonassoc "==" "!=" "===" "!=="
+%nonassoc "is" "as"
+%nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
+%left "<<" ">>" ">>>"
+%left below_minus
+%left '-' '+'
+%left '/' '*' '%'
+%left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
+%left "--" "++"
+%left '[' ']' '{' "new" '.' ".." "::"
%nonassoc T_IDENTIFIER
-%left below_semicolon
-%left ';'
+%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 "new" "false" "true" "null"
-
-%left prec_highest
+%nonassoc "false" "true" "null" "undefined" "super"
%{
DECLARE_LIST(import);
-typedef struct _state {
- abc_file_t*file;
- abc_script_t*init;
-
- int level;
+typedef struct _classstate {
+ /* class data */
+ classinfo_t*info;
+ abc_class_t*abc;
+ code_t*init;
+ code_t*static_init;
+} classstate_t;
- char*package;
- char*function;
+typedef struct _methodstate {
+ /* method data */
+ memberinfo_t*info;
+ char late_binding;
/* code that needs to be executed at the start of
a method (like initializing local registers) */
code_t*initcode;
+ char is_constructor;
+ char has_super;
+} methodstate_t;
+typedef struct _state {
+ int level;
+
+ char*package;
import_list_t*wildcard_imports;
dict_t*imports;
char has_own_imports;
-
- /* class data */
- classinfo_t*clsinfo;
- abc_class_t*cls;
- code_t*cls_init;
- code_t*cls_static_init;
+
+ classstate_t*cls;
+ methodstate_t*method;
- /* method data */
- memberinfo_t*minfo;
- abc_method_t*m;
-
dict_t*vars;
- char late_binding;
} state_t;
typedef struct _global {
+ abc_file_t*file;
+ abc_script_t*init;
+
int variable_count;
} global_t;
DECLARE_LIST(state);
-#define MULTINAME(m,x) multiname_t m;namespace_t m##_ns;registry_fill_multiname(&m, &m##_ns, x);
+#define MULTINAME(m,x) \
+ multiname_t m;\
+ namespace_t m##_ns;\
+ registry_fill_multiname(&m, &m##_ns, x);
+
+#define MEMBER_MULTINAME(m,f,n) \
+ multiname_t m;\
+ namespace_t m##_ns;\
+ if(f) { \
+ m##_ns.access = flags2access(f->flags); \
+ m##_ns.name = ""; \
+ m.type = QNAME; \
+ m.ns = &m##_ns; \
+ m.namespace_set = 0; \
+ m.name = f->name; \
+ } else { \
+ m.type = MULTINAME; \
+ m.ns =0; \
+ m.namespace_set = &nopackage_namespace_set; \
+ m.name = n; \
+ }
/* warning: list length of namespace set is undefined */
#define MULTINAME_LATE(m, access, package) \
m##_l.namespace = &m##_ns; \
multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
+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_list_t nl3 = {&ns3,&nl4};
+static namespace_list_t nl2 = {&ns2,&nl3};
+static namespace_list_t nl1 = {&ns1,&nl2};
+static namespace_set_t nopackage_namespace_set = {&nl1};
+
static state_list_t*state_stack=0;
static void init_globals()
state_stack = sl;
state = s;
state->level++;
+ state->has_own_imports = 0;
state->vars = dict_new();
- state->initcode = 0;
- state->has_own_imports = 0;
}
static void state_has_imports()
{
state_stack = state_stack->next;
free(old);
state = state_stack->state;
- /*if(state->initcode) {
+ /*if(state->method->initcode) {
printf("residual initcode\n");
- code_dump(state->initcode, 0, 0, "", stdout);
+ code_dump(state->method->initcode, 0, 0, "", stdout);
}*/
if(oldstate->has_own_imports) {
list_free(oldstate->wildcard_imports);
dict_destroy(oldstate->imports);oldstate->imports=0;
}
- state->initcode = code_append(state->initcode, oldstate->initcode);
}
void initialize_state()
{
init_globals();
new_state();
- state->file = abc_file_new();
- state->file->flags &= ~ABCFILE_LAZY;
+ global->file = abc_file_new();
+ global->file->flags &= ~ABCFILE_LAZY;
- state->init = abc_initscript(state->file, 0, 0);
- code_t*c = state->init->method->body->code;
+ global->init = abc_initscript(global->file, 0);
+ code_t*c = global->init->method->body->code;
c = abc_getlocal_0(c);
c = abc_pushscope(c);
c = abc_pushstring(c, "[entering global init function]");
c = abc_callpropvoid(c, "[package]::trace", 1);
- state->init->method->body->code = c;
+ global->init->method->body->code = c;
}
void* finalize_state()
{
if(state->level!=1) {
syntaxerror("unexpected end of file");
}
- abc_method_body_t*m = state->init->method->body;
+ abc_method_body_t*m = global->init->method->body;
//__ popscope(m);
__ findpropstrict(m, "[package]::trace");
__ pushstring(m, "[leaving global init function]");
__ callpropvoid(m, "[package]::trace", 1);
__ returnvoid(m);
- return state->file;
+ return global->file;
}
syntaxerror("inner classes now allowed");
}
new_state();
+ state->cls = rfx_calloc(sizeof(classstate_t));
token_list_t*t=0;
classinfo_list_t*mlist=0;
syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
}
+
/* build info struct */
int num_interfaces = (list_length(implements));
- state->clsinfo = classinfo_register(access, package, classname, num_interfaces);
- state->clsinfo->superclass = extends;
+ 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->clsinfo->interfaces[pos++] = l->classinfo;
+ state->cls->info->interfaces[pos++] = l->classinfo;
}
-
- MULTINAME(classname2,state->clsinfo);
multiname_t*extends2 = sig2mname(extends);
+ MULTINAME(classname2,state->cls->info);
+
/*if(extends) {
state->cls_init = abc_getlocal_0(state->cls_init);
state->cls_init = abc_constructsuper(state->cls_init, 0);
}*/
- state->cls = abc_class_new(state->file, &classname2, extends2);
- if(flags&FLAG_FINAL) abc_class_final(state->cls);
- if(flags&FLAG_DYNAMIC) abc_class_sealed(state->cls);
- if(interface) abc_class_interface(state->cls);
+ 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);
for(mlist=implements;mlist;mlist=mlist->next) {
MULTINAME(m, mlist->classinfo);
- abc_class_add_interface(state->cls, &m);
+ abc_class_add_interface(state->cls->abc, &m);
}
/* now write the construction code for this class */
- int slotindex = abc_initscript_addClassTrait(state->init, &classname2, state->cls);
+ int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
- abc_method_body_t*m = state->init->method->body;
+ abc_method_body_t*m = global->init->method->body;
__ getglobalscope(m);
classinfo_t*s = extends;
__ getlocal_0(m);
__ pushscope(m);count++;
}
- __ newclass(m,state->cls);
+ __ newclass(m,state->cls->abc);
while(count--) {
__ popscope(m);
}
static void endclass()
{
- if(state->cls_init) {
- if(!state->cls->constructor) {
- abc_method_t*m = abc_class_constructor(state->cls, 0, 0);
- m->body->code = code_append(m->body->code, state->cls_init);
+ 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->constructor->body->code;
- c = code_append(state->cls_init, c);
- state->cls->constructor->body->code = c;
+ 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_static_init) {
- if(!state->cls->static_constructor) {
- abc_method_t*m = abc_class_staticconstructor(state->cls, 0, 0);
- m->body->code = state->cls_static_init;
+ 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->static_constructor->body->code =
- code_append(state->cls_static_init, state->cls->static_constructor->body->code);
+ state->cls->abc->static_constructor->body->code =
+ code_append(state->cls->static_init, state->cls->abc->static_constructor->body->code);
}
}
{
state_list_t* s = state_stack;
while(s) {
- variable_t*v = dict_lookup(s->state->vars, name);
+ variable_t*v = 0;
+ if(s->state->method)
+ v = dict_lookup(s->state->vars, name);
if(v) {
if(m) {
*m = v->type;
{
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)
}
}
-static memberinfo_t*registerfunction(enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type, int slot)
+static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
{
memberinfo_t*minfo = 0;
if(getset != KW_GET && getset != KW_SET) {
- if(registry_findmember(state->clsinfo, name)) {
+ if(registry_findmember(state->cls->info, name)) {
syntaxerror("class already contains a member/method called '%s'", name);
}
- minfo = memberinfo_register(state->clsinfo, name, MEMBER_METHOD);
+ minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
minfo->return_type = return_type;
// getslot on a member slot only returns "undefined", so no need
// to actually store these
- //state->minfo->slot = state->m->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;
type = return_type;
else if(params->list)
type = params->list->param->type;
- if((minfo=registry_findmember(state->clsinfo, name))) {
+ if((minfo=registry_findmember(state->cls->info, name))) {
if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
syntaxerror("class already contains a member or method called '%s'", name);
if(minfo->kind & gs)
if(type && minfo->type != type)
syntaxerror("different type in getter and setter");
} else {
- minfo = memberinfo_register(state->clsinfo, name, gs);
+ minfo = memberinfo_register(state->cls->info, name, gs);
minfo->type = type;
}
/* can't assign a slot as getter and setter might have different slots */
//minfo->slot = slot;
}
+ if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
+ if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
+ if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
+ if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
+ if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
return minfo;
}
+static int flags2access(int flags)
+{
+ int access = 0;
+ if(flags&FLAG_PUBLIC) {
+ if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+ access = ACCESS_PACKAGE;
+ } else if(flags&FLAG_PRIVATE) {
+ if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+ access = ACCESS_PRIVATE;
+ } else if(flags&FLAG_PROTECTED) {
+ if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
+ access = ACCESS_PROTECTED;
+ } else {
+ access = ACCESS_PACKAGEINTERNAL;
+ }
+ return access;
+}
+
static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
params_t*params, classinfo_t*return_type)
{
- token_list_t*t;
+ if(state->method) {
+ syntaxerror("not able to start another method scope");
+ }
new_state();
+ state->method = rfx_calloc(sizeof(methodstate_t));
+ state->method->initcode = 0;
+ state->method->is_constructor = !strcmp(state->cls->info->name,name);
+ state->method->has_super = 0;
+
global->variable_count = 0;
- state->function = name;
-
- if(state->m) {
- syntaxerror("not able to start another method scope");
+
+ /* 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);
+}
+
+static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
+ params_t*params, classinfo_t*return_type, code_t*body)
+{
+ namespace_t mname_ns = {flags2access(flags), ""};
+ multiname_t mname = {QNAME, &mname_ns, 0, name};
+
+ abc_method_t*f = 0;
multiname_t*type2 = sig2mname(return_type);
- if(!strcmp(state->clsinfo->name,name)) {
- state->m = abc_class_constructor(state->cls, type2, 0);
+ int slot = 0;
+ if(state->method->is_constructor) {
+ f = abc_class_constructor(state->cls->abc, type2);
} else {
- state->m = abc_class_method(state->cls, type2, name, 0);
- int slot = state->m->trait->slot_id;
- state->minfo = registerfunction(getset, name, params, return_type, slot);
+ if(flags&FLAG_STATIC)
+ f = abc_class_staticmethod(state->cls->abc, type2, &mname);
+ else
+ f = abc_class_method(state->cls->abc, type2, &mname);
+ slot = f->trait->slot_id;
}
+ //flash doesn't seem to allow us to access function slots
+ //state->method->info->slot = slot;
- if(getset == KW_GET) state->m->trait->kind = TRAIT_GETTER;
- if(getset == KW_SET) state->m->trait->kind = TRAIT_SETTER;
- if(params->varargs) state->m->flags |= METHOD_NEED_REST;
+ 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;
char opt=0;
param_list_t*p=0;
break; //varargs: omit last parameter in function signature
}
multiname_t*m = sig2mname(p->param->type);
- list_append(state->m->parameters, m);
+ list_append(f->parameters, m);
if(p->param->value) {
check_constant_against_type(p->param->type, p->param->value);
- opt=1;list_append(state->m->optional_parameters, 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");
}
}
-
- /* state->vars is initialized by state_new */
- if(new_variable("this", state->clsinfo)!=0) syntaxerror("Internal error");
-
- for(p=params->list;p;p=p->next) {
- new_variable(p->param->name, p->param->type);
- }
-}
-static void endfunction(code_t*body)
-{
+ f->body->code = body;
- if(!(state->cls->flags & CLASS_INTERFACE)) {
- code_t*c = 0;
- if(state->late_binding) {
- c = abc_getlocal_0(c);
- c = abc_pushscope(c);
- }
- c = code_append(c, state->initcode);
- c = code_append(c, body);
-
- /* append return if necessary */
- if(!c || c->opcode != OPCODE_RETURNVOID &&
- c->opcode != OPCODE_RETURNVALUE) {
- c = abc_returnvoid(c);
- }
- if(state->m->body->code) syntaxerror("internal error");
- state->m->body->code = c;
- }
old_state();
}
}
if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
return c;
+ if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
+ return c;
syntaxerror("can't convert type %s to %s", from->name, to->name);
}
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 {
if(!b) syntaxerror("internal error: assertion failed");
}
+static classinfo_t* find_class(char*name)
+{
+ classinfo_t*c=0;
+
+ c = registry_findclass(state->package, name);
+
+ /* try explicit imports */
+ dictentry_t* e = dict_get_slot(state->imports, name);
+ while(e) {
+ if(c)
+ break;
+ if(!strcmp(e->key, name)) {
+ c = (classinfo_t*)e->data;
+ }
+ e = e->next;
+ }
+
+ /* try package.* imports */
+ import_list_t*l = state->wildcard_imports;
+ while(l) {
+ if(c)
+ break;
+ //printf("does package %s contain a class %s?\n", l->import->package, name);
+ c = registry_findclass(l->import->package, name);
+ l = l->next;
+ }
+
+ /* try global package */
+ if(!c) {
+ c = registry_findclass("", name);
+ }
+ return c;
+}
+
static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
{
/* converts this:
write->opcode = OPCODE_SETPROPERTY;
multiname_t*m = (multiname_t*)r->data[0];
write->data[0] = multiname_clone(m);
- if(m->type != QNAME)
- syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)");
- if(!justassign) {
- prefix = abc_dup(prefix); // we need the object, too
+ if(m->type == QNAME || m->type == MULTINAME) {
+ if(!justassign) {
+ prefix = abc_dup(prefix); // we need the object, too
+ }
+ use_temp_var = 1;
+ } else if(m->type == MULTINAMEL) {
+ if(!justassign) {
+ /* dupping two values on the stack requires 5 operations and one register-
+ couldn't adobe just have given us a dup2? */
+ int temp = gettempvar();
+ prefix = abc_setlocal(prefix, temp);
+ prefix = abc_dup(prefix);
+ prefix = abc_getlocal(prefix, temp);
+ prefix = abc_swap(prefix);
+ prefix = abc_getlocal(prefix, temp);
+ }
+ use_temp_var = 1;
+ } else {
+ syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
}
- use_temp_var = 1;
} else if(r->opcode == OPCODE_GETSLOT) {
write->opcode = OPCODE_SETSLOT;
write->data[0] = r->data[0];
return c;
}
+#define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
+#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
%}
PROGRAM: MAYBECODE
-MAYBECODE: CODE {$$=$1;}
+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);}
/* if this is a typed variable:
push default value for type on stack */
if($3) {
- state->initcode = defaultvalue(state->initcode, $3);
- state->initcode = abc_setlocal(state->initcode, index);
+ state->method->initcode = defaultvalue(state->method->initcode, $3);
+ state->method->initcode = abc_setlocal(state->method->initcode, index);
}
} else {
if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
/* that's the default for a local register, anyway
else {
- state->initcode = abc_pushundefined(state->initcode);
- state->initcode = abc_setlocal(state->initcode, index);
+ state->method->initcode = abc_pushundefined(state->method->initcode);
+ state->method->initcode = abc_setlocal(state->method->initcode, index);
}*/
//printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
}
/* ------------ control flow ------------------------- */
-MAYBEELSE: %prec prec_none {$$ = code_new();}
+MAYBEELSE: %prec below_else {$$ = code_new();}
MAYBEELSE: "else" CODEBLOCK {$$=$2;}
//MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
- $$ = state->initcode;state->initcode=0;
-
+ $$ = code_new();
$$ = code_append($$, $4.c);
code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
FOR_INIT : VOIDEXPRESSION
FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
- $$ = state->initcode;state->initcode=0;
-
+ $$ = code_new();
$$ = code_append($$, $4);
code_t*loopstart = $$ = abc_label($$);
$$ = code_append($$, $6.c);
}
WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
- $$ = state->initcode;state->initcode=0;
-
+ $$ = code_new();
code_t*myjmp = $$ = abc_jump($$, 0);
code_t*loopstart = $$ = abc_label($$);
$$ = code_append($$, $6);
syntaxerror("variable declarations not allowed in interfaces");
}
IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
- if($1&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
- syntaxerror("invalid method modifiers: interface methods are always public");
+ $1 |= FLAG_PUBLIC;
+ if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
+ syntaxerror("invalid method modifiers: interface methods always need to be public");
}
startfunction(0,$1,$3,$4,&$6,$8);
- endfunction(0);
+ endfunction(0,$1,$3,$4,&$6,$8, 0);
}
/* ------------ classes and interfaces (body, slots ) ------- */
VARCONST: "var" | "const"
-SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
- memberinfo_t* info = memberinfo_register(state->clsinfo, $3, MEMBER_SLOT);
+SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
+ int flags = $1;
+ memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
info->type = $4;
-
+ info->flags = flags;
trait_t*t=0;
- if($4) {
- MULTINAME(m, $4);
- t=abc_class_slot(state->cls, $3, &m);
+
+ namespace_t mname_ns = {flags2access(flags), ""};
+ multiname_t mname = {QNAME, &mname_ns, 0, $3};
+
+ if(!(flags&FLAG_STATIC)) {
+ if($4) {
+ MULTINAME(m, $4);
+ t=abc_class_slot(state->cls->abc, &mname, &m);
+ } else {
+ t=abc_class_slot(state->cls->abc, &mname, 0);
+ }
+ info->slot = t->slot_id;
} else {
- t=abc_class_slot(state->cls, $3, 0);
- }
- if($2==KW_CONST) {
- t->kind= TRAIT_CONST;
+ if($4) {
+ MULTINAME(m, $4);
+ t=abc_class_staticslot(state->cls->abc, &mname, &m);
+ } else {
+ t=abc_class_staticslot(state->cls->abc, &mname, 0);
+ }
+ info->slot = t->slot_id;
}
- info->slot = t->slot_id;
if($5.c && !is_pushundefined($5.c)) {
code_t*c = 0;
c = abc_getlocal_0(c);
c = code_append(c, $5.c);
c = converttype(c, $5.t, $4);
c = abc_setslot(c, t->slot_id);
- //c = abc_setproperty(c, $3);
- state->cls_init = code_append(state->cls_init, c);
+ if(!(flags&FLAG_STATIC))
+ state->cls->init = code_append(state->cls->init, c);
+ else
+ state->cls->static_init = code_append(state->cls->static_init, c);
+ }
+ if($2==KW_CONST) {
+ t->kind= TRAIT_CONST;
}
}
STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
//STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
-STATICCONSTANT : KW_TRUE {$$ = constant_new_true($1);}
-STATICCONSTANT : KW_FALSE {$$ = constant_new_false($1);}
-STATICCONSTANT : KW_NULL {$$ = constant_new_null($1);}
+STATICCONSTANT : "true" {$$ = constant_new_true($1);}
+STATICCONSTANT : "false" {$$ = constant_new_false($1);}
+STATICCONSTANT : "null" {$$ = constant_new_null($1);}
/* ------------ classes and interfaces (body, functions) ------- */
FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
{
- if(!state->m) syntaxerror("internal error: undefined function");
- endfunction($11);
+ 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) {
+ // generate 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);
+ }
+ endfunction(0,$1,$3,$4,&$6,$8,c);
}
/* ------------- package + class ids --------------- */
CLASS: T_IDENTIFIER {
/* try current package */
- $$ = registry_findclass(state->package, $1);
-
- /* try explicit imports */
- dictentry_t* e = dict_get_slot(state->imports, $1);
- while(e) {
- if($$)
- break;
- if(!strcmp(e->key, $1)) {
- $$ = (classinfo_t*)e->data;
- }
- e = e->next;
- }
-
- /* try package.* imports */
- import_list_t*l = state->wildcard_imports;
- while(l) {
- if($$)
- break;
- //printf("does package %s contain a class %s?\n", l->import->package, $1);
- $$ = registry_findclass(l->import->package, $1);
- l = l->next;
- }
-
- /* try global package */
- if(!$$) {
- $$ = registry_findclass("", $1);
- }
-
+ $$ = find_class($1);
if(!$$) syntaxerror("Could not find class %s\n", $1);
}
TYPE : QNAME {$$=$1;}
| '*' {$$=registry_getanytype();}
+ /*
| "String" {$$=registry_getstringclass();}
| "int" {$$=registry_getintclass();}
| "uint" {$$=registry_getuintclass();}
| "Boolean" {$$=registry_getbooleanclass();}
| "Number" {$$=registry_getnumberclass();}
+ */
MAYBETYPE: ':' TYPE {$$=$2;}
MAYBETYPE: {$$=0;}
-/* ----------function calls, constructor calls ------ */
+/* ----------function calls, delete, constructor calls ------ */
MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
MULTINAME(m, $2);
$$.c = code_new();
- /* TODO: why do we have to *find* our own classes? */
- $$.c = abc_findpropstrict2($$.c, &m);
+ if($2->slot) {
+ $$.c = abc_getglobalscope($$.c);
+ $$.c = abc_getslot($$.c, $2->slot);
+ } else {
+ $$.c = abc_findpropstrict2($$.c, &m);
+ }
typedcode_list_t*l = $3;
int len = 0;
l = l->next;
len ++;
}
- $$.c = abc_constructprop2($$.c, &m, len);
+ if($2->slot)
+ $$.c = abc_construct($$.c, len);
+ else
+ $$.c = abc_constructprop2($$.c, &m, len);
$$.t = $2;
}
$$.c = abc_callproperty2($$.c, name, len);
} else if($$.c->opcode == OPCODE_GETSLOT) {
int slot = (int)(ptroff_t)$$.c->data[0];
- trait_t*t = abc_class_find_slotid(state->cls,slot);//FIXME
+ 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");
$$.c = code_append($$.c, paramcode);
//$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
$$.c = abc_callproperty2($$.c, name, len);
+ } else if($$.c->opcode == OPCODE_GETSUPER) {
+ name = multiname_clone($$.c->data[0]);
+ $$.c = code_cutlast($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_callsuper2($$.c, name, len);
} else {
$$.c = abc_getlocal_0($$.c);
$$.c = code_append($$.c, paramcode);
memberinfo_t*f = 0;
- if(TYPE_IS_FUNCTION($1.t) &&
- (f = registry_findmember($1.t, "call"))) {
- $$.t = f->return_type;
+ 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");
+ if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
+
+ $$.c = code_new();
+ $$.c = abc_getlocal_0($$.c);
+ typedcode_list_t*l = 0;
+ int len = 0;
+ 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);
+ $$.t = TYPE_ANY;
+}
+
+DELETE: "delete" E {
+ $$.c = $2.c;
+ if($$.c->opcode == OPCODE_COERCE_A) {
+ $$.c = code_cutlast($$.c);
+ }
+ multiname_t*name = 0;
+ if($$.c->opcode == OPCODE_GETPROPERTY) {
+ $$.c->opcode = OPCODE_DELETEPROPERTY;
+ } else if($$.c->opcode == OPCODE_GETSLOT) {
+ int slot = (int)(ptroff_t)$$.c->data[0];
+ multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
+ $$.c = code_cutlast($$.c);
+ $$.c = abc_deleteproperty2($$.c, name);
+ } else {
+ $$.c = abc_getlocal_0($$.c);
+ MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
+ $$.c = abc_deleteproperty2($$.c, &m);
+ }
+ $$.t = TYPE_BOOLEAN;
+}
RETURN: "return" %prec prec_none {
$$ = abc_returnvoid(0);
$$ = $2.c;
$$ = abc_returnvalue($$);
}
+
// ----------------------- expression types -------------------------------------
-NONCOMMAEXPRESSION : E %prec prec_belowminus {$$=$1;}
-EXPRESSION : E %prec prec_belowminus {$$ = $1;}
-EXPRESSION : EXPRESSION ',' E %prec prec_belowminus {
+NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
+EXPRESSION : E %prec below_minus {$$ = $1;}
+EXPRESSION : EXPRESSION ',' E %prec below_minus {
$$.c = $1.c;
$$.c = cut_last_push($$.c);
$$.c = code_append($$.c,$3.c);
$$.t = $3.t;
}
-VOIDEXPRESSION : EXPRESSION %prec prec_belowminus {$$=cut_last_push($1.c);}
+VOIDEXPRESSION : EXPRESSION %prec below_minus {
+ $$=cut_last_push($1.c);
+}
// ----------------------- expression evaluation -------------------------------------
E : CONSTANT
E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
E : NEW {$$ = $1;}
+E : DELETE {$$ = $1;}
E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
$$.t = TYPE_ANY;
}
CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
$$.t = TYPE_STRING;
}
-CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
+CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
+ $$.t = TYPE_ANY;
+ }
+CONSTANT : "true" {$$.c = abc_pushtrue(0);
$$.t = TYPE_BOOLEAN;
}
-CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
+CONSTANT : "false" {$$.c = abc_pushfalse(0);
$$.t = TYPE_BOOLEAN;
}
-CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
+CONSTANT : "null" {$$.c = abc_pushnull(0);
$$.t = TYPE_NULL;
}
}
E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
$$.t = TYPE_BOOLEAN;
+ }
+E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
}
E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
$$.t = TYPE_BOOLEAN;
$$.t = TYPE_BOOLEAN;
}
-E : E '-' E
-E : E '/' E
-E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c);
- $$.t = join_types($1.t, $3.t, '+');
+E : '~' E {$$.c=$2.c;
+ $$.c = abc_bitnot($$.c);
+ $$.t = TYPE_INT;
+ }
+
+E : E '&' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_bitand($$.c);
+ $$.t = TYPE_INT;
}
-E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c);
- $$.t = join_types($1.t, $3.t, '%');
+
+E : E '^' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_bitxor($$.c);
+ $$.t = TYPE_INT;
}
-E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c);
- $$.t = join_types($1.t, $3.t, '*');
+
+E : E '|' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_bitor($$.c);
+ $$.t = TYPE_INT;
}
-E : E "as" E
-E : E "is" E
-E : '(' E ')' {$$=$2;}
-E : '-' E {$$=$2;}
+E : E '-' E {$$.c = code_append($1.c,$3.c);
+ if(BOTH_INT($1,$3)) {
+ $$.c = abc_subtract_i($$.c);
+ $$.t = TYPE_INT;
+ } else {
+ $$.c = abc_subtract($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+ }
+E : E ">>" E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_rshift($$.c);
+ $$.t = TYPE_INT;
+ }
+E : E ">>>" E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_urshift($$.c);
+ $$.t = TYPE_INT;
+ }
+E : E "<<" E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_lshift($$.c);
+ $$.t = TYPE_INT;
+ }
+
+E : E '/' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_divide($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+E : E '+' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_add($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+E : E '%' E {$$.c = code_append($1.c,$3.c);
+ $$.c = abc_modulo($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+E : E '*' E {$$.c = code_append($1.c,$3.c);
+ if(BOTH_INT($1,$3)) {
+ $$.c = abc_multiply_i($$.c);
+ $$.t = TYPE_INT;
+ } else {
+ $$.c = abc_multiply($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+ }
+
+E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
+ if(use_astype && TYPE_IS_CLASS($3.t)) {
+ MULTINAME(m,$3.t->cls);
+ $$.c = abc_astype2($1.c, &m);
+ $$.t = $3.t->cls;
+ } else {
+ $$.c = code_append($1.c, $3.c);
+ $$.c = abc_astypelate($$.c);
+ $$.t = TYPE_ANY;
+ }
+ }
+
+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;
+ }
+
+E : "typeof" '(' E ')' {
+ $$.c = $3.c;
+ $$.c = abc_typeof($$.c);
+ $$.t = TYPE_STRING;
+ }
+
+E : "void" E {
+ $$.c = cut_last_push($2.c);
+ $$.c = abc_pushundefined($$.c);
+ $$.t = TYPE_ANY;
+ }
+
+E : "void" { $$.c = abc_pushundefined(0);
+ $$.t = TYPE_ANY;
+ }
+
+E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
+
+E : '-' E {
+ $$=$2;
+ if(IS_INT($2)) {
+ $$.c=abc_negate_i($$.c);
+ $$.t = TYPE_INT;
+ } else {
+ $$.c=abc_negate($$.c);
+ $$.t = TYPE_NUMBER;
+ }
+}
E : E '[' E ']' {
$$.c = $1.c;
MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
$$.c = abc_getproperty2($$.c, &m);
+ $$.t = 0; // array elements have unknown type
}
E : E "*=" E {
code_t*c = $3.c;
- if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
+ if(BOTH_INT($1,$3)) {
c=abc_multiply_i(c);
} else {
c=abc_multiply(c);
$$.c = toreadwrite($1.c, c, 0, 0);
$$.t = $1.t;
}
+
E : E "%=" E {
code_t*c = abc_modulo($3.c);
c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
$$.t = $1.t;
}
+E : E '?' E ':' E %prec below_assignment {
+ $$.c = $1.c;
+ code_t*j1 = $$.c = abc_iffalse($$.c, 0);
+ $$.c = code_append($$.c, $3.c);
+ code_t*j2 = $$.c = abc_jump($$.c, 0);
+ $$.c = j1->branch = abc_label($$.c);
+ $$.c = code_append($$.c, $5.c);
+ $$.c = j2->branch = abc_label($$.c);
+ $$.t = join_types($3.t,$5.t,'?');
+ }
+
// TODO: use inclocal where appropriate
E : E "++" { code_t*c = 0;
classinfo_t*type = $1.t;
$$.t = $1.t;
}
-E : "++" E { code_t*c = 0;
+E : "++" %prec plusplus_prefix E { code_t*c = 0;
classinfo_t*type = $2.t;
if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
c=abc_increment_i(c);
$$.t = $2.t;
}
-E : "--" E { code_t*c = 0;
+E : "--" %prec minusminus_prefix E { code_t*c = 0;
classinfo_t*type = $2.t;
if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
c=abc_decrement_i(c);
$$.t = $2.t;
}
+E : "super" '.' T_IDENTIFIER
+ { if(!state->cls->info)
+ syntaxerror("super keyword not allowed outside a class");
+ classinfo_t*t = state->cls->info->superclass;
+ if(!t) t = TYPE_OBJECT;
+
+ memberinfo_t*f = registry_findmember(t, $3);
+ namespace_t ns = {flags2access(f->flags), ""};
+ MEMBER_MULTINAME(m, f, $3);
+ $$.c = 0;
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getsuper2($$.c, &m);
+ $$.t = memberinfo_gettype(f);
+ }
+
E : E '.' T_IDENTIFIER
{$$.c = $1.c;
- if($$.t) {
- memberinfo_t*f = registry_findmember($$.t, $3);
-
- if(f && f->slot) {
+ classinfo_t*t = $1.t;
+ char is_static = 0;
+ if(TYPE_IS_CLASS(t) && t->cls) {
+ t = t->cls;
+ is_static = 1;
+ }
+ if(t) {
+ memberinfo_t*f = registry_findmember(t, $3);
+ char noslot = 0;
+ if(f && !is_static != !(f->flags&FLAG_STATIC))
+ noslot=1;
+ if(f && f->slot && !noslot) {
$$.c = abc_getslot($$.c, f->slot);
} else {
- namespace_t ns = {$$.t->access, ""}; // needs to be "", not $$.t->package
- multiname_t m = {QNAME, &ns, 0, $3};
+ MEMBER_MULTINAME(m, f, $3);
$$.c = abc_getproperty2($$.c, &m);
}
/* determine type */
- if(f) {
- if(f->kind == MEMBER_METHOD) {
- $$.t = TYPE_FUNCTION(f);
- } else {
- $$.t = f->type;
- }
- } else {
+ $$.t = memberinfo_gettype(f);
+ if(!$$.t)
$$.c = abc_coerce_a($$.c);
- $$.t = registry_getanytype();
- }
} else {
- namespace_t ns = {ACCESS_PACKAGE, ""};
- multiname_t m = {QNAME, &ns, 0, $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 */
+ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
$$.c = abc_getproperty2($$.c, &m);
$$.c = abc_coerce_a($$.c);
$$.t = registry_getanytype();
$$.t = 0;
$$.c = 0;
int i;
+ classinfo_t*a = 0;
memberinfo_t*f = 0;
+
+ /* look at variables */
if((i = find_variable($1, &$$.t)) >= 0) {
// $1 is a local variable
$$.c = abc_getlocal($$.c, i);
- } else if((f = registry_findmember(state->clsinfo, $1))) {
+
+ /* look at current class' members */
+ } else if((f = registry_findmember(state->cls->info, $1))) {
// $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 */
+ 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);
+ } 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;
}
- if(f->slot>0) {
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getslot($$.c, f->slot);
+
+ /* 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 {
- namespace_t ns = {state->clsinfo->access, ""};
- multiname_t m = {QNAME, &ns, 0, $1};
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getproperty2($$.c, &m);
+ MULTINAME(m, a);
+ $$.c = abc_getlex2($$.c, &m);
}
+ $$.t = TYPE_CLASS(a);
+
+ /* unknown object, let the avm2 resolve it */
} else {
- // let the avm2 resolve $1
if(strcmp($1,"trace"))
- warning("Couldn't resolve %s, doing late binding", $1);
- state->late_binding = 1;
+ 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_findpropstrict($$.c, $1);
- $$.c = abc_getproperty($$.c, $1);
+ $$.c = abc_findpropstrict2($$.c, &m);
+ $$.c = abc_getproperty2($$.c, &m);
}
}