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 "==" "!=" "===" "!=="
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;
434 global->init = abc_initscript(global->file, 0);
435 code_t*c = global->init->method->body->code;
437 c = abc_getlocal_0(c);
438 c = abc_pushscope(c);
440 /* findpropstrict doesn't just return a scope object- it
441 also makes it "active" somehow. Push local_0 on the
442 scope stack and read it back with findpropstrict, it'll
443 contain properties like "trace". Trying to find the same
444 property on a "vanilla" local_0 yields only a "undefined" */
445 //c = abc_findpropstrict(c, "[package]::trace");
447 /*c = abc_getlocal_0(c);
448 c = abc_findpropstrict(c, "[package]::trace");
450 c = abc_setlocal_1(c);
452 c = abc_pushbyte(c, 0);
453 c = abc_setlocal_2(c);
455 code_t*xx = c = abc_label(c);
456 c = abc_findpropstrict(c, "[package]::trace");
457 c = abc_pushstring(c, "prop:");
458 c = abc_hasnext2(c, 1, 2);
460 c = abc_setlocal_3(c);
461 c = abc_callpropvoid(c, "[package]::trace", 2);
462 c = abc_getlocal_3(c);
464 c = abc_iftrue(c,xx);*/
466 c = abc_findpropstrict(c, "[package]::trace");
467 c = abc_pushstring(c, "[entering global init function]");
468 c = abc_callpropvoid(c, "[package]::trace", 1);
470 global->init->method->body->code = c;
472 void* finalize_state()
474 if(state->level!=1) {
475 syntaxerror("unexpected end of file");
477 abc_method_body_t*m = global->init->method->body;
480 __ findpropstrict(m, "[package]::trace");
481 __ pushstring(m, "[leaving global init function]");
482 __ callpropvoid(m, "[package]::trace", 1);
485 state_destroy(state);
491 static void startpackage(char*name)
494 syntaxerror("Packages can not be nested.");
497 /*printf("entering package \"%s\"\n", name);*/
498 state->package = strdup(name);
500 static void endpackage()
502 /*printf("leaving package \"%s\"\n", state->package);*/
504 //used e.g. in classinfo_register:
505 //free(state->package);state->package=0;
511 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
514 syntaxerror("inner classes now allowed");
517 state->cls = rfx_calloc(sizeof(classstate_t));
520 classinfo_list_t*mlist=0;
521 /*printf("entering class %s\n", name);
522 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n");
524 printf(" extends: %s.%s\n", extends->package, extends->name);
525 printf(" implements (%d): ", list_length(implements));
526 for(mlist=implements;mlist;mlist=mlist->next) {
527 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
532 if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
533 syntaxerror("invalid modifier(s)");
535 if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
536 syntaxerror("public and internal not supported at the same time.");
538 /* create the class name, together with the proper attributes */
542 if(!(flags&FLAG_PUBLIC) && !state->package) {
543 access = ACCESS_PRIVATE; package = current_filename;
544 } else if(!(flags&FLAG_PUBLIC) && state->package) {
545 access = ACCESS_PACKAGEINTERNAL; package = state->package;
546 } else if(state->package) {
547 access = ACCESS_PACKAGE; package = state->package;
549 syntaxerror("public classes only allowed inside a package");
552 if(registry_findclass(package, classname)) {
553 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
557 /* build info struct */
558 int num_interfaces = (list_length(implements));
559 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
560 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
562 classinfo_list_t*l = implements;
563 for(l=implements;l;l=l->next) {
564 state->cls->info->interfaces[pos++] = l->classinfo;
567 multiname_t*extends2 = sig2mname(extends);
569 MULTINAME(classname2,state->cls->info);
572 state->cls_init = abc_getlocal_0(state->cls_init);
573 state->cls_init = abc_constructsuper(state->cls_init, 0);
576 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
577 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
578 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
580 state->cls->info->flags |= CLASS_INTERFACE;
581 abc_class_interface(state->cls->abc);
584 abc_class_protectedNS(state->cls->abc, classname);
586 for(mlist=implements;mlist;mlist=mlist->next) {
587 MULTINAME(m, mlist->classinfo);
588 abc_class_add_interface(state->cls->abc, &m);
591 /* now write the construction code for this class */
592 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
594 abc_method_body_t*m = global->init->method->body;
595 __ getglobalscope(m);
596 classinfo_t*s = extends;
601 //TODO: take a look at the current scope stack, maybe
602 // we can re-use something
607 multiname_t*s2 = sig2mname(s);
609 multiname_destroy(s2);
611 __ pushscope(m); count++;
612 m->code = m->code->prev->prev; // invert
614 /* continue appending after last op end */
615 while(m->code && m->code->next) m->code = m->code->next;
617 /* TODO: if this is one of *our* classes, we can also
618 do a getglobalscope/getslot <nr> (which references
619 the init function's slots) */
621 __ getlex2(m, extends2);
623 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
624 stack is not the superclass */
625 __ pushscope(m);count++;
628 /* notice: we get a verify error #1107 if the top element on the scope
629 stack is not the global object */
631 __ pushscope(m);count++;
633 __ newclass(m,state->cls->abc);
637 __ setslot(m, slotindex);
639 /* flash.display.MovieClip handling */
640 if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
641 if(state->package && state->package[0]) {
642 globalclass = concat3(state->package, ".", classname);
644 globalclass = strdup(classname);
647 multiname_destroy(extends2);
650 static code_t* wrap_function(code_t*c,code_t*initcode, code_t*body)
652 c = code_append(c, initcode);
653 c = code_append(c, body);
654 /* append return if necessary */
655 if(!c || c->opcode != OPCODE_RETURNVOID &&
656 c->opcode != OPCODE_RETURNVALUE) {
657 c = abc_returnvoid(c);
662 static void endclass()
664 if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
666 c = abc_getlocal_0(c);
667 c = abc_constructsuper(c, 0);
668 state->cls->init = code_append(state->cls->init, c);
671 if(state->cls->init) {
672 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
673 m->body->code = wrap_function(0, state->cls->init, m->body->code);
675 if(state->cls->static_init) {
676 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
677 m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
679 // handy for scope testing
683 abc_class_getstaticconstructor(state->cls->abc,0)->body->code = c;*/
686 free(state->cls);state->cls=0;
690 typedef struct _variable {
695 static variable_t* find_variable(char*name)
701 v = dict_lookup(s->vars, name);
709 static variable_t* find_variable_safe(char*name)
711 variable_t* v = find_variable(name);
713 syntaxerror("undefined variable: %s", name);
716 static char variable_exists(char*name)
718 return dict_lookup(state->vars, name)!=0;
720 code_t*defaultvalue(code_t*c, classinfo_t*type);
721 static int new_variable(char*name, classinfo_t*type, char init)
724 v->index = global->variable_count;
726 dict_put(state->vars, name, v);
728 if(init && state->method && type) {
729 /* if this is a typed variable:
730 push default value for type on stack at the very beginning of the
731 method, so that it always has that type regardless of the control
733 state->method->initcode = defaultvalue(state->method->initcode, type);
734 state->method->initcode = abc_setlocal(state->method->initcode, v->index);
736 return global->variable_count++;
738 #define TEMPVARNAME "__as3_temp__"
739 static int gettempvar()
741 variable_t*v = find_variable(TEMPVARNAME);
744 return new_variable(TEMPVARNAME, 0, 0);
747 code_t* killvars(code_t*c)
750 for(t=0;t<state->vars->hashsize;t++) {
751 dictentry_t*e =state->vars->slots[t];
753 variable_t*v = (variable_t*)e->data;
754 //do this always, otherwise register types don't match
755 //in the verifier when doing nested loops
756 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
757 c = abc_kill(c, v->index);
764 void check_code_for_break(code_t*c)
767 if(c->opcode == OPCODE___BREAK__) {
768 char*name = string_cstr(c->data[0]);
769 syntaxerror("Unresolved \"break %s\"", name);
771 if(c->opcode == OPCODE___CONTINUE__) {
772 char*name = string_cstr(c->data[0]);
773 syntaxerror("Unresolved \"continue %s\"", name);
780 static void check_constant_against_type(classinfo_t*t, constant_t*c)
782 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
783 if(TYPE_IS_NUMBER(t)) {
784 xassert(c->type == CONSTANT_FLOAT
785 || c->type == CONSTANT_INT
786 || c->type == CONSTANT_UINT);
787 } else if(TYPE_IS_UINT(t)) {
788 xassert(c->type == CONSTANT_UINT ||
789 (c->type == CONSTANT_INT && c->i>0));
790 } else if(TYPE_IS_INT(t)) {
791 xassert(c->type == CONSTANT_INT);
792 } else if(TYPE_IS_BOOLEAN(t)) {
793 xassert(c->type == CONSTANT_TRUE
794 || c->type == CONSTANT_FALSE);
798 static int flags2access(int flags)
801 if(flags&FLAG_PUBLIC) {
802 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
803 access = ACCESS_PACKAGE;
804 } else if(flags&FLAG_PRIVATE) {
805 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
806 access = ACCESS_PRIVATE;
807 } else if(flags&FLAG_PROTECTED) {
808 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
809 access = ACCESS_PROTECTED;
811 access = ACCESS_PACKAGEINTERNAL;
816 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
818 memberinfo_t*minfo = 0;
821 minfo = rfx_calloc(sizeof(memberinfo_t));
822 classinfo_t*c = classinfo_register(flags2access(flags), state->package, name, 0);
823 c->flags |= FLAG_METHOD;
825 minfo->kind = MEMBER_METHOD;
827 minfo->flags = FLAG_STATIC;
828 minfo->return_type = return_type;
829 } else if(getset != KW_GET && getset != KW_SET) {
831 if((minfo = registry_findmember(state->cls->info, name, 0))) {
832 if(minfo->parent == state->cls->info) {
833 syntaxerror("class already contains a member/method called '%s'", name);
834 } else if(!minfo->parent) {
835 syntaxerror("internal error: overriding method %s, which doesn't have parent", name);
837 if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE)))
838 syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?");
841 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
842 minfo->return_type = return_type;
843 // getslot on a member slot only returns "undefined", so no need
844 // to actually store these
845 //state->minfo->slot = state->method->abc->method->trait->slot_id;
847 //class getter/setter
848 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
852 else if(params->list)
853 type = params->list->param->type;
854 // not sure wether to look into superclasses here, too
855 if((minfo=registry_findmember(state->cls->info, name, 0))) {
856 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
857 syntaxerror("class already contains a member or method called '%s'", name);
859 syntaxerror("getter/setter for '%s' already defined", name);
860 /* make a setter or getter into a getset */
865 if(type && minfo->type != type)
866 syntaxerror("different type in getter and setter");
868 minfo = memberinfo_register(state->cls->info, name, gs);
871 /* can't assign a slot as getter and setter might have different slots */
872 //minfo->slot = slot;
874 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
875 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
876 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
877 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
878 if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
879 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
883 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
884 params_t*params, classinfo_t*return_type)
887 syntaxerror("not able to start another method scope");
890 state->method = rfx_calloc(sizeof(methodstate_t));
891 state->method->initcode = 0;
892 state->method->has_super = 0;
894 state->method->is_constructor = !strcmp(state->cls->info->name,name);
895 state->cls->has_constructor |= state->method->is_constructor;
897 new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
899 state->method->is_global = 1;
900 new_variable("globalscope", 0, 0);
902 /* for global methods, always push local_0 on the scope stack: */
903 state->method->late_binding = 1;
906 global->variable_count = 0;
908 /* state->vars is initialized by state_new */
911 for(p=params->list;p;p=p->next) {
912 new_variable(p->param->name, p->param->type, 0);
914 if(state->method->is_constructor)
915 name = "__as3_constructor__";
916 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
919 static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
920 params_t*params, classinfo_t*return_type, code_t*body)
924 multiname_t*type2 = sig2mname(return_type);
926 if(state->method->is_constructor) {
927 f = abc_class_getconstructor(state->cls->abc, type2);
928 } else if(!state->method->is_global) {
929 namespace_t mname_ns = {flags2access(flags), ""};
930 multiname_t mname = {QNAME, &mname_ns, 0, name};
932 if(flags&FLAG_STATIC)
933 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
935 f = abc_class_method(state->cls->abc, type2, &mname);
936 slot = f->trait->slot_id;
938 namespace_t mname_ns = {flags2access(flags), state->package};
939 multiname_t mname = {QNAME, &mname_ns, 0, name};
941 f = abc_method_new(global->file, type2, 1);
942 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
943 //abc_code_t*c = global->init->method->body->code;
945 //flash doesn't seem to allow us to access function slots
946 //state->method->info->slot = slot;
948 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
949 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
950 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
951 if(params->varargs) f->flags |= METHOD_NEED_REST;
955 for(p=params->list;p;p=p->next) {
956 if(params->varargs && !p->next) {
957 break; //varargs: omit last parameter in function signature
959 multiname_t*m = sig2mname(p->param->type);
960 list_append(f->parameters, m);
961 if(p->param->value) {
962 check_constant_against_type(p->param->type, p->param->value);
963 opt=1;list_append(f->optional_parameters, p->param->value);
965 syntaxerror("non-optional parameter not allowed after optional parameters");
968 check_code_for_break(body);
971 f->body->code = body;
974 syntaxerror("interface methods can't have a method body");
976 free(state->method);state->method=0;
982 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
987 void breakjumpsto(code_t*c, char*name, code_t*jump)
990 if(c->opcode == OPCODE___BREAK__) {
991 string_t*name2 = c->data[0];
992 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
993 c->opcode = OPCODE_JUMP;
1000 void continuejumpsto(code_t*c, char*name, code_t*jump)
1003 if(c->opcode == OPCODE___CONTINUE__) {
1004 string_t*name2 = c->data[0];
1005 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1006 c->opcode = OPCODE_JUMP;
1014 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1016 if(!type1 || !type2)
1017 return registry_getanytype();
1018 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1019 return registry_getanytype();
1022 return registry_getanytype();
1024 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1029 return abc_coerce_a(c);
1033 // cast an "any" type to a specific type. subject to
1034 // runtime exceptions
1035 return abc_coerce2(c, &m);
1038 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1039 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1040 // allow conversion between number types
1041 return abc_coerce2(c, &m);
1043 //printf("%s.%s\n", from.package, from.name);
1044 //printf("%s.%s\n", to.package, to.name);
1046 classinfo_t*supertype = from;
1048 if(supertype == to) {
1049 // target type is one of from's superclasses
1050 return abc_coerce2(c, &m);
1053 while(supertype->interfaces[t]) {
1054 if(supertype->interfaces[t]==to) {
1055 // target type is one of from's interfaces
1056 return abc_coerce2(c, &m);
1060 supertype = supertype->superclass;
1062 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1064 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1066 syntaxerror("can't convert type %s to %s", from->name, to->name);
1069 code_t*defaultvalue(code_t*c, classinfo_t*type)
1071 if(TYPE_IS_INT(type)) {
1072 c = abc_pushbyte(c, 0);
1073 } else if(TYPE_IS_UINT(type)) {
1074 c = abc_pushuint(c, 0);
1075 } else if(TYPE_IS_FLOAT(type)) {
1077 } else if(TYPE_IS_BOOLEAN(type)) {
1078 c = abc_pushfalse(c);
1080 c = abc_pushnull(c);
1085 char is_pushundefined(code_t*c)
1087 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1090 void parserassert(int b)
1092 if(!b) syntaxerror("internal error: assertion failed");
1095 static classinfo_t* find_class(char*name)
1099 c = registry_findclass(state->package, name);
1101 /* try explicit imports */
1102 dictentry_t* e = dict_get_slot(state->imports, name);
1106 if(!strcmp(e->key, name)) {
1107 c = (classinfo_t*)e->data;
1112 /* try package.* imports */
1113 import_list_t*l = state->wildcard_imports;
1117 //printf("does package %s contain a class %s?\n", l->import->package, name);
1118 c = registry_findclass(l->import->package, name);
1122 /* try global package */
1124 c = registry_findclass("", name);
1129 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1133 [prefix code] [read instruction]
1137 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1140 if(in && in->opcode == OPCODE_COERCE_A) {
1141 in = code_cutlast(in);
1144 syntaxerror("internal error");
1146 /* chop off read instruction */
1150 prefix = r->prev;r->prev = 0;
1156 char use_temp_var = readbefore;
1158 /* generate the write instruction, and maybe append a dup to the prefix code */
1159 code_t* write = abc_nop(0);
1160 if(r->opcode == OPCODE_GETPROPERTY) {
1161 write->opcode = OPCODE_SETPROPERTY;
1162 multiname_t*m = (multiname_t*)r->data[0];
1163 write->data[0] = multiname_clone(m);
1164 if(m->type == QNAME || m->type == MULTINAME) {
1166 prefix = abc_dup(prefix); // we need the object, too
1169 } else if(m->type == MULTINAMEL) {
1171 /* dupping two values on the stack requires 5 operations and one register-
1172 couldn't adobe just have given us a dup2? */
1173 int temp = gettempvar();
1174 prefix = abc_setlocal(prefix, temp);
1175 prefix = abc_dup(prefix);
1176 prefix = abc_getlocal(prefix, temp);
1177 prefix = abc_swap(prefix);
1178 prefix = abc_getlocal(prefix, temp);
1180 prefix = abc_kill(prefix, temp);
1184 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1186 } else if(r->opcode == OPCODE_GETSLOT) {
1187 write->opcode = OPCODE_SETSLOT;
1188 write->data[0] = r->data[0];
1190 prefix = abc_dup(prefix); // we need the object, too
1193 } else if(r->opcode == OPCODE_GETLOCAL) {
1194 write->opcode = OPCODE_SETLOCAL;
1195 write->data[0] = r->data[0];
1196 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1197 write->opcode = OPCODE_SETLOCAL_0;
1198 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1199 write->opcode = OPCODE_SETLOCAL_1;
1200 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1201 write->opcode = OPCODE_SETLOCAL_2;
1202 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1203 write->opcode = OPCODE_SETLOCAL_3;
1205 code_dump(r, 0, 0, "", stdout);
1206 syntaxerror("illegal lvalue: can't assign a value to this expression");
1213 /* with getproperty/getslot, we have to be extra careful not
1214 to execute the read code twice, as it might have side-effects
1215 (e.g. if the property is in fact a setter/getter combination)
1217 So read the value, modify it, and write it again,
1218 using prefix only once and making sure (by using a temporary
1219 register) that the return value is what we just wrote */
1220 temp = gettempvar();
1221 c = code_append(c, prefix);
1222 c = code_append(c, r);
1225 c = abc_setlocal(c, temp);
1227 c = code_append(c, middlepart);
1230 c = abc_setlocal(c, temp);
1232 c = code_append(c, write);
1233 c = abc_getlocal(c, temp);
1234 c = abc_kill(c, temp);
1236 /* if we're allowed to execute the read code twice *and*
1237 the middlepart doesn't modify the code, things are easier.
1239 code_t* r2 = code_dup(r);
1240 //c = code_append(c, prefix);
1241 parserassert(!prefix);
1242 c = code_append(c, r);
1243 c = code_append(c, middlepart);
1244 c = code_append(c, write);
1245 c = code_append(c, r2);
1248 /* even smaller version: overwrite the value without reading
1252 c = code_append(c, prefix);
1255 c = code_append(c, middlepart);
1256 c = code_append(c, write);
1257 c = code_append(c, r);
1259 temp = gettempvar();
1261 c = code_append(c, prefix);
1263 c = code_append(c, middlepart);
1265 c = abc_setlocal(c, temp);
1266 c = code_append(c, write);
1267 c = abc_getlocal(c, temp);
1268 c = abc_kill(c, temp);
1275 #define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
1276 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1283 /* ------------ code blocks / statements ---------------- */
1285 PROGRAM: MAYBECODE {
1286 /* todo: do something with this code if we're outside a function */
1288 warning("ignored code");
1291 MAYBECODE: CODE {$$=$1;}
1292 MAYBECODE: {$$=code_new();}
1294 CODE: CODE CODEPIECE {
1295 $$=code_append($1,$2);
1301 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
1302 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
1303 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
1304 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
1305 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
1306 CODEPIECE: ';' {$$=code_new();}
1307 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
1308 CODEPIECE: VOIDEXPRESSION {$$=$1}
1309 CODEPIECE: FOR {$$=$1}
1310 CODEPIECE: FOR_IN {$$=$1}
1311 CODEPIECE: WHILE {$$=$1}
1312 CODEPIECE: DO_WHILE {$$=$1}
1313 CODEPIECE: SWITCH {$$=$1}
1314 CODEPIECE: BREAK {$$=$1}
1315 CODEPIECE: CONTINUE {$$=$1}
1316 CODEPIECE: RETURN {$$=$1}
1317 CODEPIECE: IF {$$=$1}
1318 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
1319 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
1321 CODEBLOCK : '{' CODE '}' {$$=$2;}
1322 CODEBLOCK : '{' '}' {$$=0;}
1323 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1324 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1326 /* ------------ variables --------------------------- */
1328 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1329 | {$$.c=abc_pushundefined(0);
1333 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1334 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1336 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1337 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1339 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1341 if(variable_exists($1))
1342 syntaxerror("Variable %s already defined", $1);
1344 if(!is_subtype_of($3.t, $2)) {
1345 syntaxerror("Can't convert %s to %s", $3.t->name,
1349 int index = new_variable($1, $2, 1);
1352 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1354 $$ = converttype($$, $3.t, $2);
1355 $$ = abc_setlocal($$, index);
1357 $$ = defaultvalue(0, $2);
1358 $$ = abc_setlocal($$, index);
1361 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1363 $$ = abc_coerce_a($$);
1364 $$ = abc_setlocal($$, index);
1370 /* that's the default for a local register, anyway
1372 state->method->initcode = abc_pushundefined(state->method->initcode);
1373 state->method->initcode = abc_setlocal(state->method->initcode, index);
1375 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1378 /* ------------ control flow ------------------------- */
1380 MAYBEELSE: %prec below_else {$$ = code_new();}
1381 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1382 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1384 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1386 $$ = code_append($$, $4.c);
1387 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1389 $$ = code_append($$, $6);
1391 myjmp = $$ = abc_jump($$, 0);
1393 myif->branch = $$ = abc_nop($$);
1395 $$ = code_append($$, $7);
1396 myjmp->branch = $$ = abc_nop($$);
1399 $$ = killvars($$);old_state();
1402 FOR_INIT : {$$=code_new();}
1403 FOR_INIT : VARIABLE_DECLARATION
1404 FOR_INIT : VOIDEXPRESSION
1405 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1406 $$=$2;new_variable($2,$3,1);
1408 FOR_IN_INIT : T_IDENTIFIER {
1412 FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
1413 FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
1415 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1416 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1418 $$ = code_append($$, $2);
1419 code_t*loopstart = $$ = abc_label($$);
1420 $$ = code_append($$, $4.c);
1421 code_t*myif = $$ = abc_iffalse($$, 0);
1422 $$ = code_append($$, $8);
1423 code_t*cont = $$ = abc_nop($$);
1424 $$ = code_append($$, $6);
1425 $$ = abc_jump($$, loopstart);
1426 code_t*out = $$ = abc_nop($$);
1427 breakjumpsto($$, $1.name, out);
1428 continuejumpsto($$, $1.name, cont);
1431 $$ = killvars($$);old_state();
1434 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
1435 variable_t*var = find_variable($2);
1436 char*tmp1name = concat2($2, "__tmp1__");
1437 int it = new_variable(tmp1name, TYPE_INT, 0);
1438 char*tmp2name = concat2($2, "__array__");
1439 int array = new_variable(tmp1name, 0, 0);
1442 $$ = code_append($$, $4.c);
1443 $$ = abc_coerce_a($$);
1444 $$ = abc_setlocal($$, array);
1445 $$ = abc_pushbyte($$, 0);
1446 $$ = abc_setlocal($$, it);
1448 code_t*loopstart = $$ = abc_label($$);
1450 $$ = abc_hasnext2($$, array, it);
1451 code_t*myif = $$ = abc_iffalse($$, 0);
1452 $$ = abc_getlocal($$, array);
1453 $$ = abc_getlocal($$, it);
1455 $$ = abc_nextname($$);
1457 $$ = abc_nextvalue($$);
1458 $$ = converttype($$, 0, var->type);
1459 $$ = abc_setlocal($$, var->index);
1461 $$ = code_append($$, $6);
1462 $$ = abc_jump($$, loopstart);
1464 code_t*out = $$ = abc_nop($$);
1465 breakjumpsto($$, $1.name, out);
1466 continuejumpsto($$, $1.name, loopstart);
1477 WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1480 code_t*myjmp = $$ = abc_jump($$, 0);
1481 code_t*loopstart = $$ = abc_label($$);
1482 $$ = code_append($$, $6);
1483 code_t*cont = $$ = abc_nop($$);
1484 myjmp->branch = cont;
1485 $$ = code_append($$, $4.c);
1486 $$ = abc_iftrue($$, loopstart);
1487 code_t*out = $$ = abc_nop($$);
1488 breakjumpsto($$, $1, out);
1489 continuejumpsto($$, $1, cont);
1495 DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
1497 code_t*loopstart = $$ = abc_label($$);
1498 $$ = code_append($$, $3);
1499 code_t*cont = $$ = abc_nop($$);
1500 $$ = code_append($$, $6.c);
1501 $$ = abc_iftrue($$, loopstart);
1502 code_t*out = $$ = abc_nop($$);
1503 breakjumpsto($$, $1, out);
1504 continuejumpsto($$, $1, cont);
1509 BREAK : "break" %prec prec_none {
1510 $$ = abc___break__(0, "");
1512 BREAK : "break" T_IDENTIFIER {
1513 $$ = abc___break__(0, $2);
1515 CONTINUE : "continue" %prec prec_none {
1516 $$ = abc___continue__(0, "");
1518 CONTINUE : "continue" T_IDENTIFIER {
1519 $$ = abc___continue__(0, $2);
1522 MAYBE_CASE_LIST : {$$=0;}
1523 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
1524 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
1525 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
1526 CASE_LIST: CASE {$$=$1}
1527 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
1529 CASE: "case" E ':' MAYBECODE {
1531 $$ = code_append($$, $2.c);
1532 code_t*j = $$ = abc_ifne($$, 0);
1533 $$ = code_append($$, $4);
1534 if($$->opcode != OPCODE___BREAK__) {
1535 $$ = abc___fallthrough__($$, "");
1537 code_t*e = $$ = abc_nop($$);
1540 DEFAULT: "default" ':' MAYBECODE {
1543 SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
1545 $$ = code_append($$, $7);
1546 code_t*out = $$ = abc_pop($$);
1547 breakjumpsto($$, $1, out);
1549 code_t*c = $$,*lastblock=0;
1551 if(c->opcode == OPCODE_IFNE) {
1552 if(!c->next) syntaxerror("internal error in fallthrough handling");
1554 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
1556 c->opcode = OPCODE_JUMP;
1557 c->branch = lastblock;
1559 /* fall through end of switch */
1560 c->opcode = OPCODE_NOP;
1568 /* ------------ packages and imports ---------------- */
1570 X_IDENTIFIER: T_IDENTIFIER
1571 | "package" {$$="package";}
1573 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;}
1574 PACKAGE: X_IDENTIFIER {$$=strdup($1);}
1576 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBECODE '}' {endpackage()}
1577 PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()}
1579 IMPORT : "import" QNAME {
1582 syntaxerror("Couldn't import class\n");
1583 state_has_imports();
1584 dict_put(state->imports, c->name, c);
1587 IMPORT : "import" PACKAGE '.' '*' {
1590 state_has_imports();
1591 list_append(state->wildcard_imports, i);
1595 /* ------------ classes and interfaces (header) -------------- */
1597 MAYBE_MODIFIERS : {$$=0;}
1598 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
1599 MODIFIER_LIST : MODIFIER {$$=$1;}
1600 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
1602 MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
1603 | KW_PRIVATE {$$=FLAG_PRIVATE;}
1604 | KW_PROTECTED {$$=FLAG_PROTECTED;}
1605 | KW_STATIC {$$=FLAG_STATIC;}
1606 | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
1607 | KW_FINAL {$$=FLAG_FINAL;}
1608 | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
1609 | KW_NATIVE {$$=FLAG_NATIVE;}
1610 | KW_INTERNAL {$$=FLAG_INTERNAL;}
1612 EXTENDS : {$$=registry_getobjectclass();}
1613 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1615 EXTENDS_LIST : {$$=list_new();}
1616 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1618 IMPLEMENTS_LIST : {$$=list_new();}
1619 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1621 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
1622 EXTENDS IMPLEMENTS_LIST
1623 '{' {startclass($1,$3,$4,$5, 0);}
1624 MAYBE_DECLARATION_LIST
1627 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
1629 '{' {startclass($1,$3,0,$4,1);}
1630 MAYBE_IDECLARATION_LIST
1633 /* ------------ classes and interfaces (body) -------------- */
1635 MAYBE_DECLARATION_LIST :
1636 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1637 DECLARATION_LIST : DECLARATION
1638 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1640 DECLARATION : SLOT_DECLARATION
1641 DECLARATION : FUNCTION_DECLARATION
1643 MAYBE_IDECLARATION_LIST :
1644 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1645 IDECLARATION_LIST : IDECLARATION
1646 IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
1648 IDECLARATION : "var" T_IDENTIFIER {
1649 syntaxerror("variable declarations not allowed in interfaces");
1651 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
1653 if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
1654 syntaxerror("invalid method modifiers: interface methods always need to be public");
1656 startfunction(0,$1,$3,$4,&$6,$8);
1657 endfunction(0,$1,$3,$4,&$6,$8, 0);
1660 /* ------------ classes and interfaces (body, slots ) ------- */
1662 VARCONST: "var" | "const"
1664 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1666 memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
1668 info->flags = flags;
1671 namespace_t mname_ns = {flags2access(flags), ""};
1672 multiname_t mname = {QNAME, &mname_ns, 0, $3};
1674 if(!(flags&FLAG_STATIC)) {
1677 t=abc_class_slot(state->cls->abc, &mname, &m);
1679 t=abc_class_slot(state->cls->abc, &mname, 0);
1681 info->slot = t->slot_id;
1685 t=abc_class_staticslot(state->cls->abc, &mname, &m);
1687 t=abc_class_staticslot(state->cls->abc, &mname, 0);
1689 info->slot = t->slot_id;
1691 if($5.c && !is_pushundefined($5.c)) {
1693 c = abc_getlocal_0(c);
1694 c = code_append(c, $5.c);
1695 c = converttype(c, $5.t, $4);
1696 c = abc_setslot(c, t->slot_id);
1697 if(!(flags&FLAG_STATIC))
1698 state->cls->init = code_append(state->cls->init, c);
1700 state->cls->static_init = code_append(state->cls->static_init, c);
1703 t->kind= TRAIT_CONST;
1707 /* ------------ constants -------------------------------------- */
1709 MAYBESTATICCONSTANT: {$$=0;}
1710 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1712 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1713 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1714 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1715 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1716 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
1717 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1718 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
1719 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
1720 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
1722 /* ------------ classes and interfaces (body, functions) ------- */
1724 // non-vararg version
1726 memset(&$$,0,sizeof($$));
1728 MAYBE_PARAM_LIST: PARAM_LIST {
1733 MAYBE_PARAM_LIST: "..." PARAM {
1734 memset(&$$,0,sizeof($$));
1736 list_append($$.list, $2);
1738 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1741 list_append($$.list, $4);
1745 PARAM_LIST: PARAM_LIST ',' PARAM {
1747 list_append($$.list, $3);
1750 memset(&$$,0,sizeof($$));
1751 list_append($$.list, $1);
1754 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1755 $$ = malloc(sizeof(param_t));
1760 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1761 $$ = malloc(sizeof(param_t));
1763 $$->type = TYPE_ANY;
1766 GETSET : "get" {$$=$1;}
1770 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1771 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1774 if(state->method->late_binding) {
1775 c = abc_getlocal_0(c);
1776 c = abc_pushscope(c);
1778 if(state->method->is_constructor && !state->method->has_super) {
1779 // call default constructor
1780 c = abc_getlocal_0(c);
1781 c = abc_constructsuper(c, 0);
1783 c = wrap_function(c, state->method->initcode, $11);
1784 endfunction(0,$1,$3,$4,&$6,$8,c);
1787 /* ------------- package + class ids --------------- */
1789 CLASS: T_IDENTIFIER {
1791 /* try current package */
1792 $$ = find_class($1);
1793 if(!$$) syntaxerror("Could not find class %s\n", $1);
1796 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1797 $$ = registry_findclass($1, $3);
1798 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
1802 QNAME: PACKAGEANDCLASS
1805 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1806 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1808 TYPE : QNAME {$$=$1;}
1809 | '*' {$$=registry_getanytype();}
1810 | "void" {$$=registry_getanytype();}
1812 | "String" {$$=registry_getstringclass();}
1813 | "int" {$$=registry_getintclass();}
1814 | "uint" {$$=registry_getuintclass();}
1815 | "Boolean" {$$=registry_getbooleanclass();}
1816 | "Number" {$$=registry_getnumberclass();}
1819 MAYBETYPE: ':' TYPE {$$=$2;}
1822 /* ----------function calls, delete, constructor calls ------ */
1824 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
1825 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1827 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
1828 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1829 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
1832 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
1834 $$.cc = code_append($1.cc, $3.c);
1837 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1842 $$.c = abc_getglobalscope($$.c);
1843 $$.c = abc_getslot($$.c, $2->slot);
1845 $$.c = abc_findpropstrict2($$.c, &m);
1848 $$.c = code_append($$.c, $3.cc);
1851 $$.c = abc_construct($$.c, $3.len);
1853 $$.c = abc_constructprop2($$.c, &m, $3.len);
1857 /* TODO: use abc_call (for calling local variables),
1858 abc_callstatic (for calling own methods)
1861 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1864 if($$.c->opcode == OPCODE_COERCE_A) {
1865 $$.c = code_cutlast($$.c);
1867 code_t*paramcode = $3.cc;
1870 if($$.c->opcode == OPCODE_GETPROPERTY) {
1871 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
1872 $$.c = code_cutlast($$.c);
1873 $$.c = code_append($$.c, paramcode);
1874 $$.c = abc_callproperty2($$.c, name, $3.len);
1875 multiname_destroy(name);
1876 } else if($$.c->opcode == OPCODE_GETSLOT) {
1877 int slot = (int)(ptroff_t)$$.c->data[0];
1878 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
1879 if(t->kind!=TRAIT_METHOD) {
1880 //ok: flash allows to assign closures to members.
1882 multiname_t*name = t->name;
1883 $$.c = code_cutlast($$.c);
1884 $$.c = code_append($$.c, paramcode);
1885 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1886 $$.c = abc_callproperty2($$.c, name, $3.len);
1887 } else if($$.c->opcode == OPCODE_GETSUPER) {
1888 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
1889 $$.c = code_cutlast($$.c);
1890 $$.c = code_append($$.c, paramcode);
1891 $$.c = abc_callsuper2($$.c, name, $3.len);
1892 multiname_destroy(name);
1894 $$.c = abc_getlocal_0($$.c);
1895 $$.c = code_append($$.c, paramcode);
1896 $$.c = abc_call($$.c, $3.len);
1901 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
1902 $$.t = $1.t->function->return_type;
1904 $$.c = abc_coerce_a($$.c);
1909 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
1910 if(!state->cls) syntaxerror("super() not allowed outside of a class");
1911 if(!state->method) syntaxerror("super() not allowed outside of a function");
1912 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
1915 $$.c = abc_getlocal_0($$.c);
1917 $$.c = code_append($$.c, $3.cc);
1919 this is dependent on the control path, check this somewhere else
1920 if(state->method->has_super)
1921 syntaxerror("constructor may call super() only once");
1923 state->method->has_super = 1;
1924 $$.c = abc_constructsuper($$.c, $3.len);
1925 $$.c = abc_pushundefined($$.c);
1929 DELETE: "delete" E {
1931 if($$.c->opcode == OPCODE_COERCE_A) {
1932 $$.c = code_cutlast($$.c);
1934 multiname_t*name = 0;
1935 if($$.c->opcode == OPCODE_GETPROPERTY) {
1936 $$.c->opcode = OPCODE_DELETEPROPERTY;
1937 } else if($$.c->opcode == OPCODE_GETSLOT) {
1938 int slot = (int)(ptroff_t)$$.c->data[0];
1939 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
1940 $$.c = code_cutlast($$.c);
1941 $$.c = abc_deleteproperty2($$.c, name);
1943 $$.c = abc_getlocal_0($$.c);
1944 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
1945 $$.c = abc_deleteproperty2($$.c, &m);
1947 $$.t = TYPE_BOOLEAN;
1950 RETURN: "return" %prec prec_none {
1951 $$ = abc_returnvoid(0);
1953 RETURN: "return" EXPRESSION {
1955 $$ = abc_returnvalue($$);
1958 // ----------------------- expression types -------------------------------------
1960 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
1961 EXPRESSION : E %prec below_minus {$$ = $1;}
1962 EXPRESSION : EXPRESSION ',' E %prec below_minus {
1964 $$.c = cut_last_push($$.c);
1965 $$.c = code_append($$.c,$3.c);
1968 VOIDEXPRESSION : EXPRESSION %prec below_minus {
1969 $$=cut_last_push($1.c);
1972 // ----------------------- expression evaluation -------------------------------------
1975 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1977 E : DELETE {$$ = $1;}
1978 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1982 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1983 //MULTINAME(m, registry_getintclass());
1984 //$$.c = abc_coerce2($$.c, &m); // FIXME
1987 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1990 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1993 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1996 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1999 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
2002 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2005 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2006 $$.t = TYPE_BOOLEAN;
2008 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2009 $$.t = TYPE_BOOLEAN;
2011 CONSTANT : "null" {$$.c = abc_pushnull(0);
2016 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2017 $$.t = TYPE_BOOLEAN;
2019 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2020 $$.t = TYPE_BOOLEAN;
2022 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2023 $$.t = TYPE_BOOLEAN;
2025 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2026 $$.t = TYPE_BOOLEAN;
2028 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2029 $$.t = TYPE_BOOLEAN;
2031 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2032 $$.t = TYPE_BOOLEAN;
2034 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2035 $$.t = TYPE_BOOLEAN;
2037 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2038 $$.t = TYPE_BOOLEAN;
2041 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2043 $$.c = converttype($$.c, $1.t, $$.t);
2044 $$.c = abc_dup($$.c);
2045 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2046 $$.c = cut_last_push($$.c);
2047 $$.c = code_append($$.c,$3.c);
2048 $$.c = converttype($$.c, $3.t, $$.t);
2049 code_t*label = $$.c = abc_label($$.c);
2050 jmp->branch = label;
2053 $$.t = join_types($1.t, $3.t, 'A');
2054 /*printf("%08x:\n",$1.t);
2055 code_dump($1.c, 0, 0, "", stdout);
2056 printf("%08x:\n",$3.t);
2057 code_dump($3.c, 0, 0, "", stdout);
2058 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2060 $$.c = converttype($$.c, $1.t, $$.t);
2061 $$.c = abc_dup($$.c);
2062 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2063 $$.c = cut_last_push($$.c);
2064 $$.c = code_append($$.c,$3.c);
2065 $$.c = converttype($$.c, $3.t, $$.t);
2066 code_t*label = $$.c = abc_label($$.c);
2067 jmp->branch = label;
2070 E : '!' E {$$.c=$2.c;
2071 $$.c = abc_not($$.c);
2072 $$.t = TYPE_BOOLEAN;
2075 E : '~' E {$$.c=$2.c;
2076 $$.c = abc_bitnot($$.c);
2080 E : E '&' E {$$.c = code_append($1.c,$3.c);
2081 $$.c = abc_bitand($$.c);
2085 E : E '^' E {$$.c = code_append($1.c,$3.c);
2086 $$.c = abc_bitxor($$.c);
2090 E : E '|' E {$$.c = code_append($1.c,$3.c);
2091 $$.c = abc_bitor($$.c);
2095 E : E '-' E {$$.c = code_append($1.c,$3.c);
2096 if(BOTH_INT($1,$3)) {
2097 $$.c = abc_subtract_i($$.c);
2100 $$.c = abc_subtract($$.c);
2104 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2105 $$.c = abc_rshift($$.c);
2108 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2109 $$.c = abc_urshift($$.c);
2112 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2113 $$.c = abc_lshift($$.c);
2117 E : E '/' E {$$.c = code_append($1.c,$3.c);
2118 $$.c = abc_divide($$.c);
2121 E : E '+' E {$$.c = code_append($1.c,$3.c);
2122 $$.c = abc_add($$.c);
2125 E : E '%' E {$$.c = code_append($1.c,$3.c);
2126 $$.c = abc_modulo($$.c);
2129 E : E '*' E {$$.c = code_append($1.c,$3.c);
2130 if(BOTH_INT($1,$3)) {
2131 $$.c = abc_multiply_i($$.c);
2134 $$.c = abc_multiply($$.c);
2139 E : E "in" E {$$.c = code_append($1.c,$3.c);
2140 $$.c = abc_in($$.c);
2141 $$.t = TYPE_BOOLEAN;
2144 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2145 if(use_astype && TYPE_IS_CLASS($3.t)) {
2146 MULTINAME(m,$3.t->cls);
2147 $$.c = abc_astype2($1.c, &m);
2150 $$.c = code_append($1.c, $3.c);
2151 $$.c = abc_astypelate($$.c);
2156 E : E "instanceof" E
2157 {$$.c = code_append($1.c, $3.c);
2158 $$.c = abc_instanceof($$.c);
2159 $$.t = TYPE_BOOLEAN;
2162 E : E "is" E {$$.c = code_append($1.c, $3.c);
2163 $$.c = abc_istypelate($$.c);
2164 $$.t = TYPE_BOOLEAN;
2167 E : "typeof" '(' E ')' {
2169 $$.c = abc_typeof($$.c);
2174 $$.c = cut_last_push($2.c);
2175 $$.c = abc_pushundefined($$.c);
2179 E : "void" { $$.c = abc_pushundefined(0);
2183 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
2188 $$.c=abc_negate_i($$.c);
2191 $$.c=abc_negate($$.c);
2198 $$.c = code_append($$.c, $3.c);
2200 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
2201 $$.c = abc_getproperty2($$.c, &m);
2202 $$.t = 0; // array elements have unknown type
2205 E : '[' MAYBE_EXPRESSION_LIST ']' {
2207 $$.c = code_append($$.c, $2.cc);
2208 $$.c = abc_newarray($$.c, $2.len);
2209 $$.t = registry_getarrayclass();
2212 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
2213 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1};
2215 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2217 $$.cc = code_append($$.cc, $1.c);
2218 $$.cc = code_append($$.cc, $3.c);
2221 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2224 $$.cc = code_append($$.cc, $3.c);
2225 $$.cc = code_append($$.cc, $5.c);
2230 E : '{' MAYBE_EXPRPAIR_LIST '}' {
2232 $$.c = code_append($$.c, $2.cc);
2233 $$.c = abc_newobject($$.c, $2.len/2);
2234 $$.t = registry_getobjectclass();
2239 if(BOTH_INT($1,$3)) {
2240 c=abc_multiply_i(c);
2244 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
2245 $$.c = toreadwrite($1.c, c, 0, 0);
2250 code_t*c = abc_modulo($3.c);
2251 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
2252 $$.c = toreadwrite($1.c, c, 0, 0);
2256 code_t*c = abc_lshift($3.c);
2257 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
2258 $$.c = toreadwrite($1.c, c, 0, 0);
2262 code_t*c = abc_rshift($3.c);
2263 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
2264 $$.c = toreadwrite($1.c, c, 0, 0);
2268 code_t*c = abc_urshift($3.c);
2269 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
2270 $$.c = toreadwrite($1.c, c, 0, 0);
2274 code_t*c = abc_divide($3.c);
2275 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
2276 $$.c = toreadwrite($1.c, c, 0, 0);
2281 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2286 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
2288 $$.c = toreadwrite($1.c, c, 0, 0);
2291 E : E "-=" E { code_t*c = $3.c;
2292 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2293 c=abc_subtract_i(c);
2297 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2299 $$.c = toreadwrite($1.c, c, 0, 0);
2302 E : E '=' E { code_t*c = 0;
2303 c = code_append(c, $3.c);
2304 c = converttype(c, $3.t, $1.t);
2305 $$.c = toreadwrite($1.c, c, 1, 0);
2309 E : E '?' E ':' E %prec below_assignment {
2311 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2312 $$.c = code_append($$.c, $3.c);
2313 code_t*j2 = $$.c = abc_jump($$.c, 0);
2314 $$.c = j1->branch = abc_label($$.c);
2315 $$.c = code_append($$.c, $5.c);
2316 $$.c = j2->branch = abc_label($$.c);
2317 $$.t = join_types($3.t,$5.t,'?');
2320 // TODO: use inclocal where appropriate
2321 E : E "++" { code_t*c = 0;
2322 classinfo_t*type = $1.t;
2323 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2324 c=abc_increment_i(c);
2330 c=converttype(c, type, $1.t);
2331 $$.c = toreadwrite($1.c, c, 0, 1);
2334 E : E "--" { code_t*c = 0;
2335 classinfo_t*type = $1.t;
2336 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2337 c=abc_decrement_i(c);
2343 c=converttype(c, type, $1.t);
2344 $$.c = toreadwrite($1.c, c, 0, 1);
2348 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2349 classinfo_t*type = $2.t;
2350 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2351 c=abc_increment_i(c);
2357 c=converttype(c, type, $2.t);
2358 $$.c = toreadwrite($2.c, c, 0, 0);
2362 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2363 classinfo_t*type = $2.t;
2364 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2365 c=abc_decrement_i(c);
2371 c=converttype(c, type, $2.t);
2372 $$.c = toreadwrite($2.c, c, 0, 0);
2376 E : "super" '.' T_IDENTIFIER
2377 { if(!state->cls->info)
2378 syntaxerror("super keyword not allowed outside a class");
2379 classinfo_t*t = state->cls->info->superclass;
2380 if(!t) t = TYPE_OBJECT;
2382 memberinfo_t*f = registry_findmember(t, $3, 1);
2383 namespace_t ns = {flags2access(f->flags), ""};
2384 MEMBER_MULTINAME(m, f, $3);
2386 $$.c = abc_getlocal_0($$.c);
2387 $$.c = abc_getsuper2($$.c, &m);
2388 $$.t = memberinfo_gettype(f);
2391 E : E '.' T_IDENTIFIER
2393 classinfo_t*t = $1.t;
2395 if(TYPE_IS_CLASS(t) && t->cls) {
2400 memberinfo_t*f = registry_findmember(t, $3, 1);
2402 if(f && !is_static != !(f->flags&FLAG_STATIC))
2404 if(f && f->slot && !noslot) {
2405 $$.c = abc_getslot($$.c, f->slot);
2407 MEMBER_MULTINAME(m, f, $3);
2408 $$.c = abc_getproperty2($$.c, &m);
2410 /* determine type */
2411 $$.t = memberinfo_gettype(f);
2413 $$.c = abc_coerce_a($$.c);
2415 /* when resolving a property on an unknown type, we do know the
2416 name of the property (and don't seem to need the package), but
2417 we need to make avm2 try out all access modes */
2418 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2419 $$.c = abc_getproperty2($$.c, &m);
2420 $$.c = abc_coerce_a($$.c);
2421 $$.t = registry_getanytype();
2425 VAR_READ : T_IDENTIFIER {
2432 /* look at variables */
2433 if((v = find_variable($1))) {
2434 // $1 is a local variable
2435 $$.c = abc_getlocal($$.c, v->index);
2438 /* look at current class' members */
2439 } else if(state->cls && (f = registry_findmember(state->cls->info, $1, 1))) {
2440 // $1 is a function in this class
2441 int var_is_static = (f->flags&FLAG_STATIC);
2442 int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
2443 if(var_is_static != i_am_static) {
2444 /* there doesn't seem to be any "static" way to access
2445 static properties of a class */
2446 state->method->late_binding = 1;
2448 namespace_t ns = {flags2access(f->flags), ""};
2449 multiname_t m = {QNAME, &ns, 0, $1};
2450 $$.c = abc_findpropstrict2($$.c, &m);
2451 $$.c = abc_getproperty2($$.c, &m);
2454 $$.c = abc_getlocal_0($$.c);
2455 $$.c = abc_getslot($$.c, f->slot);
2457 namespace_t ns = {flags2access(f->flags), ""};
2458 multiname_t m = {QNAME, &ns, 0, $1};
2459 $$.c = abc_getlocal_0($$.c);
2460 $$.c = abc_getproperty2($$.c, &m);
2463 if(f->kind == MEMBER_METHOD) {
2464 $$.t = TYPE_FUNCTION(f);
2469 /* look at actual classes, in the current package and imported */
2470 } else if((a = find_class($1))) {
2471 if(a->flags & FLAG_METHOD) {
2473 $$.c = abc_findpropstrict2($$.c, &m);
2474 $$.c = abc_getproperty2($$.c, &m);
2475 $$.t = TYPE_FUNCTION(a->function);
2478 $$.c = abc_getglobalscope($$.c);
2479 $$.c = abc_getslot($$.c, a->slot);
2482 $$.c = abc_getlex2($$.c, &m);
2484 $$.t = TYPE_CLASS(a);
2487 /* unknown object, let the avm2 resolve it */
2489 if(strcmp($1,"trace"))
2490 warning("Couldn't resolve '%s', doing late binding", $1);
2491 state->method->late_binding = 1;
2493 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
2496 $$.c = abc_findpropstrict2($$.c, &m);
2497 $$.c = abc_getproperty2($$.c, &m);
2502 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
2503 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
2504 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
2506 // ----------------- namespaces -------------------------------------------------
2508 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
2509 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
2510 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}
2512 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER