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;
61 abc_exception_t *exception;
64 abc_exception_list_t *l;
70 %token<id> T_IDENTIFIER
72 %token<regexp> T_REGEXP
74 %token<number_int> T_INT
75 %token<number_uint> T_UINT
76 %token<number_uint> T_BYTE
77 %token<number_uint> T_SHORT
78 %token<number_float> T_FLOAT
80 %token<id> T_FOR "for"
81 %token<id> T_WHILE "while"
83 %token<id> T_SWITCH "switch"
85 %token<token> KW_IMPLEMENTS "implements"
86 %token<token> KW_NAMESPACE "namespace"
87 %token<token> KW_PACKAGE "package"
88 %token<token> KW_PROTECTED "protected"
89 %token<token> KW_PUBLIC "public"
90 %token<token> KW_PRIVATE "private"
91 %token<token> KW_USE "use"
92 %token<token> KW_INTERNAL "internal"
93 %token<token> KW_NEW "new"
94 %token<token> KW_NATIVE "native"
95 %token<token> KW_FUNCTION "function"
96 %token<token> KW_FINALLY "finally"
97 %token<token> KW_UNDEFINED "undefined"
98 %token<token> KW_CONTINUE "continue"
99 %token<token> KW_CLASS "class"
100 %token<token> KW_CONST "const"
101 %token<token> KW_CATCH "catch"
102 %token<token> KW_CASE "case"
103 %token<token> KW_SET "set"
104 %token<token> KW_VOID "void"
105 %token<token> KW_THROW "throw"
106 %token<token> KW_STATIC "static"
107 %token<token> KW_WITH "with"
108 %token<token> KW_INSTANCEOF "instanceof"
109 %token<token> KW_IMPORT "import"
110 %token<token> KW_RETURN "return"
111 %token<token> KW_TYPEOF "typeof"
112 %token<token> KW_INTERFACE "interface"
113 %token<token> KW_NULL "null"
114 %token<token> KW_VAR "var"
115 %token<token> KW_DYNAMIC "dynamic"
116 %token<token> KW_OVERRIDE "override"
117 %token<token> KW_FINAL "final"
118 %token<token> KW_EACH "each"
119 %token<token> KW_GET "get"
120 %token<token> KW_TRY "try"
121 %token<token> KW_SUPER "super"
122 %token<token> KW_EXTENDS "extends"
123 %token<token> KW_FALSE "false"
124 %token<token> KW_TRUE "true"
125 %token<token> KW_BOOLEAN "Boolean"
126 %token<token> KW_UINT "uint"
127 %token<token> KW_INT "int"
128 %token<token> KW_NUMBER "Number"
129 %token<token> KW_STRING "String"
130 %token<token> KW_DEFAULT "default"
131 %token<token> KW_DELETE "delete"
132 %token<token> KW_IF "if"
133 %token<token> KW_ELSE "else"
134 %token<token> KW_BREAK "break"
135 %token<token> KW_IS "is"
136 %token<token> KW_IN "in"
137 %token<token> KW_AS "as"
139 %token<token> T_EQEQ "=="
140 %token<token> T_EQEQEQ "==="
141 %token<token> T_NE "!="
142 %token<token> T_NEE "!=="
143 %token<token> T_LE "<="
144 %token<token> T_GE ">="
145 %token<token> T_ORBY "|="
146 %token<token> T_DIVBY "/="
147 %token<token> T_MODBY "%="
148 %token<token> T_MULBY "*="
149 %token<token> T_PLUSBY "+="
150 %token<token> T_MINUSBY "-="
151 %token<token> T_SHRBY ">>="
152 %token<token> T_SHLBY "<<="
153 %token<token> T_USHRBY ">>>="
154 %token<token> T_OROR "||"
155 %token<token> T_ANDAND "&&"
156 %token<token> T_COLONCOLON "::"
157 %token<token> T_MINUSMINUS "--"
158 %token<token> T_PLUSPLUS "++"
159 %token<token> T_DOTDOT ".."
160 %token<token> T_DOTDOTDOT "..."
161 %token<token> T_SHL "<<"
162 %token<token> T_USHR ">>>"
163 %token<token> T_SHR ">>"
165 %type <for_start> FOR_START
166 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
167 %type <token> VARCONST
169 %type <code> CODEPIECE CODE_STATEMENT
170 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
171 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION
172 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
173 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
174 %type <exception> CATCH FINALLY
175 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
176 %type <code> CLASS_DECLARATION
177 %type <code> NAMESPACE_DECLARATION
178 %type <code> INTERFACE_DECLARATION
179 %type <code> VOIDEXPRESSION
180 %type <value> EXPRESSION NONCOMMAEXPRESSION
181 %type <value> MAYBEEXPRESSION
182 %type <value> E DELETE
183 %type <value> CONSTANT
184 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
185 %type <value> INNERFUNCTION
186 %type <token> USE_NAMESPACE
187 %type <code> FOR_INIT
189 %type <classinfo> MAYBETYPE
192 %type <params> PARAM_LIST
193 %type <params> MAYBE_PARAM_LIST
194 %type <flags> MAYBE_MODIFIERS
195 %type <flags> MODIFIER_LIST
196 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
197 %type <classinfo_list> IMPLEMENTS_LIST
198 %type <classinfo> EXTENDS
199 %type <classinfo_list> EXTENDS_LIST
200 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
201 %type <classinfo_list> QNAME_LIST
202 %type <classinfo> TYPE
203 //%type <token> VARIABLE
204 %type <value> VAR_READ
206 //%type <token> T_IDENTIFIER
207 %type <token> MODIFIER
208 %type <value> FUNCTIONCALL
209 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
211 // precedence: from low to high
215 %left below_semicolon
218 %nonassoc below_assignment // for ?:, contrary to spec
219 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
226 %nonassoc "==" "!=" "===" "!=="
227 %nonassoc "is" "as" "in"
228 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
229 %left "<<" ">>" ">>>"
233 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
235 %nonassoc below_curly
236 %left '[' ']' '{' "new" '.' ".." "::"
237 %nonassoc T_IDENTIFIER
238 %left above_identifier
243 // needed for "return" precedence:
244 %nonassoc T_STRING T_REGEXP
245 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
246 %nonassoc "false" "true" "null" "undefined" "super" "function"
247 %nonassoc above_function
253 static int a3_error(char*s)
255 syntaxerror("%s", s);
256 return 0; //make gcc happy
260 static char* concat2(const char* t1, const char* t2)
264 char*text = malloc(l1+l2+1);
265 memcpy(text , t1, l1);
266 memcpy(text+l1, t2, l2);
270 static char* concat3(const char* t1, const char* t2, const char* t3)
275 char*text = malloc(l1+l2+l3+1);
276 memcpy(text , t1, l1);
277 memcpy(text+l1, t2, l2);
278 memcpy(text+l1+l2, t3, l3);
283 typedef struct _import {
287 DECLARE_LIST(import);
289 typedef struct _classstate {
295 char has_constructor;
298 typedef struct _methodstate {
307 abc_exception_list_t*exceptions;
310 typedef struct _state {
315 import_list_t*wildcard_imports;
317 char has_own_imports;
320 methodstate_t*method;
327 typedef struct _global {
332 static global_t*global = 0;
333 static state_t* state = 0;
337 #define MULTINAME(m,x) \
340 registry_fill_multiname(&m, &m##_ns, x);
342 #define MEMBER_MULTINAME(m,f,n) \
346 m##_ns = flags2namespace(f->flags, ""); \
349 m.namespace_set = 0; \
352 m.type = MULTINAME; \
354 m.namespace_set = &nopackage_namespace_set; \
358 /* warning: list length of namespace set is undefined */
359 #define MULTINAME_LATE(m, access, package) \
360 namespace_t m##_ns = {access, package}; \
361 namespace_set_t m##_nsset; \
362 namespace_list_t m##_l;m##_l.next = 0; \
363 m##_nsset.namespaces = &m##_l; \
364 m##_nsset = m##_nsset; \
365 m##_l.namespace = &m##_ns; \
366 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
368 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
369 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
370 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
371 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
372 static namespace_list_t nl4 = {&ns4,0};
373 static namespace_list_t nl3 = {&ns3,&nl4};
374 static namespace_list_t nl2 = {&ns2,&nl3};
375 static namespace_list_t nl1 = {&ns1,&nl2};
376 static namespace_set_t nopackage_namespace_set = {&nl1};
378 static void new_state()
381 state_t*oldstate = state;
383 memcpy(s, state, sizeof(state_t)); //shallow copy
385 s->imports = dict_new();
389 state->has_own_imports = 0;
390 state->vars = dict_new();
391 state->old = oldstate;
393 static void state_has_imports()
395 state->wildcard_imports = list_clone(state->wildcard_imports);
396 state->imports = dict_clone(state->imports);
397 state->has_own_imports = 1;
400 static void state_destroy(state_t*state)
402 if(state->has_own_imports) {
403 list_free(state->wildcard_imports);
404 dict_destroy(state->imports);state->imports=0;
406 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
407 dict_destroy(state->imports);state->imports=0;
411 for(t=0;t<state->vars->hashsize;t++) {
412 dictentry_t*e =state->vars->slots[t];
414 free(e->data);e->data=0;
418 dict_destroy(state->vars);state->vars=0;
424 static void old_state()
426 if(!state || !state->old)
427 syntaxerror("invalid nesting");
428 state_t*leaving = state;
432 if(leaving->method && leaving->method != state->method) {
433 free(leaving->method);
436 if(leaving->cls && leaving->cls != state->cls) {
441 state_destroy(leaving);
444 void initialize_parser()
446 global = rfx_calloc(sizeof(global_t));
447 global->file = abc_file_new();
448 global->file->flags &= ~ABCFILE_LAZY;
450 global->init = abc_initscript(global->file);
451 code_t*c = global->init->method->body->code;
452 c = abc_getlocal_0(c);
453 c = abc_pushscope(c);
454 global->init->method->body->code = c;
457 void initialize_file(char*filename)
460 state->package = filename;
462 state->method = rfx_calloc(sizeof(methodstate_t));
463 state->method->variable_count = 1;
468 if(!state || state->level!=1) {
469 syntaxerror("unexpected end of file");
471 state_destroy(state);state=0;
474 void* finish_parser()
476 code_t*c = global->init->method->body->code;
477 /*c = abc_findpropstrict(c, "[package]::trace");
478 c = abc_pushstring(c, "[leaving global init function]");
479 c = abc_callpropvoid(c, "[package]::trace", 1);*/
480 c = abc_returnvoid(c);
481 global->init->method->body->code = c;
486 static void xx_scopetest()
488 /* findpropstrict doesn't just return a scope object- it
489 also makes it "active" somehow. Push local_0 on the
490 scope stack and read it back with findpropstrict, it'll
491 contain properties like "trace". Trying to find the same
492 property on a "vanilla" local_0 yields only a "undefined" */
493 //c = abc_findpropstrict(c, "[package]::trace");
495 /*c = abc_getlocal_0(c);
496 c = abc_findpropstrict(c, "[package]::trace");
498 c = abc_setlocal_1(c);
500 c = abc_pushbyte(c, 0);
501 c = abc_setlocal_2(c);
503 code_t*xx = c = abc_label(c);
504 c = abc_findpropstrict(c, "[package]::trace");
505 c = abc_pushstring(c, "prop:");
506 c = abc_hasnext2(c, 1, 2);
508 c = abc_setlocal_3(c);
509 c = abc_callpropvoid(c, "[package]::trace", 2);
510 c = abc_getlocal_3(c);
512 c = abc_iftrue(c,xx);*/
516 typedef struct _variable {
522 static variable_t* find_variable(char*name)
528 v = dict_lookup(s->vars, name);
536 static variable_t* find_variable_safe(char*name)
538 variable_t* v = find_variable(name);
540 syntaxerror("undefined variable: %s", name);
543 static char variable_exists(char*name)
545 return dict_lookup(state->vars, name)!=0;
547 code_t*defaultvalue(code_t*c, classinfo_t*type);
548 static int new_variable(char*name, classinfo_t*type, char init)
551 v->index = state->method->variable_count;
555 dict_put(state->vars, name, v);
557 return state->method->variable_count++;
559 #define TEMPVARNAME "__as3_temp__"
560 static int gettempvar()
562 variable_t*v = find_variable(TEMPVARNAME);
565 return new_variable(TEMPVARNAME, 0, 0);
568 code_t* var_block(code_t*body)
574 for(t=0;t<state->vars->hashsize;t++) {
575 dictentry_t*e = state->vars->slots[t];
577 variable_t*v = (variable_t*)e->data;
578 if(v->type && v->init) {
579 c = defaultvalue(c, v->type);
580 c = abc_setlocal(c, v->index);
581 k = abc_kill(k, v->index);
591 if(x->opcode== OPCODE___BREAK__ ||
592 x->opcode== OPCODE___CONTINUE__) {
593 /* link kill code before break/continue */
594 code_t*e = code_dup(k);
595 code_t*s = code_start(e);
607 c = code_append(c, body);
608 c = code_append(c, k);
612 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
614 c = code_append(c, header);
615 c = code_append(c, var_block(body));
616 /* append return if necessary */
617 if(!c || (c->opcode != OPCODE_RETURNVOID &&
618 c->opcode != OPCODE_RETURNVALUE)) {
619 c = abc_returnvoid(c);
625 static void startpackage(char*name)
628 /*printf("entering package \"%s\"\n", name);*/
629 state->package = strdup(name);
631 static void endpackage()
633 /*printf("leaving package \"%s\"\n", state->package);*/
635 //used e.g. in classinfo_register:
636 //free(state->package);state->package=0;
641 void parserassert(int b)
643 if(!b) syntaxerror("internal error: assertion failed");
647 char*as3_globalclass=0;
648 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
651 syntaxerror("inner classes now allowed");
654 state->cls = rfx_calloc(sizeof(classstate_t));
655 state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor
656 state->method->variable_count = 1;
659 classinfo_list_t*mlist=0;
661 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
662 syntaxerror("invalid modifier(s)");
664 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
665 syntaxerror("public and internal not supported at the same time.");
667 /* create the class name, together with the proper attributes */
671 if(!(flags&FLAG_PUBLIC) && !state->package) {
672 access = ACCESS_PRIVATE; package = current_filename;
673 } else if(!(flags&FLAG_PUBLIC) && state->package) {
674 access = ACCESS_PACKAGEINTERNAL; package = state->package;
675 } else if(state->package) {
676 access = ACCESS_PACKAGE; package = state->package;
678 syntaxerror("public classes only allowed inside a package");
682 if(registry_findclass(package, classname)) {
683 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
685 /* build info struct */
686 int num_interfaces = (list_length(implements));
687 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
691 state->cls->info = registry_findclass(package, classname);
692 parserassert((int)state->cls->info);
694 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
695 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
697 classinfo_list_t*l = implements;
698 for(l=implements;l;l=l->next) {
699 state->cls->info->interfaces[pos++] = l->classinfo;
702 /* generate the abc code for this class */
703 MULTINAME(classname2,state->cls->info);
704 multiname_t*extends2 = sig2mname(extends);
706 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
707 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
708 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
710 state->cls->info->flags |= CLASS_INTERFACE;
711 abc_class_interface(state->cls->abc);
714 abc_class_protectedNS(state->cls->abc, classname);
716 for(mlist=implements;mlist;mlist=mlist->next) {
717 MULTINAME(m, mlist->classinfo);
718 abc_class_add_interface(state->cls->abc, &m);
721 /* write the construction code for this class to the global init
723 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
725 abc_method_body_t*m = global->init->method->body;
726 __ getglobalscope(m);
727 classinfo_t*s = extends;
732 //TODO: take a look at the current scope stack, maybe
733 // we can re-use something
738 multiname_t*s2 = sig2mname(s);
740 multiname_destroy(s2);
742 __ pushscope(m); count++;
743 m->code = m->code->prev->prev; // invert
745 /* continue appending after last op end */
746 while(m->code && m->code->next) m->code = m->code->next;
748 /* TODO: if this is one of *our* classes, we can also
749 do a getglobalscope/getslot <nr> (which references
750 the init function's slots) */
752 __ getlex2(m, extends2);
754 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
755 stack is not the superclass */
756 __ pushscope(m);count++;
759 /* notice: we get a verify error #1107 if the top element on the scope
760 stack is not the global object */
762 __ pushscope(m);count++;
764 __ newclass(m,state->cls->abc);
768 __ setslot(m, slotindex);
769 multiname_destroy(extends2);
771 /* flash.display.MovieClip handling */
773 if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
774 if(state->package && state->package[0]) {
775 as3_globalclass = concat3(state->package, ".", classname);
777 as3_globalclass = strdup(classname);
783 static void endclass()
786 if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
788 c = abc_getlocal_0(c);
789 c = abc_constructsuper(c, 0);
790 state->cls->init = code_append(state->cls->init, c);
792 if(!state->method->late_binding) {
793 // class initialization code uses late binding
795 c = abc_getlocal_0(c);
796 c = abc_pushscope(c);
797 state->cls->static_init = code_append(c, state->cls->static_init);
800 if(state->cls->init) {
801 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
802 m->body->code = wrap_function(0, state->cls->init, m->body->code);
804 if(state->cls->static_init) {
805 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
806 m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
813 void check_code_for_break(code_t*c)
816 if(c->opcode == OPCODE___BREAK__) {
817 char*name = string_cstr(c->data[0]);
818 syntaxerror("Unresolved \"break %s\"", name);
820 if(c->opcode == OPCODE___CONTINUE__) {
821 char*name = string_cstr(c->data[0]);
822 syntaxerror("Unresolved \"continue %s\"", name);
829 static void check_constant_against_type(classinfo_t*t, constant_t*c)
831 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
832 if(TYPE_IS_NUMBER(t)) {
833 xassert(c->type == CONSTANT_FLOAT
834 || c->type == CONSTANT_INT
835 || c->type == CONSTANT_UINT);
836 } else if(TYPE_IS_UINT(t)) {
837 xassert(c->type == CONSTANT_UINT ||
838 (c->type == CONSTANT_INT && c->i>0));
839 } else if(TYPE_IS_INT(t)) {
840 xassert(c->type == CONSTANT_INT);
841 } else if(TYPE_IS_BOOLEAN(t)) {
842 xassert(c->type == CONSTANT_TRUE
843 || c->type == CONSTANT_FALSE);
848 static int flags2access(int flags)
851 if(flags&FLAG_PUBLIC) {
852 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
853 syntaxerror("invalid combination of access levels");
854 access = ACCESS_PACKAGE;
855 } else if(flags&FLAG_PRIVATE) {
856 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
857 syntaxerror("invalid combination of access levels");
858 access = ACCESS_PRIVATE;
859 } else if(flags&FLAG_PROTECTED) {
860 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
861 syntaxerror("invalid combination of access levels");
862 access = ACCESS_PROTECTED;
864 access = ACCESS_PACKAGEINTERNAL;
870 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
872 memberinfo_t*minfo = 0;
875 minfo = memberinfo_register_global(flags2access(flags), state->package, name, MEMBER_METHOD);
876 minfo->return_type = return_type;
877 } else if(getset != KW_GET && getset != KW_SET) {
879 if((minfo = registry_findmember(state->cls->info, name, 0))) {
880 if(minfo->parent == state->cls->info) {
881 syntaxerror("class already contains a member/method called '%s'", name);
882 } else if(!minfo->parent) {
883 syntaxerror("internal error: overriding method %s, which doesn't have parent", name);
885 if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE)))
886 syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?");
889 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
890 minfo->return_type = return_type;
891 // getslot on a member slot only returns "undefined", so no need
892 // to actually store these
893 //state->minfo->slot = state->method->abc->method->trait->slot_id;
895 //class getter/setter
896 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
900 else if(params->list)
901 type = params->list->param->type;
902 // not sure wether to look into superclasses here, too
903 if((minfo=registry_findmember(state->cls->info, name, 0))) {
904 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
905 syntaxerror("class already contains a member or method called '%s'", name);
907 syntaxerror("getter/setter for '%s' already defined", name);
908 /* make a setter or getter into a getset */
913 if(type && minfo->type != type)
914 syntaxerror("different type in getter and setter");
916 minfo = memberinfo_register(state->cls->info, name, gs);
919 /* can't assign a slot as getter and setter might have different slots */
920 //minfo->slot = slot;
922 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
923 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
924 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
925 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
926 if(flags&FLAG_PACKAGEINTERNAL) minfo->flags |= FLAG_PACKAGEINTERNAL;
927 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
931 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
933 parserassert(state->method && state->method->info);
934 memberinfo_t*parent_method = state->method->info;
943 state->method = rfx_calloc(sizeof(methodstate_t));
944 state->method->inner = 1;
945 state->method->variable_count = 0;
947 memberinfo_t*minfo = 0;
949 /* TODO: we need some better way to pass things from pass1 to pass2 */
951 sprintf(myname, "as3-innerfunction-%d-%d", current_line, current_column);
954 minfo = rfx_calloc(sizeof(memberinfo_t));
956 if(!parent_method->subfunctions)
957 parent_method->subfunctions = dict_new();
959 dict_put(parent_method->subfunctions, name, minfo);
960 dict_put(parent_method->subfunctions, myname, minfo);
964 minfo = dict_lookup(parent_method->subfunctions, myname);
967 minfo->return_type = return_type;
969 new_variable("FIXME", 0, 0); //FIXME: is local_0 "this"?
971 for(p=params->list;p;p=p->next) {
972 new_variable(p->param->name, p->param->type, 0);
975 state->method->info = minfo;
978 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
979 params_t*params, classinfo_t*return_type)
981 if(state->method && state->method->info) {
982 syntaxerror("not able to start another method scope");
985 state->method = rfx_calloc(sizeof(methodstate_t));
986 state->method->has_super = 0;
987 state->method->variable_count = 0;
990 state->method->is_constructor = !strcmp(state->cls->info->name,name);
991 state->cls->has_constructor |= state->method->is_constructor;
993 state->method->is_global = 1;
994 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
996 if(state->method->is_constructor)
997 name = "__as3_constructor__";
1001 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1005 /* retrieve the member info that we stored in the first pass.
1006 TODO: better getter/setter support? */
1007 if(!state->cls) state->method->info = registry_findclass(state->package, name)->function;
1008 else state->method->info = registry_findmember(state->cls->info, name, 0);
1009 state->method->info->return_type = return_type;
1011 /* state->vars is initialized by state_new */
1012 if(!state->method->is_global)
1013 new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
1015 new_variable("globalscope", 0, 0);
1017 for(p=params->list;p;p=p->next) {
1018 new_variable(p->param->name, p->param->type, 0);
1023 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1024 params_t*params, classinfo_t*return_type, code_t*body)
1033 multiname_t*type2 = sig2mname(return_type);
1035 if(state->method->inner) {
1036 f = abc_method_new(global->file, type2, 1);
1037 } else if(state->method->is_constructor) {
1038 f = abc_class_getconstructor(state->cls->abc, type2);
1039 } else if(!state->method->is_global) {
1040 namespace_t mname_ns = flags2namespace(flags, "");
1041 multiname_t mname = {QNAME, &mname_ns, 0, name};
1043 if(flags&FLAG_STATIC)
1044 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1046 f = abc_class_method(state->cls->abc, type2, &mname);
1047 slot = f->trait->slot_id;
1049 namespace_t mname_ns = flags2namespace(flags, state->package);
1050 multiname_t mname = {QNAME, &mname_ns, 0, name};
1052 f = abc_method_new(global->file, type2, 1);
1053 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1054 //abc_code_t*c = global->init->method->body->code;
1056 //flash doesn't seem to allow us to access function slots
1057 //state->method->info->slot = slot;
1059 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1060 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1061 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1062 if(params->varargs) f->flags |= METHOD_NEED_REST;
1066 for(p=params->list;p;p=p->next) {
1067 if(params->varargs && !p->next) {
1068 break; //varargs: omit last parameter in function signature
1070 multiname_t*m = sig2mname(p->param->type);
1071 list_append(f->parameters, m);
1072 if(p->param->value) {
1073 check_constant_against_type(p->param->type, p->param->value);
1074 opt=1;list_append(f->optional_parameters, p->param->value);
1076 syntaxerror("non-optional parameter not allowed after optional parameters");
1079 check_code_for_break(body);
1082 f->body->code = body;
1083 f->body->exceptions = state->method->exceptions;
1084 } else { //interface
1086 syntaxerror("interface methods can't have a method body");
1093 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1098 void breakjumpsto(code_t*c, char*name, code_t*jump)
1101 if(c->opcode == OPCODE___BREAK__) {
1102 string_t*name2 = c->data[0];
1103 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1104 c->opcode = OPCODE_JUMP;
1111 void continuejumpsto(code_t*c, char*name, code_t*jump)
1114 if(c->opcode == OPCODE___CONTINUE__) {
1115 string_t*name2 = c->data[0];
1116 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1117 c->opcode = OPCODE_JUMP;
1125 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1126 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1127 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1129 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1131 if(!type1 || !type2)
1132 return registry_getanytype();
1133 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1134 return registry_getanytype();
1137 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1146 return registry_getanytype();
1148 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1153 return abc_coerce_a(c);
1157 // cast an "any" type to a specific type. subject to
1158 // runtime exceptions
1159 return abc_coerce2(c, &m);
1162 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1163 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1164 // allow conversion between number types
1165 return abc_coerce2(c, &m);
1167 //printf("%s.%s\n", from.package, from.name);
1168 //printf("%s.%s\n", to.package, to.name);
1170 classinfo_t*supertype = from;
1172 if(supertype == to) {
1173 // target type is one of from's superclasses
1174 return abc_coerce2(c, &m);
1177 while(supertype->interfaces[t]) {
1178 if(supertype->interfaces[t]==to) {
1179 // target type is one of from's interfaces
1180 return abc_coerce2(c, &m);
1184 supertype = supertype->superclass;
1186 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1188 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1190 syntaxerror("can't convert type %s to %s", from->name, to->name);
1191 return 0; // make gcc happy
1194 code_t*defaultvalue(code_t*c, classinfo_t*type)
1196 if(TYPE_IS_INT(type)) {
1197 c = abc_pushbyte(c, 0);
1198 } else if(TYPE_IS_UINT(type)) {
1199 c = abc_pushuint(c, 0);
1200 } else if(TYPE_IS_FLOAT(type)) {
1202 } else if(TYPE_IS_BOOLEAN(type)) {
1203 c = abc_pushfalse(c);
1205 //c = abc_pushundefined(c);
1207 c = abc_pushnull(c);
1209 c = abc_coerce2(c, &m);
1214 char is_pushundefined(code_t*c)
1216 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1219 static classinfo_t* find_class(char*name)
1223 c = registry_findclass(state->package, name);
1226 /* try explicit imports */
1227 dictentry_t* e = dict_get_slot(state->imports, name);
1230 if(!strcmp(e->key, name)) {
1231 c = (classinfo_t*)e->data;
1237 /* try package.* imports */
1238 import_list_t*l = state->wildcard_imports;
1240 //printf("does package %s contain a class %s?\n", l->import->package, name);
1241 c = registry_findclass(l->import->package, name);
1246 /* try global package */
1247 c = registry_findclass("", name);
1250 /* try local "filename" package */
1251 c = registry_findclass(current_filename_short, name);
1257 static char is_getlocal(code_t*c)
1259 if(!c || c->prev || c->next)
1261 return(c->opcode == OPCODE_GETLOCAL
1262 || c->opcode == OPCODE_GETLOCAL_0
1263 || c->opcode == OPCODE_GETLOCAL_1
1264 || c->opcode == OPCODE_GETLOCAL_2
1265 || c->opcode == OPCODE_GETLOCAL_3);
1267 static int getlocalnr(code_t*c)
1269 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1270 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1271 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1272 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1273 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1274 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1278 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1282 [prefix code] [read instruction]
1286 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1289 if(in && in->opcode == OPCODE_COERCE_A) {
1290 in = code_cutlast(in);
1293 syntaxerror("internal error");
1295 /* chop off read instruction */
1299 prefix = r->prev;r->prev = 0;
1305 char use_temp_var = readbefore;
1307 /* generate the write instruction, and maybe append a dup to the prefix code */
1308 code_t* write = abc_nop(0);
1309 if(r->opcode == OPCODE_GETPROPERTY) {
1310 write->opcode = OPCODE_SETPROPERTY;
1311 multiname_t*m = (multiname_t*)r->data[0];
1312 write->data[0] = multiname_clone(m);
1313 if(m->type == QNAME || m->type == MULTINAME) {
1315 prefix = abc_dup(prefix); // we need the object, too
1318 } else if(m->type == MULTINAMEL) {
1320 /* dupping two values on the stack requires 5 operations and one register-
1321 couldn't adobe just have given us a dup2? */
1322 int temp = gettempvar();
1323 prefix = abc_setlocal(prefix, temp);
1324 prefix = abc_dup(prefix);
1325 prefix = abc_getlocal(prefix, temp);
1326 prefix = abc_swap(prefix);
1327 prefix = abc_getlocal(prefix, temp);
1329 prefix = abc_kill(prefix, temp);
1333 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1335 } else if(r->opcode == OPCODE_GETSLOT) {
1336 write->opcode = OPCODE_SETSLOT;
1337 write->data[0] = r->data[0];
1339 prefix = abc_dup(prefix); // we need the object, too
1342 } else if(r->opcode == OPCODE_GETLOCAL) {
1343 write->opcode = OPCODE_SETLOCAL;
1344 write->data[0] = r->data[0];
1345 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1346 write->opcode = OPCODE_SETLOCAL_0;
1347 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1348 write->opcode = OPCODE_SETLOCAL_1;
1349 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1350 write->opcode = OPCODE_SETLOCAL_2;
1351 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1352 write->opcode = OPCODE_SETLOCAL_3;
1355 syntaxerror("illegal lvalue: can't assign a value to this expression");
1362 /* with getproperty/getslot, we have to be extra careful not
1363 to execute the read code twice, as it might have side-effects
1364 (e.g. if the property is in fact a setter/getter combination)
1366 So read the value, modify it, and write it again,
1367 using prefix only once and making sure (by using a temporary
1368 register) that the return value is what we just wrote */
1369 temp = gettempvar();
1370 c = code_append(c, prefix);
1371 c = code_append(c, r);
1374 c = abc_setlocal(c, temp);
1376 c = code_append(c, middlepart);
1379 c = abc_setlocal(c, temp);
1381 c = code_append(c, write);
1382 c = abc_getlocal(c, temp);
1383 c = abc_kill(c, temp);
1385 /* if we're allowed to execute the read code twice *and*
1386 the middlepart doesn't modify the code, things are easier.
1388 code_t* r2 = code_dup(r);
1389 //c = code_append(c, prefix);
1390 parserassert(!prefix);
1391 c = code_append(c, r);
1392 c = code_append(c, middlepart);
1393 c = code_append(c, write);
1394 c = code_append(c, r2);
1397 /* even smaller version: overwrite the value without reading
1401 c = code_append(c, prefix);
1404 c = code_append(c, middlepart);
1405 c = code_append(c, write);
1406 c = code_append(c, r);
1408 temp = gettempvar();
1410 c = code_append(c, prefix);
1412 c = code_append(c, middlepart);
1414 c = abc_setlocal(c, temp);
1415 c = code_append(c, write);
1416 c = abc_getlocal(c, temp);
1417 c = abc_kill(c, temp);
1423 char is_break_or_jump(code_t*c)
1427 if(c->opcode == OPCODE_JUMP ||
1428 c->opcode == OPCODE___BREAK__ ||
1429 c->opcode == OPCODE___CONTINUE__ ||
1430 c->opcode == OPCODE_THROW ||
1431 c->opcode == OPCODE_RETURNVOID ||
1432 c->opcode == OPCODE_RETURNVALUE) {
1439 #define IS_FINALLY_TARGET(op) \
1440 ((op) == OPCODE___CONTINUE__ || \
1441 (op) == OPCODE___BREAK__ || \
1442 (op) == OPCODE_RETURNVOID || \
1443 (op) == OPCODE_RETURNVALUE || \
1444 (op) == OPCODE___RETHROW__)
1446 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1448 #define NEED_EXTRA_STACK_ARG
1449 code_t*finally_label = abc_nop(0);
1450 NEW(lookupswitch_t, l);
1456 code_t*prev = i->prev;
1457 if(IS_FINALLY_TARGET(i->opcode)) {
1460 if(i->opcode == OPCODE___RETHROW__ ||
1461 i->opcode == OPCODE_RETURNVALUE) {
1462 if(i->opcode == OPCODE___RETHROW__)
1463 i->opcode = OPCODE_THROW;
1465 p = abc_coerce_a(p);
1466 p = abc_setlocal(p, tempvar);
1468 p = abc_pushbyte(p, count++);
1469 p = abc_jump(p, finally_label);
1470 code_t*target = p = abc_label(p);
1471 #ifdef NEED_EXTRA_STACK_ARG
1475 p = abc_getlocal(p, tempvar);
1478 p->next = i;i->prev = p;
1479 list_append(l->targets, target);
1485 c = abc_pushbyte(c, -1);
1486 c = code_append(c, finally_label);
1487 c = code_append(c, finally);
1489 #ifdef NEED_EXTRA_STACK_ARG
1492 c = abc_lookupswitch(c, l);
1493 c = l->def = abc_label(c);
1494 #ifdef NEED_EXTRA_STACK_ARG
1501 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1505 code_t*prev = i->prev;
1506 if(IS_FINALLY_TARGET(i->opcode)) {
1507 if(i->opcode == OPCODE___RETHROW__)
1508 i->opcode = OPCODE_THROW;
1509 code_t*end = code_dup(finally);
1510 code_t*start = code_start(end);
1511 if(prev) prev->next = start;
1518 return code_append(c, finally);
1521 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1527 int num_insertion_points=0;
1529 if(IS_FINALLY_TARGET(i->opcode))
1530 num_insertion_points++;
1537 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1542 int simple_version_cost = (1+num_insertion_points)*code_size;
1543 int lookup_version_cost = 4*num_insertion_points + 5;
1545 if(cantdup || simple_version_cost > lookup_version_cost) {
1546 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1547 return insert_finally_lookup(c, finally, tempvar);
1549 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1550 return insert_finally_simple(c, finally, tempvar);
1554 #define PASS1 }} if(as3_pass == 1) {{
1555 #define PASS1END }} if(as3_pass == 2) {{
1556 #define PASS2 }} if(as3_pass == 2) {{
1557 #define PASS12 }} {{
1558 #define PASS12END }} if(as3_pass == 2) {{
1564 /* ------------ code blocks / statements ---------------- */
1566 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1568 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1569 PROGRAM_CODE_LIST: PROGRAM_CODE
1570 | PROGRAM_CODE_LIST PROGRAM_CODE
1572 PROGRAM_CODE: PACKAGE_DECLARATION
1573 | INTERFACE_DECLARATION
1575 | FUNCTION_DECLARATION
1580 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1581 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1582 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1584 INPACKAGE_CODE: INTERFACE_DECLARATION
1586 | FUNCTION_DECLARATION
1591 MAYBECODE: CODE {$$=$1;}
1592 MAYBECODE: {$$=code_new();}
1594 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1595 CODE: CODEPIECE {$$=$1;}
1597 // code which also may appear outside a method
1598 CODE_STATEMENT: IMPORT
1600 CODE_STATEMENT: FOR_IN
1601 CODE_STATEMENT: WHILE
1602 CODE_STATEMENT: DO_WHILE
1603 CODE_STATEMENT: SWITCH
1605 CODE_STATEMENT: WITH
1607 CODE_STATEMENT: VOIDEXPRESSION
1609 // code which may appear anywhere
1610 CODEPIECE: ';' {$$=0;}
1611 CODEPIECE: CODE_STATEMENT
1612 CODEPIECE: VARIABLE_DECLARATION
1618 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1619 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;}
1621 CODEBLOCK : '{' CODE '}' {$$=$2;}
1622 CODEBLOCK : '{' '}' {$$=0;}
1623 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1624 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1626 /* ------------ package init code ------------------- */
1628 PACKAGE_INITCODE: CODE_STATEMENT {
1629 code_t**cc = &global->init->method->body->code;
1630 *cc = code_append(*cc, $1);
1633 /* ------------ variables --------------------------- */
1635 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1636 | {$$.c=abc_pushundefined(0);
1640 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1641 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1643 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1644 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1646 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1648 if(variable_exists($1))
1649 syntaxerror("Variable %s already defined", $1);
1651 if(!is_subtype_of($3.t, $2)) {
1652 syntaxerror("Can't convert %s to %s", $3.t->name,
1656 int index = new_variable($1, $2, 1);
1659 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1661 $$ = converttype($$, $3.t, $2);
1662 $$ = abc_setlocal($$, index);
1664 $$ = defaultvalue(0, $2);
1665 $$ = abc_setlocal($$, index);
1668 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1670 $$ = abc_coerce_a($$);
1671 $$ = abc_setlocal($$, index);
1677 /* that's the default for a local register, anyway
1679 state->method->initcode = abc_pushundefined(state->method->initcode);
1680 state->method->initcode = abc_setlocal(state->method->initcode, index);
1682 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1685 /* ------------ control flow ------------------------- */
1687 MAYBEELSE: %prec below_else {$$ = code_new();}
1688 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1689 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1691 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1694 $$ = code_append($$, $4.c);
1695 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1697 $$ = code_append($$, $6);
1699 myjmp = $$ = abc_jump($$, 0);
1701 myif->branch = $$ = abc_nop($$);
1703 $$ = code_append($$, $7);
1704 myjmp->branch = $$ = abc_nop($$);
1710 FOR_INIT : {$$=code_new();}
1711 FOR_INIT : VARIABLE_DECLARATION
1712 FOR_INIT : VOIDEXPRESSION
1714 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
1715 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1716 $$=$2;new_variable($2,$3,1);
1718 FOR_IN_INIT : T_IDENTIFIER {
1722 FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
1723 FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
1725 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1726 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1728 $$ = code_append($$, $2);
1729 code_t*loopstart = $$ = abc_label($$);
1730 $$ = code_append($$, $4.c);
1731 code_t*myif = $$ = abc_iffalse($$, 0);
1732 $$ = code_append($$, $8);
1733 code_t*cont = $$ = abc_nop($$);
1734 $$ = code_append($$, $6);
1735 $$ = abc_jump($$, loopstart);
1736 code_t*out = $$ = abc_nop($$);
1737 breakjumpsto($$, $1.name, out);
1738 continuejumpsto($$, $1.name, cont);
1745 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
1746 variable_t*var = find_variable($2);
1747 char*tmp1name = concat2($2, "__tmp1__");
1748 int it = new_variable(tmp1name, TYPE_INT, 0);
1749 char*tmp2name = concat2($2, "__array__");
1750 int array = new_variable(tmp1name, 0, 0);
1753 $$ = code_append($$, $4.c);
1754 $$ = abc_coerce_a($$);
1755 $$ = abc_setlocal($$, array);
1756 $$ = abc_pushbyte($$, 0);
1757 $$ = abc_setlocal($$, it);
1759 code_t*loopstart = $$ = abc_label($$);
1761 $$ = abc_hasnext2($$, array, it);
1762 code_t*myif = $$ = abc_iffalse($$, 0);
1763 $$ = abc_getlocal($$, array);
1764 $$ = abc_getlocal($$, it);
1766 $$ = abc_nextname($$);
1768 $$ = abc_nextvalue($$);
1769 $$ = converttype($$, 0, var->type);
1770 $$ = abc_setlocal($$, var->index);
1772 $$ = code_append($$, $6);
1773 $$ = abc_jump($$, loopstart);
1775 code_t*out = $$ = abc_nop($$);
1776 breakjumpsto($$, $1.name, out);
1777 continuejumpsto($$, $1.name, loopstart);
1788 WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1792 code_t*myjmp = $$ = abc_jump($$, 0);
1793 code_t*loopstart = $$ = abc_label($$);
1794 $$ = code_append($$, $6);
1795 code_t*cont = $$ = abc_nop($$);
1796 myjmp->branch = cont;
1797 $$ = code_append($$, $4.c);
1798 $$ = abc_iftrue($$, loopstart);
1799 code_t*out = $$ = abc_nop($$);
1800 breakjumpsto($$, $1, out);
1801 continuejumpsto($$, $1, cont);
1807 DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
1809 code_t*loopstart = $$ = abc_label($$);
1810 $$ = code_append($$, $3);
1811 code_t*cont = $$ = abc_nop($$);
1812 $$ = code_append($$, $6.c);
1813 $$ = abc_iftrue($$, loopstart);
1814 code_t*out = $$ = abc_nop($$);
1815 breakjumpsto($$, $1, out);
1816 continuejumpsto($$, $1, cont);
1822 BREAK : "break" %prec prec_none {
1823 $$ = abc___break__(0, "");
1825 BREAK : "break" T_IDENTIFIER {
1826 $$ = abc___break__(0, $2);
1828 CONTINUE : "continue" %prec prec_none {
1829 $$ = abc___continue__(0, "");
1831 CONTINUE : "continue" T_IDENTIFIER {
1832 $$ = abc___continue__(0, $2);
1835 MAYBE_CASE_LIST : {$$=0;}
1836 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
1837 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
1838 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
1839 CASE_LIST: CASE {$$=$1;}
1840 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
1842 CASE: "case" E ':' MAYBECODE {
1844 $$ = code_append($$, $2.c);
1845 code_t*j = $$ = abc_ifne($$, 0);
1846 $$ = code_append($$, $4);
1847 if($$->opcode != OPCODE___BREAK__) {
1848 $$ = abc___fallthrough__($$, "");
1850 code_t*e = $$ = abc_nop($$);
1853 DEFAULT: "default" ':' MAYBECODE {
1856 SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
1858 $$ = code_append($$, $7);
1859 code_t*out = $$ = abc_pop($$);
1860 breakjumpsto($$, $1, out);
1862 code_t*c = $$,*lastblock=0;
1864 if(c->opcode == OPCODE_IFNE) {
1865 if(!c->next) syntaxerror("internal error in fallthrough handling");
1867 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
1869 c->opcode = OPCODE_JUMP;
1870 c->branch = lastblock;
1872 /* fall through end of switch */
1873 c->opcode = OPCODE_NOP;
1883 /* ------------ try / catch /finally ---------------- */
1885 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);}
1887 namespace_t name_ns = {ACCESS_PACKAGE, ""};
1888 multiname_t name = {QNAME, &name_ns, 0, $3};
1890 NEW(abc_exception_t, e)
1891 e->exc_type = sig2mname($4);
1892 e->var_name = multiname_clone(&name);
1896 int i = find_variable_safe($3)->index;
1897 e->target = c = abc_nop(0);
1898 c = abc_setlocal(c, i);
1899 c = code_append(c, $8);
1905 FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
1911 NEW(abc_exception_t, e)
1912 e->exc_type = 0; //all exceptions
1913 e->var_name = 0; //no name
1916 e->to = code_append(e->to, $4);
1922 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
1923 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
1924 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
1925 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
1929 list_append($$.l,$2);
1930 $$.finally = $2->to;$2->to=0;
1933 CATCH_FINALLY_LIST: FINALLY {
1937 list_append($$.l,$1);
1938 $$.finally = $1->to;$1->to=0;
1942 TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
1943 code_t*out = abc_nop(0);
1945 code_t*start = abc_nop(0);
1946 $$ = code_append(start, $4);
1947 if(!is_break_or_jump($4)) {
1948 $$ = abc_jump($$, out);
1950 code_t*end = $$ = abc_nop($$);
1954 tmp = new_variable("__finally__", 0, 0);
1956 abc_exception_list_t*l = $6.l;
1959 abc_exception_t*e = l->abc_exception;
1961 $$ = code_append($$, e->target);
1962 $$ = abc_jump($$, out);
1964 parserassert((ptroff_t)$6.finally);
1966 e->target = $$ = abc_nop($$);
1967 $$ = abc___rethrow__($$);
1975 $$ = code_append($$, out);
1977 $$ = insert_finally($$, $6.finally, tmp);
1979 list_concat(state->method->exceptions, $6.l);
1985 /* ------------ throw ------------------------------- */
1987 THROW : "throw" EXPRESSION {
1991 THROW : "throw" %prec prec_none {
1992 if(!state->exception_name)
1993 syntaxerror("re-throw only possible within a catch block");
1994 variable_t*v = find_variable(state->exception_name);
1996 $$=abc_getlocal($$, v->index);
2000 /* ------------ with -------------------------------- */
2002 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2004 $$ = abc_pushscope($$);
2005 $$ = code_append($$, $5);
2006 $$ = abc_popscope($$);
2009 /* ------------ packages and imports ---------------- */
2011 X_IDENTIFIER: T_IDENTIFIER
2012 | "package" {PASS12 $$="package";}
2014 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2015 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2017 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2018 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2019 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2020 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2022 IMPORT : "import" QNAME {
2025 syntaxerror("Couldn't import class\n");
2026 state_has_imports();
2027 dict_put(state->imports, c->name, c);
2030 IMPORT : "import" PACKAGE '.' '*' {
2033 state_has_imports();
2034 list_append(state->wildcard_imports, i);
2038 /* ------------ classes and interfaces (header) -------------- */
2040 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2041 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2042 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2043 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2045 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2046 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2047 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2048 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2049 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2050 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2051 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2052 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2053 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2055 EXTENDS : {$$=registry_getobjectclass();}
2056 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
2058 EXTENDS_LIST : {PASS12 $$=list_new();}
2059 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {PASS12 $$=$2;}
2061 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2062 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {PASS12 $$=$2;}
2064 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2065 EXTENDS IMPLEMENTS_LIST
2066 '{' {PASS12 startclass($1,$3,$4,$5, 0);}
2068 '}' {PASS12 endclass();$$=0;}
2070 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2072 '{' {PASS12 startclass($1,$3,0,$4,1);}
2073 MAYBE_INTERFACE_BODY
2074 '}' {PASS12 endclass();$$=0;}
2076 /* ------------ classes and interfaces (body) -------------- */
2079 MAYBE_CLASS_BODY : CLASS_BODY
2080 CLASS_BODY : CLASS_BODY_ITEM
2081 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2082 CLASS_BODY_ITEM : ';'
2083 CLASS_BODY_ITEM : SLOT_DECLARATION
2084 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2086 CLASS_BODY_ITEM : CODE_STATEMENT {
2087 code_t*c = state->cls->static_init;
2088 c = code_append(c, $1);
2089 state->cls->static_init = c;
2092 MAYBE_INTERFACE_BODY :
2093 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2094 INTERFACE_BODY : IDECLARATION
2095 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2097 IDECLARATION : "var" T_IDENTIFIER {
2098 syntaxerror("variable declarations not allowed in interfaces");
2100 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2103 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2104 syntaxerror("invalid method modifiers: interface methods always need to be public");
2106 startfunction(0,$1,$3,$4,&$6,$8);
2107 endfunction(0,$1,$3,$4,&$6,$8, 0);
2110 /* ------------ classes and interfaces (body, slots ) ------- */
2112 VARCONST: "var" | "const"
2114 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
2116 memberinfo_t* info = state->cls?
2117 memberinfo_register(state->cls->info, $3, MEMBER_SLOT):
2118 memberinfo_register_global(flags2access($1), state->package, $3, MEMBER_SLOT);
2121 info->flags = flags;
2124 namespace_t mname_ns = {flags2access(flags), ""};
2125 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2127 trait_list_t**traits;
2131 mname_ns.name = state->package;
2132 traits = &global->init->traits;
2133 code = &global->init->method->body->code;
2134 } else if(flags&FLAG_STATIC) {
2136 traits = &state->cls->abc->static_traits;
2137 code = &state->cls->static_init;
2139 // instance variable
2140 traits = &state->cls->abc->traits;
2141 code = &state->cls->init;
2147 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2149 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2151 info->slot = t->slot_id;
2153 /* initalization code (if needed) */
2155 if($5.c && !is_pushundefined($5.c)) {
2156 c = abc_getlocal_0(c);
2157 c = code_append(c, $5.c);
2158 c = converttype(c, $5.t, $4);
2159 c = abc_setslot(c, t->slot_id);
2162 *code = code_append(*code, c);
2165 t->kind= TRAIT_CONST;
2171 /* ------------ constants -------------------------------------- */
2173 MAYBESTATICCONSTANT: {$$=0;}
2174 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2176 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2177 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2178 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2179 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2180 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
2181 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2182 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2183 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2184 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2186 /* ------------ classes and interfaces (body, functions) ------- */
2188 // non-vararg version
2190 memset(&$$,0,sizeof($$));
2192 MAYBE_PARAM_LIST: PARAM_LIST {
2197 MAYBE_PARAM_LIST: "..." PARAM {
2198 memset(&$$,0,sizeof($$));
2200 list_append($$.list, $2);
2202 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2205 list_append($$.list, $4);
2209 PARAM_LIST: PARAM_LIST ',' PARAM {
2211 list_append($$.list, $3);
2214 memset(&$$,0,sizeof($$));
2215 list_append($$.list, $1);
2218 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2219 $$ = malloc(sizeof(param_t));
2224 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2225 $$ = malloc(sizeof(param_t));
2227 $$->type = TYPE_ANY;
2230 GETSET : "get" {$$=$1;}
2234 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2235 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2239 if(!state->method->info) syntaxerror("internal error");
2241 if(state->method->late_binding) {
2242 c = abc_getlocal_0(c);
2243 c = abc_pushscope(c);
2245 if(state->method->is_constructor && !state->method->has_super) {
2246 // call default constructor
2247 c = abc_getlocal_0(c);
2248 c = abc_constructsuper(c, 0);
2251 c = wrap_function(c, 0, $11);
2253 endfunction(0,$1,$3,$4,&$6,$8,c);
2257 MAYBE_IDENTIFIER: T_IDENTIFIER
2258 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2259 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2260 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2264 memberinfo_t*f = state->method->info;
2265 if(!f) syntaxerror("internal error");
2268 c = wrap_function(c, 0, $9);
2270 abc_method_t*abc = endfunction(0,0,0,$2,&$4,$6,c);
2272 $$.c = abc_newfunction(0, abc);
2273 $$.t = TYPE_FUNCTION(f);
2277 /* ------------- package + class ids --------------- */
2279 CLASS: T_IDENTIFIER {
2282 /* try current package */
2283 $$ = find_class($1);
2284 if(!$$) syntaxerror("Could not find class %s\n", $1);
2287 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2290 $$ = registry_findclass($1, $3);
2291 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
2295 QNAME: PACKAGEANDCLASS
2298 QNAME_LIST : QNAME {PASS12 $$=list_new();list_append($$, $1);}
2299 QNAME_LIST : QNAME_LIST ',' QNAME {PASS12 $$=$1;list_append($$,$3);}
2301 TYPE : QNAME {$$=$1;}
2302 | '*' {$$=registry_getanytype();}
2303 | "void" {$$=registry_getanytype();}
2305 | "String" {$$=registry_getstringclass();}
2306 | "int" {$$=registry_getintclass();}
2307 | "uint" {$$=registry_getuintclass();}
2308 | "Boolean" {$$=registry_getbooleanclass();}
2309 | "Number" {$$=registry_getnumberclass();}
2312 MAYBETYPE: ':' TYPE {$$=$2;}
2315 /* ----------function calls, delete, constructor calls ------ */
2317 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2318 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2320 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2321 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2322 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2325 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
2327 $$.cc = code_append($1.cc, $3.c);
2330 NEW : "new" CLASS MAYBE_PARAM_VALUES {
2335 $$.c = abc_getglobalscope($$.c);
2336 $$.c = abc_getslot($$.c, $2->slot);
2338 $$.c = abc_findpropstrict2($$.c, &m);
2341 $$.c = code_append($$.c, $3.cc);
2344 $$.c = abc_construct($$.c, $3.len);
2346 $$.c = abc_constructprop2($$.c, &m, $3.len);
2350 /* TODO: use abc_call (for calling local variables),
2351 abc_callstatic (for calling own methods)
2354 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2357 if($$.c->opcode == OPCODE_COERCE_A) {
2358 $$.c = code_cutlast($$.c);
2360 code_t*paramcode = $3.cc;
2363 if($$.c->opcode == OPCODE_GETPROPERTY) {
2364 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2365 $$.c = code_cutlast($$.c);
2366 $$.c = code_append($$.c, paramcode);
2367 $$.c = abc_callproperty2($$.c, name, $3.len);
2368 multiname_destroy(name);
2369 } else if($$.c->opcode == OPCODE_GETSLOT) {
2370 int slot = (int)(ptroff_t)$$.c->data[0];
2371 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2372 if(t->kind!=TRAIT_METHOD) {
2373 //ok: flash allows to assign closures to members.
2375 multiname_t*name = t->name;
2376 $$.c = code_cutlast($$.c);
2377 $$.c = code_append($$.c, paramcode);
2378 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2379 $$.c = abc_callproperty2($$.c, name, $3.len);
2380 } else if($$.c->opcode == OPCODE_GETSUPER) {
2381 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2382 $$.c = code_cutlast($$.c);
2383 $$.c = code_append($$.c, paramcode);
2384 $$.c = abc_callsuper2($$.c, name, $3.len);
2385 multiname_destroy(name);
2387 $$.c = abc_getlocal_0($$.c);
2388 $$.c = code_append($$.c, paramcode);
2389 $$.c = abc_call($$.c, $3.len);
2394 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
2395 $$.t = $1.t->function->return_type;
2397 $$.c = abc_coerce_a($$.c);
2402 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2403 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2404 if(!state->method) syntaxerror("super() not allowed outside of a function");
2405 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2408 $$.c = abc_getlocal_0($$.c);
2410 $$.c = code_append($$.c, $3.cc);
2412 this is dependent on the control path, check this somewhere else
2413 if(state->method->has_super)
2414 syntaxerror("constructor may call super() only once");
2416 state->method->has_super = 1;
2417 $$.c = abc_constructsuper($$.c, $3.len);
2418 $$.c = abc_pushundefined($$.c);
2422 DELETE: "delete" E {
2424 if($$.c->opcode == OPCODE_COERCE_A) {
2425 $$.c = code_cutlast($$.c);
2427 multiname_t*name = 0;
2428 if($$.c->opcode == OPCODE_GETPROPERTY) {
2429 $$.c->opcode = OPCODE_DELETEPROPERTY;
2430 } else if($$.c->opcode == OPCODE_GETSLOT) {
2431 int slot = (int)(ptroff_t)$$.c->data[0];
2432 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
2433 $$.c = code_cutlast($$.c);
2434 $$.c = abc_deleteproperty2($$.c, name);
2436 $$.c = abc_getlocal_0($$.c);
2437 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2438 $$.c = abc_deleteproperty2($$.c, &m);
2440 $$.t = TYPE_BOOLEAN;
2443 RETURN: "return" %prec prec_none {
2444 $$ = abc_returnvoid(0);
2446 RETURN: "return" EXPRESSION {
2448 $$ = abc_returnvalue($$);
2451 // ----------------------- expression types -------------------------------------
2453 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2454 EXPRESSION : E %prec below_minus {$$ = $1;}
2455 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2457 $$.c = cut_last_push($$.c);
2458 $$.c = code_append($$.c,$3.c);
2461 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2462 $$=cut_last_push($1.c);
2465 // ----------------------- expression evaluation -------------------------------------
2467 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2468 //V : CONSTANT {$$ = 0;}
2470 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2471 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2472 //V : NEW {$$ = $1.c;}
2474 //V : DELETE {$$ = $1.c;}
2475 E : DELETE {$$ = $1;}
2479 namespace_t ns = {ACCESS_PACKAGE, ""};
2480 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2482 $$.c = abc_getlex2($$.c, &m);
2483 $$.c = abc_pushstring($$.c, $1.pattern);
2484 $$.c = abc_construct($$.c, 1);
2486 $$.c = abc_getlex2($$.c, &m);
2487 $$.c = abc_pushstring($$.c, $1.pattern);
2488 $$.c = abc_pushstring($$.c, $1.options);
2489 $$.c = abc_construct($$.c, 2);
2494 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2495 //MULTINAME(m, registry_getintclass());
2496 //$$.c = abc_coerce2($$.c, &m); // FIXME
2499 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2502 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2505 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2508 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2511 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
2514 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2517 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2518 $$.t = TYPE_BOOLEAN;
2520 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2521 $$.t = TYPE_BOOLEAN;
2523 CONSTANT : "null" {$$.c = abc_pushnull(0);
2528 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2529 $$.t = TYPE_BOOLEAN;
2531 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2532 $$.t = TYPE_BOOLEAN;
2534 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2535 $$.t = TYPE_BOOLEAN;
2537 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2538 $$.t = TYPE_BOOLEAN;
2540 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2541 $$.t = TYPE_BOOLEAN;
2543 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2544 $$.t = TYPE_BOOLEAN;
2546 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2547 $$.t = TYPE_BOOLEAN;
2549 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2550 $$.t = TYPE_BOOLEAN;
2553 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2555 $$.c = converttype($$.c, $1.t, $$.t);
2556 $$.c = abc_dup($$.c);
2557 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2558 $$.c = cut_last_push($$.c);
2559 $$.c = code_append($$.c,$3.c);
2560 $$.c = converttype($$.c, $3.t, $$.t);
2561 code_t*label = $$.c = abc_label($$.c);
2562 jmp->branch = label;
2565 $$.t = join_types($1.t, $3.t, 'A');
2566 /*printf("%08x:\n",$1.t);
2567 code_dump($1.c, 0, 0, "", stdout);
2568 printf("%08x:\n",$3.t);
2569 code_dump($3.c, 0, 0, "", stdout);
2570 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2572 $$.c = converttype($$.c, $1.t, $$.t);
2573 $$.c = abc_dup($$.c);
2574 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2575 $$.c = cut_last_push($$.c);
2576 $$.c = code_append($$.c,$3.c);
2577 $$.c = converttype($$.c, $3.t, $$.t);
2578 code_t*label = $$.c = abc_label($$.c);
2579 jmp->branch = label;
2582 E : '!' E {$$.c=$2.c;
2583 $$.c = abc_not($$.c);
2584 $$.t = TYPE_BOOLEAN;
2587 E : '~' E {$$.c=$2.c;
2588 $$.c = abc_bitnot($$.c);
2592 E : E '&' E {$$.c = code_append($1.c,$3.c);
2593 $$.c = abc_bitand($$.c);
2597 E : E '^' E {$$.c = code_append($1.c,$3.c);
2598 $$.c = abc_bitxor($$.c);
2602 E : E '|' E {$$.c = code_append($1.c,$3.c);
2603 $$.c = abc_bitor($$.c);
2607 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2608 $$.c = abc_rshift($$.c);
2611 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2612 $$.c = abc_urshift($$.c);
2615 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2616 $$.c = abc_lshift($$.c);
2620 E : E '/' E {$$.c = code_append($1.c,$3.c);
2621 $$.c = abc_divide($$.c);
2624 E : E '%' E {$$.c = code_append($1.c,$3.c);
2625 $$.c = abc_modulo($$.c);
2628 E : E '+' E {$$.c = code_append($1.c,$3.c);
2629 if(BOTH_INT($1.t, $3.t)) {
2630 $$.c = abc_add_i($$.c);
2633 $$.c = abc_add($$.c);
2634 $$.t = join_types($1.t,$3.t,'+');
2637 E : E '-' E {$$.c = code_append($1.c,$3.c);
2638 if(BOTH_INT($1.t,$3.t)) {
2639 $$.c = abc_subtract_i($$.c);
2642 $$.c = abc_subtract($$.c);
2646 E : E '*' E {$$.c = code_append($1.c,$3.c);
2647 if(BOTH_INT($1.t,$3.t)) {
2648 $$.c = abc_multiply_i($$.c);
2651 $$.c = abc_multiply($$.c);
2656 E : E "in" E {$$.c = code_append($1.c,$3.c);
2657 $$.c = abc_in($$.c);
2658 $$.t = TYPE_BOOLEAN;
2661 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2662 if(use_astype && TYPE_IS_CLASS($3.t)) {
2663 MULTINAME(m,$3.t->cls);
2664 $$.c = abc_astype2($1.c, &m);
2667 $$.c = code_append($1.c, $3.c);
2668 $$.c = abc_astypelate($$.c);
2673 E : E "instanceof" E
2674 {$$.c = code_append($1.c, $3.c);
2675 $$.c = abc_instanceof($$.c);
2676 $$.t = TYPE_BOOLEAN;
2679 E : E "is" E {$$.c = code_append($1.c, $3.c);
2680 $$.c = abc_istypelate($$.c);
2681 $$.t = TYPE_BOOLEAN;
2684 E : "typeof" '(' E ')' {
2686 $$.c = abc_typeof($$.c);
2691 $$.c = cut_last_push($2.c);
2692 $$.c = abc_pushundefined($$.c);
2696 E : "void" { $$.c = abc_pushundefined(0);
2700 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
2705 $$.c=abc_negate_i($$.c);
2708 $$.c=abc_negate($$.c);
2715 $$.c = code_append($$.c, $3.c);
2717 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
2718 $$.c = abc_getproperty2($$.c, &m);
2719 $$.t = 0; // array elements have unknown type
2722 E : '[' MAYBE_EXPRESSION_LIST ']' {
2724 $$.c = code_append($$.c, $2.cc);
2725 $$.c = abc_newarray($$.c, $2.len);
2726 $$.t = registry_getarrayclass();
2729 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
2730 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
2732 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2734 $$.cc = code_append($$.cc, $1.c);
2735 $$.cc = code_append($$.cc, $3.c);
2738 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2741 $$.cc = code_append($$.cc, $3.c);
2742 $$.cc = code_append($$.cc, $5.c);
2747 E : '{' MAYBE_EXPRPAIR_LIST '}' {
2749 $$.c = code_append($$.c, $2.cc);
2750 $$.c = abc_newobject($$.c, $2.len/2);
2751 $$.t = registry_getobjectclass();
2756 if(BOTH_INT($1.t,$3.t)) {
2757 c=abc_multiply_i(c);
2761 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
2762 $$.c = toreadwrite($1.c, c, 0, 0);
2767 code_t*c = abc_modulo($3.c);
2768 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
2769 $$.c = toreadwrite($1.c, c, 0, 0);
2773 code_t*c = abc_lshift($3.c);
2774 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
2775 $$.c = toreadwrite($1.c, c, 0, 0);
2779 code_t*c = abc_rshift($3.c);
2780 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
2781 $$.c = toreadwrite($1.c, c, 0, 0);
2785 code_t*c = abc_urshift($3.c);
2786 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
2787 $$.c = toreadwrite($1.c, c, 0, 0);
2791 code_t*c = abc_divide($3.c);
2792 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
2793 $$.c = toreadwrite($1.c, c, 0, 0);
2797 code_t*c = abc_bitor($3.c);
2798 c=converttype(c, TYPE_INT, $1.t);
2799 $$.c = toreadwrite($1.c, c, 0, 0);
2805 if(TYPE_IS_INT($1.t)) {
2809 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
2812 $$.c = toreadwrite($1.c, c, 0, 0);
2815 E : E "-=" E { code_t*c = $3.c;
2816 if(TYPE_IS_INT($1.t)) {
2817 c=abc_subtract_i(c);
2820 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2823 $$.c = toreadwrite($1.c, c, 0, 0);
2826 E : E '=' E { code_t*c = 0;
2827 c = code_append(c, $3.c);
2828 c = converttype(c, $3.t, $1.t);
2829 $$.c = toreadwrite($1.c, c, 1, 0);
2833 E : E '?' E ':' E %prec below_assignment {
2834 $$.t = join_types($3.t,$5.t,'?');
2836 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2837 $$.c = code_append($$.c, $3.c);
2838 $$.c = converttype($$.c, $3.t, $$.t);
2839 code_t*j2 = $$.c = abc_jump($$.c, 0);
2840 $$.c = j1->branch = abc_label($$.c);
2841 $$.c = code_append($$.c, $5.c);
2842 $$.c = converttype($$.c, $3.t, $$.t);
2843 $$.c = j2->branch = abc_label($$.c);
2846 E : E "++" { code_t*c = 0;
2847 classinfo_t*type = $1.t;
2848 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
2849 int nr = getlocalnr($1.c);
2850 code_free($1.c);$1.c=0;
2851 if(TYPE_IS_INT($1.t)) {
2852 $$.c = abc_getlocal(0, nr);
2853 $$.c = abc_inclocal_i($$.c, nr);
2854 } else if(TYPE_IS_NUMBER($1.t)) {
2855 $$.c = abc_getlocal(0, nr);
2856 $$.c = abc_inclocal($$.c, nr);
2857 } else syntaxerror("internal error");
2859 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2860 c=abc_increment_i(c);
2866 c=converttype(c, type, $1.t);
2867 $$.c = toreadwrite($1.c, c, 0, 1);
2872 // TODO: use inclocal, like with ++
2873 E : E "--" { code_t*c = 0;
2874 classinfo_t*type = $1.t;
2875 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2876 c=abc_decrement_i(c);
2882 c=converttype(c, type, $1.t);
2883 $$.c = toreadwrite($1.c, c, 0, 1);
2887 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2888 classinfo_t*type = $2.t;
2889 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2890 c=abc_increment_i(c);
2896 c=converttype(c, type, $2.t);
2897 $$.c = toreadwrite($2.c, c, 0, 0);
2901 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2902 classinfo_t*type = $2.t;
2903 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2904 c=abc_decrement_i(c);
2910 c=converttype(c, type, $2.t);
2911 $$.c = toreadwrite($2.c, c, 0, 0);
2915 E : "super" '.' T_IDENTIFIER
2916 { if(!state->cls->info)
2917 syntaxerror("super keyword not allowed outside a class");
2918 classinfo_t*t = state->cls->info->superclass;
2919 if(!t) t = TYPE_OBJECT;
2921 memberinfo_t*f = registry_findmember(t, $3, 1);
2922 namespace_t ns = flags2namespace(f->flags, "");
2923 MEMBER_MULTINAME(m, f, $3);
2925 $$.c = abc_getlocal_0($$.c);
2926 $$.c = abc_getsuper2($$.c, &m);
2927 $$.t = memberinfo_gettype(f);
2930 E : E '.' T_IDENTIFIER
2932 classinfo_t*t = $1.t;
2934 if(TYPE_IS_CLASS(t) && t->cls) {
2939 memberinfo_t*f = registry_findmember(t, $3, 1);
2941 if(f && !is_static != !(f->flags&FLAG_STATIC))
2943 if(f && f->slot && !noslot) {
2944 $$.c = abc_getslot($$.c, f->slot);
2946 MEMBER_MULTINAME(m, f, $3);
2947 $$.c = abc_getproperty2($$.c, &m);
2949 /* determine type */
2950 $$.t = memberinfo_gettype(f);
2952 $$.c = abc_coerce_a($$.c);
2954 /* when resolving a property on an unknown type, we do know the
2955 name of the property (and don't seem to need the package), but
2956 we need to make avm2 try out all access modes */
2957 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2958 $$.c = abc_getproperty2($$.c, &m);
2959 $$.c = abc_coerce_a($$.c);
2960 $$.t = registry_getanytype();
2964 VAR_READ : T_IDENTIFIER {
2971 /* look at variables */
2972 if((v = find_variable($1))) {
2973 // $1 is a local variable
2974 $$.c = abc_getlocal($$.c, v->index);
2979 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
2981 /* look at current class' members */
2982 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
2983 (f->flags&FLAG_STATIC) >= i_am_static) {
2984 // $1 is a function in this class
2985 int var_is_static = (f->flags&FLAG_STATIC);
2987 if(f->kind == MEMBER_METHOD) {
2988 $$.t = TYPE_FUNCTION(f);
2992 if(var_is_static && !i_am_static) {
2993 /* access to a static member from a non-static location.
2994 do this via findpropstrict:
2995 there doesn't seem to be any non-lookup way to access
2996 static properties of a class */
2997 state->method->late_binding = 1;
2999 namespace_t ns = {flags2access(f->flags), ""};
3000 multiname_t m = {QNAME, &ns, 0, $1};
3001 $$.c = abc_findpropstrict2($$.c, &m);
3002 $$.c = abc_getproperty2($$.c, &m);
3004 } else if(f->slot>0) {
3005 $$.c = abc_getlocal_0($$.c);
3006 $$.c = abc_getslot($$.c, f->slot);
3009 namespace_t ns = {flags2access(f->flags), ""};
3010 multiname_t m = {QNAME, &ns, 0, $1};
3011 $$.c = abc_getlocal_0($$.c);
3012 $$.c = abc_getproperty2($$.c, &m);
3017 /* look at actual classes, in the current package and imported */
3018 if((a = find_class($1))) {
3019 if(a->flags & FLAG_METHOD) {
3021 $$.c = abc_findpropstrict2($$.c, &m);
3022 $$.c = abc_getproperty2($$.c, &m);
3023 if(a->function->kind == MEMBER_METHOD) {
3024 $$.t = TYPE_FUNCTION(a->function);
3026 $$.t = a->function->type;
3030 $$.c = abc_getglobalscope($$.c);
3031 $$.c = abc_getslot($$.c, a->slot);
3034 $$.c = abc_getlex2($$.c, &m);
3036 $$.t = TYPE_CLASS(a);
3041 /* unknown object, let the avm2 resolve it */
3043 if(strcmp($1,"trace"))
3044 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3045 state->method->late_binding = 1;
3047 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3050 $$.c = abc_findpropstrict2($$.c, &m);
3051 $$.c = abc_getproperty2($$.c, &m);
3056 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
3057 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
3058 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3060 // ----------------- namespaces -------------------------------------------------
3062 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3063 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3064 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3066 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {$$=0;}