3 Routines for compiling Flash2 AVM2 ABC Actionscript
5 Extension module for the rfxswf library.
6 Part of the swftools package.
8 Copyright (c) 2008 Matthias Kramm <kramm@quiss.org>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
30 #include "tokenizer.h"
42 enum yytokentype token;
45 classinfo_t*classinfo;
46 classinfo_list_t*classinfo_list;
49 unsigned int number_uint;
53 //typedcode_list_t*value_list;
54 codeandnumber_t value_list;
60 for_start_t for_start;
64 %token<id> T_IDENTIFIER
66 %token<token> T_REGEXP
68 %token<number_int> T_INT
69 %token<number_uint> T_UINT
70 %token<number_uint> T_BYTE
71 %token<number_uint> T_SHORT
72 %token<number_float> T_FLOAT
74 %token<id> T_FOR "for"
75 %token<id> T_WHILE "while"
77 %token<id> T_SWITCH "switch"
79 %token<token> KW_IMPLEMENTS
80 %token<token> KW_NAMESPACE "namespace"
81 %token<token> KW_PACKAGE "package"
82 %token<token> KW_PROTECTED
83 %token<token> KW_PUBLIC
84 %token<token> KW_PRIVATE
85 %token<token> KW_USE "use"
86 %token<token> KW_INTERNAL
87 %token<token> KW_NEW "new"
88 %token<token> KW_NATIVE
89 %token<token> KW_FUNCTION "function"
90 %token<token> KW_UNDEFINED "undefined"
91 %token<token> KW_CONTINUE "continue"
92 %token<token> KW_CLASS "class"
93 %token<token> KW_CONST "const"
94 %token<token> KW_CATCH "catch"
95 %token<token> KW_CASE "case"
96 %token<token> KW_SET "set"
97 %token<token> KW_VOID "void"
98 %token<token> KW_STATIC
99 %token<token> KW_INSTANCEOF "instanceof"
100 %token<token> KW_IMPORT "import"
101 %token<token> KW_RETURN "return"
102 %token<token> KW_TYPEOF "typeof"
103 %token<token> KW_INTERFACE "interface"
104 %token<token> KW_NULL "null"
105 %token<token> KW_VAR "var"
106 %token<token> KW_DYNAMIC "dynamic"
107 %token<token> KW_OVERRIDE
108 %token<token> KW_FINAL
109 %token<token> KW_EACH "each"
110 %token<token> KW_GET "get"
111 %token<token> KW_TRY "try"
112 %token<token> KW_SUPER "super"
113 %token<token> KW_EXTENDS
114 %token<token> KW_FALSE "false"
115 %token<token> KW_TRUE "true"
116 %token<token> KW_BOOLEAN "Boolean"
117 %token<token> KW_UINT "uint"
118 %token<token> KW_INT "int"
119 %token<token> KW_NUMBER "Number"
120 %token<token> KW_STRING "String"
121 %token<token> KW_DEFAULT "default"
122 %token<token> KW_DELETE "delete"
123 %token<token> KW_IF "if"
124 %token<token> KW_ELSE "else"
125 %token<token> KW_BREAK "break"
126 %token<token> KW_IS "is"
127 %token<token> KW_IN "in"
128 %token<token> KW_AS "as"
130 %token<token> T_EQEQ "=="
131 %token<token> T_EQEQEQ "==="
132 %token<token> T_NE "!="
133 %token<token> T_NEE "!=="
134 %token<token> T_LE "<="
135 %token<token> T_GE ">="
136 %token<token> T_DIVBY "/="
137 %token<token> T_MODBY "%="
138 %token<token> T_MULBY "*="
139 %token<token> T_PLUSBY "+="
140 %token<token> T_MINUSBY "-="
141 %token<token> T_SHRBY ">>="
142 %token<token> T_SHLBY "<<="
143 %token<token> T_USHRBY ">>>="
144 %token<token> T_OROR "||"
145 %token<token> T_ANDAND "&&"
146 %token<token> T_COLONCOLON "::"
147 %token<token> T_MINUSMINUS "--"
148 %token<token> T_PLUSPLUS "++"
149 %token<token> T_DOTDOT ".."
150 %token<token> T_DOTDOTDOT "..."
151 %token<token> T_SHL "<<"
152 %token<token> T_USHR ">>>"
153 %token<token> T_SHR ">>"
155 %type <for_start> FOR_START
156 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT
157 %type <token> VARCONST
159 %type <code> CODEPIECE
160 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH
161 %type <token> PACKAGE_DECLARATION
162 %type <token> FUNCTION_DECLARATION
163 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST
164 %type <token> CLASS_DECLARATION
165 %type <token> NAMESPACE_DECLARATION
166 %type <token> INTERFACE_DECLARATION
167 %type <code> VOIDEXPRESSION
168 %type <value> EXPRESSION NONCOMMAEXPRESSION
169 %type <value> MAYBEEXPRESSION
170 %type <value> E DELETE
171 %type <value> CONSTANT
172 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE
173 %type <token> USE_NAMESPACE
174 %type <code> FOR_INIT
176 %type <classinfo> MAYBETYPE
179 %type <params> PARAM_LIST
180 %type <params> MAYBE_PARAM_LIST
181 %type <flags> MAYBE_MODIFIERS
182 %type <flags> MODIFIER_LIST
183 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
184 %type <classinfo_list> IMPLEMENTS_LIST
185 %type <classinfo> EXTENDS
186 %type <classinfo_list> EXTENDS_LIST
187 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
188 %type <classinfo_list> QNAME_LIST
189 %type <classinfo> TYPE
190 //%type <token> VARIABLE
191 %type <value> VAR_READ
193 //%type <token> T_IDENTIFIER
194 %type <token> MODIFIER
195 %type <value> FUNCTIONCALL
196 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
198 // precedence: from low to high
202 %left below_semicolon
205 %nonassoc below_assignment // for ?:, contrary to spec
206 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
213 %nonassoc "==" "!=" "===" "!=="
215 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
216 %left "<<" ">>" ">>>"
220 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
222 %nonassoc below_curly
223 %left '[' ']' '{' "new" '.' ".." "::"
224 %nonassoc T_IDENTIFIER
229 // needed for "return" precedence:
230 %nonassoc T_STRING T_REGEXP
231 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
232 %nonassoc "false" "true" "null" "undefined" "super"
238 static int yyerror(char*s)
240 syntaxerror("%s", s);
243 static char* concat2(const char* t1, const char* t2)
247 char*text = malloc(l1+l2+1);
248 memcpy(text , t1, l1);
249 memcpy(text+l1, t2, l2);
253 static char* concat3(const char* t1, const char* t2, const char* t3)
258 char*text = malloc(l1+l2+l3+1);
259 memcpy(text , t1, l1);
260 memcpy(text+l1, t2, l2);
261 memcpy(text+l1+l2, t3, l3);
266 typedef struct _import {
270 DECLARE_LIST(import);
272 typedef struct _classstate {
278 char has_constructor;
281 typedef struct _methodstate {
285 /* code that needs to be executed at the start of
286 a method (like initializing local registers) */
292 typedef struct _state {
297 import_list_t*wildcard_imports;
299 char has_own_imports;
302 methodstate_t*method;
307 typedef struct _global {
314 static global_t*global = 0;
315 static state_t* state = 0;
319 #define MULTINAME(m,x) \
322 registry_fill_multiname(&m, &m##_ns, x);
324 #define MEMBER_MULTINAME(m,f,n) \
328 m##_ns.access = flags2access(f->flags); \
332 m.namespace_set = 0; \
335 m.type = MULTINAME; \
337 m.namespace_set = &nopackage_namespace_set; \
341 /* warning: list length of namespace set is undefined */
342 #define MULTINAME_LATE(m, access, package) \
343 namespace_t m##_ns = {access, package}; \
344 namespace_set_t m##_nsset; \
345 namespace_list_t m##_l;m##_l.next = 0; \
346 m##_nsset.namespaces = &m##_l; \
347 m##_nsset = m##_nsset; \
348 m##_l.namespace = &m##_ns; \
349 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
351 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
352 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
353 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
354 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
355 static namespace_list_t nl4 = {&ns4,0};
356 static namespace_list_t nl3 = {&ns3,&nl4};
357 static namespace_list_t nl2 = {&ns2,&nl3};
358 static namespace_list_t nl1 = {&ns1,&nl2};
359 static namespace_set_t nopackage_namespace_set = {&nl1};
361 static void init_globals()
363 global = rfx_calloc(sizeof(global_t));
366 static void new_state()
369 state_t*oldstate = state;
371 memcpy(s, state, sizeof(state_t)); //shallow copy
373 s->imports = dict_new();
377 state->has_own_imports = 0;
378 state->vars = dict_new();
379 state->old = oldstate;
381 static void state_has_imports()
383 state->wildcard_imports = list_clone(state->wildcard_imports);
384 state->imports = dict_clone(state->imports);
385 state->has_own_imports = 1;
388 static void state_destroy(state_t*state)
390 if(state->has_own_imports) {
391 list_free(state->wildcard_imports);
392 dict_destroy(state->imports);state->imports=0;
394 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
395 dict_destroy(state->imports);state->imports=0;
399 for(t=0;t<state->vars->hashsize;t++) {
400 dictentry_t*e =state->vars->slots[t];
402 free(e->data);e->data=0;
406 dict_destroy(state->vars);state->vars=0;
412 static void old_state()
414 if(!state || !state->old)
415 syntaxerror("invalid nesting");
416 state_t*leaving = state;
418 /*if(state->method->initcode) {
419 printf("residual initcode\n");
420 code_dump(state->method->initcode, 0, 0, "", stdout);
422 state_destroy(leaving);
424 void initialize_state()
429 global->file = abc_file_new();
430 global->file->flags &= ~ABCFILE_LAZY;
432 global->init = abc_initscript(global->file, 0);
433 code_t*c = global->init->method->body->code;
435 c = abc_getlocal_0(c);
436 c = abc_pushscope(c);
438 /* findpropstrict doesn't just return a scope object- it
439 also makes it "active" somehow. Push local_0 on the
440 scope stack and read it back with findpropstrict, it'll
441 contain properties like "trace". Trying to find the same
442 property on a "vanilla" local_0 yields only a "undefined" */
443 //c = abc_findpropstrict(c, "[package]::trace");
445 /*c = abc_getlocal_0(c);
446 c = abc_findpropstrict(c, "[package]::trace");
448 c = abc_setlocal_1(c);
450 c = abc_pushbyte(c, 0);
451 c = abc_setlocal_2(c);
453 code_t*xx = c = abc_label(c);
454 c = abc_findpropstrict(c, "[package]::trace");
455 c = abc_pushstring(c, "prop:");
456 c = abc_hasnext2(c, 1, 2);
458 c = abc_setlocal_3(c);
459 c = abc_callpropvoid(c, "[package]::trace", 2);
460 c = abc_getlocal_3(c);
462 c = abc_iftrue(c,xx);*/
464 c = abc_findpropstrict(c, "[package]::trace");
465 c = abc_pushstring(c, "[entering global init function]");
466 c = abc_callpropvoid(c, "[package]::trace", 1);
468 global->init->method->body->code = c;
470 void* finalize_state()
472 if(state->level!=1) {
473 syntaxerror("unexpected end of file");
475 abc_method_body_t*m = global->init->method->body;
478 __ findpropstrict(m, "[package]::trace");
479 __ pushstring(m, "[leaving global init function]");
480 __ callpropvoid(m, "[package]::trace", 1);
483 state_destroy(state);
489 static void startpackage(char*name)
492 syntaxerror("Packages can not be nested.");
495 /*printf("entering package \"%s\"\n", name);*/
496 state->package = strdup(name);
498 static void endpackage()
500 /*printf("leaving package \"%s\"\n", state->package);*/
502 //used e.g. in classinfo_register:
503 //free(state->package);state->package=0;
509 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
512 syntaxerror("inner classes now allowed");
515 state->cls = rfx_calloc(sizeof(classstate_t));
518 classinfo_list_t*mlist=0;
519 /*printf("entering class %s\n", name);
520 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n");
522 printf(" extends: %s.%s\n", extends->package, extends->name);
523 printf(" implements (%d): ", list_length(implements));
524 for(mlist=implements;mlist;mlist=mlist->next) {
525 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
530 if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
531 syntaxerror("invalid modifier(s)");
533 if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
534 syntaxerror("public and internal not supported at the same time.");
536 /* create the class name, together with the proper attributes */
540 if(!(flags&FLAG_PUBLIC) && !state->package) {
541 access = ACCESS_PRIVATE; package = current_filename;
542 } else if(!(flags&FLAG_PUBLIC) && state->package) {
543 access = ACCESS_PACKAGEINTERNAL; package = state->package;
544 } else if(state->package) {
545 access = ACCESS_PACKAGE; package = state->package;
547 syntaxerror("public classes only allowed inside a package");
550 if(registry_findclass(package, classname)) {
551 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
555 /* build info struct */
556 int num_interfaces = (list_length(implements));
557 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
558 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
560 classinfo_list_t*l = implements;
561 for(l=implements;l;l=l->next) {
562 state->cls->info->interfaces[pos++] = l->classinfo;
565 multiname_t*extends2 = sig2mname(extends);
567 MULTINAME(classname2,state->cls->info);
570 state->cls_init = abc_getlocal_0(state->cls_init);
571 state->cls_init = abc_constructsuper(state->cls_init, 0);
574 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
575 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
576 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
578 state->cls->info->flags |= CLASS_INTERFACE;
579 abc_class_interface(state->cls->abc);
582 abc_class_protectedNS(state->cls->abc, classname);
584 for(mlist=implements;mlist;mlist=mlist->next) {
585 MULTINAME(m, mlist->classinfo);
586 abc_class_add_interface(state->cls->abc, &m);
589 /* now write the construction code for this class */
590 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
592 abc_method_body_t*m = global->init->method->body;
593 __ getglobalscope(m);
594 classinfo_t*s = extends;
599 //TODO: take a look at the current scope stack, maybe
600 // we can re-use something
605 multiname_t*s2 = sig2mname(s);
607 multiname_destroy(s2);
609 __ pushscope(m); count++;
610 m->code = m->code->prev->prev; // invert
612 /* continue appending after last op end */
613 while(m->code && m->code->next) m->code = m->code->next;
615 /* TODO: if this is one of *our* classes, we can also
616 do a getglobalscope/getslot <nr> (which references
617 the init function's slots) */
619 __ getlex2(m, extends2);
621 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
622 stack is not the superclass */
623 __ pushscope(m);count++;
626 /* notice: we get a verify error #1107 if the top element on the scope
627 stack is not the global object */
629 __ pushscope(m);count++;
631 __ newclass(m,state->cls->abc);
635 __ setslot(m, slotindex);
637 /* flash.display.MovieClip handling */
638 if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
639 if(state->package && state->package[0]) {
640 globalclass = concat3(state->package, ".", classname);
642 globalclass = strdup(classname);
645 multiname_destroy(extends2);
648 static code_t* wrap_function(code_t*c,code_t*initcode, code_t*body)
650 c = code_append(c, initcode);
651 c = code_append(c, body);
652 /* append return if necessary */
653 if(!c || c->opcode != OPCODE_RETURNVOID &&
654 c->opcode != OPCODE_RETURNVALUE) {
655 c = abc_returnvoid(c);
660 static void endclass()
662 if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
664 c = abc_getlocal_0(c);
665 c = abc_constructsuper(c, 0);
666 state->cls->init = code_append(state->cls->init, c);
669 if(state->cls->init) {
670 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
671 m->body->code = wrap_function(0, state->cls->init, m->body->code);
673 if(state->cls->static_init) {
674 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
675 m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
677 // handy for scope testing
681 abc_class_getstaticconstructor(state->cls->abc,0)->body->code = c;*/
684 free(state->cls);state->cls=0;
688 typedef struct _variable {
693 static variable_t* find_variable(char*name)
699 v = dict_lookup(s->vars, name);
707 static variable_t* find_variable_safe(char*name)
709 variable_t* v = find_variable(name);
711 syntaxerror("undefined variable: %s", name);
714 static char variable_exists(char*name)
716 return dict_lookup(state->vars, name)!=0;
718 code_t*defaultvalue(code_t*c, classinfo_t*type);
719 static int new_variable(char*name, classinfo_t*type, char init)
722 v->index = global->variable_count;
724 dict_put(state->vars, name, v);
726 if(init && state->method && type) {
727 /* if this is a typed variable:
728 push default value for type on stack at the very beginning of the
729 method, so that it always has that type regardless of the control
731 state->method->initcode = defaultvalue(state->method->initcode, type);
732 state->method->initcode = abc_setlocal(state->method->initcode, v->index);
734 return global->variable_count++;
736 #define TEMPVARNAME "__as3_temp__"
737 static int gettempvar()
739 variable_t*v = find_variable(TEMPVARNAME);
742 return new_variable(TEMPVARNAME, 0, 0);
745 code_t* killvars(code_t*c)
748 for(t=0;t<state->vars->hashsize;t++) {
749 dictentry_t*e =state->vars->slots[t];
751 variable_t*v = (variable_t*)e->data;
752 //do this always, otherwise register types don't match
753 //in the verifier when doing nested loops
754 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
755 c = abc_kill(c, v->index);
762 void check_code_for_break(code_t*c)
765 if(c->opcode == OPCODE___BREAK__) {
766 char*name = string_cstr(c->data[0]);
767 syntaxerror("Unresolved \"break %s\"", name);
769 if(c->opcode == OPCODE___CONTINUE__) {
770 char*name = string_cstr(c->data[0]);
771 syntaxerror("Unresolved \"continue %s\"", name);
778 static void check_constant_against_type(classinfo_t*t, constant_t*c)
780 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
781 if(TYPE_IS_NUMBER(t)) {
782 xassert(c->type == CONSTANT_FLOAT
783 || c->type == CONSTANT_INT
784 || c->type == CONSTANT_UINT);
785 } else if(TYPE_IS_UINT(t)) {
786 xassert(c->type == CONSTANT_UINT ||
787 (c->type == CONSTANT_INT && c->i>0));
788 } else if(TYPE_IS_INT(t)) {
789 xassert(c->type == CONSTANT_INT);
790 } else if(TYPE_IS_BOOLEAN(t)) {
791 xassert(c->type == CONSTANT_TRUE
792 || c->type == CONSTANT_FALSE);
796 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
798 memberinfo_t*minfo = 0;
799 if(getset != KW_GET && getset != KW_SET) {
800 if((minfo = registry_findmember(state->cls->info, name, 0))) {
801 if(minfo->parent == state->cls->info) {
802 syntaxerror("class already contains a member/method called '%s'", name);
803 } else if(!minfo->parent) {
804 syntaxerror("internal error: overriding method %s, which doesn't have parent", name);
806 if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE)))
807 syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?");
810 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
811 minfo->return_type = return_type;
812 // getslot on a member slot only returns "undefined", so no need
813 // to actually store these
814 //state->minfo->slot = state->method->abc->method->trait->slot_id;
816 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
820 else if(params->list)
821 type = params->list->param->type;
822 // not sure wether to look into superclasses here, too
823 if((minfo=registry_findmember(state->cls->info, name, 0))) {
824 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
825 syntaxerror("class already contains a member or method called '%s'", name);
827 syntaxerror("getter/setter for '%s' already defined", name);
828 /* make a setter or getter into a getset */
833 if(type && minfo->type != type)
834 syntaxerror("different type in getter and setter");
836 minfo = memberinfo_register(state->cls->info, name, gs);
839 /* can't assign a slot as getter and setter might have different slots */
840 //minfo->slot = slot;
842 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
843 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
844 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
845 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
846 if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
847 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
851 static int flags2access(int flags)
854 if(flags&FLAG_PUBLIC) {
855 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
856 access = ACCESS_PACKAGE;
857 } else if(flags&FLAG_PRIVATE) {
858 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
859 access = ACCESS_PRIVATE;
860 } else if(flags&FLAG_PROTECTED) {
861 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
862 access = ACCESS_PROTECTED;
864 access = ACCESS_PACKAGEINTERNAL;
869 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
870 params_t*params, classinfo_t*return_type)
873 syntaxerror("not able to start another method scope");
876 state->method = rfx_calloc(sizeof(methodstate_t));
877 state->method->initcode = 0;
878 state->method->is_constructor = !strcmp(state->cls->info->name,name);
879 state->method->has_super = 0;
881 state->cls->has_constructor |= state->method->is_constructor;
883 global->variable_count = 0;
885 /* state->vars is initialized by state_new */
886 if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0)!=0) syntaxerror("Internal error");
888 for(p=params->list;p;p=p->next) {
889 new_variable(p->param->name, p->param->type, 0);
891 if(state->method->is_constructor)
892 name = "__as3_constructor__";
893 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
896 static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
897 params_t*params, classinfo_t*return_type, code_t*body)
899 namespace_t mname_ns = {flags2access(flags), ""};
900 multiname_t mname = {QNAME, &mname_ns, 0, name};
904 multiname_t*type2 = sig2mname(return_type);
906 if(state->method->is_constructor) {
907 f = abc_class_getconstructor(state->cls->abc, type2);
909 if(flags&FLAG_STATIC)
910 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
912 f = abc_class_method(state->cls->abc, type2, &mname);
913 slot = f->trait->slot_id;
915 //flash doesn't seem to allow us to access function slots
916 //state->method->info->slot = slot;
918 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
919 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
920 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
921 if(params->varargs) f->flags |= METHOD_NEED_REST;
925 for(p=params->list;p;p=p->next) {
926 if(params->varargs && !p->next) {
927 break; //varargs: omit last parameter in function signature
929 multiname_t*m = sig2mname(p->param->type);
930 list_append(f->parameters, m);
931 if(p->param->value) {
932 check_constant_against_type(p->param->type, p->param->value);
933 opt=1;list_append(f->optional_parameters, p->param->value);
935 syntaxerror("non-optional parameter not allowed after optional parameters");
938 check_code_for_break(body);
941 f->body->code = body;
944 syntaxerror("interface methods can't have a method body");
946 free(state->method);state->method=0;
952 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
957 void breakjumpsto(code_t*c, char*name, code_t*jump)
960 if(c->opcode == OPCODE___BREAK__) {
961 string_t*name2 = c->data[0];
962 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
963 c->opcode = OPCODE_JUMP;
970 void continuejumpsto(code_t*c, char*name, code_t*jump)
973 if(c->opcode == OPCODE___CONTINUE__) {
974 string_t*name2 = c->data[0];
975 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
976 c->opcode = OPCODE_JUMP;
984 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
987 return registry_getanytype();
988 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
989 return registry_getanytype();
992 return registry_getanytype();
994 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
999 return abc_coerce_a(c);
1003 // cast an "any" type to a specific type. subject to
1004 // runtime exceptions
1005 return abc_coerce2(c, &m);
1008 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1009 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1010 // allow conversion between number types
1011 return abc_coerce2(c, &m);
1013 //printf("%s.%s\n", from.package, from.name);
1014 //printf("%s.%s\n", to.package, to.name);
1016 classinfo_t*supertype = from;
1018 if(supertype == to) {
1019 // target type is one of from's superclasses
1020 return abc_coerce2(c, &m);
1023 while(supertype->interfaces[t]) {
1024 if(supertype->interfaces[t]==to) {
1025 // target type is one of from's interfaces
1026 return abc_coerce2(c, &m);
1030 supertype = supertype->superclass;
1032 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1034 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1036 syntaxerror("can't convert type %s to %s", from->name, to->name);
1039 code_t*defaultvalue(code_t*c, classinfo_t*type)
1041 if(TYPE_IS_INT(type)) {
1042 c = abc_pushbyte(c, 0);
1043 } else if(TYPE_IS_UINT(type)) {
1044 c = abc_pushuint(c, 0);
1045 } else if(TYPE_IS_FLOAT(type)) {
1047 } else if(TYPE_IS_BOOLEAN(type)) {
1048 c = abc_pushfalse(c);
1050 c = abc_pushnull(c);
1055 char is_pushundefined(code_t*c)
1057 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1060 void parserassert(int b)
1062 if(!b) syntaxerror("internal error: assertion failed");
1065 static classinfo_t* find_class(char*name)
1069 c = registry_findclass(state->package, name);
1071 /* try explicit imports */
1072 dictentry_t* e = dict_get_slot(state->imports, name);
1076 if(!strcmp(e->key, name)) {
1077 c = (classinfo_t*)e->data;
1082 /* try package.* imports */
1083 import_list_t*l = state->wildcard_imports;
1087 //printf("does package %s contain a class %s?\n", l->import->package, name);
1088 c = registry_findclass(l->import->package, name);
1092 /* try global package */
1094 c = registry_findclass("", name);
1099 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1103 [prefix code] [read instruction]
1107 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1110 if(in && in->opcode == OPCODE_COERCE_A) {
1111 in = code_cutlast(in);
1114 syntaxerror("internal error");
1116 /* chop off read instruction */
1120 prefix = r->prev;r->prev = 0;
1126 char use_temp_var = readbefore;
1128 /* generate the write instruction, and maybe append a dup to the prefix code */
1129 code_t* write = abc_nop(0);
1130 if(r->opcode == OPCODE_GETPROPERTY) {
1131 write->opcode = OPCODE_SETPROPERTY;
1132 multiname_t*m = (multiname_t*)r->data[0];
1133 write->data[0] = multiname_clone(m);
1134 if(m->type == QNAME || m->type == MULTINAME) {
1136 prefix = abc_dup(prefix); // we need the object, too
1139 } else if(m->type == MULTINAMEL) {
1141 /* dupping two values on the stack requires 5 operations and one register-
1142 couldn't adobe just have given us a dup2? */
1143 int temp = gettempvar();
1144 prefix = abc_setlocal(prefix, temp);
1145 prefix = abc_dup(prefix);
1146 prefix = abc_getlocal(prefix, temp);
1147 prefix = abc_swap(prefix);
1148 prefix = abc_getlocal(prefix, temp);
1152 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1154 } else if(r->opcode == OPCODE_GETSLOT) {
1155 write->opcode = OPCODE_SETSLOT;
1156 write->data[0] = r->data[0];
1158 prefix = abc_dup(prefix); // we need the object, too
1161 } else if(r->opcode == OPCODE_GETLOCAL) {
1162 write->opcode = OPCODE_SETLOCAL;
1163 write->data[0] = r->data[0];
1164 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1165 write->opcode = OPCODE_SETLOCAL_0;
1166 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1167 write->opcode = OPCODE_SETLOCAL_1;
1168 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1169 write->opcode = OPCODE_SETLOCAL_2;
1170 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1171 write->opcode = OPCODE_SETLOCAL_3;
1173 code_dump(r, 0, 0, "", stdout);
1174 syntaxerror("illegal lvalue: can't assign a value to this expression");
1181 /* with getproperty/getslot, we have to be extra careful not
1182 to execute the read code twice, as it might have side-effects
1183 (e.g. if the property is in fact a setter/getter combination)
1185 So read the value, modify it, and write it again,
1186 using prefix only once and making sure (by using a temporary
1187 register) that the return value is what we just wrote */
1188 temp = gettempvar();
1189 c = code_append(c, prefix);
1190 c = code_append(c, r);
1193 c = abc_setlocal(c, temp);
1195 c = code_append(c, middlepart);
1198 c = abc_setlocal(c, temp);
1200 c = code_append(c, write);
1201 c = abc_getlocal(c, temp);
1202 c = abc_kill(c, temp);
1204 /* if we're allowed to execute the read code twice *and*
1205 the middlepart doesn't modify the code, things are easier.
1207 code_t* r2 = code_dup(r);
1208 //c = code_append(c, prefix);
1209 parserassert(!prefix);
1210 c = code_append(c, r);
1211 c = code_append(c, middlepart);
1212 c = code_append(c, write);
1213 c = code_append(c, r2);
1216 /* even smaller version: overwrite the value without reading
1220 c = code_append(c, prefix);
1223 c = code_append(c, middlepart);
1224 c = code_append(c, write);
1225 c = code_append(c, r);
1227 temp = gettempvar();
1229 c = code_append(c, prefix);
1231 c = code_append(c, middlepart);
1233 c = abc_setlocal(c, temp);
1234 c = code_append(c, write);
1235 c = abc_getlocal(c, temp);
1242 #define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
1243 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1250 /* ------------ code blocks / statements ---------------- */
1252 PROGRAM: MAYBECODE {
1253 /* todo: do something with this code if we're outside a function */
1255 warning("ignored code");
1258 MAYBECODE: CODE {$$=$1;}
1259 MAYBECODE: {$$=code_new();}
1261 CODE: CODE CODEPIECE {
1262 $$=code_append($1,$2);
1268 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
1269 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
1270 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
1271 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
1272 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
1273 CODEPIECE: ';' {$$=code_new();}
1274 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
1275 CODEPIECE: VOIDEXPRESSION {$$=$1}
1276 CODEPIECE: FOR {$$=$1}
1277 CODEPIECE: FOR_IN {$$=$1}
1278 CODEPIECE: WHILE {$$=$1}
1279 CODEPIECE: DO_WHILE {$$=$1}
1280 CODEPIECE: SWITCH {$$=$1}
1281 CODEPIECE: BREAK {$$=$1}
1282 CODEPIECE: CONTINUE {$$=$1}
1283 CODEPIECE: RETURN {$$=$1}
1284 CODEPIECE: IF {$$=$1}
1285 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
1286 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
1288 CODEBLOCK : '{' CODE '}' {$$=$2;}
1289 CODEBLOCK : '{' '}' {$$=0;}
1290 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1291 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1293 /* ------------ variables --------------------------- */
1295 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1296 | {$$.c=abc_pushundefined(0);
1300 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1301 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1303 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1304 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1306 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1308 if(variable_exists($1))
1309 syntaxerror("Variable %s already defined", $1);
1311 if(!is_subtype_of($3.t, $2)) {
1312 syntaxerror("Can't convert %s to %s", $3.t->name,
1316 int index = new_variable($1, $2, 1);
1319 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1321 $$ = converttype($$, $3.t, $2);
1322 $$ = abc_setlocal($$, index);
1324 $$ = defaultvalue(0, $2);
1325 $$ = abc_setlocal($$, index);
1328 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1330 $$ = abc_coerce_a($$);
1331 $$ = abc_setlocal($$, index);
1337 /* that's the default for a local register, anyway
1339 state->method->initcode = abc_pushundefined(state->method->initcode);
1340 state->method->initcode = abc_setlocal(state->method->initcode, index);
1342 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1345 /* ------------ control flow ------------------------- */
1347 MAYBEELSE: %prec below_else {$$ = code_new();}
1348 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1349 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1351 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1353 $$ = code_append($$, $4.c);
1354 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1356 $$ = code_append($$, $6);
1358 myjmp = $$ = abc_jump($$, 0);
1360 myif->branch = $$ = abc_nop($$);
1362 $$ = code_append($$, $7);
1363 myjmp->branch = $$ = abc_nop($$);
1366 $$ = killvars($$);old_state();
1369 FOR_INIT : {$$=code_new();}
1370 FOR_INIT : VARIABLE_DECLARATION
1371 FOR_INIT : VOIDEXPRESSION
1372 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1373 $$=$2;new_variable($2,$3,1);
1375 FOR_IN_INIT : T_IDENTIFIER {
1379 FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
1380 FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
1382 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1383 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1385 $$ = code_append($$, $2);
1386 code_t*loopstart = $$ = abc_label($$);
1387 $$ = code_append($$, $4.c);
1388 code_t*myif = $$ = abc_iffalse($$, 0);
1389 $$ = code_append($$, $8);
1390 code_t*cont = $$ = abc_nop($$);
1391 $$ = code_append($$, $6);
1392 $$ = abc_jump($$, loopstart);
1393 code_t*out = $$ = abc_nop($$);
1394 breakjumpsto($$, $1.name, out);
1395 continuejumpsto($$, $1.name, cont);
1398 $$ = killvars($$);old_state();
1401 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
1402 variable_t*var = find_variable($2);
1403 char*tmp1name = concat2($2, "__tmp1__");
1404 int it = new_variable(tmp1name, TYPE_INT, 0);
1405 char*tmp2name = concat2($2, "__array__");
1406 int array = new_variable(tmp1name, 0, 0);
1409 $$ = code_append($$, $4.c);
1410 $$ = abc_coerce_a($$);
1411 $$ = abc_setlocal($$, array);
1412 $$ = abc_pushbyte($$, 0);
1413 $$ = abc_setlocal($$, it);
1415 code_t*loopstart = $$ = abc_label($$);
1417 $$ = abc_hasnext2($$, array, it);
1418 code_t*myif = $$ = abc_iffalse($$, 0);
1419 $$ = abc_getlocal($$, array);
1420 $$ = abc_getlocal($$, it);
1422 $$ = abc_nextname($$);
1424 $$ = abc_nextvalue($$);
1425 $$ = converttype($$, 0, var->type);
1426 $$ = abc_setlocal($$, var->index);
1428 $$ = code_append($$, $6);
1429 $$ = abc_jump($$, loopstart);
1431 code_t*out = $$ = abc_nop($$);
1432 breakjumpsto($$, $1.name, out);
1433 continuejumpsto($$, $1.name, loopstart);
1444 WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1447 code_t*myjmp = $$ = abc_jump($$, 0);
1448 code_t*loopstart = $$ = abc_label($$);
1449 $$ = code_append($$, $6);
1450 code_t*cont = $$ = abc_nop($$);
1451 myjmp->branch = cont;
1452 $$ = code_append($$, $4.c);
1453 $$ = abc_iftrue($$, loopstart);
1454 code_t*out = $$ = abc_nop($$);
1455 breakjumpsto($$, $1, out);
1456 continuejumpsto($$, $1, cont);
1462 DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
1464 code_t*loopstart = $$ = abc_label($$);
1465 $$ = code_append($$, $3);
1466 code_t*cont = $$ = abc_nop($$);
1467 $$ = code_append($$, $6.c);
1468 $$ = abc_iftrue($$, loopstart);
1469 code_t*out = $$ = abc_nop($$);
1470 breakjumpsto($$, $1, out);
1471 continuejumpsto($$, $1, cont);
1476 BREAK : "break" %prec prec_none {
1477 $$ = abc___break__(0, "");
1479 BREAK : "break" T_IDENTIFIER {
1480 $$ = abc___break__(0, $2);
1482 CONTINUE : "continue" %prec prec_none {
1483 $$ = abc___continue__(0, "");
1485 CONTINUE : "continue" T_IDENTIFIER {
1486 $$ = abc___continue__(0, $2);
1489 MAYBE_CASE_LIST : {$$=0;}
1490 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
1491 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
1492 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
1493 CASE_LIST: CASE {$$=$1}
1494 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
1496 CASE: "case" E ':' MAYBECODE {
1498 $$ = code_append($$, $2.c);
1499 code_t*j = $$ = abc_ifne($$, 0);
1500 $$ = code_append($$, $4);
1501 if($$->opcode != OPCODE___BREAK__) {
1502 $$ = abc___fallthrough__($$, "");
1504 code_t*e = $$ = abc_nop($$);
1507 DEFAULT: "default" ':' MAYBECODE {
1510 SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
1512 $$ = code_append($$, $7);
1513 code_t*out = $$ = abc_pop($$);
1514 breakjumpsto($$, $1, out);
1516 code_t*c = $$,*lastblock=0;
1518 if(c->opcode == OPCODE_IFNE) {
1519 if(!c->next) syntaxerror("internal error in fallthrough handling");
1521 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
1523 c->opcode = OPCODE_JUMP;
1524 c->branch = lastblock;
1526 /* fall through end of switch */
1527 c->opcode = OPCODE_NOP;
1535 /* ------------ packages and imports ---------------- */
1537 X_IDENTIFIER: T_IDENTIFIER
1538 | "package" {$$="package";}
1540 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;}
1541 PACKAGE: X_IDENTIFIER {$$=strdup($1);}
1543 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBECODE '}' {endpackage()}
1544 PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()}
1546 IMPORT : "import" QNAME {
1549 syntaxerror("Couldn't import class\n");
1550 state_has_imports();
1551 dict_put(state->imports, c->name, c);
1554 IMPORT : "import" PACKAGE '.' '*' {
1557 state_has_imports();
1558 list_append(state->wildcard_imports, i);
1562 /* ------------ classes and interfaces (header) -------------- */
1564 MAYBE_MODIFIERS : {$$=0;}
1565 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
1566 MODIFIER_LIST : MODIFIER {$$=$1;}
1567 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
1569 MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
1570 | KW_PRIVATE {$$=FLAG_PRIVATE;}
1571 | KW_PROTECTED {$$=FLAG_PROTECTED;}
1572 | KW_STATIC {$$=FLAG_STATIC;}
1573 | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
1574 | KW_FINAL {$$=FLAG_FINAL;}
1575 | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
1576 | KW_NATIVE {$$=FLAG_NATIVE;}
1577 | KW_INTERNAL {$$=FLAG_INTERNAL;}
1579 EXTENDS : {$$=registry_getobjectclass();}
1580 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1582 EXTENDS_LIST : {$$=list_new();}
1583 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1585 IMPLEMENTS_LIST : {$$=list_new();}
1586 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1588 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
1589 EXTENDS IMPLEMENTS_LIST
1590 '{' {startclass($1,$3,$4,$5, 0);}
1591 MAYBE_DECLARATION_LIST
1594 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
1596 '{' {startclass($1,$3,0,$4,1);}
1597 MAYBE_IDECLARATION_LIST
1600 /* ------------ classes and interfaces (body) -------------- */
1602 MAYBE_DECLARATION_LIST :
1603 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1604 DECLARATION_LIST : DECLARATION
1605 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1607 DECLARATION : SLOT_DECLARATION
1608 DECLARATION : FUNCTION_DECLARATION
1610 MAYBE_IDECLARATION_LIST :
1611 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1612 IDECLARATION_LIST : IDECLARATION
1613 IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
1615 IDECLARATION : "var" T_IDENTIFIER {
1616 syntaxerror("variable declarations not allowed in interfaces");
1618 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
1620 if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
1621 syntaxerror("invalid method modifiers: interface methods always need to be public");
1623 startfunction(0,$1,$3,$4,&$6,$8);
1624 endfunction(0,$1,$3,$4,&$6,$8, 0);
1627 /* ------------ classes and interfaces (body, slots ) ------- */
1629 VARCONST: "var" | "const"
1631 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1633 memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
1635 info->flags = flags;
1638 namespace_t mname_ns = {flags2access(flags), ""};
1639 multiname_t mname = {QNAME, &mname_ns, 0, $3};
1641 if(!(flags&FLAG_STATIC)) {
1644 t=abc_class_slot(state->cls->abc, &mname, &m);
1646 t=abc_class_slot(state->cls->abc, &mname, 0);
1648 info->slot = t->slot_id;
1652 t=abc_class_staticslot(state->cls->abc, &mname, &m);
1654 t=abc_class_staticslot(state->cls->abc, &mname, 0);
1656 info->slot = t->slot_id;
1658 if($5.c && !is_pushundefined($5.c)) {
1660 c = abc_getlocal_0(c);
1661 c = code_append(c, $5.c);
1662 c = converttype(c, $5.t, $4);
1663 c = abc_setslot(c, t->slot_id);
1664 if(!(flags&FLAG_STATIC))
1665 state->cls->init = code_append(state->cls->init, c);
1667 state->cls->static_init = code_append(state->cls->static_init, c);
1670 t->kind= TRAIT_CONST;
1674 /* ------------ constants -------------------------------------- */
1676 MAYBESTATICCONSTANT: {$$=0;}
1677 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1679 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1680 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1681 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1682 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1683 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
1684 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1685 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
1686 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
1687 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
1689 /* ------------ classes and interfaces (body, functions) ------- */
1691 // non-vararg version
1693 memset(&$$,0,sizeof($$));
1695 MAYBE_PARAM_LIST: PARAM_LIST {
1700 MAYBE_PARAM_LIST: "..." PARAM {
1701 memset(&$$,0,sizeof($$));
1703 list_append($$.list, $2);
1705 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1708 list_append($$.list, $4);
1712 PARAM_LIST: PARAM_LIST ',' PARAM {
1714 list_append($$.list, $3);
1717 memset(&$$,0,sizeof($$));
1718 list_append($$.list, $1);
1721 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1722 $$ = malloc(sizeof(param_t));
1727 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1728 $$ = malloc(sizeof(param_t));
1730 $$->type = TYPE_ANY;
1733 GETSET : "get" {$$=$1;}
1737 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1738 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1741 if(state->method->late_binding) {
1742 c = abc_getlocal_0(c);
1743 c = abc_pushscope(c);
1745 if(state->method->is_constructor && !state->method->has_super) {
1746 // call default constructor
1747 c = abc_getlocal_0(c);
1748 c = abc_constructsuper(c, 0);
1750 c = wrap_function(c, state->method->initcode, $11);
1751 endfunction(0,$1,$3,$4,&$6,$8,c);
1754 /* ------------- package + class ids --------------- */
1756 CLASS: T_IDENTIFIER {
1758 /* try current package */
1759 $$ = find_class($1);
1760 if(!$$) syntaxerror("Could not find class %s\n", $1);
1763 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1764 $$ = registry_findclass($1, $3);
1765 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
1769 QNAME: PACKAGEANDCLASS
1772 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1773 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1775 TYPE : QNAME {$$=$1;}
1776 | '*' {$$=registry_getanytype();}
1777 | "void" {$$=registry_getanytype();}
1779 | "String" {$$=registry_getstringclass();}
1780 | "int" {$$=registry_getintclass();}
1781 | "uint" {$$=registry_getuintclass();}
1782 | "Boolean" {$$=registry_getbooleanclass();}
1783 | "Number" {$$=registry_getnumberclass();}
1786 MAYBETYPE: ':' TYPE {$$=$2;}
1789 /* ----------function calls, delete, constructor calls ------ */
1791 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
1792 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1794 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
1795 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1796 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
1799 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
1801 $$.cc = code_append($1.cc, $3.c);
1804 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1809 $$.c = abc_getglobalscope($$.c);
1810 $$.c = abc_getslot($$.c, $2->slot);
1812 $$.c = abc_findpropstrict2($$.c, &m);
1815 $$.c = code_append($$.c, $3.cc);
1818 $$.c = abc_construct($$.c, $3.len);
1820 $$.c = abc_constructprop2($$.c, &m, $3.len);
1824 /* TODO: use abc_call (for calling local variables),
1825 abc_callstatic (for calling own methods)
1828 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1831 if($$.c->opcode == OPCODE_COERCE_A) {
1832 $$.c = code_cutlast($$.c);
1834 code_t*paramcode = $3.cc;
1837 if($$.c->opcode == OPCODE_GETPROPERTY) {
1838 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
1839 $$.c = code_cutlast($$.c);
1840 $$.c = code_append($$.c, paramcode);
1841 $$.c = abc_callproperty2($$.c, name, $3.len);
1842 multiname_destroy(name);
1843 } else if($$.c->opcode == OPCODE_GETSLOT) {
1844 int slot = (int)(ptroff_t)$$.c->data[0];
1845 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
1846 if(t->kind!=TRAIT_METHOD) {
1847 //ok: flash allows to assign closures to members.
1849 multiname_t*name = t->name;
1850 $$.c = code_cutlast($$.c);
1851 $$.c = code_append($$.c, paramcode);
1852 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1853 $$.c = abc_callproperty2($$.c, name, $3.len);
1854 } else if($$.c->opcode == OPCODE_GETSUPER) {
1855 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
1856 $$.c = code_cutlast($$.c);
1857 $$.c = code_append($$.c, paramcode);
1858 $$.c = abc_callsuper2($$.c, name, $3.len);
1859 multiname_destroy(name);
1861 $$.c = abc_getlocal_0($$.c);
1862 $$.c = code_append($$.c, paramcode);
1863 $$.c = abc_call($$.c, $3.len);
1868 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
1869 $$.t = $1.t->function->return_type;
1871 $$.c = abc_coerce_a($$.c);
1876 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
1877 if(!state->cls) syntaxerror("super() not allowed outside of a class");
1878 if(!state->method) syntaxerror("super() not allowed outside of a function");
1879 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
1882 $$.c = abc_getlocal_0($$.c);
1884 $$.c = code_append($$.c, $3.cc);
1886 this is dependent on the control path, check this somewhere else
1887 if(state->method->has_super)
1888 syntaxerror("constructor may call super() only once");
1890 state->method->has_super = 1;
1891 $$.c = abc_constructsuper($$.c, $3.len);
1892 $$.c = abc_pushundefined($$.c);
1896 DELETE: "delete" E {
1898 if($$.c->opcode == OPCODE_COERCE_A) {
1899 $$.c = code_cutlast($$.c);
1901 multiname_t*name = 0;
1902 if($$.c->opcode == OPCODE_GETPROPERTY) {
1903 $$.c->opcode = OPCODE_DELETEPROPERTY;
1904 } else if($$.c->opcode == OPCODE_GETSLOT) {
1905 int slot = (int)(ptroff_t)$$.c->data[0];
1906 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
1907 $$.c = code_cutlast($$.c);
1908 $$.c = abc_deleteproperty2($$.c, name);
1910 $$.c = abc_getlocal_0($$.c);
1911 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
1912 $$.c = abc_deleteproperty2($$.c, &m);
1914 $$.t = TYPE_BOOLEAN;
1917 RETURN: "return" %prec prec_none {
1918 $$ = abc_returnvoid(0);
1920 RETURN: "return" EXPRESSION {
1922 $$ = abc_returnvalue($$);
1925 // ----------------------- expression types -------------------------------------
1927 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
1928 EXPRESSION : E %prec below_minus {$$ = $1;}
1929 EXPRESSION : EXPRESSION ',' E %prec below_minus {
1931 $$.c = cut_last_push($$.c);
1932 $$.c = code_append($$.c,$3.c);
1935 VOIDEXPRESSION : EXPRESSION %prec below_minus {
1936 $$=cut_last_push($1.c);
1939 // ----------------------- expression evaluation -------------------------------------
1942 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1944 E : DELETE {$$ = $1;}
1945 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1949 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1950 //MULTINAME(m, registry_getintclass());
1951 //$$.c = abc_coerce2($$.c, &m); // FIXME
1954 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1957 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1960 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1963 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1966 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
1969 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
1972 CONSTANT : "true" {$$.c = abc_pushtrue(0);
1973 $$.t = TYPE_BOOLEAN;
1975 CONSTANT : "false" {$$.c = abc_pushfalse(0);
1976 $$.t = TYPE_BOOLEAN;
1978 CONSTANT : "null" {$$.c = abc_pushnull(0);
1983 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1984 $$.t = TYPE_BOOLEAN;
1986 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1987 $$.t = TYPE_BOOLEAN;
1989 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1990 $$.t = TYPE_BOOLEAN;
1992 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1993 $$.t = TYPE_BOOLEAN;
1995 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1996 $$.t = TYPE_BOOLEAN;
1998 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1999 $$.t = TYPE_BOOLEAN;
2001 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2002 $$.t = TYPE_BOOLEAN;
2004 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2005 $$.t = TYPE_BOOLEAN;
2008 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2010 $$.c = converttype($$.c, $1.t, $$.t);
2011 $$.c = abc_dup($$.c);
2012 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2013 $$.c = cut_last_push($$.c);
2014 $$.c = code_append($$.c,$3.c);
2015 $$.c = converttype($$.c, $3.t, $$.t);
2016 code_t*label = $$.c = abc_label($$.c);
2017 jmp->branch = label;
2020 $$.t = join_types($1.t, $3.t, 'A');
2021 /*printf("%08x:\n",$1.t);
2022 code_dump($1.c, 0, 0, "", stdout);
2023 printf("%08x:\n",$3.t);
2024 code_dump($3.c, 0, 0, "", stdout);
2025 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2027 $$.c = converttype($$.c, $1.t, $$.t);
2028 $$.c = abc_dup($$.c);
2029 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2030 $$.c = cut_last_push($$.c);
2031 $$.c = code_append($$.c,$3.c);
2032 $$.c = converttype($$.c, $3.t, $$.t);
2033 code_t*label = $$.c = abc_label($$.c);
2034 jmp->branch = label;
2037 E : '!' E {$$.c=$2.c;
2038 $$.c = abc_not($$.c);
2039 $$.t = TYPE_BOOLEAN;
2042 E : '~' E {$$.c=$2.c;
2043 $$.c = abc_bitnot($$.c);
2047 E : E '&' E {$$.c = code_append($1.c,$3.c);
2048 $$.c = abc_bitand($$.c);
2052 E : E '^' E {$$.c = code_append($1.c,$3.c);
2053 $$.c = abc_bitxor($$.c);
2057 E : E '|' E {$$.c = code_append($1.c,$3.c);
2058 $$.c = abc_bitor($$.c);
2062 E : E '-' E {$$.c = code_append($1.c,$3.c);
2063 if(BOTH_INT($1,$3)) {
2064 $$.c = abc_subtract_i($$.c);
2067 $$.c = abc_subtract($$.c);
2071 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2072 $$.c = abc_rshift($$.c);
2075 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2076 $$.c = abc_urshift($$.c);
2079 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2080 $$.c = abc_lshift($$.c);
2084 E : E '/' E {$$.c = code_append($1.c,$3.c);
2085 $$.c = abc_divide($$.c);
2088 E : E '+' E {$$.c = code_append($1.c,$3.c);
2089 $$.c = abc_add($$.c);
2092 E : E '%' E {$$.c = code_append($1.c,$3.c);
2093 $$.c = abc_modulo($$.c);
2096 E : E '*' E {$$.c = code_append($1.c,$3.c);
2097 if(BOTH_INT($1,$3)) {
2098 $$.c = abc_multiply_i($$.c);
2101 $$.c = abc_multiply($$.c);
2106 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2107 if(use_astype && TYPE_IS_CLASS($3.t)) {
2108 MULTINAME(m,$3.t->cls);
2109 $$.c = abc_astype2($1.c, &m);
2112 $$.c = code_append($1.c, $3.c);
2113 $$.c = abc_astypelate($$.c);
2118 E : E "instanceof" E
2119 {$$.c = code_append($1.c, $3.c);
2120 $$.c = abc_instanceof($$.c);
2121 $$.t = TYPE_BOOLEAN;
2124 E : E "is" E {$$.c = code_append($1.c, $3.c);
2125 $$.c = abc_istypelate($$.c);
2126 $$.t = TYPE_BOOLEAN;
2129 E : "typeof" '(' E ')' {
2131 $$.c = abc_typeof($$.c);
2136 $$.c = cut_last_push($2.c);
2137 $$.c = abc_pushundefined($$.c);
2141 E : "void" { $$.c = abc_pushundefined(0);
2145 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
2150 $$.c=abc_negate_i($$.c);
2153 $$.c=abc_negate($$.c);
2160 $$.c = code_append($$.c, $3.c);
2162 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
2163 $$.c = abc_getproperty2($$.c, &m);
2164 $$.t = 0; // array elements have unknown type
2167 E : '[' MAYBE_EXPRESSION_LIST ']' {
2169 $$.c = code_append($$.c, $2.cc);
2170 $$.c = abc_newarray($$.c, $2.len);
2171 $$.t = registry_getarrayclass();
2174 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
2175 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1};
2177 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2179 $$.cc = code_append($$.cc, $1.c);
2180 $$.cc = code_append($$.cc, $3.c);
2183 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2186 $$.cc = code_append($$.cc, $3.c);
2187 $$.cc = code_append($$.cc, $5.c);
2192 E : '{' MAYBE_EXPRPAIR_LIST '}' {
2194 $$.c = code_append($$.c, $2.cc);
2195 $$.c = abc_newobject($$.c, $2.len/2);
2196 $$.t = registry_getobjectclass();
2201 if(BOTH_INT($1,$3)) {
2202 c=abc_multiply_i(c);
2206 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
2207 $$.c = toreadwrite($1.c, c, 0, 0);
2212 code_t*c = abc_modulo($3.c);
2213 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
2214 $$.c = toreadwrite($1.c, c, 0, 0);
2218 code_t*c = abc_lshift($3.c);
2219 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
2220 $$.c = toreadwrite($1.c, c, 0, 0);
2224 code_t*c = abc_rshift($3.c);
2225 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
2226 $$.c = toreadwrite($1.c, c, 0, 0);
2230 code_t*c = abc_urshift($3.c);
2231 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
2232 $$.c = toreadwrite($1.c, c, 0, 0);
2236 code_t*c = abc_divide($3.c);
2237 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
2238 $$.c = toreadwrite($1.c, c, 0, 0);
2243 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2248 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
2250 $$.c = toreadwrite($1.c, c, 0, 0);
2253 E : E "-=" E { code_t*c = $3.c;
2254 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2255 c=abc_subtract_i(c);
2259 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2261 $$.c = toreadwrite($1.c, c, 0, 0);
2264 E : E '=' E { code_t*c = 0;
2265 c = code_append(c, $3.c);
2266 c = converttype(c, $3.t, $1.t);
2267 $$.c = toreadwrite($1.c, c, 1, 0);
2271 E : E '?' E ':' E %prec below_assignment {
2273 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2274 $$.c = code_append($$.c, $3.c);
2275 code_t*j2 = $$.c = abc_jump($$.c, 0);
2276 $$.c = j1->branch = abc_label($$.c);
2277 $$.c = code_append($$.c, $5.c);
2278 $$.c = j2->branch = abc_label($$.c);
2279 $$.t = join_types($3.t,$5.t,'?');
2282 // TODO: use inclocal where appropriate
2283 E : E "++" { code_t*c = 0;
2284 classinfo_t*type = $1.t;
2285 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2286 c=abc_increment_i(c);
2292 c=converttype(c, type, $1.t);
2293 $$.c = toreadwrite($1.c, c, 0, 1);
2296 E : E "--" { code_t*c = 0;
2297 classinfo_t*type = $1.t;
2298 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2299 c=abc_decrement_i(c);
2305 c=converttype(c, type, $1.t);
2306 $$.c = toreadwrite($1.c, c, 0, 1);
2310 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2311 classinfo_t*type = $2.t;
2312 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2313 c=abc_increment_i(c);
2319 c=converttype(c, type, $2.t);
2320 $$.c = toreadwrite($2.c, c, 0, 0);
2324 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2325 classinfo_t*type = $2.t;
2326 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2327 c=abc_decrement_i(c);
2333 c=converttype(c, type, $2.t);
2334 $$.c = toreadwrite($2.c, c, 0, 0);
2338 E : "super" '.' T_IDENTIFIER
2339 { if(!state->cls->info)
2340 syntaxerror("super keyword not allowed outside a class");
2341 classinfo_t*t = state->cls->info->superclass;
2342 if(!t) t = TYPE_OBJECT;
2344 memberinfo_t*f = registry_findmember(t, $3, 1);
2345 namespace_t ns = {flags2access(f->flags), ""};
2346 MEMBER_MULTINAME(m, f, $3);
2348 $$.c = abc_getlocal_0($$.c);
2349 $$.c = abc_getsuper2($$.c, &m);
2350 $$.t = memberinfo_gettype(f);
2353 E : E '.' T_IDENTIFIER
2355 classinfo_t*t = $1.t;
2357 if(TYPE_IS_CLASS(t) && t->cls) {
2362 memberinfo_t*f = registry_findmember(t, $3, 1);
2364 if(f && !is_static != !(f->flags&FLAG_STATIC))
2366 if(f && f->slot && !noslot) {
2367 $$.c = abc_getslot($$.c, f->slot);
2369 MEMBER_MULTINAME(m, f, $3);
2370 $$.c = abc_getproperty2($$.c, &m);
2372 /* determine type */
2373 $$.t = memberinfo_gettype(f);
2375 $$.c = abc_coerce_a($$.c);
2377 /* when resolving a property on an unknown type, we do know the
2378 name of the property (and don't seem to need the package), but
2379 we need to make avm2 try out all access modes */
2380 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2381 $$.c = abc_getproperty2($$.c, &m);
2382 $$.c = abc_coerce_a($$.c);
2383 $$.t = registry_getanytype();
2387 VAR_READ : T_IDENTIFIER {
2394 /* look at variables */
2395 if((v = find_variable($1))) {
2396 // $1 is a local variable
2397 $$.c = abc_getlocal($$.c, v->index);
2400 /* look at current class' members */
2401 } else if((f = registry_findmember(state->cls->info, $1, 1))) {
2402 // $1 is a function in this class
2403 int var_is_static = (f->flags&FLAG_STATIC);
2404 int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
2405 if(var_is_static != i_am_static) {
2406 /* there doesn't seem to be any "static" way to access
2407 static properties of a class */
2408 state->method->late_binding = 1;
2410 namespace_t ns = {flags2access(f->flags), ""};
2411 multiname_t m = {QNAME, &ns, 0, $1};
2412 $$.c = abc_findpropstrict2($$.c, &m);
2413 $$.c = abc_getproperty2($$.c, &m);
2416 $$.c = abc_getlocal_0($$.c);
2417 $$.c = abc_getslot($$.c, f->slot);
2419 namespace_t ns = {flags2access(f->flags), ""};
2420 multiname_t m = {QNAME, &ns, 0, $1};
2421 $$.c = abc_getlocal_0($$.c);
2422 $$.c = abc_getproperty2($$.c, &m);
2425 if(f->kind == MEMBER_METHOD) {
2426 $$.t = TYPE_FUNCTION(f);
2431 /* look at actual classes, in the current package and imported */
2432 } else if((a = find_class($1))) {
2433 if(a->flags & FLAG_METHOD) {
2435 $$.c = abc_findpropstrict2($$.c, &m);
2436 $$.c = abc_getproperty2($$.c, &m);
2437 $$.t = TYPE_FUNCTION(a->function);
2440 $$.c = abc_getglobalscope($$.c);
2441 $$.c = abc_getslot($$.c, a->slot);
2444 $$.c = abc_getlex2($$.c, &m);
2446 $$.t = TYPE_CLASS(a);
2449 /* unknown object, let the avm2 resolve it */
2451 if(strcmp($1,"trace"))
2452 warning("Couldn't resolve '%s', doing late binding", $1);
2453 state->method->late_binding = 1;
2455 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
2458 $$.c = abc_findpropstrict2($$.c, &m);
2459 $$.c = abc_getproperty2($$.c, &m);
2464 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
2465 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
2466 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
2468 // ----------------- namespaces -------------------------------------------------
2470 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
2471 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
2472 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}
2474 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER