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 CODE_STATEMENT
160 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH
161 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION
162 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
163 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST
164 %type <code> CLASS_DECLARATION
165 %type <code> NAMESPACE_DECLARATION
166 %type <code> 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 "==" "!=" "===" "!=="
214 %nonassoc "is" "as" "in"
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
225 %left above_identifier
230 // needed for "return" precedence:
231 %nonassoc T_STRING T_REGEXP
232 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
233 %nonassoc "false" "true" "null" "undefined" "super"
239 static int yyerror(char*s)
241 syntaxerror("%s", s);
244 static char* concat2(const char* t1, const char* t2)
248 char*text = malloc(l1+l2+1);
249 memcpy(text , t1, l1);
250 memcpy(text+l1, t2, l2);
254 static char* concat3(const char* t1, const char* t2, const char* t3)
259 char*text = malloc(l1+l2+l3+1);
260 memcpy(text , t1, l1);
261 memcpy(text+l1, t2, l2);
262 memcpy(text+l1+l2, t3, l3);
267 typedef struct _import {
271 DECLARE_LIST(import);
273 typedef struct _classstate {
279 char has_constructor;
282 typedef struct _methodstate {
286 /* code that needs to be executed at the start of
287 a method (like initializing local registers) */
294 typedef struct _state {
299 import_list_t*wildcard_imports;
301 char has_own_imports;
304 methodstate_t*method;
309 typedef struct _global {
316 static global_t*global = 0;
317 static state_t* state = 0;
321 #define MULTINAME(m,x) \
324 registry_fill_multiname(&m, &m##_ns, x);
326 #define MEMBER_MULTINAME(m,f,n) \
330 m##_ns.access = flags2access(f->flags); \
334 m.namespace_set = 0; \
337 m.type = MULTINAME; \
339 m.namespace_set = &nopackage_namespace_set; \
343 /* warning: list length of namespace set is undefined */
344 #define MULTINAME_LATE(m, access, package) \
345 namespace_t m##_ns = {access, package}; \
346 namespace_set_t m##_nsset; \
347 namespace_list_t m##_l;m##_l.next = 0; \
348 m##_nsset.namespaces = &m##_l; \
349 m##_nsset = m##_nsset; \
350 m##_l.namespace = &m##_ns; \
351 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
353 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
354 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
355 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
356 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
357 static namespace_list_t nl4 = {&ns4,0};
358 static namespace_list_t nl3 = {&ns3,&nl4};
359 static namespace_list_t nl2 = {&ns2,&nl3};
360 static namespace_list_t nl1 = {&ns1,&nl2};
361 static namespace_set_t nopackage_namespace_set = {&nl1};
363 static void init_globals()
365 global = rfx_calloc(sizeof(global_t));
368 static void new_state()
371 state_t*oldstate = state;
373 memcpy(s, state, sizeof(state_t)); //shallow copy
375 s->imports = dict_new();
379 state->has_own_imports = 0;
380 state->vars = dict_new();
381 state->old = oldstate;
383 static void state_has_imports()
385 state->wildcard_imports = list_clone(state->wildcard_imports);
386 state->imports = dict_clone(state->imports);
387 state->has_own_imports = 1;
390 static void state_destroy(state_t*state)
392 if(state->has_own_imports) {
393 list_free(state->wildcard_imports);
394 dict_destroy(state->imports);state->imports=0;
396 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
397 dict_destroy(state->imports);state->imports=0;
401 for(t=0;t<state->vars->hashsize;t++) {
402 dictentry_t*e =state->vars->slots[t];
404 free(e->data);e->data=0;
408 dict_destroy(state->vars);state->vars=0;
414 static void old_state()
416 if(!state || !state->old)
417 syntaxerror("invalid nesting");
418 state_t*leaving = state;
420 /*if(state->method->initcode) {
421 printf("residual initcode\n");
422 code_dump(state->method->initcode, 0, 0, "", stdout);
424 state_destroy(leaving);
426 void initialize_state()
431 global->file = abc_file_new();
432 global->file->flags &= ~ABCFILE_LAZY;
433 global->variable_count = 1;
435 global->init = abc_initscript(global->file, 0);
436 code_t*c = global->init->method->body->code;
438 c = abc_getlocal_0(c);
439 c = abc_pushscope(c);
441 /* findpropstrict doesn't just return a scope object- it
442 also makes it "active" somehow. Push local_0 on the
443 scope stack and read it back with findpropstrict, it'll
444 contain properties like "trace". Trying to find the same
445 property on a "vanilla" local_0 yields only a "undefined" */
446 //c = abc_findpropstrict(c, "[package]::trace");
448 /*c = abc_getlocal_0(c);
449 c = abc_findpropstrict(c, "[package]::trace");
451 c = abc_setlocal_1(c);
453 c = abc_pushbyte(c, 0);
454 c = abc_setlocal_2(c);
456 code_t*xx = c = abc_label(c);
457 c = abc_findpropstrict(c, "[package]::trace");
458 c = abc_pushstring(c, "prop:");
459 c = abc_hasnext2(c, 1, 2);
461 c = abc_setlocal_3(c);
462 c = abc_callpropvoid(c, "[package]::trace", 2);
463 c = abc_getlocal_3(c);
465 c = abc_iftrue(c,xx);*/
467 c = abc_findpropstrict(c, "[package]::trace");
468 c = abc_pushstring(c, "[entering global init function]");
469 c = abc_callpropvoid(c, "[package]::trace", 1);
471 global->init->method->body->code = c;
473 void* finalize_state()
475 if(state->level!=1) {
476 syntaxerror("unexpected end of file");
478 abc_method_body_t*m = global->init->method->body;
481 __ findpropstrict(m, "[package]::trace");
482 __ pushstring(m, "[leaving global init function]");
483 __ callpropvoid(m, "[package]::trace", 1);
486 state_destroy(state);
492 static void startpackage(char*name)
495 syntaxerror("Packages can not be nested.");
498 /*printf("entering package \"%s\"\n", name);*/
499 state->package = strdup(name);
500 global->variable_count = 1;
502 static void endpackage()
504 /*printf("leaving package \"%s\"\n", state->package);*/
506 //used e.g. in classinfo_register:
507 //free(state->package);state->package=0;
513 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
516 syntaxerror("inner classes now allowed");
519 global->variable_count = 1;
520 state->cls = rfx_calloc(sizeof(classstate_t));
523 classinfo_list_t*mlist=0;
524 /*printf("entering class %s\n", name);
525 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n");
527 printf(" extends: %s.%s\n", extends->package, extends->name);
528 printf(" implements (%d): ", list_length(implements));
529 for(mlist=implements;mlist;mlist=mlist->next) {
530 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
535 if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
536 syntaxerror("invalid modifier(s)");
538 if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
539 syntaxerror("public and internal not supported at the same time.");
541 /* create the class name, together with the proper attributes */
545 if(!(flags&FLAG_PUBLIC) && !state->package) {
546 access = ACCESS_PRIVATE; package = current_filename;
547 } else if(!(flags&FLAG_PUBLIC) && state->package) {
548 access = ACCESS_PACKAGEINTERNAL; package = state->package;
549 } else if(state->package) {
550 access = ACCESS_PACKAGE; package = state->package;
552 syntaxerror("public classes only allowed inside a package");
555 if(registry_findclass(package, classname)) {
556 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
560 /* build info struct */
561 int num_interfaces = (list_length(implements));
562 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
563 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
565 classinfo_list_t*l = implements;
566 for(l=implements;l;l=l->next) {
567 state->cls->info->interfaces[pos++] = l->classinfo;
570 multiname_t*extends2 = sig2mname(extends);
572 MULTINAME(classname2,state->cls->info);
575 state->cls_init = abc_getlocal_0(state->cls_init);
576 state->cls_init = abc_constructsuper(state->cls_init, 0);
579 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
580 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
581 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
583 state->cls->info->flags |= CLASS_INTERFACE;
584 abc_class_interface(state->cls->abc);
587 abc_class_protectedNS(state->cls->abc, classname);
589 for(mlist=implements;mlist;mlist=mlist->next) {
590 MULTINAME(m, mlist->classinfo);
591 abc_class_add_interface(state->cls->abc, &m);
594 /* now write the construction code for this class */
595 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
597 abc_method_body_t*m = global->init->method->body;
598 __ getglobalscope(m);
599 classinfo_t*s = extends;
604 //TODO: take a look at the current scope stack, maybe
605 // we can re-use something
610 multiname_t*s2 = sig2mname(s);
612 multiname_destroy(s2);
614 __ pushscope(m); count++;
615 m->code = m->code->prev->prev; // invert
617 /* continue appending after last op end */
618 while(m->code && m->code->next) m->code = m->code->next;
620 /* TODO: if this is one of *our* classes, we can also
621 do a getglobalscope/getslot <nr> (which references
622 the init function's slots) */
624 __ getlex2(m, extends2);
626 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
627 stack is not the superclass */
628 __ pushscope(m);count++;
631 /* notice: we get a verify error #1107 if the top element on the scope
632 stack is not the global object */
634 __ pushscope(m);count++;
636 __ newclass(m,state->cls->abc);
640 __ setslot(m, slotindex);
642 /* flash.display.MovieClip handling */
643 if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
644 if(state->package && state->package[0]) {
645 globalclass = concat3(state->package, ".", classname);
647 globalclass = strdup(classname);
650 multiname_destroy(extends2);
653 static code_t* wrap_function(code_t*c,code_t*initcode, code_t*body)
655 c = code_append(c, initcode);
656 c = code_append(c, body);
657 /* append return if necessary */
658 if(!c || c->opcode != OPCODE_RETURNVOID &&
659 c->opcode != OPCODE_RETURNVALUE) {
660 c = abc_returnvoid(c);
665 static void endclass()
667 if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
669 c = abc_getlocal_0(c);
670 c = abc_constructsuper(c, 0);
671 state->cls->init = code_append(state->cls->init, c);
674 if(state->cls->init) {
675 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
676 m->body->code = wrap_function(0, state->cls->init, m->body->code);
678 if(state->cls->static_init) {
679 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
680 m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
682 // handy for scope testing
686 abc_class_getstaticconstructor(state->cls->abc,0)->body->code = c;*/
689 free(state->cls);state->cls=0;
693 typedef struct _variable {
698 static variable_t* find_variable(char*name)
704 v = dict_lookup(s->vars, name);
712 static variable_t* find_variable_safe(char*name)
714 variable_t* v = find_variable(name);
716 syntaxerror("undefined variable: %s", name);
719 static char variable_exists(char*name)
721 return dict_lookup(state->vars, name)!=0;
723 code_t*defaultvalue(code_t*c, classinfo_t*type);
724 static int new_variable(char*name, classinfo_t*type, char init)
727 v->index = global->variable_count;
730 dict_put(state->vars, name, v);
732 if(init && state->method && type) {
733 /* if this is a typed variable:
734 push default value for type on stack at the very beginning of the
735 method, so that it always has that type regardless of the control
737 state->method->initcode = defaultvalue(state->method->initcode, type);
738 state->method->initcode = abc_setlocal(state->method->initcode, v->index);
740 return global->variable_count++;
742 #define TEMPVARNAME "__as3_temp__"
743 static int gettempvar()
745 variable_t*v = find_variable(TEMPVARNAME);
748 return new_variable(TEMPVARNAME, 0, 0);
751 code_t* killvars(code_t*c)
754 for(t=0;t<state->vars->hashsize;t++) {
755 dictentry_t*e =state->vars->slots[t];
757 variable_t*v = (variable_t*)e->data;
758 //do this always, otherwise register types don't match
759 //in the verifier when doing nested loops
760 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
761 c = abc_kill(c, v->index);
768 void check_code_for_break(code_t*c)
771 if(c->opcode == OPCODE___BREAK__) {
772 char*name = string_cstr(c->data[0]);
773 syntaxerror("Unresolved \"break %s\"", name);
775 if(c->opcode == OPCODE___CONTINUE__) {
776 char*name = string_cstr(c->data[0]);
777 syntaxerror("Unresolved \"continue %s\"", name);
784 static void check_constant_against_type(classinfo_t*t, constant_t*c)
786 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
787 if(TYPE_IS_NUMBER(t)) {
788 xassert(c->type == CONSTANT_FLOAT
789 || c->type == CONSTANT_INT
790 || c->type == CONSTANT_UINT);
791 } else if(TYPE_IS_UINT(t)) {
792 xassert(c->type == CONSTANT_UINT ||
793 (c->type == CONSTANT_INT && c->i>0));
794 } else if(TYPE_IS_INT(t)) {
795 xassert(c->type == CONSTANT_INT);
796 } else if(TYPE_IS_BOOLEAN(t)) {
797 xassert(c->type == CONSTANT_TRUE
798 || c->type == CONSTANT_FALSE);
802 static int flags2access(int flags)
805 if(flags&FLAG_PUBLIC) {
806 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
807 access = ACCESS_PACKAGE;
808 } else if(flags&FLAG_PRIVATE) {
809 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
810 access = ACCESS_PRIVATE;
811 } else if(flags&FLAG_PROTECTED) {
812 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
813 access = ACCESS_PROTECTED;
815 access = ACCESS_PACKAGEINTERNAL;
820 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
822 memberinfo_t*minfo = 0;
825 minfo = rfx_calloc(sizeof(memberinfo_t));
826 classinfo_t*c = classinfo_register(flags2access(flags), state->package, name, 0);
827 c->flags |= FLAG_METHOD;
829 minfo->kind = MEMBER_METHOD;
831 minfo->flags = FLAG_STATIC;
832 minfo->return_type = return_type;
833 } else if(getset != KW_GET && getset != KW_SET) {
835 if((minfo = registry_findmember(state->cls->info, name, 0))) {
836 if(minfo->parent == state->cls->info) {
837 syntaxerror("class already contains a member/method called '%s'", name);
838 } else if(!minfo->parent) {
839 syntaxerror("internal error: overriding method %s, which doesn't have parent", name);
841 if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE)))
842 syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?");
845 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
846 minfo->return_type = return_type;
847 // getslot on a member slot only returns "undefined", so no need
848 // to actually store these
849 //state->minfo->slot = state->method->abc->method->trait->slot_id;
851 //class getter/setter
852 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
856 else if(params->list)
857 type = params->list->param->type;
858 // not sure wether to look into superclasses here, too
859 if((minfo=registry_findmember(state->cls->info, name, 0))) {
860 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
861 syntaxerror("class already contains a member or method called '%s'", name);
863 syntaxerror("getter/setter for '%s' already defined", name);
864 /* make a setter or getter into a getset */
869 if(type && minfo->type != type)
870 syntaxerror("different type in getter and setter");
872 minfo = memberinfo_register(state->cls->info, name, gs);
875 /* can't assign a slot as getter and setter might have different slots */
876 //minfo->slot = slot;
878 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
879 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
880 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
881 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
882 if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
883 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
887 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
888 params_t*params, classinfo_t*return_type)
891 syntaxerror("not able to start another method scope");
894 global->variable_count = 0;
895 state->method = rfx_calloc(sizeof(methodstate_t));
896 state->method->initcode = 0;
897 state->method->has_super = 0;
899 state->method->is_constructor = !strcmp(state->cls->info->name,name);
900 state->cls->has_constructor |= state->method->is_constructor;
902 new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
904 state->method->is_global = 1;
905 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
907 new_variable("globalscope", 0, 0);
910 /* state->vars is initialized by state_new */
913 for(p=params->list;p;p=p->next) {
914 new_variable(p->param->name, p->param->type, 0);
916 if(state->method->is_constructor)
917 name = "__as3_constructor__";
918 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
921 static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
922 params_t*params, classinfo_t*return_type, code_t*body)
926 multiname_t*type2 = sig2mname(return_type);
928 if(state->method->is_constructor) {
929 f = abc_class_getconstructor(state->cls->abc, type2);
930 } else if(!state->method->is_global) {
931 namespace_t mname_ns = {flags2access(flags), ""};
932 multiname_t mname = {QNAME, &mname_ns, 0, name};
934 if(flags&FLAG_STATIC)
935 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
937 f = abc_class_method(state->cls->abc, type2, &mname);
938 slot = f->trait->slot_id;
940 namespace_t mname_ns = {flags2access(flags), state->package};
941 multiname_t mname = {QNAME, &mname_ns, 0, name};
943 f = abc_method_new(global->file, type2, 1);
944 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
945 //abc_code_t*c = global->init->method->body->code;
947 //flash doesn't seem to allow us to access function slots
948 //state->method->info->slot = slot;
950 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
951 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
952 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
953 if(params->varargs) f->flags |= METHOD_NEED_REST;
957 for(p=params->list;p;p=p->next) {
958 if(params->varargs && !p->next) {
959 break; //varargs: omit last parameter in function signature
961 multiname_t*m = sig2mname(p->param->type);
962 list_append(f->parameters, m);
963 if(p->param->value) {
964 check_constant_against_type(p->param->type, p->param->value);
965 opt=1;list_append(f->optional_parameters, p->param->value);
967 syntaxerror("non-optional parameter not allowed after optional parameters");
970 check_code_for_break(body);
973 f->body->code = body;
976 syntaxerror("interface methods can't have a method body");
978 free(state->method);state->method=0;
984 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
989 void breakjumpsto(code_t*c, char*name, code_t*jump)
992 if(c->opcode == OPCODE___BREAK__) {
993 string_t*name2 = c->data[0];
994 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
995 c->opcode = OPCODE_JUMP;
1002 void continuejumpsto(code_t*c, char*name, code_t*jump)
1005 if(c->opcode == OPCODE___CONTINUE__) {
1006 string_t*name2 = c->data[0];
1007 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1008 c->opcode = OPCODE_JUMP;
1016 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1018 if(!type1 || !type2)
1019 return registry_getanytype();
1020 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1021 return registry_getanytype();
1024 return registry_getanytype();
1026 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1031 return abc_coerce_a(c);
1035 // cast an "any" type to a specific type. subject to
1036 // runtime exceptions
1037 return abc_coerce2(c, &m);
1040 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1041 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1042 // allow conversion between number types
1043 return abc_coerce2(c, &m);
1045 //printf("%s.%s\n", from.package, from.name);
1046 //printf("%s.%s\n", to.package, to.name);
1048 classinfo_t*supertype = from;
1050 if(supertype == to) {
1051 // target type is one of from's superclasses
1052 return abc_coerce2(c, &m);
1055 while(supertype->interfaces[t]) {
1056 if(supertype->interfaces[t]==to) {
1057 // target type is one of from's interfaces
1058 return abc_coerce2(c, &m);
1062 supertype = supertype->superclass;
1064 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1066 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1068 syntaxerror("can't convert type %s to %s", from->name, to->name);
1071 code_t*defaultvalue(code_t*c, classinfo_t*type)
1073 if(TYPE_IS_INT(type)) {
1074 c = abc_pushbyte(c, 0);
1075 } else if(TYPE_IS_UINT(type)) {
1076 c = abc_pushuint(c, 0);
1077 } else if(TYPE_IS_FLOAT(type)) {
1079 } else if(TYPE_IS_BOOLEAN(type)) {
1080 c = abc_pushfalse(c);
1082 c = abc_pushnull(c);
1087 char is_pushundefined(code_t*c)
1089 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1092 void parserassert(int b)
1094 if(!b) syntaxerror("internal error: assertion failed");
1097 static classinfo_t* find_class(char*name)
1101 c = registry_findclass(state->package, name);
1103 /* try explicit imports */
1104 dictentry_t* e = dict_get_slot(state->imports, name);
1108 if(!strcmp(e->key, name)) {
1109 c = (classinfo_t*)e->data;
1114 /* try package.* imports */
1115 import_list_t*l = state->wildcard_imports;
1119 //printf("does package %s contain a class %s?\n", l->import->package, name);
1120 c = registry_findclass(l->import->package, name);
1124 /* try global package */
1126 c = registry_findclass("", name);
1131 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1135 [prefix code] [read instruction]
1139 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1142 if(in && in->opcode == OPCODE_COERCE_A) {
1143 in = code_cutlast(in);
1146 syntaxerror("internal error");
1148 /* chop off read instruction */
1152 prefix = r->prev;r->prev = 0;
1158 char use_temp_var = readbefore;
1160 /* generate the write instruction, and maybe append a dup to the prefix code */
1161 code_t* write = abc_nop(0);
1162 if(r->opcode == OPCODE_GETPROPERTY) {
1163 write->opcode = OPCODE_SETPROPERTY;
1164 multiname_t*m = (multiname_t*)r->data[0];
1165 write->data[0] = multiname_clone(m);
1166 if(m->type == QNAME || m->type == MULTINAME) {
1168 prefix = abc_dup(prefix); // we need the object, too
1171 } else if(m->type == MULTINAMEL) {
1173 /* dupping two values on the stack requires 5 operations and one register-
1174 couldn't adobe just have given us a dup2? */
1175 int temp = gettempvar();
1176 prefix = abc_setlocal(prefix, temp);
1177 prefix = abc_dup(prefix);
1178 prefix = abc_getlocal(prefix, temp);
1179 prefix = abc_swap(prefix);
1180 prefix = abc_getlocal(prefix, temp);
1182 prefix = abc_kill(prefix, temp);
1186 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1188 } else if(r->opcode == OPCODE_GETSLOT) {
1189 write->opcode = OPCODE_SETSLOT;
1190 write->data[0] = r->data[0];
1192 prefix = abc_dup(prefix); // we need the object, too
1195 } else if(r->opcode == OPCODE_GETLOCAL) {
1196 write->opcode = OPCODE_SETLOCAL;
1197 write->data[0] = r->data[0];
1198 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1199 write->opcode = OPCODE_SETLOCAL_0;
1200 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1201 write->opcode = OPCODE_SETLOCAL_1;
1202 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1203 write->opcode = OPCODE_SETLOCAL_2;
1204 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1205 write->opcode = OPCODE_SETLOCAL_3;
1207 code_dump(r, 0, 0, "", stdout);
1208 syntaxerror("illegal lvalue: can't assign a value to this expression");
1215 /* with getproperty/getslot, we have to be extra careful not
1216 to execute the read code twice, as it might have side-effects
1217 (e.g. if the property is in fact a setter/getter combination)
1219 So read the value, modify it, and write it again,
1220 using prefix only once and making sure (by using a temporary
1221 register) that the return value is what we just wrote */
1222 temp = gettempvar();
1223 c = code_append(c, prefix);
1224 c = code_append(c, r);
1227 c = abc_setlocal(c, temp);
1229 c = code_append(c, middlepart);
1232 c = abc_setlocal(c, temp);
1234 c = code_append(c, write);
1235 c = abc_getlocal(c, temp);
1236 c = abc_kill(c, temp);
1238 /* if we're allowed to execute the read code twice *and*
1239 the middlepart doesn't modify the code, things are easier.
1241 code_t* r2 = code_dup(r);
1242 //c = code_append(c, prefix);
1243 parserassert(!prefix);
1244 c = code_append(c, r);
1245 c = code_append(c, middlepart);
1246 c = code_append(c, write);
1247 c = code_append(c, r2);
1250 /* even smaller version: overwrite the value without reading
1254 c = code_append(c, prefix);
1257 c = code_append(c, middlepart);
1258 c = code_append(c, write);
1259 c = code_append(c, r);
1261 temp = gettempvar();
1263 c = code_append(c, prefix);
1265 c = code_append(c, middlepart);
1267 c = abc_setlocal(c, temp);
1268 c = code_append(c, write);
1269 c = abc_getlocal(c, temp);
1270 c = abc_kill(c, temp);
1277 #define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
1278 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1285 /* ------------ code blocks / statements ---------------- */
1287 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1289 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1290 PROGRAM_CODE_LIST: PROGRAM_CODE
1291 | PROGRAM_CODE_LIST PROGRAM_CODE
1293 PROGRAM_CODE: PACKAGE_DECLARATION
1294 | INTERFACE_DECLARATION
1296 | FUNCTION_DECLARATION
1300 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1301 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1302 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1304 INPACKAGE_CODE: INTERFACE_DECLARATION
1306 | FUNCTION_DECLARATION
1310 MAYBECODE: CODE {$$=$1;}
1311 MAYBECODE: {$$=code_new();}
1313 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1314 CODE: CODEPIECE {$$=$1;}
1316 // code which also may appear outside a method
1317 CODE_STATEMENT: IMPORT
1318 CODE_STATEMENT: VOIDEXPRESSION
1320 CODE_STATEMENT: FOR_IN
1321 CODE_STATEMENT: WHILE
1322 CODE_STATEMENT: DO_WHILE
1323 CODE_STATEMENT: SWITCH
1326 // code which may appear anywhere
1327 CODEPIECE: ';' {$$=0;}
1328 //CODEPIECE: PACKAGE_DECLARATION
1329 //CODEPIECE: CLASS_DECLARATION
1330 //CODEPIECE: FUNCTION_DECLARATION
1331 //CODEPIECE: INTERFACE_DECLARATION
1332 CODEPIECE: VARIABLE_DECLARATION
1333 CODEPIECE: CODE_STATEMENT
1338 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1339 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;}
1341 CODEBLOCK : '{' CODE '}' {$$=$2;}
1342 CODEBLOCK : '{' '}' {$$=0;}
1343 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1344 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1346 /* ------------ package init code ------------------- */
1348 PACKAGE_INITCODE: CODE_STATEMENT {
1349 if($1) warning("code ignored");
1352 /* ------------ variables --------------------------- */
1354 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1355 | {$$.c=abc_pushundefined(0);
1359 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1360 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1362 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1363 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1365 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1367 if(variable_exists($1))
1368 syntaxerror("Variable %s already defined", $1);
1370 if(!is_subtype_of($3.t, $2)) {
1371 syntaxerror("Can't convert %s to %s", $3.t->name,
1375 int index = new_variable($1, $2, 1);
1378 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1380 $$ = converttype($$, $3.t, $2);
1381 $$ = abc_setlocal($$, index);
1383 $$ = defaultvalue(0, $2);
1384 $$ = abc_setlocal($$, index);
1387 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1389 $$ = abc_coerce_a($$);
1390 $$ = abc_setlocal($$, index);
1396 /* that's the default for a local register, anyway
1398 state->method->initcode = abc_pushundefined(state->method->initcode);
1399 state->method->initcode = abc_setlocal(state->method->initcode, index);
1401 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1404 /* ------------ control flow ------------------------- */
1406 MAYBEELSE: %prec below_else {$$ = code_new();}
1407 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1408 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1410 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1412 $$ = code_append($$, $4.c);
1413 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1415 $$ = code_append($$, $6);
1417 myjmp = $$ = abc_jump($$, 0);
1419 myif->branch = $$ = abc_nop($$);
1421 $$ = code_append($$, $7);
1422 myjmp->branch = $$ = abc_nop($$);
1425 $$ = killvars($$);old_state();
1428 FOR_INIT : {$$=code_new();}
1429 FOR_INIT : VARIABLE_DECLARATION
1430 FOR_INIT : VOIDEXPRESSION
1431 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1432 $$=$2;new_variable($2,$3,1);
1434 FOR_IN_INIT : T_IDENTIFIER {
1438 FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
1439 FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
1441 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1442 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1444 $$ = code_append($$, $2);
1445 code_t*loopstart = $$ = abc_label($$);
1446 $$ = code_append($$, $4.c);
1447 code_t*myif = $$ = abc_iffalse($$, 0);
1448 $$ = code_append($$, $8);
1449 code_t*cont = $$ = abc_nop($$);
1450 $$ = code_append($$, $6);
1451 $$ = abc_jump($$, loopstart);
1452 code_t*out = $$ = abc_nop($$);
1453 breakjumpsto($$, $1.name, out);
1454 continuejumpsto($$, $1.name, cont);
1457 $$ = killvars($$);old_state();
1460 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
1461 variable_t*var = find_variable($2);
1462 char*tmp1name = concat2($2, "__tmp1__");
1463 int it = new_variable(tmp1name, TYPE_INT, 0);
1464 char*tmp2name = concat2($2, "__array__");
1465 int array = new_variable(tmp1name, 0, 0);
1468 $$ = code_append($$, $4.c);
1469 $$ = abc_coerce_a($$);
1470 $$ = abc_setlocal($$, array);
1471 $$ = abc_pushbyte($$, 0);
1472 $$ = abc_setlocal($$, it);
1474 code_t*loopstart = $$ = abc_label($$);
1476 $$ = abc_hasnext2($$, array, it);
1477 code_t*myif = $$ = abc_iffalse($$, 0);
1478 $$ = abc_getlocal($$, array);
1479 $$ = abc_getlocal($$, it);
1481 $$ = abc_nextname($$);
1483 $$ = abc_nextvalue($$);
1484 $$ = converttype($$, 0, var->type);
1485 $$ = abc_setlocal($$, var->index);
1487 $$ = code_append($$, $6);
1488 $$ = abc_jump($$, loopstart);
1490 code_t*out = $$ = abc_nop($$);
1491 breakjumpsto($$, $1.name, out);
1492 continuejumpsto($$, $1.name, loopstart);
1503 WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1506 code_t*myjmp = $$ = abc_jump($$, 0);
1507 code_t*loopstart = $$ = abc_label($$);
1508 $$ = code_append($$, $6);
1509 code_t*cont = $$ = abc_nop($$);
1510 myjmp->branch = cont;
1511 $$ = code_append($$, $4.c);
1512 $$ = abc_iftrue($$, loopstart);
1513 code_t*out = $$ = abc_nop($$);
1514 breakjumpsto($$, $1, out);
1515 continuejumpsto($$, $1, cont);
1521 DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
1523 code_t*loopstart = $$ = abc_label($$);
1524 $$ = code_append($$, $3);
1525 code_t*cont = $$ = abc_nop($$);
1526 $$ = code_append($$, $6.c);
1527 $$ = abc_iftrue($$, loopstart);
1528 code_t*out = $$ = abc_nop($$);
1529 breakjumpsto($$, $1, out);
1530 continuejumpsto($$, $1, cont);
1535 BREAK : "break" %prec prec_none {
1536 $$ = abc___break__(0, "");
1538 BREAK : "break" T_IDENTIFIER {
1539 $$ = abc___break__(0, $2);
1541 CONTINUE : "continue" %prec prec_none {
1542 $$ = abc___continue__(0, "");
1544 CONTINUE : "continue" T_IDENTIFIER {
1545 $$ = abc___continue__(0, $2);
1548 MAYBE_CASE_LIST : {$$=0;}
1549 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
1550 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
1551 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
1552 CASE_LIST: CASE {$$=$1}
1553 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
1555 CASE: "case" E ':' MAYBECODE {
1557 $$ = code_append($$, $2.c);
1558 code_t*j = $$ = abc_ifne($$, 0);
1559 $$ = code_append($$, $4);
1560 if($$->opcode != OPCODE___BREAK__) {
1561 $$ = abc___fallthrough__($$, "");
1563 code_t*e = $$ = abc_nop($$);
1566 DEFAULT: "default" ':' MAYBECODE {
1569 SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
1571 $$ = code_append($$, $7);
1572 code_t*out = $$ = abc_pop($$);
1573 breakjumpsto($$, $1, out);
1575 code_t*c = $$,*lastblock=0;
1577 if(c->opcode == OPCODE_IFNE) {
1578 if(!c->next) syntaxerror("internal error in fallthrough handling");
1580 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
1582 c->opcode = OPCODE_JUMP;
1583 c->branch = lastblock;
1585 /* fall through end of switch */
1586 c->opcode = OPCODE_NOP;
1594 /* ------------ packages and imports ---------------- */
1596 X_IDENTIFIER: T_IDENTIFIER
1597 | "package" {$$="package";}
1599 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;}
1600 PACKAGE: X_IDENTIFIER {$$=strdup($1);}
1602 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;}
1603 PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;}
1605 IMPORT : "import" QNAME {
1608 syntaxerror("Couldn't import class\n");
1609 state_has_imports();
1610 dict_put(state->imports, c->name, c);
1613 IMPORT : "import" PACKAGE '.' '*' {
1616 state_has_imports();
1617 list_append(state->wildcard_imports, i);
1621 /* ------------ classes and interfaces (header) -------------- */
1623 MAYBE_MODIFIERS : {$$=0;}
1624 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
1625 MODIFIER_LIST : MODIFIER {$$=$1;}
1626 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
1628 MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
1629 | KW_PRIVATE {$$=FLAG_PRIVATE;}
1630 | KW_PROTECTED {$$=FLAG_PROTECTED;}
1631 | KW_STATIC {$$=FLAG_STATIC;}
1632 | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
1633 | KW_FINAL {$$=FLAG_FINAL;}
1634 | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
1635 | KW_NATIVE {$$=FLAG_NATIVE;}
1636 | KW_INTERNAL {$$=FLAG_INTERNAL;}
1638 EXTENDS : {$$=registry_getobjectclass();}
1639 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1641 EXTENDS_LIST : {$$=list_new();}
1642 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1644 IMPLEMENTS_LIST : {$$=list_new();}
1645 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1647 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
1648 EXTENDS IMPLEMENTS_LIST
1649 '{' {startclass($1,$3,$4,$5, 0);}
1651 '}' {endclass();$$=0;}
1653 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
1655 '{' {startclass($1,$3,0,$4,1);}
1656 MAYBE_INTERFACE_BODY
1657 '}' {endclass();$$=0;}
1659 /* ------------ classes and interfaces (body) -------------- */
1662 MAYBE_CLASS_BODY : CLASS_BODY
1663 CLASS_BODY : CLASS_BODY_ITEM
1664 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
1665 CLASS_BODY_ITEM : ';'
1666 CLASS_BODY_ITEM : SLOT_DECLARATION
1667 CLASS_BODY_ITEM : FUNCTION_DECLARATION
1669 CLASS_BODY_ITEM : CODE_STATEMENT {
1670 code_t*c = state->cls->static_init;
1671 c = code_append(c, $1);
1672 state->cls->static_init = c;
1675 MAYBE_INTERFACE_BODY :
1676 MAYBE_INTERFACE_BODY : INTERFACE_BODY
1677 INTERFACE_BODY : IDECLARATION
1678 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
1680 IDECLARATION : "var" T_IDENTIFIER {
1681 syntaxerror("variable declarations not allowed in interfaces");
1683 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
1685 if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
1686 syntaxerror("invalid method modifiers: interface methods always need to be public");
1688 startfunction(0,$1,$3,$4,&$6,$8);
1689 endfunction(0,$1,$3,$4,&$6,$8, 0);
1692 /* ------------ classes and interfaces (body, slots ) ------- */
1694 VARCONST: "var" | "const"
1696 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1698 memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
1700 info->flags = flags;
1703 namespace_t mname_ns = {flags2access(flags), ""};
1704 multiname_t mname = {QNAME, &mname_ns, 0, $3};
1706 if(!(flags&FLAG_STATIC)) {
1709 t=abc_class_slot(state->cls->abc, &mname, &m);
1711 t=abc_class_slot(state->cls->abc, &mname, 0);
1713 info->slot = t->slot_id;
1717 t=abc_class_staticslot(state->cls->abc, &mname, &m);
1719 t=abc_class_staticslot(state->cls->abc, &mname, 0);
1721 info->slot = t->slot_id;
1723 if($5.c && !is_pushundefined($5.c)) {
1725 c = abc_getlocal_0(c);
1726 c = code_append(c, $5.c);
1727 c = converttype(c, $5.t, $4);
1728 c = abc_setslot(c, t->slot_id);
1729 if(!(flags&FLAG_STATIC))
1730 state->cls->init = code_append(state->cls->init, c);
1732 state->cls->static_init = code_append(state->cls->static_init, c);
1735 t->kind= TRAIT_CONST;
1741 /* ------------ constants -------------------------------------- */
1743 MAYBESTATICCONSTANT: {$$=0;}
1744 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1746 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1747 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1748 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1749 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1750 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
1751 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1752 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
1753 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
1754 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
1756 /* ------------ classes and interfaces (body, functions) ------- */
1758 // non-vararg version
1760 memset(&$$,0,sizeof($$));
1762 MAYBE_PARAM_LIST: PARAM_LIST {
1767 MAYBE_PARAM_LIST: "..." PARAM {
1768 memset(&$$,0,sizeof($$));
1770 list_append($$.list, $2);
1772 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1775 list_append($$.list, $4);
1779 PARAM_LIST: PARAM_LIST ',' PARAM {
1781 list_append($$.list, $3);
1784 memset(&$$,0,sizeof($$));
1785 list_append($$.list, $1);
1788 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1789 $$ = malloc(sizeof(param_t));
1794 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1795 $$ = malloc(sizeof(param_t));
1797 $$->type = TYPE_ANY;
1800 GETSET : "get" {$$=$1;}
1804 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1805 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1808 if(state->method->late_binding) {
1809 c = abc_getlocal_0(c);
1810 c = abc_pushscope(c);
1812 if(state->method->is_constructor && !state->method->has_super) {
1813 // call default constructor
1814 c = abc_getlocal_0(c);
1815 c = abc_constructsuper(c, 0);
1817 c = wrap_function(c, state->method->initcode, $11);
1818 endfunction(0,$1,$3,$4,&$6,$8,c);
1822 /* ------------- package + class ids --------------- */
1824 CLASS: T_IDENTIFIER {
1826 /* try current package */
1827 $$ = find_class($1);
1828 if(!$$) syntaxerror("Could not find class %s\n", $1);
1831 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1832 $$ = registry_findclass($1, $3);
1833 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
1837 QNAME: PACKAGEANDCLASS
1840 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1841 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1843 TYPE : QNAME {$$=$1;}
1844 | '*' {$$=registry_getanytype();}
1845 | "void" {$$=registry_getanytype();}
1847 | "String" {$$=registry_getstringclass();}
1848 | "int" {$$=registry_getintclass();}
1849 | "uint" {$$=registry_getuintclass();}
1850 | "Boolean" {$$=registry_getbooleanclass();}
1851 | "Number" {$$=registry_getnumberclass();}
1854 MAYBETYPE: ':' TYPE {$$=$2;}
1857 /* ----------function calls, delete, constructor calls ------ */
1859 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
1860 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1862 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
1863 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1864 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
1867 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
1869 $$.cc = code_append($1.cc, $3.c);
1872 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1877 $$.c = abc_getglobalscope($$.c);
1878 $$.c = abc_getslot($$.c, $2->slot);
1880 $$.c = abc_findpropstrict2($$.c, &m);
1883 $$.c = code_append($$.c, $3.cc);
1886 $$.c = abc_construct($$.c, $3.len);
1888 $$.c = abc_constructprop2($$.c, &m, $3.len);
1892 /* TODO: use abc_call (for calling local variables),
1893 abc_callstatic (for calling own methods)
1896 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1899 if($$.c->opcode == OPCODE_COERCE_A) {
1900 $$.c = code_cutlast($$.c);
1902 code_t*paramcode = $3.cc;
1905 if($$.c->opcode == OPCODE_GETPROPERTY) {
1906 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
1907 $$.c = code_cutlast($$.c);
1908 $$.c = code_append($$.c, paramcode);
1909 $$.c = abc_callproperty2($$.c, name, $3.len);
1910 multiname_destroy(name);
1911 } else if($$.c->opcode == OPCODE_GETSLOT) {
1912 int slot = (int)(ptroff_t)$$.c->data[0];
1913 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
1914 if(t->kind!=TRAIT_METHOD) {
1915 //ok: flash allows to assign closures to members.
1917 multiname_t*name = t->name;
1918 $$.c = code_cutlast($$.c);
1919 $$.c = code_append($$.c, paramcode);
1920 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1921 $$.c = abc_callproperty2($$.c, name, $3.len);
1922 } else if($$.c->opcode == OPCODE_GETSUPER) {
1923 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
1924 $$.c = code_cutlast($$.c);
1925 $$.c = code_append($$.c, paramcode);
1926 $$.c = abc_callsuper2($$.c, name, $3.len);
1927 multiname_destroy(name);
1929 $$.c = abc_getlocal_0($$.c);
1930 $$.c = code_append($$.c, paramcode);
1931 $$.c = abc_call($$.c, $3.len);
1936 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
1937 $$.t = $1.t->function->return_type;
1939 $$.c = abc_coerce_a($$.c);
1944 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
1945 if(!state->cls) syntaxerror("super() not allowed outside of a class");
1946 if(!state->method) syntaxerror("super() not allowed outside of a function");
1947 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
1950 $$.c = abc_getlocal_0($$.c);
1952 $$.c = code_append($$.c, $3.cc);
1954 this is dependent on the control path, check this somewhere else
1955 if(state->method->has_super)
1956 syntaxerror("constructor may call super() only once");
1958 state->method->has_super = 1;
1959 $$.c = abc_constructsuper($$.c, $3.len);
1960 $$.c = abc_pushundefined($$.c);
1964 DELETE: "delete" E {
1966 if($$.c->opcode == OPCODE_COERCE_A) {
1967 $$.c = code_cutlast($$.c);
1969 multiname_t*name = 0;
1970 if($$.c->opcode == OPCODE_GETPROPERTY) {
1971 $$.c->opcode = OPCODE_DELETEPROPERTY;
1972 } else if($$.c->opcode == OPCODE_GETSLOT) {
1973 int slot = (int)(ptroff_t)$$.c->data[0];
1974 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
1975 $$.c = code_cutlast($$.c);
1976 $$.c = abc_deleteproperty2($$.c, name);
1978 $$.c = abc_getlocal_0($$.c);
1979 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
1980 $$.c = abc_deleteproperty2($$.c, &m);
1982 $$.t = TYPE_BOOLEAN;
1985 RETURN: "return" %prec prec_none {
1986 $$ = abc_returnvoid(0);
1988 RETURN: "return" EXPRESSION {
1990 $$ = abc_returnvalue($$);
1993 // ----------------------- expression types -------------------------------------
1995 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
1996 EXPRESSION : E %prec below_minus {$$ = $1;}
1997 EXPRESSION : EXPRESSION ',' E %prec below_minus {
1999 $$.c = cut_last_push($$.c);
2000 $$.c = code_append($$.c,$3.c);
2003 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2004 $$=cut_last_push($1.c);
2007 // ----------------------- expression evaluation -------------------------------------
2010 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2012 E : DELETE {$$ = $1;}
2013 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
2017 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2018 //MULTINAME(m, registry_getintclass());
2019 //$$.c = abc_coerce2($$.c, &m); // FIXME
2022 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2025 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2028 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2031 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2034 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
2037 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2040 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2041 $$.t = TYPE_BOOLEAN;
2043 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2044 $$.t = TYPE_BOOLEAN;
2046 CONSTANT : "null" {$$.c = abc_pushnull(0);
2051 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2052 $$.t = TYPE_BOOLEAN;
2054 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2055 $$.t = TYPE_BOOLEAN;
2057 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2058 $$.t = TYPE_BOOLEAN;
2060 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2061 $$.t = TYPE_BOOLEAN;
2063 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2064 $$.t = TYPE_BOOLEAN;
2066 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2067 $$.t = TYPE_BOOLEAN;
2069 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2070 $$.t = TYPE_BOOLEAN;
2072 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2073 $$.t = TYPE_BOOLEAN;
2076 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2078 $$.c = converttype($$.c, $1.t, $$.t);
2079 $$.c = abc_dup($$.c);
2080 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2081 $$.c = cut_last_push($$.c);
2082 $$.c = code_append($$.c,$3.c);
2083 $$.c = converttype($$.c, $3.t, $$.t);
2084 code_t*label = $$.c = abc_label($$.c);
2085 jmp->branch = label;
2088 $$.t = join_types($1.t, $3.t, 'A');
2089 /*printf("%08x:\n",$1.t);
2090 code_dump($1.c, 0, 0, "", stdout);
2091 printf("%08x:\n",$3.t);
2092 code_dump($3.c, 0, 0, "", stdout);
2093 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2095 $$.c = converttype($$.c, $1.t, $$.t);
2096 $$.c = abc_dup($$.c);
2097 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2098 $$.c = cut_last_push($$.c);
2099 $$.c = code_append($$.c,$3.c);
2100 $$.c = converttype($$.c, $3.t, $$.t);
2101 code_t*label = $$.c = abc_label($$.c);
2102 jmp->branch = label;
2105 E : '!' E {$$.c=$2.c;
2106 $$.c = abc_not($$.c);
2107 $$.t = TYPE_BOOLEAN;
2110 E : '~' E {$$.c=$2.c;
2111 $$.c = abc_bitnot($$.c);
2115 E : E '&' E {$$.c = code_append($1.c,$3.c);
2116 $$.c = abc_bitand($$.c);
2120 E : E '^' E {$$.c = code_append($1.c,$3.c);
2121 $$.c = abc_bitxor($$.c);
2125 E : E '|' E {$$.c = code_append($1.c,$3.c);
2126 $$.c = abc_bitor($$.c);
2130 E : E '-' E {$$.c = code_append($1.c,$3.c);
2131 if(BOTH_INT($1,$3)) {
2132 $$.c = abc_subtract_i($$.c);
2135 $$.c = abc_subtract($$.c);
2139 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2140 $$.c = abc_rshift($$.c);
2143 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2144 $$.c = abc_urshift($$.c);
2147 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2148 $$.c = abc_lshift($$.c);
2152 E : E '/' E {$$.c = code_append($1.c,$3.c);
2153 $$.c = abc_divide($$.c);
2156 E : E '+' E {$$.c = code_append($1.c,$3.c);
2157 $$.c = abc_add($$.c);
2160 E : E '%' E {$$.c = code_append($1.c,$3.c);
2161 $$.c = abc_modulo($$.c);
2164 E : E '*' E {$$.c = code_append($1.c,$3.c);
2165 if(BOTH_INT($1,$3)) {
2166 $$.c = abc_multiply_i($$.c);
2169 $$.c = abc_multiply($$.c);
2174 E : E "in" E {$$.c = code_append($1.c,$3.c);
2175 $$.c = abc_in($$.c);
2176 $$.t = TYPE_BOOLEAN;
2179 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2180 if(use_astype && TYPE_IS_CLASS($3.t)) {
2181 MULTINAME(m,$3.t->cls);
2182 $$.c = abc_astype2($1.c, &m);
2185 $$.c = code_append($1.c, $3.c);
2186 $$.c = abc_astypelate($$.c);
2191 E : E "instanceof" E
2192 {$$.c = code_append($1.c, $3.c);
2193 $$.c = abc_instanceof($$.c);
2194 $$.t = TYPE_BOOLEAN;
2197 E : E "is" E {$$.c = code_append($1.c, $3.c);
2198 $$.c = abc_istypelate($$.c);
2199 $$.t = TYPE_BOOLEAN;
2202 E : "typeof" '(' E ')' {
2204 $$.c = abc_typeof($$.c);
2209 $$.c = cut_last_push($2.c);
2210 $$.c = abc_pushundefined($$.c);
2214 E : "void" { $$.c = abc_pushundefined(0);
2218 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
2223 $$.c=abc_negate_i($$.c);
2226 $$.c=abc_negate($$.c);
2233 $$.c = code_append($$.c, $3.c);
2235 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
2236 $$.c = abc_getproperty2($$.c, &m);
2237 $$.t = 0; // array elements have unknown type
2240 E : '[' MAYBE_EXPRESSION_LIST ']' {
2242 $$.c = code_append($$.c, $2.cc);
2243 $$.c = abc_newarray($$.c, $2.len);
2244 $$.t = registry_getarrayclass();
2247 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
2248 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1};
2250 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2252 $$.cc = code_append($$.cc, $1.c);
2253 $$.cc = code_append($$.cc, $3.c);
2256 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2259 $$.cc = code_append($$.cc, $3.c);
2260 $$.cc = code_append($$.cc, $5.c);
2265 E : '{' MAYBE_EXPRPAIR_LIST '}' {
2267 $$.c = code_append($$.c, $2.cc);
2268 $$.c = abc_newobject($$.c, $2.len/2);
2269 $$.t = registry_getobjectclass();
2274 if(BOTH_INT($1,$3)) {
2275 c=abc_multiply_i(c);
2279 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
2280 $$.c = toreadwrite($1.c, c, 0, 0);
2285 code_t*c = abc_modulo($3.c);
2286 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
2287 $$.c = toreadwrite($1.c, c, 0, 0);
2291 code_t*c = abc_lshift($3.c);
2292 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
2293 $$.c = toreadwrite($1.c, c, 0, 0);
2297 code_t*c = abc_rshift($3.c);
2298 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
2299 $$.c = toreadwrite($1.c, c, 0, 0);
2303 code_t*c = abc_urshift($3.c);
2304 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
2305 $$.c = toreadwrite($1.c, c, 0, 0);
2309 code_t*c = abc_divide($3.c);
2310 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
2311 $$.c = toreadwrite($1.c, c, 0, 0);
2316 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2321 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
2323 $$.c = toreadwrite($1.c, c, 0, 0);
2326 E : E "-=" E { code_t*c = $3.c;
2327 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2328 c=abc_subtract_i(c);
2332 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2334 $$.c = toreadwrite($1.c, c, 0, 0);
2337 E : E '=' E { code_t*c = 0;
2338 c = code_append(c, $3.c);
2339 c = converttype(c, $3.t, $1.t);
2340 $$.c = toreadwrite($1.c, c, 1, 0);
2344 E : E '?' E ':' E %prec below_assignment {
2346 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2347 $$.c = code_append($$.c, $3.c);
2348 code_t*j2 = $$.c = abc_jump($$.c, 0);
2349 $$.c = j1->branch = abc_label($$.c);
2350 $$.c = code_append($$.c, $5.c);
2351 $$.c = j2->branch = abc_label($$.c);
2352 $$.t = join_types($3.t,$5.t,'?');
2355 // TODO: use inclocal where appropriate
2356 E : E "++" { code_t*c = 0;
2357 classinfo_t*type = $1.t;
2358 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2359 c=abc_increment_i(c);
2365 c=converttype(c, type, $1.t);
2366 $$.c = toreadwrite($1.c, c, 0, 1);
2369 E : E "--" { code_t*c = 0;
2370 classinfo_t*type = $1.t;
2371 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2372 c=abc_decrement_i(c);
2378 c=converttype(c, type, $1.t);
2379 $$.c = toreadwrite($1.c, c, 0, 1);
2383 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2384 classinfo_t*type = $2.t;
2385 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2386 c=abc_increment_i(c);
2392 c=converttype(c, type, $2.t);
2393 $$.c = toreadwrite($2.c, c, 0, 0);
2397 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2398 classinfo_t*type = $2.t;
2399 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2400 c=abc_decrement_i(c);
2406 c=converttype(c, type, $2.t);
2407 $$.c = toreadwrite($2.c, c, 0, 0);
2411 E : "super" '.' T_IDENTIFIER
2412 { if(!state->cls->info)
2413 syntaxerror("super keyword not allowed outside a class");
2414 classinfo_t*t = state->cls->info->superclass;
2415 if(!t) t = TYPE_OBJECT;
2417 memberinfo_t*f = registry_findmember(t, $3, 1);
2418 namespace_t ns = {flags2access(f->flags), ""};
2419 MEMBER_MULTINAME(m, f, $3);
2421 $$.c = abc_getlocal_0($$.c);
2422 $$.c = abc_getsuper2($$.c, &m);
2423 $$.t = memberinfo_gettype(f);
2426 E : E '.' T_IDENTIFIER
2428 classinfo_t*t = $1.t;
2430 if(TYPE_IS_CLASS(t) && t->cls) {
2435 memberinfo_t*f = registry_findmember(t, $3, 1);
2437 if(f && !is_static != !(f->flags&FLAG_STATIC))
2439 if(f && f->slot && !noslot) {
2440 $$.c = abc_getslot($$.c, f->slot);
2442 MEMBER_MULTINAME(m, f, $3);
2443 $$.c = abc_getproperty2($$.c, &m);
2445 /* determine type */
2446 $$.t = memberinfo_gettype(f);
2448 $$.c = abc_coerce_a($$.c);
2450 /* when resolving a property on an unknown type, we do know the
2451 name of the property (and don't seem to need the package), but
2452 we need to make avm2 try out all access modes */
2453 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2454 $$.c = abc_getproperty2($$.c, &m);
2455 $$.c = abc_coerce_a($$.c);
2456 $$.t = registry_getanytype();
2460 VAR_READ : T_IDENTIFIER {
2467 /* look at variables */
2468 if((v = find_variable($1))) {
2469 // $1 is a local variable
2470 $$.c = abc_getlocal($$.c, v->index);
2473 /* look at current class' members */
2474 } else if(state->cls && (f = registry_findmember(state->cls->info, $1, 1))) {
2475 // $1 is a function in this class
2476 int var_is_static = (f->flags&FLAG_STATIC);
2477 int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
2478 if(var_is_static != i_am_static) {
2479 /* there doesn't seem to be any "static" way to access
2480 static properties of a class */
2481 state->method->late_binding = 1;
2483 namespace_t ns = {flags2access(f->flags), ""};
2484 multiname_t m = {QNAME, &ns, 0, $1};
2485 $$.c = abc_findpropstrict2($$.c, &m);
2486 $$.c = abc_getproperty2($$.c, &m);
2489 $$.c = abc_getlocal_0($$.c);
2490 $$.c = abc_getslot($$.c, f->slot);
2492 namespace_t ns = {flags2access(f->flags), ""};
2493 multiname_t m = {QNAME, &ns, 0, $1};
2494 $$.c = abc_getlocal_0($$.c);
2495 $$.c = abc_getproperty2($$.c, &m);
2498 if(f->kind == MEMBER_METHOD) {
2499 $$.t = TYPE_FUNCTION(f);
2504 /* look at actual classes, in the current package and imported */
2505 } else if((a = find_class($1))) {
2506 if(a->flags & FLAG_METHOD) {
2508 $$.c = abc_findpropstrict2($$.c, &m);
2509 $$.c = abc_getproperty2($$.c, &m);
2510 $$.t = TYPE_FUNCTION(a->function);
2513 $$.c = abc_getglobalscope($$.c);
2514 $$.c = abc_getslot($$.c, a->slot);
2517 $$.c = abc_getlex2($$.c, &m);
2519 $$.t = TYPE_CLASS(a);
2522 /* unknown object, let the avm2 resolve it */
2524 if(strcmp($1,"trace"))
2525 warning("Couldn't resolve '%s', doing late binding", $1);
2526 state->method->late_binding = 1;
2528 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
2531 $$.c = abc_findpropstrict2($$.c, &m);
2532 $$.c = abc_getproperty2($$.c, &m);
2537 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
2538 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
2539 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
2541 // ----------------- namespaces -------------------------------------------------
2543 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
2544 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
2545 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
2547 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {$$=0;}