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"
44 enum yytokentype token;
47 classinfo_t*classinfo;
48 classinfo_list_t*classinfo_list;
51 unsigned int number_uint;
55 //typedcode_list_t*value_list;
56 codeandnumber_t value_list;
62 for_start_t for_start;
63 abc_exception_t *exception;
66 abc_exception_list_t *l;
72 %token<id> T_IDENTIFIER
74 %token<regexp> T_REGEXP
76 %token<number_int> T_INT
77 %token<number_uint> T_UINT
78 %token<number_uint> T_BYTE
79 %token<number_uint> T_SHORT
80 %token<number_float> T_FLOAT
82 %token<id> T_FOR "for"
83 %token<id> T_WHILE "while"
85 %token<id> T_SWITCH "switch"
87 %token<token> KW_IMPLEMENTS "implements"
88 %token<token> KW_NAMESPACE "namespace"
89 %token<token> KW_PACKAGE "package"
90 %token<token> KW_PROTECTED "protected"
91 %token<token> KW_PUBLIC "public"
92 %token<token> KW_PRIVATE "private"
93 %token<token> KW_USE "use"
94 %token<token> KW_INTERNAL "internal"
95 %token<token> KW_NEW "new"
96 %token<token> KW_NATIVE "native"
97 %token<token> KW_FUNCTION "function"
98 %token<token> KW_FINALLY "finally"
99 %token<token> KW_UNDEFINED "undefined"
100 %token<token> KW_CONTINUE "continue"
101 %token<token> KW_CLASS "class"
102 %token<token> KW_CONST "const"
103 %token<token> KW_CATCH "catch"
104 %token<token> KW_CASE "case"
105 %token<token> KW_SET "set"
106 %token<token> KW_VOID "void"
107 %token<token> KW_THROW "throw"
108 %token<token> KW_STATIC "static"
109 %token<token> KW_WITH "with"
110 %token<token> KW_INSTANCEOF "instanceof"
111 %token<token> KW_IMPORT "import"
112 %token<token> KW_RETURN "return"
113 %token<token> KW_TYPEOF "typeof"
114 %token<token> KW_INTERFACE "interface"
115 %token<token> KW_NULL "null"
116 %token<token> KW_VAR "var"
117 %token<token> KW_DYNAMIC "dynamic"
118 %token<token> KW_OVERRIDE "override"
119 %token<token> KW_FINAL "final"
120 %token<token> KW_EACH "each"
121 %token<token> KW_GET "get"
122 %token<token> KW_TRY "try"
123 %token<token> KW_SUPER "super"
124 %token<token> KW_EXTENDS "extends"
125 %token<token> KW_FALSE "false"
126 %token<token> KW_TRUE "true"
127 %token<token> KW_BOOLEAN "Boolean"
128 %token<token> KW_UINT "uint"
129 %token<token> KW_INT "int"
130 %token<token> KW_NUMBER "Number"
131 %token<token> KW_STRING "String"
132 %token<token> KW_DEFAULT "default"
133 %token<token> KW_DELETE "delete"
134 %token<token> KW_IF "if"
135 %token<token> KW_ELSE "else"
136 %token<token> KW_BREAK "break"
137 %token<token> KW_IS "is"
138 %token<token> KW_IN "in"
139 %token<token> KW_AS "as"
141 %token<token> T_EQEQ "=="
142 %token<token> T_EQEQEQ "==="
143 %token<token> T_NE "!="
144 %token<token> T_NEE "!=="
145 %token<token> T_LE "<="
146 %token<token> T_GE ">="
147 %token<token> T_ORBY "|="
148 %token<token> T_DIVBY "/="
149 %token<token> T_MODBY "%="
150 %token<token> T_MULBY "*="
151 %token<token> T_PLUSBY "+="
152 %token<token> T_MINUSBY "-="
153 %token<token> T_SHRBY ">>="
154 %token<token> T_SHLBY "<<="
155 %token<token> T_USHRBY ">>>="
156 %token<token> T_OROR "||"
157 %token<token> T_ANDAND "&&"
158 %token<token> T_COLONCOLON "::"
159 %token<token> T_MINUSMINUS "--"
160 %token<token> T_PLUSPLUS "++"
161 %token<token> T_DOTDOT ".."
162 %token<token> T_DOTDOTDOT "..."
163 %token<token> T_SHL "<<"
164 %token<token> T_USHR ">>>"
165 %token<token> T_SHR ">>"
167 %type <for_start> FOR_START
168 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
169 %type <token> VARCONST
171 %type <code> CODEPIECE CODE_STATEMENT
172 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
173 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION
174 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
175 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
176 %type <exception> CATCH FINALLY
177 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
178 %type <code> CLASS_DECLARATION
179 %type <code> NAMESPACE_DECLARATION
180 %type <code> INTERFACE_DECLARATION
181 %type <code> VOIDEXPRESSION
182 %type <value> EXPRESSION NONCOMMAEXPRESSION
183 %type <value> MAYBEEXPRESSION
184 %type <value> E DELETE
185 %type <value> CONSTANT
186 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
187 %type <value> INNERFUNCTION
188 %type <token> USE_NAMESPACE
189 %type <code> FOR_INIT
191 %type <classinfo> MAYBETYPE
194 %type <params> PARAM_LIST
195 %type <params> MAYBE_PARAM_LIST
196 %type <flags> MAYBE_MODIFIERS
197 %type <flags> MODIFIER_LIST
198 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
199 %type <classinfo_list> IMPLEMENTS_LIST
200 %type <classinfo> EXTENDS
201 %type <classinfo_list> EXTENDS_LIST
202 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
203 %type <classinfo_list> QNAME_LIST
204 %type <classinfo> TYPE
205 //%type <token> VARIABLE
206 %type <value> VAR_READ
208 //%type <token> T_IDENTIFIER
209 %type <token> MODIFIER
210 %type <value> FUNCTIONCALL
211 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
213 // precedence: from low to high
217 %left below_semicolon
220 %nonassoc below_assignment // for ?:, contrary to spec
221 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
228 %nonassoc "==" "!=" "===" "!=="
229 %nonassoc "is" "as" "in"
230 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
231 %left "<<" ">>" ">>>"
235 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
237 %nonassoc below_curly
238 %left '[' ']' '{' "new" '.' ".." "::"
239 %nonassoc T_IDENTIFIER
240 %left above_identifier
245 // needed for "return" precedence:
246 %nonassoc T_STRING T_REGEXP
247 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
248 %nonassoc "false" "true" "null" "undefined" "super" "function"
249 %nonassoc above_function
255 static int a3_error(char*s)
257 syntaxerror("%s", s);
258 return 0; //make gcc happy
262 static char* concat2(const char* t1, const char* t2)
266 char*text = malloc(l1+l2+1);
267 memcpy(text , t1, l1);
268 memcpy(text+l1, t2, l2);
272 static char* concat3(const char* t1, const char* t2, const char* t3)
277 char*text = malloc(l1+l2+l3+1);
278 memcpy(text , t1, l1);
279 memcpy(text+l1, t2, l2);
280 memcpy(text+l1+l2, t3, l3);
285 typedef struct _import {
289 DECLARE_LIST(import);
291 typedef struct _classstate {
297 char has_constructor;
300 DECLARE_LIST(methodstate);
302 typedef struct _methodstate {
313 int var_index; // for inner methods
315 abc_exception_list_t*exceptions;
317 methodstate_list_t*innerfunctions;
320 typedef struct _state {
325 import_list_t*wildcard_imports;
327 char has_own_imports;
328 char new_vars; // e.g. transition between two functions
331 methodstate_t*method;
338 typedef struct _global {
344 static global_t*global = 0;
345 static state_t* state = 0;
349 #define MULTINAME(m,x) \
352 registry_fill_multiname(&m, &m##_ns, x);
354 #define MEMBER_MULTINAME(m,f,n) \
358 m##_ns = flags2namespace(f->flags, ""); \
361 m.namespace_set = 0; \
364 m.type = MULTINAME; \
366 m.namespace_set = &nopackage_namespace_set; \
370 /* warning: list length of namespace set is undefined */
371 #define MULTINAME_LATE(m, access, package) \
372 namespace_t m##_ns = {access, package}; \
373 namespace_set_t m##_nsset; \
374 namespace_list_t m##_l;m##_l.next = 0; \
375 m##_nsset.namespaces = &m##_l; \
376 m##_nsset = m##_nsset; \
377 m##_l.namespace = &m##_ns; \
378 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
380 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
381 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
382 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
383 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
384 static namespace_list_t nl4 = {&ns4,0};
385 static namespace_list_t nl3 = {&ns3,&nl4};
386 static namespace_list_t nl2 = {&ns2,&nl3};
387 static namespace_list_t nl1 = {&ns1,&nl2};
388 static namespace_set_t nopackage_namespace_set = {&nl1};
390 static void new_state()
393 state_t*oldstate = state;
395 memcpy(s, state, sizeof(state_t)); //shallow copy
397 s->imports = dict_new();
401 state->has_own_imports = 0;
402 state->vars = dict_new();
403 state->old = oldstate;
405 static void state_has_imports()
407 state->wildcard_imports = list_clone(state->wildcard_imports);
408 state->imports = dict_clone(state->imports);
409 state->has_own_imports = 1;
412 static void state_destroy(state_t*state)
414 if(state->has_own_imports) {
415 list_free(state->wildcard_imports);
416 dict_destroy(state->imports);state->imports=0;
418 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
419 dict_destroy(state->imports);state->imports=0;
423 for(t=0;t<state->vars->hashsize;t++) {
424 dictentry_t*e =state->vars->slots[t];
426 free(e->data);e->data=0;
430 dict_destroy(state->vars);state->vars=0;
436 static void old_state()
438 if(!state || !state->old)
439 syntaxerror("invalid nesting");
440 state_t*leaving = state;
444 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
445 free(leaving->method);
448 if(leaving->cls && leaving->cls != state->cls) {
453 state_destroy(leaving);
456 void initialize_file(char*filename)
459 state->package = filename;
461 state->method = rfx_calloc(sizeof(methodstate_t));
462 state->method->variable_count = 1;
467 if(!state || state->level!=1) {
468 syntaxerror("unexpected end of file in pass %d", as3_pass);
470 state_destroy(state);state=0;
473 void initialize_parser()
475 global = rfx_calloc(sizeof(global_t));
476 global->file = abc_file_new();
477 global->file->flags &= ~ABCFILE_LAZY;
478 global->token2info = dict_new2(&ptr_type);
480 global->init = abc_initscript(global->file);
481 code_t*c = global->init->method->body->code;
482 c = abc_getlocal_0(c);
483 c = abc_pushscope(c);
484 global->init->method->body->code = c;
487 void* finish_parser()
489 code_t*c = global->init->method->body->code;
490 /*c = abc_findpropstrict(c, "[package]::trace");
491 c = abc_pushstring(c, "[leaving global init function]");
492 c = abc_callpropvoid(c, "[package]::trace", 1);*/
493 c = abc_returnvoid(c);
494 global->init->method->body->code = c;
495 dict_destroy(global->token2info);global->token2info=0;
500 static void xx_scopetest()
502 /* findpropstrict doesn't just return a scope object- it
503 also makes it "active" somehow. Push local_0 on the
504 scope stack and read it back with findpropstrict, it'll
505 contain properties like "trace". Trying to find the same
506 property on a "vanilla" local_0 yields only a "undefined" */
507 //c = abc_findpropstrict(c, "[package]::trace");
509 /*c = abc_getlocal_0(c);
510 c = abc_findpropstrict(c, "[package]::trace");
512 c = abc_setlocal_1(c);
514 c = abc_pushbyte(c, 0);
515 c = abc_setlocal_2(c);
517 code_t*xx = c = abc_label(c);
518 c = abc_findpropstrict(c, "[package]::trace");
519 c = abc_pushstring(c, "prop:");
520 c = abc_hasnext2(c, 1, 2);
522 c = abc_setlocal_3(c);
523 c = abc_callpropvoid(c, "[package]::trace", 2);
524 c = abc_getlocal_3(c);
526 c = abc_iftrue(c,xx);*/
530 typedef struct _variable {
536 static variable_t* find_variable(char*name)
542 v = dict_lookup(s->vars, name);
552 static variable_t* find_variable_safe(char*name)
554 variable_t* v = find_variable(name);
556 syntaxerror("undefined variable: %s", name);
559 static char variable_exists(char*name)
561 return dict_lookup(state->vars, name)!=0;
563 code_t*defaultvalue(code_t*c, classinfo_t*type);
564 static int new_variable(const char*name, classinfo_t*type, char init)
567 v->index = state->method->variable_count;
571 dict_put(state->vars, name, v);
573 return state->method->variable_count++;
575 #define TEMPVARNAME "__as3_temp__"
576 static int gettempvar()
578 variable_t*v = find_variable(TEMPVARNAME);
581 return new_variable(TEMPVARNAME, 0, 0);
584 code_t* var_block(code_t*body)
590 for(t=0;t<state->vars->hashsize;t++) {
591 dictentry_t*e = state->vars->slots[t];
593 variable_t*v = (variable_t*)e->data;
594 if(v->type && v->init) {
595 c = defaultvalue(c, v->type);
596 c = abc_setlocal(c, v->index);
597 k = abc_kill(k, v->index);
607 if(x->opcode== OPCODE___BREAK__ ||
608 x->opcode== OPCODE___CONTINUE__) {
609 /* link kill code before break/continue */
610 code_t*e = code_dup(k);
611 code_t*s = code_start(e);
623 c = code_append(c, body);
624 c = code_append(c, k);
628 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
630 c = code_append(c, header);
631 c = code_append(c, var_block(body));
632 /* append return if necessary */
633 if(!c || (c->opcode != OPCODE_RETURNVOID &&
634 c->opcode != OPCODE_RETURNVALUE)) {
635 c = abc_returnvoid(c);
641 static void startpackage(char*name)
644 /*printf("entering package \"%s\"\n", name);*/
645 state->package = strdup(name);
647 static void endpackage()
649 /*printf("leaving package \"%s\"\n", state->package);*/
651 //used e.g. in classinfo_register:
652 //free(state->package);state->package=0;
657 #define _TRACE_ {printf("vfw: %s: %d (%s)\n",__FILE__,__LINE__,__func__);fflush(stdout);}
658 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
660 static void parsererror(const char*file, int line, const char*f)
662 syntaxerror("internal error in %s, %s:%d", f, file, line);
666 char*as3_globalclass=0;
667 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
670 syntaxerror("inner classes now allowed");
673 state->cls = rfx_calloc(sizeof(classstate_t));
674 state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor
675 state->method->variable_count = 1;
678 classinfo_list_t*mlist=0;
680 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
681 syntaxerror("invalid modifier(s)");
683 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
684 syntaxerror("public and internal not supported at the same time.");
686 /* create the class name, together with the proper attributes */
690 if(!(flags&FLAG_PUBLIC) && !state->package) {
691 access = ACCESS_PRIVATE; package = current_filename;
692 } else if(!(flags&FLAG_PUBLIC) && state->package) {
693 access = ACCESS_PACKAGEINTERNAL; package = state->package;
694 } else if(state->package) {
695 access = ACCESS_PACKAGE; package = state->package;
697 syntaxerror("public classes only allowed inside a package");
701 if(registry_findclass(package, classname)) {
702 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
704 /* build info struct */
705 int num_interfaces = (list_length(implements));
706 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
710 state->cls->info = registry_findclass(package, classname);
711 parserassert((int)state->cls->info);
713 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
714 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
716 classinfo_list_t*l = implements;
717 for(l=implements;l;l=l->next) {
718 state->cls->info->interfaces[pos++] = l->classinfo;
721 /* generate the abc code for this class */
722 MULTINAME(classname2,state->cls->info);
723 multiname_t*extends2 = sig2mname(extends);
725 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
726 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
727 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
729 state->cls->info->flags |= CLASS_INTERFACE;
730 abc_class_interface(state->cls->abc);
733 abc_class_protectedNS(state->cls->abc, classname);
735 for(mlist=implements;mlist;mlist=mlist->next) {
736 MULTINAME(m, mlist->classinfo);
737 abc_class_add_interface(state->cls->abc, &m);
740 /* write the construction code for this class to the global init
742 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
744 abc_method_body_t*m = global->init->method->body;
745 __ getglobalscope(m);
746 classinfo_t*s = extends;
751 //TODO: take a look at the current scope stack, maybe
752 // we can re-use something
757 multiname_t*s2 = sig2mname(s);
759 multiname_destroy(s2);
761 __ pushscope(m); count++;
762 m->code = m->code->prev->prev; // invert
764 /* continue appending after last op end */
765 while(m->code && m->code->next) m->code = m->code->next;
767 /* TODO: if this is one of *our* classes, we can also
768 do a getglobalscope/getslot <nr> (which references
769 the init function's slots) */
771 __ getlex2(m, extends2);
773 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
774 stack is not the superclass */
775 __ pushscope(m);count++;
778 /* notice: we get a verify error #1107 if the top element on the scope
779 stack is not the global object */
781 __ pushscope(m);count++;
783 __ newclass(m,state->cls->abc);
787 __ setslot(m, slotindex);
788 multiname_destroy(extends2);
790 /* flash.display.MovieClip handling */
792 if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
793 if(state->package && state->package[0]) {
794 as3_globalclass = concat3(state->package, ".", classname);
796 as3_globalclass = strdup(classname);
802 static void endclass()
805 if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
807 c = abc_getlocal_0(c);
808 c = abc_constructsuper(c, 0);
809 state->cls->init = code_append(state->cls->init, c);
811 if(!state->method->late_binding) {
812 // class initialization code uses late binding
814 c = abc_getlocal_0(c);
815 c = abc_pushscope(c);
816 state->cls->static_init = code_append(c, state->cls->static_init);
819 if(state->cls->init) {
820 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
821 m->body->code = wrap_function(0, state->cls->init, m->body->code);
823 if(state->cls->static_init) {
824 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
825 m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
832 void check_code_for_break(code_t*c)
835 if(c->opcode == OPCODE___BREAK__) {
836 char*name = string_cstr(c->data[0]);
837 syntaxerror("Unresolved \"break %s\"", name);
839 if(c->opcode == OPCODE___CONTINUE__) {
840 char*name = string_cstr(c->data[0]);
841 syntaxerror("Unresolved \"continue %s\"", name);
848 static void check_constant_against_type(classinfo_t*t, constant_t*c)
850 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
851 if(TYPE_IS_NUMBER(t)) {
852 xassert(c->type == CONSTANT_FLOAT
853 || c->type == CONSTANT_INT
854 || c->type == CONSTANT_UINT);
855 } else if(TYPE_IS_UINT(t)) {
856 xassert(c->type == CONSTANT_UINT ||
857 (c->type == CONSTANT_INT && c->i>0));
858 } else if(TYPE_IS_INT(t)) {
859 xassert(c->type == CONSTANT_INT);
860 } else if(TYPE_IS_BOOLEAN(t)) {
861 xassert(c->type == CONSTANT_TRUE
862 || c->type == CONSTANT_FALSE);
867 static int flags2access(int flags)
870 if(flags&FLAG_PUBLIC) {
871 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
872 syntaxerror("invalid combination of access levels");
873 access = ACCESS_PACKAGE;
874 } else if(flags&FLAG_PRIVATE) {
875 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
876 syntaxerror("invalid combination of access levels");
877 access = ACCESS_PRIVATE;
878 } else if(flags&FLAG_PROTECTED) {
879 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
880 syntaxerror("invalid combination of access levels");
881 access = ACCESS_PROTECTED;
883 access = ACCESS_PACKAGEINTERNAL;
889 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
891 memberinfo_t*minfo = 0;
894 minfo = memberinfo_register_global(flags2access(flags), state->package, name, MEMBER_METHOD);
895 minfo->return_type = return_type;
896 } else if(getset != KW_GET && getset != KW_SET) {
898 if((minfo = registry_findmember(state->cls->info, name, 0))) {
899 if(minfo->parent == state->cls->info) {
900 syntaxerror("class already contains a member/method called '%s'", name);
901 } else if(!minfo->parent) {
902 syntaxerror("internal error: overriding method %s, which doesn't have parent", name);
904 if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE)))
905 syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?");
908 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
909 minfo->return_type = return_type;
910 // getslot on a member slot only returns "undefined", so no need
911 // to actually store these
912 //state->minfo->slot = state->method->abc->method->trait->slot_id;
914 //class getter/setter
915 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
919 else if(params->list && params->list->param)
920 type = params->list->param->type;
921 // not sure wether to look into superclasses here, too
922 if((minfo=registry_findmember(state->cls->info, name, 0))) {
923 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
924 syntaxerror("class already contains a member or method called '%s'", name);
926 syntaxerror("getter/setter for '%s' already defined", name);
927 /* make a setter or getter into a getset */
932 if(type && minfo->type != type)
933 syntaxerror("different type in getter and setter");
935 minfo = memberinfo_register(state->cls->info, name, gs);
938 /* can't assign a slot as getter and setter might have different slots */
939 //minfo->slot = slot;
941 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
942 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
943 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
944 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
945 if(flags&FLAG_PACKAGEINTERNAL) minfo->flags |= FLAG_PACKAGEINTERNAL;
946 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
950 static void function_initvars(params_t*params, int flags)
952 if(state->method->inner)
953 new_variable("this", 0, 0);
954 else if(!state->method->is_global)
955 new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
957 new_variable("globalscope", 0, 0);
960 for(p=params->list;p;p=p->next) {
961 new_variable(p->param->name, p->param->type, 0);
964 methodstate_list_t*l = state->method->innerfunctions;
966 methodstate_t*m = l->methodstate;
967 m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0);
972 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
974 parserassert(state->method && state->method->info);
976 methodstate_t*parent_method = state->method;
988 state->method = rfx_calloc(sizeof(methodstate_t));
989 state->method->inner = 1;
990 state->method->variable_count = 0;
991 state->method->abc = rfx_calloc(sizeof(abc_method_t));
993 NEW(memberinfo_t,minfo);
995 state->method->info = minfo;
997 list_append(parent_method->innerfunctions, state->method);
999 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1003 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1004 parserassert(state->method);
1006 state->method->info->return_type = return_type;
1007 function_initvars(params, 0);
1011 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1012 params_t*params, classinfo_t*return_type)
1014 if(state->method && state->method->info) {
1015 syntaxerror("not able to start another method scope");
1020 state->method = rfx_calloc(sizeof(methodstate_t));
1021 state->method->has_super = 0;
1022 state->method->variable_count = 0;
1025 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1027 state->method->is_global = 1;
1028 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1030 if(state->method->is_constructor)
1031 name = "__as3_constructor__";
1034 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1036 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1040 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1041 parserassert(state->method);
1044 state->cls->has_constructor |= state->method->is_constructor;
1047 state->method->info->return_type = return_type;
1048 function_initvars(params, flags);
1052 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1053 params_t*params, classinfo_t*return_type, code_t*body)
1062 multiname_t*type2 = sig2mname(return_type);
1064 if(state->method->inner) {
1065 f = state->method->abc;
1066 abc_method_init(f, global->file, type2, 1);
1067 } else if(state->method->is_constructor) {
1068 f = abc_class_getconstructor(state->cls->abc, type2);
1069 } else if(!state->method->is_global) {
1070 namespace_t mname_ns = flags2namespace(flags, "");
1071 multiname_t mname = {QNAME, &mname_ns, 0, name};
1073 if(flags&FLAG_STATIC)
1074 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1076 f = abc_class_method(state->cls->abc, type2, &mname);
1077 slot = f->trait->slot_id;
1079 namespace_t mname_ns = flags2namespace(flags, state->package);
1080 multiname_t mname = {QNAME, &mname_ns, 0, name};
1082 f = abc_method_new(global->file, type2, 1);
1083 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1084 //abc_code_t*c = global->init->method->body->code;
1086 //flash doesn't seem to allow us to access function slots
1087 //state->method->info->slot = slot;
1089 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1090 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1091 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1092 if(params->varargs) f->flags |= METHOD_NEED_REST;
1096 for(p=params->list;p;p=p->next) {
1097 if(params->varargs && !p->next) {
1098 break; //varargs: omit last parameter in function signature
1100 multiname_t*m = sig2mname(p->param->type);
1101 list_append(f->parameters, m);
1102 if(p->param->value) {
1103 check_constant_against_type(p->param->type, p->param->value);
1104 opt=1;list_append(f->optional_parameters, p->param->value);
1106 syntaxerror("non-optional parameter not allowed after optional parameters");
1109 check_code_for_break(body);
1112 f->body->code = body;
1113 f->body->exceptions = state->method->exceptions;
1114 } else { //interface
1116 syntaxerror("interface methods can't have a method body");
1123 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1128 void breakjumpsto(code_t*c, char*name, code_t*jump)
1131 if(c->opcode == OPCODE___BREAK__) {
1132 string_t*name2 = c->data[0];
1133 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1134 c->opcode = OPCODE_JUMP;
1141 void continuejumpsto(code_t*c, char*name, code_t*jump)
1144 if(c->opcode == OPCODE___CONTINUE__) {
1145 string_t*name2 = c->data[0];
1146 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1147 c->opcode = OPCODE_JUMP;
1155 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1156 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1157 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1159 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1161 if(!type1 || !type2)
1162 return registry_getanytype();
1163 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1164 return registry_getanytype();
1167 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1176 return registry_getanytype();
1178 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1183 return abc_coerce_a(c);
1187 // cast an "any" type to a specific type. subject to
1188 // runtime exceptions
1189 return abc_coerce2(c, &m);
1192 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1193 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1194 // allow conversion between number types
1195 return abc_coerce2(c, &m);
1197 //printf("%s.%s\n", from.package, from.name);
1198 //printf("%s.%s\n", to.package, to.name);
1200 classinfo_t*supertype = from;
1202 if(supertype == to) {
1203 // target type is one of from's superclasses
1204 return abc_coerce2(c, &m);
1207 while(supertype->interfaces[t]) {
1208 if(supertype->interfaces[t]==to) {
1209 // target type is one of from's interfaces
1210 return abc_coerce2(c, &m);
1214 supertype = supertype->superclass;
1216 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1218 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1220 syntaxerror("can't convert type %s to %s", from->name, to->name);
1221 return 0; // make gcc happy
1224 code_t*defaultvalue(code_t*c, classinfo_t*type)
1226 if(TYPE_IS_INT(type)) {
1227 c = abc_pushbyte(c, 0);
1228 } else if(TYPE_IS_UINT(type)) {
1229 c = abc_pushuint(c, 0);
1230 } else if(TYPE_IS_FLOAT(type)) {
1232 } else if(TYPE_IS_BOOLEAN(type)) {
1233 c = abc_pushfalse(c);
1235 //c = abc_pushundefined(c);
1237 c = abc_pushnull(c);
1239 c = abc_coerce2(c, &m);
1244 char is_pushundefined(code_t*c)
1246 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1249 static classinfo_t* find_class(char*name)
1253 c = registry_findclass(state->package, name);
1256 /* try explicit imports */
1257 dictentry_t* e = dict_get_slot(state->imports, name);
1260 if(!strcmp(e->key, name)) {
1261 c = (classinfo_t*)e->data;
1267 /* try package.* imports */
1268 import_list_t*l = state->wildcard_imports;
1270 //printf("does package %s contain a class %s?\n", l->import->package, name);
1271 c = registry_findclass(l->import->package, name);
1276 /* try global package */
1277 c = registry_findclass("", name);
1280 /* try local "filename" package */
1281 c = registry_findclass(current_filename_short, name);
1287 static char is_getlocal(code_t*c)
1289 if(!c || c->prev || c->next)
1291 return(c->opcode == OPCODE_GETLOCAL
1292 || c->opcode == OPCODE_GETLOCAL_0
1293 || c->opcode == OPCODE_GETLOCAL_1
1294 || c->opcode == OPCODE_GETLOCAL_2
1295 || c->opcode == OPCODE_GETLOCAL_3);
1297 static int getlocalnr(code_t*c)
1299 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1300 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1301 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1302 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1303 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1304 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1308 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1312 [prefix code] [read instruction]
1316 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1319 if(in && in->opcode == OPCODE_COERCE_A) {
1320 in = code_cutlast(in);
1323 syntaxerror("internal error");
1325 /* chop off read instruction */
1329 prefix = r->prev;r->prev = 0;
1335 char use_temp_var = readbefore;
1337 /* generate the write instruction, and maybe append a dup to the prefix code */
1338 code_t* write = abc_nop(0);
1339 if(r->opcode == OPCODE_GETPROPERTY) {
1340 write->opcode = OPCODE_SETPROPERTY;
1341 multiname_t*m = (multiname_t*)r->data[0];
1342 write->data[0] = multiname_clone(m);
1343 if(m->type == QNAME || m->type == MULTINAME) {
1345 prefix = abc_dup(prefix); // we need the object, too
1348 } else if(m->type == MULTINAMEL) {
1350 /* dupping two values on the stack requires 5 operations and one register-
1351 couldn't adobe just have given us a dup2? */
1352 int temp = gettempvar();
1353 prefix = abc_setlocal(prefix, temp);
1354 prefix = abc_dup(prefix);
1355 prefix = abc_getlocal(prefix, temp);
1356 prefix = abc_swap(prefix);
1357 prefix = abc_getlocal(prefix, temp);
1359 prefix = abc_kill(prefix, temp);
1363 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1365 } else if(r->opcode == OPCODE_GETSLOT) {
1366 write->opcode = OPCODE_SETSLOT;
1367 write->data[0] = r->data[0];
1369 prefix = abc_dup(prefix); // we need the object, too
1372 } else if(r->opcode == OPCODE_GETLOCAL) {
1373 write->opcode = OPCODE_SETLOCAL;
1374 write->data[0] = r->data[0];
1375 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1376 write->opcode = OPCODE_SETLOCAL_0;
1377 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1378 write->opcode = OPCODE_SETLOCAL_1;
1379 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1380 write->opcode = OPCODE_SETLOCAL_2;
1381 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1382 write->opcode = OPCODE_SETLOCAL_3;
1385 syntaxerror("illegal lvalue: can't assign a value to this expression");
1392 /* with getproperty/getslot, we have to be extra careful not
1393 to execute the read code twice, as it might have side-effects
1394 (e.g. if the property is in fact a setter/getter combination)
1396 So read the value, modify it, and write it again,
1397 using prefix only once and making sure (by using a temporary
1398 register) that the return value is what we just wrote */
1399 temp = gettempvar();
1400 c = code_append(c, prefix);
1401 c = code_append(c, r);
1404 c = abc_setlocal(c, temp);
1406 c = code_append(c, middlepart);
1409 c = abc_setlocal(c, temp);
1411 c = code_append(c, write);
1412 c = abc_getlocal(c, temp);
1413 c = abc_kill(c, temp);
1415 /* if we're allowed to execute the read code twice *and*
1416 the middlepart doesn't modify the code, things are easier.
1418 code_t* r2 = code_dup(r);
1419 //c = code_append(c, prefix);
1420 parserassert(!prefix);
1421 c = code_append(c, r);
1422 c = code_append(c, middlepart);
1423 c = code_append(c, write);
1424 c = code_append(c, r2);
1427 /* even smaller version: overwrite the value without reading
1431 c = code_append(c, prefix);
1434 c = code_append(c, middlepart);
1435 c = code_append(c, write);
1436 c = code_append(c, r);
1438 temp = gettempvar();
1440 c = code_append(c, prefix);
1442 c = code_append(c, middlepart);
1444 c = abc_setlocal(c, temp);
1445 c = code_append(c, write);
1446 c = abc_getlocal(c, temp);
1447 c = abc_kill(c, temp);
1453 char is_break_or_jump(code_t*c)
1457 if(c->opcode == OPCODE_JUMP ||
1458 c->opcode == OPCODE___BREAK__ ||
1459 c->opcode == OPCODE___CONTINUE__ ||
1460 c->opcode == OPCODE_THROW ||
1461 c->opcode == OPCODE_RETURNVOID ||
1462 c->opcode == OPCODE_RETURNVALUE) {
1469 #define IS_FINALLY_TARGET(op) \
1470 ((op) == OPCODE___CONTINUE__ || \
1471 (op) == OPCODE___BREAK__ || \
1472 (op) == OPCODE_RETURNVOID || \
1473 (op) == OPCODE_RETURNVALUE || \
1474 (op) == OPCODE___RETHROW__)
1476 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1478 #define NEED_EXTRA_STACK_ARG
1479 code_t*finally_label = abc_nop(0);
1480 NEW(lookupswitch_t, l);
1486 code_t*prev = i->prev;
1487 if(IS_FINALLY_TARGET(i->opcode)) {
1490 if(i->opcode == OPCODE___RETHROW__ ||
1491 i->opcode == OPCODE_RETURNVALUE) {
1492 if(i->opcode == OPCODE___RETHROW__)
1493 i->opcode = OPCODE_THROW;
1495 p = abc_coerce_a(p);
1496 p = abc_setlocal(p, tempvar);
1498 p = abc_pushbyte(p, count++);
1499 p = abc_jump(p, finally_label);
1500 code_t*target = p = abc_label(p);
1501 #ifdef NEED_EXTRA_STACK_ARG
1505 p = abc_getlocal(p, tempvar);
1508 p->next = i;i->prev = p;
1509 list_append(l->targets, target);
1515 c = abc_pushbyte(c, -1);
1516 c = code_append(c, finally_label);
1517 c = code_append(c, finally);
1519 #ifdef NEED_EXTRA_STACK_ARG
1522 c = abc_lookupswitch(c, l);
1523 c = l->def = abc_label(c);
1524 #ifdef NEED_EXTRA_STACK_ARG
1531 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1535 code_t*prev = i->prev;
1536 if(IS_FINALLY_TARGET(i->opcode)) {
1537 if(i->opcode == OPCODE___RETHROW__)
1538 i->opcode = OPCODE_THROW;
1539 code_t*end = code_dup(finally);
1540 code_t*start = code_start(end);
1541 if(prev) prev->next = start;
1548 return code_append(c, finally);
1551 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1557 int num_insertion_points=0;
1559 if(IS_FINALLY_TARGET(i->opcode))
1560 num_insertion_points++;
1567 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1572 int simple_version_cost = (1+num_insertion_points)*code_size;
1573 int lookup_version_cost = 4*num_insertion_points + 5;
1575 if(cantdup || simple_version_cost > lookup_version_cost) {
1576 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1577 return insert_finally_lookup(c, finally, tempvar);
1579 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1580 return insert_finally_simple(c, finally, tempvar);
1584 #define PASS1 }} if(as3_pass == 1) {{
1585 #define PASS1END }} if(as3_pass == 2) {{
1586 #define PASS2 }} if(as3_pass == 2) {{
1587 #define PASS12 }} {{
1588 #define PASS12END }} if(as3_pass == 2) {{
1594 /* ------------ code blocks / statements ---------------- */
1596 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1598 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1599 PROGRAM_CODE_LIST: PROGRAM_CODE
1600 | PROGRAM_CODE_LIST PROGRAM_CODE
1602 PROGRAM_CODE: PACKAGE_DECLARATION
1603 | INTERFACE_DECLARATION
1605 | FUNCTION_DECLARATION
1610 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1611 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1612 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1614 INPACKAGE_CODE: INTERFACE_DECLARATION
1616 | FUNCTION_DECLARATION
1621 MAYBECODE: CODE {$$=$1;}
1622 MAYBECODE: {$$=code_new();}
1624 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1625 CODE: CODEPIECE {$$=$1;}
1627 // code which also may appear outside a method
1628 CODE_STATEMENT: IMPORT
1630 CODE_STATEMENT: FOR_IN
1631 CODE_STATEMENT: WHILE
1632 CODE_STATEMENT: DO_WHILE
1633 CODE_STATEMENT: SWITCH
1635 CODE_STATEMENT: WITH
1637 CODE_STATEMENT: VOIDEXPRESSION
1639 // code which may appear anywhere
1640 CODEPIECE: ';' {$$=0;}
1641 CODEPIECE: CODE_STATEMENT
1642 CODEPIECE: VARIABLE_DECLARATION
1648 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1649 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;}
1651 CODEBLOCK : '{' CODE '}' {$$=$2;}
1652 CODEBLOCK : '{' '}' {$$=0;}
1653 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1654 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1656 /* ------------ package init code ------------------- */
1658 PACKAGE_INITCODE: CODE_STATEMENT {
1659 code_t**cc = &global->init->method->body->code;
1660 *cc = code_append(*cc, $1);
1663 /* ------------ variables --------------------------- */
1665 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1666 | {$$.c=abc_pushundefined(0);
1670 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1671 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1673 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1674 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1676 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1678 if(variable_exists($1))
1679 syntaxerror("Variable %s already defined", $1);
1681 if(!is_subtype_of($3.t, $2)) {
1682 syntaxerror("Can't convert %s to %s", $3.t->name,
1686 int index = new_variable($1, $2, 1);
1689 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1691 $$ = converttype($$, $3.t, $2);
1692 $$ = abc_setlocal($$, index);
1694 $$ = defaultvalue(0, $2);
1695 $$ = abc_setlocal($$, index);
1698 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1700 $$ = abc_coerce_a($$);
1701 $$ = abc_setlocal($$, index);
1707 /* that's the default for a local register, anyway
1709 state->method->initcode = abc_pushundefined(state->method->initcode);
1710 state->method->initcode = abc_setlocal(state->method->initcode, index);
1712 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1715 /* ------------ control flow ------------------------- */
1717 MAYBEELSE: %prec below_else {$$ = code_new();}
1718 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1719 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1721 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1724 $$ = code_append($$, $4.c);
1725 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1727 $$ = code_append($$, $6);
1729 myjmp = $$ = abc_jump($$, 0);
1731 myif->branch = $$ = abc_nop($$);
1733 $$ = code_append($$, $7);
1734 myjmp->branch = $$ = abc_nop($$);
1740 FOR_INIT : {$$=code_new();}
1741 FOR_INIT : VARIABLE_DECLARATION
1742 FOR_INIT : VOIDEXPRESSION
1744 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
1745 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1746 $$=$2;new_variable($2,$3,1);
1748 FOR_IN_INIT : T_IDENTIFIER {
1752 FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
1753 FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
1755 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1756 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1758 $$ = code_append($$, $2);
1759 code_t*loopstart = $$ = abc_label($$);
1760 $$ = code_append($$, $4.c);
1761 code_t*myif = $$ = abc_iffalse($$, 0);
1762 $$ = code_append($$, $8);
1763 code_t*cont = $$ = abc_nop($$);
1764 $$ = code_append($$, $6);
1765 $$ = abc_jump($$, loopstart);
1766 code_t*out = $$ = abc_nop($$);
1767 breakjumpsto($$, $1.name, out);
1768 continuejumpsto($$, $1.name, cont);
1775 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
1776 variable_t*var = find_variable($2);
1777 char*tmp1name = concat2($2, "__tmp1__");
1778 int it = new_variable(tmp1name, TYPE_INT, 0);
1779 char*tmp2name = concat2($2, "__array__");
1780 int array = new_variable(tmp1name, 0, 0);
1783 $$ = code_append($$, $4.c);
1784 $$ = abc_coerce_a($$);
1785 $$ = abc_setlocal($$, array);
1786 $$ = abc_pushbyte($$, 0);
1787 $$ = abc_setlocal($$, it);
1789 code_t*loopstart = $$ = abc_label($$);
1791 $$ = abc_hasnext2($$, array, it);
1792 code_t*myif = $$ = abc_iffalse($$, 0);
1793 $$ = abc_getlocal($$, array);
1794 $$ = abc_getlocal($$, it);
1796 $$ = abc_nextname($$);
1798 $$ = abc_nextvalue($$);
1799 $$ = converttype($$, 0, var->type);
1800 $$ = abc_setlocal($$, var->index);
1802 $$ = code_append($$, $6);
1803 $$ = abc_jump($$, loopstart);
1805 code_t*out = $$ = abc_nop($$);
1806 breakjumpsto($$, $1.name, out);
1807 continuejumpsto($$, $1.name, loopstart);
1818 WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1822 code_t*myjmp = $$ = abc_jump($$, 0);
1823 code_t*loopstart = $$ = abc_label($$);
1824 $$ = code_append($$, $6);
1825 code_t*cont = $$ = abc_nop($$);
1826 myjmp->branch = cont;
1827 $$ = code_append($$, $4.c);
1828 $$ = abc_iftrue($$, loopstart);
1829 code_t*out = $$ = abc_nop($$);
1830 breakjumpsto($$, $1, out);
1831 continuejumpsto($$, $1, cont);
1837 DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
1839 code_t*loopstart = $$ = abc_label($$);
1840 $$ = code_append($$, $3);
1841 code_t*cont = $$ = abc_nop($$);
1842 $$ = code_append($$, $6.c);
1843 $$ = abc_iftrue($$, loopstart);
1844 code_t*out = $$ = abc_nop($$);
1845 breakjumpsto($$, $1, out);
1846 continuejumpsto($$, $1, cont);
1852 BREAK : "break" %prec prec_none {
1853 $$ = abc___break__(0, "");
1855 BREAK : "break" T_IDENTIFIER {
1856 $$ = abc___break__(0, $2);
1858 CONTINUE : "continue" %prec prec_none {
1859 $$ = abc___continue__(0, "");
1861 CONTINUE : "continue" T_IDENTIFIER {
1862 $$ = abc___continue__(0, $2);
1865 MAYBE_CASE_LIST : {$$=0;}
1866 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
1867 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
1868 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
1869 CASE_LIST: CASE {$$=$1;}
1870 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
1872 CASE: "case" E ':' MAYBECODE {
1874 $$ = code_append($$, $2.c);
1875 code_t*j = $$ = abc_ifne($$, 0);
1876 $$ = code_append($$, $4);
1877 if($$->opcode != OPCODE___BREAK__) {
1878 $$ = abc___fallthrough__($$, "");
1880 code_t*e = $$ = abc_nop($$);
1883 DEFAULT: "default" ':' MAYBECODE {
1886 SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
1888 $$ = code_append($$, $7);
1889 code_t*out = $$ = abc_pop($$);
1890 breakjumpsto($$, $1, out);
1892 code_t*c = $$,*lastblock=0;
1894 if(c->opcode == OPCODE_IFNE) {
1895 if(!c->next) syntaxerror("internal error in fallthrough handling");
1897 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
1899 c->opcode = OPCODE_JUMP;
1900 c->branch = lastblock;
1902 /* fall through end of switch */
1903 c->opcode = OPCODE_NOP;
1913 /* ------------ try / catch /finally ---------------- */
1915 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);}
1917 namespace_t name_ns = {ACCESS_PACKAGE, ""};
1918 multiname_t name = {QNAME, &name_ns, 0, $3};
1920 NEW(abc_exception_t, e)
1921 e->exc_type = sig2mname($4);
1922 e->var_name = multiname_clone(&name);
1926 int i = find_variable_safe($3)->index;
1927 e->target = c = abc_nop(0);
1928 c = abc_setlocal(c, i);
1929 c = code_append(c, $8);
1935 FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
1941 NEW(abc_exception_t, e)
1942 e->exc_type = 0; //all exceptions
1943 e->var_name = 0; //no name
1946 e->to = code_append(e->to, $4);
1952 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
1953 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
1954 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
1955 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
1959 list_append($$.l,$2);
1960 $$.finally = $2->to;$2->to=0;
1963 CATCH_FINALLY_LIST: FINALLY {
1967 list_append($$.l,$1);
1968 $$.finally = $1->to;$1->to=0;
1972 TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
1973 code_t*out = abc_nop(0);
1975 code_t*start = abc_nop(0);
1976 $$ = code_append(start, $4);
1977 if(!is_break_or_jump($4)) {
1978 $$ = abc_jump($$, out);
1980 code_t*end = $$ = abc_nop($$);
1984 tmp = new_variable("__finally__", 0, 0);
1986 abc_exception_list_t*l = $6.l;
1989 abc_exception_t*e = l->abc_exception;
1991 $$ = code_append($$, e->target);
1992 $$ = abc_jump($$, out);
1994 parserassert((ptroff_t)$6.finally);
1996 e->target = $$ = abc_nop($$);
1997 $$ = abc___rethrow__($$);
2005 $$ = code_append($$, out);
2007 $$ = insert_finally($$, $6.finally, tmp);
2009 list_concat(state->method->exceptions, $6.l);
2015 /* ------------ throw ------------------------------- */
2017 THROW : "throw" EXPRESSION {
2021 THROW : "throw" %prec prec_none {
2022 if(!state->exception_name)
2023 syntaxerror("re-throw only possible within a catch block");
2024 variable_t*v = find_variable(state->exception_name);
2026 $$=abc_getlocal($$, v->index);
2030 /* ------------ with -------------------------------- */
2032 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2034 $$ = abc_pushscope($$);
2035 $$ = code_append($$, $5);
2036 $$ = abc_popscope($$);
2039 /* ------------ packages and imports ---------------- */
2041 X_IDENTIFIER: T_IDENTIFIER
2042 | "package" {PASS12 $$="package";}
2044 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2045 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2047 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2048 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2049 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2050 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2052 IMPORT : "import" QNAME {
2055 syntaxerror("Couldn't import class\n");
2056 state_has_imports();
2057 dict_put(state->imports, c->name, c);
2060 IMPORT : "import" PACKAGE '.' '*' {
2063 state_has_imports();
2064 list_append(state->wildcard_imports, i);
2068 /* ------------ classes and interfaces (header) -------------- */
2070 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2071 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2072 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2073 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2075 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2076 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2077 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2078 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2079 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2080 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2081 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2082 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2083 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2085 EXTENDS : {$$=registry_getobjectclass();}
2086 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
2088 EXTENDS_LIST : {PASS12 $$=list_new();}
2089 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {PASS12 $$=$2;}
2091 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2092 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {PASS12 $$=$2;}
2094 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2095 EXTENDS IMPLEMENTS_LIST
2096 '{' {PASS12 startclass($1,$3,$4,$5, 0);}
2098 '}' {PASS12 endclass();$$=0;}
2100 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2102 '{' {PASS12 startclass($1,$3,0,$4,1);}
2103 MAYBE_INTERFACE_BODY
2104 '}' {PASS12 endclass();$$=0;}
2106 /* ------------ classes and interfaces (body) -------------- */
2109 MAYBE_CLASS_BODY : CLASS_BODY
2110 CLASS_BODY : CLASS_BODY_ITEM
2111 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2112 CLASS_BODY_ITEM : ';'
2113 CLASS_BODY_ITEM : SLOT_DECLARATION
2114 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2116 CLASS_BODY_ITEM : CODE_STATEMENT {
2117 code_t*c = state->cls->static_init;
2118 c = code_append(c, $1);
2119 state->cls->static_init = c;
2122 MAYBE_INTERFACE_BODY :
2123 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2124 INTERFACE_BODY : IDECLARATION
2125 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2127 IDECLARATION : "var" T_IDENTIFIER {
2128 syntaxerror("variable declarations not allowed in interfaces");
2130 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2133 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2134 syntaxerror("invalid method modifiers: interface methods always need to be public");
2136 startfunction(0,$1,$3,$4,&$6,$8);
2137 endfunction(0,$1,$3,$4,&$6,$8, 0);
2140 /* ------------ classes and interfaces (body, slots ) ------- */
2142 VARCONST: "var" | "const"
2144 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
2146 memberinfo_t* info = state->cls?
2147 memberinfo_register(state->cls->info, $3, MEMBER_SLOT):
2148 memberinfo_register_global(flags2access($1), state->package, $3, MEMBER_SLOT);
2151 info->flags = flags;
2154 namespace_t mname_ns = {flags2access(flags), ""};
2155 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2157 trait_list_t**traits;
2161 mname_ns.name = state->package;
2162 traits = &global->init->traits;
2163 code = &global->init->method->body->code;
2164 } else if(flags&FLAG_STATIC) {
2166 traits = &state->cls->abc->static_traits;
2167 code = &state->cls->static_init;
2169 // instance variable
2170 traits = &state->cls->abc->traits;
2171 code = &state->cls->init;
2177 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2179 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2181 info->slot = t->slot_id;
2183 /* initalization code (if needed) */
2185 if($5.c && !is_pushundefined($5.c)) {
2186 c = abc_getlocal_0(c);
2187 c = code_append(c, $5.c);
2188 c = converttype(c, $5.t, $4);
2189 c = abc_setslot(c, t->slot_id);
2192 *code = code_append(*code, c);
2195 t->kind= TRAIT_CONST;
2201 /* ------------ constants -------------------------------------- */
2203 MAYBESTATICCONSTANT: {$$=0;}
2204 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2206 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2207 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2208 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2209 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2210 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
2211 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2212 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2213 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2214 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2216 /* ------------ classes and interfaces (body, functions) ------- */
2218 // non-vararg version
2221 memset(&$$,0,sizeof($$));
2223 MAYBE_PARAM_LIST: PARAM_LIST {
2229 MAYBE_PARAM_LIST: "..." PARAM {
2231 memset(&$$,0,sizeof($$));
2233 list_append($$.list, $2);
2235 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2239 list_append($$.list, $4);
2243 PARAM_LIST: PARAM_LIST ',' PARAM {
2246 list_append($$.list, $3);
2250 memset(&$$,0,sizeof($$));
2251 list_append($$.list, $1);
2254 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2257 $$ = malloc(sizeof(param_t));
2262 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2265 $$ = malloc(sizeof(param_t));
2267 $$->type = TYPE_ANY;
2270 GETSET : "get" {$$=$1;}
2274 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2275 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2279 if(!state->method->info) syntaxerror("internal error");
2281 if(state->method->late_binding) {
2282 c = abc_getlocal_0(c);
2283 c = abc_pushscope(c);
2285 /*if(state->method->innerfunctions) {
2286 c = abc_newactivation(c);
2287 c = abc_pushscope(c);
2289 if(state->method->is_constructor && !state->method->has_super) {
2290 // call default constructor
2291 c = abc_getlocal_0(c);
2292 c = abc_constructsuper(c, 0);
2294 methodstate_list_t*l = state->method->innerfunctions;
2296 parserassert(l->methodstate->abc);
2297 c = abc_newfunction(c, l->methodstate->abc);
2298 c = abc_setlocal(c, l->methodstate->var_index);
2299 free(l->methodstate);l->methodstate=0;
2302 list_free(state->method->innerfunctions);
2303 state->method->innerfunctions = 0;
2305 c = wrap_function(c, 0, $11);
2307 endfunction(0,$1,$3,$4,&$6,$8,c);
2311 MAYBE_IDENTIFIER: T_IDENTIFIER
2312 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2313 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2314 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2318 memberinfo_t*f = state->method->info;
2319 if(!f) syntaxerror("internal error");
2322 c = wrap_function(c, 0, $9);
2324 int index = state->method->var_index;
2325 endfunction(0,0,0,$2,&$4,$6,c);
2327 $$.c = abc_getlocal(0, index);
2328 $$.t = TYPE_FUNCTION(f);
2332 /* ------------- package + class ids --------------- */
2334 CLASS: T_IDENTIFIER {
2337 /* try current package */
2338 $$ = find_class($1);
2339 if(!$$) syntaxerror("Could not find class %s\n", $1);
2342 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2345 $$ = registry_findclass($1, $3);
2346 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
2350 QNAME: PACKAGEANDCLASS
2353 QNAME_LIST : QNAME {PASS12 $$=list_new();list_append($$, $1);}
2354 QNAME_LIST : QNAME_LIST ',' QNAME {PASS12 $$=$1;list_append($$,$3);}
2356 TYPE : QNAME {$$=$1;}
2357 | '*' {$$=registry_getanytype();}
2358 | "void" {$$=registry_getanytype();}
2360 | "String" {$$=registry_getstringclass();}
2361 | "int" {$$=registry_getintclass();}
2362 | "uint" {$$=registry_getuintclass();}
2363 | "Boolean" {$$=registry_getbooleanclass();}
2364 | "Number" {$$=registry_getnumberclass();}
2367 MAYBETYPE: ':' TYPE {$$=$2;}
2370 /* ----------function calls, delete, constructor calls ------ */
2372 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2373 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2375 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2376 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2377 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2380 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
2382 $$.cc = code_append($1.cc, $3.c);
2387 if($2.c->opcode == OPCODE_CALL)
2388 $2.c->opcode = OPCODE_CONSTRUCT;
2389 else if($2.c->opcode == OPCODE_CALLPROPERTY)
2390 $2.c->opcode = OPCODE_CONSTRUCTPROP;
2392 as3_error("invalid argument to 'new'");
2395 NEW : "new" CLASS MAYBE_PARAM_VALUES {
2400 $$.c = abc_getglobalscope($$.c);
2401 $$.c = abc_getslot($$.c, $2->slot);
2403 $$.c = abc_findpropstrict2($$.c, &m);
2406 $$.c = code_append($$.c, $3.cc);
2409 $$.c = abc_construct($$.c, $3.len);
2411 $$.c = abc_constructprop2($$.c, &m, $3.len);
2415 /* TODO: use abc_call (for calling local variables),
2416 abc_callstatic (for calling own methods)
2419 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2422 if($$.c->opcode == OPCODE_COERCE_A) {
2423 $$.c = code_cutlast($$.c);
2425 code_t*paramcode = $3.cc;
2428 if($$.c->opcode == OPCODE_GETPROPERTY) {
2429 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2430 $$.c = code_cutlast($$.c);
2431 $$.c = code_append($$.c, paramcode);
2432 $$.c = abc_callproperty2($$.c, name, $3.len);
2433 multiname_destroy(name);
2434 } else if($$.c->opcode == OPCODE_GETSLOT) {
2435 int slot = (int)(ptroff_t)$$.c->data[0];
2436 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2437 if(t->kind!=TRAIT_METHOD) {
2438 //ok: flash allows to assign closures to members.
2440 multiname_t*name = t->name;
2441 $$.c = code_cutlast($$.c);
2442 $$.c = code_append($$.c, paramcode);
2443 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2444 $$.c = abc_callproperty2($$.c, name, $3.len);
2445 } else if($$.c->opcode == OPCODE_GETSUPER) {
2446 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2447 $$.c = code_cutlast($$.c);
2448 $$.c = code_append($$.c, paramcode);
2449 $$.c = abc_callsuper2($$.c, name, $3.len);
2450 multiname_destroy(name);
2452 $$.c = abc_getglobalscope($$.c);
2453 $$.c = code_append($$.c, paramcode);
2454 $$.c = abc_call($$.c, $3.len);
2459 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
2460 $$.t = $1.t->function->return_type;
2462 $$.c = abc_coerce_a($$.c);
2467 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2468 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2469 if(!state->method) syntaxerror("super() not allowed outside of a function");
2470 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2473 $$.c = abc_getlocal_0($$.c);
2475 $$.c = code_append($$.c, $3.cc);
2477 this is dependent on the control path, check this somewhere else
2478 if(state->method->has_super)
2479 syntaxerror("constructor may call super() only once");
2481 state->method->has_super = 1;
2483 $$.c = abc_constructsuper($$.c, $3.len);
2484 $$.c = abc_pushundefined($$.c);
2488 DELETE: "delete" E {
2490 if($$.c->opcode == OPCODE_COERCE_A) {
2491 $$.c = code_cutlast($$.c);
2493 multiname_t*name = 0;
2494 if($$.c->opcode == OPCODE_GETPROPERTY) {
2495 $$.c->opcode = OPCODE_DELETEPROPERTY;
2496 } else if($$.c->opcode == OPCODE_GETSLOT) {
2497 int slot = (int)(ptroff_t)$$.c->data[0];
2498 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
2499 $$.c = code_cutlast($$.c);
2500 $$.c = abc_deleteproperty2($$.c, name);
2502 $$.c = abc_getlocal_0($$.c);
2503 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2504 $$.c = abc_deleteproperty2($$.c, &m);
2506 $$.t = TYPE_BOOLEAN;
2509 RETURN: "return" %prec prec_none {
2510 $$ = abc_returnvoid(0);
2512 RETURN: "return" EXPRESSION {
2514 $$ = abc_returnvalue($$);
2517 // ----------------------- expression types -------------------------------------
2519 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2520 EXPRESSION : E %prec below_minus {$$ = $1;}
2521 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2523 $$.c = cut_last_push($$.c);
2524 $$.c = code_append($$.c,$3.c);
2527 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2528 $$=cut_last_push($1.c);
2531 // ----------------------- expression evaluation -------------------------------------
2533 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2534 //V : CONSTANT {$$ = 0;}
2536 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2537 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2538 //V : NEW {$$ = $1.c;}
2540 //V : DELETE {$$ = $1.c;}
2541 E : DELETE {$$ = $1;}
2547 namespace_t ns = {ACCESS_PACKAGE, ""};
2548 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2550 $$.c = abc_getlex2($$.c, &m);
2551 $$.c = abc_pushstring($$.c, $1.pattern);
2552 $$.c = abc_construct($$.c, 1);
2554 $$.c = abc_getlex2($$.c, &m);
2555 $$.c = abc_pushstring($$.c, $1.pattern);
2556 $$.c = abc_pushstring($$.c, $1.options);
2557 $$.c = abc_construct($$.c, 2);
2562 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2563 //MULTINAME(m, registry_getintclass());
2564 //$$.c = abc_coerce2($$.c, &m); // FIXME
2567 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2570 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2573 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2576 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2579 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
2582 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2585 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2586 $$.t = TYPE_BOOLEAN;
2588 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2589 $$.t = TYPE_BOOLEAN;
2591 CONSTANT : "null" {$$.c = abc_pushnull(0);
2595 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2596 $$.t = TYPE_BOOLEAN;
2598 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2599 $$.t = TYPE_BOOLEAN;
2601 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2602 $$.t = TYPE_BOOLEAN;
2604 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2605 $$.t = TYPE_BOOLEAN;
2607 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2608 $$.t = TYPE_BOOLEAN;
2610 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2611 $$.t = TYPE_BOOLEAN;
2613 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2614 $$.t = TYPE_BOOLEAN;
2616 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2617 $$.t = TYPE_BOOLEAN;
2620 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2622 $$.c = converttype($$.c, $1.t, $$.t);
2623 $$.c = abc_dup($$.c);
2624 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2625 $$.c = cut_last_push($$.c);
2626 $$.c = code_append($$.c,$3.c);
2627 $$.c = converttype($$.c, $3.t, $$.t);
2628 code_t*label = $$.c = abc_label($$.c);
2629 jmp->branch = label;
2632 $$.t = join_types($1.t, $3.t, 'A');
2633 /*printf("%08x:\n",$1.t);
2634 code_dump($1.c, 0, 0, "", stdout);
2635 printf("%08x:\n",$3.t);
2636 code_dump($3.c, 0, 0, "", stdout);
2637 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2639 $$.c = converttype($$.c, $1.t, $$.t);
2640 $$.c = abc_dup($$.c);
2641 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2642 $$.c = cut_last_push($$.c);
2643 $$.c = code_append($$.c,$3.c);
2644 $$.c = converttype($$.c, $3.t, $$.t);
2645 code_t*label = $$.c = abc_label($$.c);
2646 jmp->branch = label;
2649 E : '!' E {$$.c=$2.c;
2650 $$.c = abc_not($$.c);
2651 $$.t = TYPE_BOOLEAN;
2654 E : '~' E {$$.c=$2.c;
2655 $$.c = abc_bitnot($$.c);
2659 E : E '&' E {$$.c = code_append($1.c,$3.c);
2660 $$.c = abc_bitand($$.c);
2664 E : E '^' E {$$.c = code_append($1.c,$3.c);
2665 $$.c = abc_bitxor($$.c);
2669 E : E '|' E {$$.c = code_append($1.c,$3.c);
2670 $$.c = abc_bitor($$.c);
2674 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2675 $$.c = abc_rshift($$.c);
2678 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2679 $$.c = abc_urshift($$.c);
2682 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2683 $$.c = abc_lshift($$.c);
2687 E : E '/' E {$$.c = code_append($1.c,$3.c);
2688 $$.c = abc_divide($$.c);
2691 E : E '%' E {$$.c = code_append($1.c,$3.c);
2692 $$.c = abc_modulo($$.c);
2695 E : E '+' E {$$.c = code_append($1.c,$3.c);
2696 if(BOTH_INT($1.t, $3.t)) {
2697 $$.c = abc_add_i($$.c);
2700 $$.c = abc_add($$.c);
2701 $$.t = join_types($1.t,$3.t,'+');
2704 E : E '-' E {$$.c = code_append($1.c,$3.c);
2705 if(BOTH_INT($1.t,$3.t)) {
2706 $$.c = abc_subtract_i($$.c);
2709 $$.c = abc_subtract($$.c);
2713 E : E '*' E {$$.c = code_append($1.c,$3.c);
2714 if(BOTH_INT($1.t,$3.t)) {
2715 $$.c = abc_multiply_i($$.c);
2718 $$.c = abc_multiply($$.c);
2723 E : E "in" E {$$.c = code_append($1.c,$3.c);
2724 $$.c = abc_in($$.c);
2725 $$.t = TYPE_BOOLEAN;
2728 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2729 if(use_astype && TYPE_IS_CLASS($3.t)) {
2730 MULTINAME(m,$3.t->cls);
2731 $$.c = abc_astype2($1.c, &m);
2734 $$.c = code_append($1.c, $3.c);
2735 $$.c = abc_astypelate($$.c);
2740 E : E "instanceof" E
2741 {$$.c = code_append($1.c, $3.c);
2742 $$.c = abc_instanceof($$.c);
2743 $$.t = TYPE_BOOLEAN;
2746 E : E "is" E {$$.c = code_append($1.c, $3.c);
2747 $$.c = abc_istypelate($$.c);
2748 $$.t = TYPE_BOOLEAN;
2751 E : "typeof" '(' E ')' {
2753 $$.c = abc_typeof($$.c);
2758 $$.c = cut_last_push($2.c);
2759 $$.c = abc_pushundefined($$.c);
2763 E : "void" { $$.c = abc_pushundefined(0);
2767 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
2772 $$.c=abc_negate_i($$.c);
2775 $$.c=abc_negate($$.c);
2782 $$.c = code_append($$.c, $3.c);
2784 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
2785 $$.c = abc_getproperty2($$.c, &m);
2786 $$.t = 0; // array elements have unknown type
2789 E : '[' MAYBE_EXPRESSION_LIST ']' {
2791 $$.c = code_append($$.c, $2.cc);
2792 $$.c = abc_newarray($$.c, $2.len);
2793 $$.t = registry_getarrayclass();
2796 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
2797 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
2799 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2801 $$.cc = code_append($$.cc, $1.c);
2802 $$.cc = code_append($$.cc, $3.c);
2805 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2808 $$.cc = code_append($$.cc, $3.c);
2809 $$.cc = code_append($$.cc, $5.c);
2814 E : '{' MAYBE_EXPRPAIR_LIST '}' {
2816 $$.c = code_append($$.c, $2.cc);
2817 $$.c = abc_newobject($$.c, $2.len/2);
2818 $$.t = registry_getobjectclass();
2823 if(BOTH_INT($1.t,$3.t)) {
2824 c=abc_multiply_i(c);
2828 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
2829 $$.c = toreadwrite($1.c, c, 0, 0);
2834 code_t*c = abc_modulo($3.c);
2835 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
2836 $$.c = toreadwrite($1.c, c, 0, 0);
2840 code_t*c = abc_lshift($3.c);
2841 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
2842 $$.c = toreadwrite($1.c, c, 0, 0);
2846 code_t*c = abc_rshift($3.c);
2847 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
2848 $$.c = toreadwrite($1.c, c, 0, 0);
2852 code_t*c = abc_urshift($3.c);
2853 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
2854 $$.c = toreadwrite($1.c, c, 0, 0);
2858 code_t*c = abc_divide($3.c);
2859 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
2860 $$.c = toreadwrite($1.c, c, 0, 0);
2864 code_t*c = abc_bitor($3.c);
2865 c=converttype(c, TYPE_INT, $1.t);
2866 $$.c = toreadwrite($1.c, c, 0, 0);
2872 if(TYPE_IS_INT($1.t)) {
2876 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
2879 $$.c = toreadwrite($1.c, c, 0, 0);
2882 E : E "-=" E { code_t*c = $3.c;
2883 if(TYPE_IS_INT($1.t)) {
2884 c=abc_subtract_i(c);
2887 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2890 $$.c = toreadwrite($1.c, c, 0, 0);
2893 E : E '=' E { code_t*c = 0;
2894 c = code_append(c, $3.c);
2895 c = converttype(c, $3.t, $1.t);
2896 $$.c = toreadwrite($1.c, c, 1, 0);
2900 E : E '?' E ':' E %prec below_assignment {
2901 $$.t = join_types($3.t,$5.t,'?');
2903 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2904 $$.c = code_append($$.c, $3.c);
2905 $$.c = converttype($$.c, $3.t, $$.t);
2906 code_t*j2 = $$.c = abc_jump($$.c, 0);
2907 $$.c = j1->branch = abc_label($$.c);
2908 $$.c = code_append($$.c, $5.c);
2909 $$.c = converttype($$.c, $3.t, $$.t);
2910 $$.c = j2->branch = abc_label($$.c);
2913 E : E "++" { code_t*c = 0;
2914 classinfo_t*type = $1.t;
2915 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
2916 int nr = getlocalnr($1.c);
2917 code_free($1.c);$1.c=0;
2918 if(TYPE_IS_INT($1.t)) {
2919 $$.c = abc_getlocal(0, nr);
2920 $$.c = abc_inclocal_i($$.c, nr);
2921 } else if(TYPE_IS_NUMBER($1.t)) {
2922 $$.c = abc_getlocal(0, nr);
2923 $$.c = abc_inclocal($$.c, nr);
2924 } else syntaxerror("internal error");
2926 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2927 c=abc_increment_i(c);
2933 c=converttype(c, type, $1.t);
2934 $$.c = toreadwrite($1.c, c, 0, 1);
2939 // TODO: use inclocal, like with ++
2940 E : E "--" { code_t*c = 0;
2941 classinfo_t*type = $1.t;
2942 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2943 c=abc_decrement_i(c);
2949 c=converttype(c, type, $1.t);
2950 $$.c = toreadwrite($1.c, c, 0, 1);
2954 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2955 classinfo_t*type = $2.t;
2956 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2957 c=abc_increment_i(c);
2963 c=converttype(c, type, $2.t);
2964 $$.c = toreadwrite($2.c, c, 0, 0);
2968 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2969 classinfo_t*type = $2.t;
2970 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2971 c=abc_decrement_i(c);
2977 c=converttype(c, type, $2.t);
2978 $$.c = toreadwrite($2.c, c, 0, 0);
2982 E : "super" '.' T_IDENTIFIER
2983 { if(!state->cls->info)
2984 syntaxerror("super keyword not allowed outside a class");
2985 classinfo_t*t = state->cls->info->superclass;
2986 if(!t) t = TYPE_OBJECT;
2988 memberinfo_t*f = registry_findmember(t, $3, 1);
2989 namespace_t ns = flags2namespace(f->flags, "");
2990 MEMBER_MULTINAME(m, f, $3);
2992 $$.c = abc_getlocal_0($$.c);
2993 $$.c = abc_getsuper2($$.c, &m);
2994 $$.t = memberinfo_gettype(f);
2997 E : E '.' T_IDENTIFIER
2999 classinfo_t*t = $1.t;
3001 if(TYPE_IS_CLASS(t) && t->cls) {
3006 memberinfo_t*f = registry_findmember(t, $3, 1);
3008 if(f && !is_static != !(f->flags&FLAG_STATIC))
3010 if(f && f->slot && !noslot) {
3011 $$.c = abc_getslot($$.c, f->slot);
3013 MEMBER_MULTINAME(m, f, $3);
3014 $$.c = abc_getproperty2($$.c, &m);
3016 /* determine type */
3017 $$.t = memberinfo_gettype(f);
3019 $$.c = abc_coerce_a($$.c);
3021 /* when resolving a property on an unknown type, we do know the
3022 name of the property (and don't seem to need the package), but
3023 we need to make avm2 try out all access modes */
3024 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3025 $$.c = abc_getproperty2($$.c, &m);
3026 $$.c = abc_coerce_a($$.c);
3027 $$.t = registry_getanytype();
3031 VAR_READ : T_IDENTIFIER {
3038 /* look at variables */
3039 if((v = find_variable($1))) {
3040 // $1 is a local variable
3041 $$.c = abc_getlocal($$.c, v->index);
3046 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3048 /* look at current class' members */
3049 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
3050 (f->flags&FLAG_STATIC) >= i_am_static) {
3051 // $1 is a function in this class
3052 int var_is_static = (f->flags&FLAG_STATIC);
3054 if(f->kind == MEMBER_METHOD) {
3055 $$.t = TYPE_FUNCTION(f);
3059 if(var_is_static && !i_am_static) {
3060 /* access to a static member from a non-static location.
3061 do this via findpropstrict:
3062 there doesn't seem to be any non-lookup way to access
3063 static properties of a class */
3064 state->method->late_binding = 1;
3066 namespace_t ns = {flags2access(f->flags), ""};
3067 multiname_t m = {QNAME, &ns, 0, $1};
3068 $$.c = abc_findpropstrict2($$.c, &m);
3069 $$.c = abc_getproperty2($$.c, &m);
3071 } else if(f->slot>0) {
3072 $$.c = abc_getlocal_0($$.c);
3073 $$.c = abc_getslot($$.c, f->slot);
3076 namespace_t ns = {flags2access(f->flags), ""};
3077 multiname_t m = {QNAME, &ns, 0, $1};
3078 $$.c = abc_getlocal_0($$.c);
3079 $$.c = abc_getproperty2($$.c, &m);
3084 /* look at actual classes, in the current package and imported */
3085 if((a = find_class($1))) {
3086 if(a->flags & FLAG_METHOD) {
3088 $$.c = abc_findpropstrict2($$.c, &m);
3089 $$.c = abc_getproperty2($$.c, &m);
3090 if(a->function->kind == MEMBER_METHOD) {
3091 $$.t = TYPE_FUNCTION(a->function);
3093 $$.t = a->function->type;
3097 $$.c = abc_getglobalscope($$.c);
3098 $$.c = abc_getslot($$.c, a->slot);
3101 $$.c = abc_getlex2($$.c, &m);
3103 $$.t = TYPE_CLASS(a);
3108 /* unknown object, let the avm2 resolve it */
3110 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3111 state->method->late_binding = 1;
3113 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3116 $$.c = abc_findpropstrict2($$.c, &m);
3117 $$.c = abc_getproperty2($$.c, &m);
3122 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
3123 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
3124 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3126 // ----------------- namespaces -------------------------------------------------
3128 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3129 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3130 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3132 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {$$=0;}