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
241 %left '[' ']' "new" '{' '.' ".." "::"
243 %nonassoc T_IDENTIFIER
244 %left above_identifier
248 // needed for "return" precedence:
249 %nonassoc T_STRING T_REGEXP
250 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
251 %nonassoc "false" "true" "null" "undefined" "super" "function"
258 static int a3_error(char*s)
260 syntaxerror("%s", s);
261 return 0; //make gcc happy
265 static char* concat2(const char* t1, const char* t2)
269 char*text = malloc(l1+l2+1);
270 memcpy(text , t1, l1);
271 memcpy(text+l1, t2, l2);
275 static char* concat3(const char* t1, const char* t2, const char* t3)
280 char*text = malloc(l1+l2+l3+1);
281 memcpy(text , t1, l1);
282 memcpy(text+l1, t2, l2);
283 memcpy(text+l1+l2, t3, l3);
288 typedef struct _import {
292 DECLARE_LIST(import);
294 typedef struct _classstate {
300 char has_constructor;
303 DECLARE_LIST(methodstate);
305 typedef struct _methodstate {
316 int var_index; // for inner methods
318 abc_exception_list_t*exceptions;
320 methodstate_list_t*innerfunctions;
323 typedef struct _state {
328 import_list_t*wildcard_imports;
330 char has_own_imports;
331 char new_vars; // e.g. transition between two functions
334 methodstate_t*method;
341 typedef struct _global {
347 static global_t*global = 0;
348 static state_t* state = 0;
352 #define MULTINAME(m,x) \
355 registry_fill_multiname(&m, &m##_ns, x);
357 #define MEMBER_MULTINAME(m,f,n) \
361 m##_ns = flags2namespace(f->flags, ""); \
364 m.namespace_set = 0; \
367 m.type = MULTINAME; \
369 m.namespace_set = &nopackage_namespace_set; \
373 /* warning: list length of namespace set is undefined */
374 #define MULTINAME_LATE(m, access, package) \
375 namespace_t m##_ns = {access, package}; \
376 namespace_set_t m##_nsset; \
377 namespace_list_t m##_l;m##_l.next = 0; \
378 m##_nsset.namespaces = &m##_l; \
379 m##_nsset = m##_nsset; \
380 m##_l.namespace = &m##_ns; \
381 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
383 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
384 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
385 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
386 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
387 static namespace_list_t nl4 = {&ns4,0};
388 static namespace_list_t nl3 = {&ns3,&nl4};
389 static namespace_list_t nl2 = {&ns2,&nl3};
390 static namespace_list_t nl1 = {&ns1,&nl2};
391 static namespace_set_t nopackage_namespace_set = {&nl1};
393 static void new_state()
396 state_t*oldstate = state;
398 memcpy(s, state, sizeof(state_t)); //shallow copy
400 s->imports = dict_new();
404 state->has_own_imports = 0;
405 state->vars = dict_new();
406 state->old = oldstate;
408 static void state_has_imports()
410 state->wildcard_imports = list_clone(state->wildcard_imports);
411 state->imports = dict_clone(state->imports);
412 state->has_own_imports = 1;
415 static void state_destroy(state_t*state)
417 if(state->has_own_imports) {
418 list_free(state->wildcard_imports);
419 dict_destroy(state->imports);state->imports=0;
421 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
422 dict_destroy(state->imports);state->imports=0;
426 for(t=0;t<state->vars->hashsize;t++) {
427 dictentry_t*e =state->vars->slots[t];
429 free(e->data);e->data=0;
433 dict_destroy(state->vars);state->vars=0;
439 static void old_state()
441 if(!state || !state->old)
442 syntaxerror("invalid nesting");
443 state_t*leaving = state;
447 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
448 free(leaving->method);
451 if(leaving->cls && leaving->cls != state->cls) {
456 state_destroy(leaving);
459 void initialize_file(char*filename)
462 state->package = strdup(filename);
464 state->method = rfx_calloc(sizeof(methodstate_t));
465 state->method->variable_count = 1;
470 if(!state || state->level!=1) {
471 syntaxerror("unexpected end of file in pass %d", as3_pass);
473 free(state->package);state->package=0;
474 state_destroy(state);state=0;
477 void initialize_parser()
479 global = rfx_calloc(sizeof(global_t));
480 global->file = abc_file_new();
481 global->file->flags &= ~ABCFILE_LAZY;
482 global->token2info = dict_new2(&ptr_type);
484 global->init = abc_initscript(global->file);
485 code_t*c = global->init->method->body->code;
486 c = abc_getlocal_0(c);
487 c = abc_pushscope(c);
488 global->init->method->body->code = c;
491 void* finish_parser()
493 code_t*c = global->init->method->body->code;
494 /*c = abc_findpropstrict(c, "[package]::trace");
495 c = abc_pushstring(c, "[leaving global init function]");
496 c = abc_callpropvoid(c, "[package]::trace", 1);*/
497 c = abc_returnvoid(c);
498 global->init->method->body->code = c;
499 dict_destroy(global->token2info);global->token2info=0;
504 static void xx_scopetest()
506 /* findpropstrict doesn't just return a scope object- it
507 also makes it "active" somehow. Push local_0 on the
508 scope stack and read it back with findpropstrict, it'll
509 contain properties like "trace". Trying to find the same
510 property on a "vanilla" local_0 yields only a "undefined" */
511 //c = abc_findpropstrict(c, "[package]::trace");
513 /*c = abc_getlocal_0(c);
514 c = abc_findpropstrict(c, "[package]::trace");
516 c = abc_setlocal_1(c);
518 c = abc_pushbyte(c, 0);
519 c = abc_setlocal_2(c);
521 code_t*xx = c = abc_label(c);
522 c = abc_findpropstrict(c, "[package]::trace");
523 c = abc_pushstring(c, "prop:");
524 c = abc_hasnext2(c, 1, 2);
526 c = abc_setlocal_3(c);
527 c = abc_callpropvoid(c, "[package]::trace", 2);
528 c = abc_getlocal_3(c);
530 c = abc_iftrue(c,xx);*/
534 typedef struct _variable {
540 static variable_t* find_variable(char*name)
546 v = dict_lookup(s->vars, name);
556 static variable_t* find_variable_safe(char*name)
558 variable_t* v = find_variable(name);
560 syntaxerror("undefined variable: %s", name);
563 static char variable_exists(char*name)
565 return dict_lookup(state->vars, name)!=0;
567 code_t*defaultvalue(code_t*c, classinfo_t*type);
568 static int new_variable(const char*name, classinfo_t*type, char init)
571 v->index = state->method->variable_count;
575 dict_put(state->vars, name, v);
577 return state->method->variable_count++;
579 #define TEMPVARNAME "__as3_temp__"
580 static int gettempvar()
582 variable_t*v = find_variable(TEMPVARNAME);
585 return new_variable(TEMPVARNAME, 0, 0);
588 code_t* var_block(code_t*body)
594 for(t=0;t<state->vars->hashsize;t++) {
595 dictentry_t*e = state->vars->slots[t];
597 variable_t*v = (variable_t*)e->data;
598 if(v->type && v->init) {
599 c = defaultvalue(c, v->type);
600 c = abc_setlocal(c, v->index);
601 k = abc_kill(k, v->index);
611 if(x->opcode== OPCODE___BREAK__ ||
612 x->opcode== OPCODE___CONTINUE__) {
613 /* link kill code before break/continue */
614 code_t*e = code_dup(k);
615 code_t*s = code_start(e);
627 c = code_append(c, body);
628 c = code_append(c, k);
632 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
634 static void parsererror(const char*file, int line, const char*f)
636 syntaxerror("internal error in %s, %s:%d", f, file, line);
640 code_t* method_header()
643 if(state->method->late_binding && !state->method->inner) {
644 c = abc_getlocal_0(c);
645 c = abc_pushscope(c);
647 /*if(state->method->innerfunctions) {
648 c = abc_newactivation(c);
649 c = abc_pushscope(c);
651 if(state->method->is_constructor && !state->method->has_super) {
652 // call default constructor
653 c = abc_getlocal_0(c);
654 c = abc_constructsuper(c, 0);
656 methodstate_list_t*l = state->method->innerfunctions;
658 parserassert(l->methodstate->abc);
659 c = abc_newfunction(c, l->methodstate->abc);
660 c = abc_setlocal(c, l->methodstate->var_index);
661 free(l->methodstate);l->methodstate=0;
664 list_free(state->method->innerfunctions);
665 state->method->innerfunctions = 0;
670 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
672 c = code_append(c, header);
673 c = code_append(c, var_block(body));
674 /* append return if necessary */
675 if(!c || (c->opcode != OPCODE_RETURNVOID &&
676 c->opcode != OPCODE_RETURNVALUE)) {
677 c = abc_returnvoid(c);
683 static void startpackage(char*name)
686 /*printf("entering package \"%s\"\n", name);*/
687 state->package = strdup(name);
689 static void endpackage()
691 /*printf("leaving package \"%s\"\n", state->package);*/
693 //used e.g. in classinfo_register:
694 //free(state->package);state->package=0;
700 char*as3_globalclass=0;
701 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
704 syntaxerror("inner classes now allowed");
707 state->cls = rfx_calloc(sizeof(classstate_t));
708 state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor
709 state->method->variable_count = 1;
712 classinfo_list_t*mlist=0;
714 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
715 syntaxerror("invalid modifier(s)");
717 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
718 syntaxerror("public and internal not supported at the same time.");
720 /* create the class name, together with the proper attributes */
724 if(!(flags&FLAG_PUBLIC) && !state->package) {
725 access = ACCESS_PRIVATE; package = current_filename;
726 } else if(!(flags&FLAG_PUBLIC) && state->package) {
727 access = ACCESS_PACKAGEINTERNAL; package = state->package;
728 } else if(state->package) {
729 access = ACCESS_PACKAGE; package = state->package;
731 syntaxerror("public classes only allowed inside a package");
735 if(registry_findclass(package, classname)) {
736 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
738 /* build info struct */
739 int num_interfaces = (list_length(implements));
740 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
744 state->cls->info = registry_findclass(package, classname);
745 parserassert((int)state->cls->info);
747 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
748 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
750 classinfo_list_t*l = implements;
751 for(l=implements;l;l=l->next) {
752 state->cls->info->interfaces[pos++] = l->classinfo;
755 /* generate the abc code for this class */
756 MULTINAME(classname2,state->cls->info);
757 multiname_t*extends2 = sig2mname(extends);
759 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
760 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
761 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
763 state->cls->info->flags |= CLASS_INTERFACE;
764 abc_class_interface(state->cls->abc);
767 abc_class_protectedNS(state->cls->abc, classname);
769 for(mlist=implements;mlist;mlist=mlist->next) {
770 MULTINAME(m, mlist->classinfo);
771 abc_class_add_interface(state->cls->abc, &m);
774 /* write the construction code for this class to the global init
776 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
778 abc_method_body_t*m = global->init->method->body;
779 __ getglobalscope(m);
780 classinfo_t*s = extends;
785 //TODO: take a look at the current scope stack, maybe
786 // we can re-use something
791 multiname_t*s2 = sig2mname(s);
793 multiname_destroy(s2);
795 __ pushscope(m); count++;
796 m->code = m->code->prev->prev; // invert
798 /* continue appending after last op end */
799 while(m->code && m->code->next) m->code = m->code->next;
801 /* TODO: if this is one of *our* classes, we can also
802 do a getglobalscope/getslot <nr> (which references
803 the init function's slots) */
805 __ getlex2(m, extends2);
807 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
808 stack is not the superclass */
809 __ pushscope(m);count++;
812 /* notice: we get a verify error #1107 if the top element on the scope
813 stack is not the global object */
815 __ pushscope(m);count++;
817 __ newclass(m,state->cls->abc);
821 __ setslot(m, slotindex);
822 multiname_destroy(extends2);
824 /* flash.display.MovieClip handling */
826 if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
827 if(state->package && state->package[0]) {
828 as3_globalclass = concat3(state->package, ".", classname);
830 as3_globalclass = strdup(classname);
836 static void endclass()
839 if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
841 c = abc_getlocal_0(c);
842 c = abc_constructsuper(c, 0);
843 state->cls->init = code_append(state->cls->init, c);
845 if(!state->method->late_binding) {
846 // class initialization code uses late binding
848 c = abc_getlocal_0(c);
849 c = abc_pushscope(c);
850 state->cls->static_init = code_append(c, state->cls->static_init);
853 if(state->cls->init) {
854 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
855 m->body->code = wrap_function(0, state->cls->init, m->body->code);
857 if(state->cls->static_init) {
858 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
859 m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
866 void check_code_for_break(code_t*c)
869 if(c->opcode == OPCODE___BREAK__) {
870 char*name = string_cstr(c->data[0]);
871 syntaxerror("Unresolved \"break %s\"", name);
873 if(c->opcode == OPCODE___CONTINUE__) {
874 char*name = string_cstr(c->data[0]);
875 syntaxerror("Unresolved \"continue %s\"", name);
882 static void check_constant_against_type(classinfo_t*t, constant_t*c)
884 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
885 if(TYPE_IS_NUMBER(t)) {
886 xassert(c->type == CONSTANT_FLOAT
887 || c->type == CONSTANT_INT
888 || c->type == CONSTANT_UINT);
889 } else if(TYPE_IS_UINT(t)) {
890 xassert(c->type == CONSTANT_UINT ||
891 (c->type == CONSTANT_INT && c->i>0));
892 } else if(TYPE_IS_INT(t)) {
893 xassert(c->type == CONSTANT_INT);
894 } else if(TYPE_IS_BOOLEAN(t)) {
895 xassert(c->type == CONSTANT_TRUE
896 || c->type == CONSTANT_FALSE);
901 static int flags2access(int flags)
904 if(flags&FLAG_PUBLIC) {
905 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
906 syntaxerror("invalid combination of access levels");
907 access = ACCESS_PACKAGE;
908 } else if(flags&FLAG_PRIVATE) {
909 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
910 syntaxerror("invalid combination of access levels");
911 access = ACCESS_PRIVATE;
912 } else if(flags&FLAG_PROTECTED) {
913 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
914 syntaxerror("invalid combination of access levels");
915 access = ACCESS_PROTECTED;
917 access = ACCESS_PACKAGEINTERNAL;
923 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
925 memberinfo_t*minfo = 0;
928 minfo = memberinfo_register_global(flags2access(flags), state->package, name, MEMBER_METHOD);
929 minfo->return_type = return_type;
930 } else if(getset != KW_GET && getset != KW_SET) {
932 if((minfo = registry_findmember(state->cls->info, name, 0))) {
933 if(minfo->parent == state->cls->info) {
934 syntaxerror("class already contains a member/method called '%s'", name);
935 } else if(!minfo->parent) {
936 syntaxerror("internal error: overriding method %s, which doesn't have parent", name);
938 if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE)))
939 syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?");
942 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
943 minfo->return_type = return_type;
944 // getslot on a member slot only returns "undefined", so no need
945 // to actually store these
946 //state->minfo->slot = state->method->abc->method->trait->slot_id;
948 //class getter/setter
949 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
953 else if(params->list && params->list->param)
954 type = params->list->param->type;
955 // not sure wether to look into superclasses here, too
956 if((minfo=registry_findmember(state->cls->info, name, 0))) {
957 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
958 syntaxerror("class already contains a member or method called '%s'", name);
960 syntaxerror("getter/setter for '%s' already defined", name);
961 /* make a setter or getter into a getset */
966 if(type && minfo->type != type)
967 syntaxerror("different type in getter and setter");
969 minfo = memberinfo_register(state->cls->info, name, gs);
972 /* can't assign a slot as getter and setter might have different slots */
973 //minfo->slot = slot;
975 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
976 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
977 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
978 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
979 if(flags&FLAG_PACKAGEINTERNAL) minfo->flags |= FLAG_PACKAGEINTERNAL;
980 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
984 static void function_initvars(params_t*params, int flags)
986 if(state->method->inner)
987 new_variable("this", 0, 0);
988 else if(!state->method->is_global)
989 new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
991 new_variable("globalscope", 0, 0);
994 for(p=params->list;p;p=p->next) {
995 new_variable(p->param->name, p->param->type, 0);
998 methodstate_list_t*l = state->method->innerfunctions;
1000 methodstate_t*m = l->methodstate;
1001 m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0);
1006 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1008 parserassert(state->method && state->method->info);
1010 methodstate_t*parent_method = state->method;
1019 state->new_vars = 1;
1022 state->method = rfx_calloc(sizeof(methodstate_t));
1023 state->method->inner = 1;
1024 state->method->variable_count = 0;
1025 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1027 NEW(memberinfo_t,minfo);
1029 state->method->info = minfo;
1031 list_append(parent_method->innerfunctions, state->method);
1033 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1037 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1038 parserassert(state->method);
1040 state->method->info->return_type = return_type;
1041 function_initvars(params, 0);
1045 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1046 params_t*params, classinfo_t*return_type)
1048 if(state->method && state->method->info) {
1049 syntaxerror("not able to start another method scope");
1054 state->method = rfx_calloc(sizeof(methodstate_t));
1055 state->method->has_super = 0;
1056 state->method->variable_count = 0;
1059 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1061 state->method->is_global = 1;
1062 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1064 if(state->method->is_constructor)
1065 name = "__as3_constructor__";
1068 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1070 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1074 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1075 parserassert(state->method);
1078 state->cls->has_constructor |= state->method->is_constructor;
1081 state->method->info->return_type = return_type;
1082 function_initvars(params, flags);
1086 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1087 params_t*params, classinfo_t*return_type, code_t*body)
1096 multiname_t*type2 = sig2mname(return_type);
1098 if(state->method->inner) {
1099 f = state->method->abc;
1100 abc_method_init(f, global->file, type2, 1);
1101 } else if(state->method->is_constructor) {
1102 f = abc_class_getconstructor(state->cls->abc, type2);
1103 } else if(!state->method->is_global) {
1104 namespace_t mname_ns = flags2namespace(flags, "");
1105 multiname_t mname = {QNAME, &mname_ns, 0, name};
1107 if(flags&FLAG_STATIC)
1108 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1110 f = abc_class_method(state->cls->abc, type2, &mname);
1111 slot = f->trait->slot_id;
1113 namespace_t mname_ns = flags2namespace(flags, state->package);
1114 multiname_t mname = {QNAME, &mname_ns, 0, name};
1116 f = abc_method_new(global->file, type2, 1);
1117 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1118 //abc_code_t*c = global->init->method->body->code;
1120 //flash doesn't seem to allow us to access function slots
1121 //state->method->info->slot = slot;
1123 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1124 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1125 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1126 if(params->varargs) f->flags |= METHOD_NEED_REST;
1130 for(p=params->list;p;p=p->next) {
1131 if(params->varargs && !p->next) {
1132 break; //varargs: omit last parameter in function signature
1134 multiname_t*m = sig2mname(p->param->type);
1135 list_append(f->parameters, m);
1136 if(p->param->value) {
1137 check_constant_against_type(p->param->type, p->param->value);
1138 opt=1;list_append(f->optional_parameters, p->param->value);
1140 syntaxerror("non-optional parameter not allowed after optional parameters");
1143 check_code_for_break(body);
1146 f->body->code = body;
1147 f->body->exceptions = state->method->exceptions;
1148 } else { //interface
1150 syntaxerror("interface methods can't have a method body");
1157 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1162 void breakjumpsto(code_t*c, char*name, code_t*jump)
1165 if(c->opcode == OPCODE___BREAK__) {
1166 string_t*name2 = c->data[0];
1167 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1168 c->opcode = OPCODE_JUMP;
1175 void continuejumpsto(code_t*c, char*name, code_t*jump)
1178 if(c->opcode == OPCODE___CONTINUE__) {
1179 string_t*name2 = c->data[0];
1180 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1181 c->opcode = OPCODE_JUMP;
1189 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1190 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1191 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1193 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1195 if(!type1 || !type2)
1196 return registry_getanytype();
1197 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1198 return registry_getanytype();
1201 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1210 return registry_getanytype();
1212 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1217 return abc_coerce_a(c);
1221 // cast an "any" type to a specific type. subject to
1222 // runtime exceptions
1223 return abc_coerce2(c, &m);
1226 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1227 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1228 // allow conversion between number types
1229 return abc_coerce2(c, &m);
1231 //printf("%s.%s\n", from.package, from.name);
1232 //printf("%s.%s\n", to.package, to.name);
1234 classinfo_t*supertype = from;
1236 if(supertype == to) {
1237 // target type is one of from's superclasses
1238 return abc_coerce2(c, &m);
1241 while(supertype->interfaces[t]) {
1242 if(supertype->interfaces[t]==to) {
1243 // target type is one of from's interfaces
1244 return abc_coerce2(c, &m);
1248 supertype = supertype->superclass;
1250 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1252 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1254 syntaxerror("can't convert type %s to %s", from->name, to->name);
1255 return 0; // make gcc happy
1258 code_t*defaultvalue(code_t*c, classinfo_t*type)
1260 if(TYPE_IS_INT(type)) {
1261 c = abc_pushbyte(c, 0);
1262 } else if(TYPE_IS_UINT(type)) {
1263 c = abc_pushuint(c, 0);
1264 } else if(TYPE_IS_FLOAT(type)) {
1266 } else if(TYPE_IS_BOOLEAN(type)) {
1267 c = abc_pushfalse(c);
1269 //c = abc_pushundefined(c);
1271 c = abc_pushnull(c);
1273 c = abc_coerce2(c, &m);
1278 char is_pushundefined(code_t*c)
1280 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1283 static classinfo_t* find_class(char*name)
1287 c = registry_findclass(state->package, name);
1290 /* try explicit imports */
1291 dictentry_t* e = dict_get_slot(state->imports, name);
1294 if(!strcmp(e->key, name)) {
1295 c = (classinfo_t*)e->data;
1301 /* try package.* imports */
1302 import_list_t*l = state->wildcard_imports;
1304 //printf("does package %s contain a class %s?\n", l->import->package, name);
1305 c = registry_findclass(l->import->package, name);
1310 /* try global package */
1311 c = registry_findclass("", name);
1314 /* try local "filename" package */
1315 c = registry_findclass(current_filename_short, name);
1321 static char is_getlocal(code_t*c)
1323 if(!c || c->prev || c->next)
1325 return(c->opcode == OPCODE_GETLOCAL
1326 || c->opcode == OPCODE_GETLOCAL_0
1327 || c->opcode == OPCODE_GETLOCAL_1
1328 || c->opcode == OPCODE_GETLOCAL_2
1329 || c->opcode == OPCODE_GETLOCAL_3);
1331 static int getlocalnr(code_t*c)
1333 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1334 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1335 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1336 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1337 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1338 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1342 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1346 [prefix code] [read instruction]
1350 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1353 if(in && in->opcode == OPCODE_COERCE_A) {
1354 in = code_cutlast(in);
1357 syntaxerror("internal error");
1359 /* chop off read instruction */
1363 prefix = r->prev;r->prev = 0;
1369 char use_temp_var = readbefore;
1371 /* generate the write instruction, and maybe append a dup to the prefix code */
1372 code_t* write = abc_nop(0);
1373 if(r->opcode == OPCODE_GETPROPERTY) {
1374 write->opcode = OPCODE_SETPROPERTY;
1375 multiname_t*m = (multiname_t*)r->data[0];
1376 write->data[0] = multiname_clone(m);
1377 if(m->type == QNAME || m->type == MULTINAME) {
1379 prefix = abc_dup(prefix); // we need the object, too
1382 } else if(m->type == MULTINAMEL) {
1384 /* dupping two values on the stack requires 5 operations and one register-
1385 couldn't adobe just have given us a dup2? */
1386 int temp = gettempvar();
1387 prefix = abc_setlocal(prefix, temp);
1388 prefix = abc_dup(prefix);
1389 prefix = abc_getlocal(prefix, temp);
1390 prefix = abc_swap(prefix);
1391 prefix = abc_getlocal(prefix, temp);
1393 prefix = abc_kill(prefix, temp);
1397 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1399 } else if(r->opcode == OPCODE_GETSLOT) {
1400 write->opcode = OPCODE_SETSLOT;
1401 write->data[0] = r->data[0];
1403 prefix = abc_dup(prefix); // we need the object, too
1406 } else if(r->opcode == OPCODE_GETLOCAL) {
1407 write->opcode = OPCODE_SETLOCAL;
1408 write->data[0] = r->data[0];
1409 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1410 write->opcode = OPCODE_SETLOCAL_0;
1411 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1412 write->opcode = OPCODE_SETLOCAL_1;
1413 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1414 write->opcode = OPCODE_SETLOCAL_2;
1415 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1416 write->opcode = OPCODE_SETLOCAL_3;
1419 syntaxerror("illegal lvalue: can't assign a value to this expression");
1426 /* with getproperty/getslot, we have to be extra careful not
1427 to execute the read code twice, as it might have side-effects
1428 (e.g. if the property is in fact a setter/getter combination)
1430 So read the value, modify it, and write it again,
1431 using prefix only once and making sure (by using a temporary
1432 register) that the return value is what we just wrote */
1433 temp = gettempvar();
1434 c = code_append(c, prefix);
1435 c = code_append(c, r);
1438 c = abc_setlocal(c, temp);
1440 c = code_append(c, middlepart);
1443 c = abc_setlocal(c, temp);
1445 c = code_append(c, write);
1446 c = abc_getlocal(c, temp);
1447 c = abc_kill(c, temp);
1449 /* if we're allowed to execute the read code twice *and*
1450 the middlepart doesn't modify the code, things are easier.
1452 code_t* r2 = code_dup(r);
1453 //c = code_append(c, prefix);
1454 parserassert(!prefix);
1455 c = code_append(c, r);
1456 c = code_append(c, middlepart);
1457 c = code_append(c, write);
1458 c = code_append(c, r2);
1461 /* even smaller version: overwrite the value without reading
1465 c = code_append(c, prefix);
1468 c = code_append(c, middlepart);
1469 c = code_append(c, write);
1470 c = code_append(c, r);
1472 temp = gettempvar();
1474 c = code_append(c, prefix);
1476 c = code_append(c, middlepart);
1478 c = abc_setlocal(c, temp);
1479 c = code_append(c, write);
1480 c = abc_getlocal(c, temp);
1481 c = abc_kill(c, temp);
1487 char is_break_or_jump(code_t*c)
1491 if(c->opcode == OPCODE_JUMP ||
1492 c->opcode == OPCODE___BREAK__ ||
1493 c->opcode == OPCODE___CONTINUE__ ||
1494 c->opcode == OPCODE_THROW ||
1495 c->opcode == OPCODE_RETURNVOID ||
1496 c->opcode == OPCODE_RETURNVALUE) {
1503 #define IS_FINALLY_TARGET(op) \
1504 ((op) == OPCODE___CONTINUE__ || \
1505 (op) == OPCODE___BREAK__ || \
1506 (op) == OPCODE_RETURNVOID || \
1507 (op) == OPCODE_RETURNVALUE || \
1508 (op) == OPCODE___RETHROW__)
1510 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1512 #define NEED_EXTRA_STACK_ARG
1513 code_t*finally_label = abc_nop(0);
1514 NEW(lookupswitch_t, l);
1520 code_t*prev = i->prev;
1521 if(IS_FINALLY_TARGET(i->opcode)) {
1524 if(i->opcode == OPCODE___RETHROW__ ||
1525 i->opcode == OPCODE_RETURNVALUE) {
1526 if(i->opcode == OPCODE___RETHROW__)
1527 i->opcode = OPCODE_THROW;
1529 p = abc_coerce_a(p);
1530 p = abc_setlocal(p, tempvar);
1532 p = abc_pushbyte(p, count++);
1533 p = abc_jump(p, finally_label);
1534 code_t*target = p = abc_label(p);
1535 #ifdef NEED_EXTRA_STACK_ARG
1539 p = abc_getlocal(p, tempvar);
1542 p->next = i;i->prev = p;
1543 list_append(l->targets, target);
1549 c = abc_pushbyte(c, -1);
1550 c = code_append(c, finally_label);
1551 c = code_append(c, finally);
1553 #ifdef NEED_EXTRA_STACK_ARG
1556 c = abc_lookupswitch(c, l);
1557 c = l->def = abc_label(c);
1558 #ifdef NEED_EXTRA_STACK_ARG
1565 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1569 code_t*prev = i->prev;
1570 if(IS_FINALLY_TARGET(i->opcode)) {
1571 if(i->opcode == OPCODE___RETHROW__)
1572 i->opcode = OPCODE_THROW;
1573 code_t*end = code_dup(finally);
1574 code_t*start = code_start(end);
1575 if(prev) prev->next = start;
1582 return code_append(c, finally);
1585 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1591 int num_insertion_points=0;
1593 if(IS_FINALLY_TARGET(i->opcode))
1594 num_insertion_points++;
1601 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1606 int simple_version_cost = (1+num_insertion_points)*code_size;
1607 int lookup_version_cost = 4*num_insertion_points + 5;
1609 if(cantdup || simple_version_cost > lookup_version_cost) {
1610 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1611 return insert_finally_lookup(c, finally, tempvar);
1613 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1614 return insert_finally_simple(c, finally, tempvar);
1618 #define PASS1 }} if(as3_pass == 1) {{
1619 #define PASS1END }} if(as3_pass == 2) {{
1620 #define PASS2 }} if(as3_pass == 2) {{
1621 #define PASS12 }} {{
1622 #define PASS12END }} if(as3_pass == 2) {{
1628 /* ------------ code blocks / statements ---------------- */
1630 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1632 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1633 PROGRAM_CODE_LIST: PROGRAM_CODE
1634 | PROGRAM_CODE_LIST PROGRAM_CODE
1636 PROGRAM_CODE: PACKAGE_DECLARATION
1637 | INTERFACE_DECLARATION
1639 | FUNCTION_DECLARATION
1644 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1645 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1646 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1648 INPACKAGE_CODE: INTERFACE_DECLARATION
1650 | FUNCTION_DECLARATION
1655 MAYBECODE: CODE {$$=$1;}
1656 MAYBECODE: {$$=code_new();}
1658 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1659 CODE: CODEPIECE {$$=$1;}
1661 // code which also may appear outside a method
1662 CODE_STATEMENT: IMPORT
1664 CODE_STATEMENT: FOR_IN
1665 CODE_STATEMENT: WHILE
1666 CODE_STATEMENT: DO_WHILE
1667 CODE_STATEMENT: SWITCH
1669 CODE_STATEMENT: WITH
1671 CODE_STATEMENT: VOIDEXPRESSION
1673 // code which may appear anywhere
1674 CODEPIECE: ';' {$$=0;}
1675 CODEPIECE: CODE_STATEMENT
1676 CODEPIECE: VARIABLE_DECLARATION
1682 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1683 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;}
1685 CODEBLOCK : '{' CODE '}' {$$=$2;}
1686 CODEBLOCK : '{' '}' {$$=0;}
1687 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1688 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1690 /* ------------ package init code ------------------- */
1692 PACKAGE_INITCODE: CODE_STATEMENT {
1693 code_t**cc = &global->init->method->body->code;
1694 *cc = code_append(*cc, $1);
1697 /* ------------ variables --------------------------- */
1699 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1700 | {$$.c=abc_pushundefined(0);
1704 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1705 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1707 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1708 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1710 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1712 if(variable_exists($1))
1713 syntaxerror("Variable %s already defined", $1);
1715 if(!is_subtype_of($3.t, $2)) {
1716 syntaxerror("Can't convert %s to %s", $3.t->name,
1720 int index = new_variable($1, $2, 1);
1723 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1725 $$ = converttype($$, $3.t, $2);
1726 $$ = abc_setlocal($$, index);
1728 $$ = defaultvalue(0, $2);
1729 $$ = abc_setlocal($$, index);
1732 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1734 $$ = abc_coerce_a($$);
1735 $$ = abc_setlocal($$, index);
1741 /* that's the default for a local register, anyway
1743 state->method->initcode = abc_pushundefined(state->method->initcode);
1744 state->method->initcode = abc_setlocal(state->method->initcode, index);
1746 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1749 /* ------------ control flow ------------------------- */
1751 MAYBEELSE: %prec below_else {$$ = code_new();}
1752 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1753 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1755 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1758 $$ = code_append($$, $4.c);
1759 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1761 $$ = code_append($$, $6);
1763 myjmp = $$ = abc_jump($$, 0);
1765 myif->branch = $$ = abc_nop($$);
1767 $$ = code_append($$, $7);
1768 myjmp->branch = $$ = abc_nop($$);
1774 FOR_INIT : {$$=code_new();}
1775 FOR_INIT : VARIABLE_DECLARATION
1776 FOR_INIT : VOIDEXPRESSION
1778 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
1779 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1780 $$=$2;new_variable($2,$3,1);
1782 FOR_IN_INIT : T_IDENTIFIER {
1786 FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
1787 FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
1789 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1790 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1792 $$ = code_append($$, $2);
1793 code_t*loopstart = $$ = abc_label($$);
1794 $$ = code_append($$, $4.c);
1795 code_t*myif = $$ = abc_iffalse($$, 0);
1796 $$ = code_append($$, $8);
1797 code_t*cont = $$ = abc_nop($$);
1798 $$ = code_append($$, $6);
1799 $$ = abc_jump($$, loopstart);
1800 code_t*out = $$ = abc_nop($$);
1801 breakjumpsto($$, $1.name, out);
1802 continuejumpsto($$, $1.name, cont);
1809 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
1810 variable_t*var = find_variable($2);
1811 char*tmp1name = concat2($2, "__tmp1__");
1812 int it = new_variable(tmp1name, TYPE_INT, 0);
1813 char*tmp2name = concat2($2, "__array__");
1814 int array = new_variable(tmp1name, 0, 0);
1817 $$ = code_append($$, $4.c);
1818 $$ = abc_coerce_a($$);
1819 $$ = abc_setlocal($$, array);
1820 $$ = abc_pushbyte($$, 0);
1821 $$ = abc_setlocal($$, it);
1823 code_t*loopstart = $$ = abc_label($$);
1825 $$ = abc_hasnext2($$, array, it);
1826 code_t*myif = $$ = abc_iffalse($$, 0);
1827 $$ = abc_getlocal($$, array);
1828 $$ = abc_getlocal($$, it);
1830 $$ = abc_nextname($$);
1832 $$ = abc_nextvalue($$);
1833 $$ = converttype($$, 0, var->type);
1834 $$ = abc_setlocal($$, var->index);
1836 $$ = code_append($$, $6);
1837 $$ = abc_jump($$, loopstart);
1839 code_t*out = $$ = abc_nop($$);
1840 breakjumpsto($$, $1.name, out);
1841 continuejumpsto($$, $1.name, loopstart);
1852 WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1856 code_t*myjmp = $$ = abc_jump($$, 0);
1857 code_t*loopstart = $$ = abc_label($$);
1858 $$ = code_append($$, $6);
1859 code_t*cont = $$ = abc_nop($$);
1860 myjmp->branch = cont;
1861 $$ = code_append($$, $4.c);
1862 $$ = abc_iftrue($$, loopstart);
1863 code_t*out = $$ = abc_nop($$);
1864 breakjumpsto($$, $1, out);
1865 continuejumpsto($$, $1, cont);
1871 DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
1873 code_t*loopstart = $$ = abc_label($$);
1874 $$ = code_append($$, $3);
1875 code_t*cont = $$ = abc_nop($$);
1876 $$ = code_append($$, $6.c);
1877 $$ = abc_iftrue($$, loopstart);
1878 code_t*out = $$ = abc_nop($$);
1879 breakjumpsto($$, $1, out);
1880 continuejumpsto($$, $1, cont);
1886 BREAK : "break" %prec prec_none {
1887 $$ = abc___break__(0, "");
1889 BREAK : "break" T_IDENTIFIER {
1890 $$ = abc___break__(0, $2);
1892 CONTINUE : "continue" %prec prec_none {
1893 $$ = abc___continue__(0, "");
1895 CONTINUE : "continue" T_IDENTIFIER {
1896 $$ = abc___continue__(0, $2);
1899 MAYBE_CASE_LIST : {$$=0;}
1900 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
1901 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
1902 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
1903 CASE_LIST: CASE {$$=$1;}
1904 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
1906 CASE: "case" E ':' MAYBECODE {
1908 $$ = code_append($$, $2.c);
1909 code_t*j = $$ = abc_ifne($$, 0);
1910 $$ = code_append($$, $4);
1911 if($$->opcode != OPCODE___BREAK__) {
1912 $$ = abc___fallthrough__($$, "");
1914 code_t*e = $$ = abc_nop($$);
1917 DEFAULT: "default" ':' MAYBECODE {
1920 SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
1922 $$ = code_append($$, $7);
1923 code_t*out = $$ = abc_pop($$);
1924 breakjumpsto($$, $1, out);
1926 code_t*c = $$,*lastblock=0;
1928 if(c->opcode == OPCODE_IFNE) {
1929 if(!c->next) syntaxerror("internal error in fallthrough handling");
1931 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
1933 c->opcode = OPCODE_JUMP;
1934 c->branch = lastblock;
1936 /* fall through end of switch */
1937 c->opcode = OPCODE_NOP;
1947 /* ------------ try / catch /finally ---------------- */
1949 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);}
1951 namespace_t name_ns = {ACCESS_PACKAGE, ""};
1952 multiname_t name = {QNAME, &name_ns, 0, $3};
1954 NEW(abc_exception_t, e)
1955 e->exc_type = sig2mname($4);
1956 e->var_name = multiname_clone(&name);
1960 int i = find_variable_safe($3)->index;
1961 e->target = c = abc_nop(0);
1962 c = abc_setlocal(c, i);
1963 c = code_append(c, $8);
1969 FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
1975 NEW(abc_exception_t, e)
1976 e->exc_type = 0; //all exceptions
1977 e->var_name = 0; //no name
1980 e->to = code_append(e->to, $4);
1986 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
1987 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
1988 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
1989 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
1993 list_append($$.l,$2);
1994 $$.finally = $2->to;$2->to=0;
1997 CATCH_FINALLY_LIST: FINALLY {
2001 list_append($$.l,$1);
2002 $$.finally = $1->to;$1->to=0;
2006 TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2007 code_t*out = abc_nop(0);
2009 code_t*start = abc_nop(0);
2010 $$ = code_append(start, $4);
2011 if(!is_break_or_jump($4)) {
2012 $$ = abc_jump($$, out);
2014 code_t*end = $$ = abc_nop($$);
2018 tmp = new_variable("__finally__", 0, 0);
2020 abc_exception_list_t*l = $6.l;
2023 abc_exception_t*e = l->abc_exception;
2025 $$ = code_append($$, e->target);
2026 $$ = abc_jump($$, out);
2028 parserassert((ptroff_t)$6.finally);
2030 e->target = $$ = abc_nop($$);
2031 $$ = abc___rethrow__($$);
2039 $$ = code_append($$, out);
2041 $$ = insert_finally($$, $6.finally, tmp);
2043 list_concat(state->method->exceptions, $6.l);
2049 /* ------------ throw ------------------------------- */
2051 THROW : "throw" EXPRESSION {
2055 THROW : "throw" %prec prec_none {
2056 if(!state->exception_name)
2057 syntaxerror("re-throw only possible within a catch block");
2058 variable_t*v = find_variable(state->exception_name);
2060 $$=abc_getlocal($$, v->index);
2064 /* ------------ with -------------------------------- */
2066 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2068 $$ = abc_pushscope($$);
2069 $$ = code_append($$, $5);
2070 $$ = abc_popscope($$);
2073 /* ------------ packages and imports ---------------- */
2075 X_IDENTIFIER: T_IDENTIFIER
2076 | "package" {PASS12 $$="package";}
2078 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2079 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2081 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2082 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2083 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2084 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2086 IMPORT : "import" QNAME {
2089 syntaxerror("Couldn't import class\n");
2090 state_has_imports();
2091 dict_put(state->imports, c->name, c);
2094 IMPORT : "import" PACKAGE '.' '*' {
2097 state_has_imports();
2098 list_append(state->wildcard_imports, i);
2102 /* ------------ classes and interfaces (header) -------------- */
2104 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2105 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2106 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2107 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2109 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2110 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2111 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2112 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2113 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2114 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2115 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2116 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2117 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2119 EXTENDS : {$$=registry_getobjectclass();}
2120 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
2122 EXTENDS_LIST : {PASS12 $$=list_new();}
2123 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {PASS12 $$=$2;}
2125 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2126 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {PASS12 $$=$2;}
2128 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2129 EXTENDS IMPLEMENTS_LIST
2130 '{' {PASS12 startclass($1,$3,$4,$5, 0);}
2132 '}' {PASS12 endclass();$$=0;}
2134 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2136 '{' {PASS12 startclass($1,$3,0,$4,1);}
2137 MAYBE_INTERFACE_BODY
2138 '}' {PASS12 endclass();$$=0;}
2140 /* ------------ classes and interfaces (body) -------------- */
2143 MAYBE_CLASS_BODY : CLASS_BODY
2144 CLASS_BODY : CLASS_BODY_ITEM
2145 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2146 CLASS_BODY_ITEM : ';'
2147 CLASS_BODY_ITEM : SLOT_DECLARATION
2148 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2150 CLASS_BODY_ITEM : CODE_STATEMENT {
2151 code_t*c = state->cls->static_init;
2152 c = code_append(c, $1);
2153 state->cls->static_init = c;
2156 MAYBE_INTERFACE_BODY :
2157 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2158 INTERFACE_BODY : IDECLARATION
2159 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2161 IDECLARATION : "var" T_IDENTIFIER {
2162 syntaxerror("variable declarations not allowed in interfaces");
2164 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2167 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2168 syntaxerror("invalid method modifiers: interface methods always need to be public");
2170 startfunction(0,$1,$3,$4,&$6,$8);
2171 endfunction(0,$1,$3,$4,&$6,$8, 0);
2172 list_deep_free($6.list);
2175 /* ------------ classes and interfaces (body, slots ) ------- */
2177 VARCONST: "var" | "const"
2179 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
2181 memberinfo_t* info = state->cls?
2182 memberinfo_register(state->cls->info, $3, MEMBER_SLOT):
2183 memberinfo_register_global(flags2access($1), state->package, $3, MEMBER_SLOT);
2186 info->flags = flags;
2189 namespace_t mname_ns = {flags2access(flags), ""};
2190 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2192 trait_list_t**traits;
2196 mname_ns.name = state->package;
2197 traits = &global->init->traits;
2198 code = &global->init->method->body->code;
2199 } else if(flags&FLAG_STATIC) {
2201 traits = &state->cls->abc->static_traits;
2202 code = &state->cls->static_init;
2204 // instance variable
2205 traits = &state->cls->abc->traits;
2206 code = &state->cls->init;
2212 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2214 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2216 info->slot = t->slot_id;
2218 /* initalization code (if needed) */
2220 if($5.c && !is_pushundefined($5.c)) {
2221 c = abc_getlocal_0(c);
2222 c = code_append(c, $5.c);
2223 c = converttype(c, $5.t, $4);
2224 c = abc_setslot(c, t->slot_id);
2227 *code = code_append(*code, c);
2230 t->kind= TRAIT_CONST;
2236 /* ------------ constants -------------------------------------- */
2238 MAYBESTATICCONSTANT: {$$=0;}
2239 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2241 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2242 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2243 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2244 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2245 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
2246 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2247 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2248 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2249 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2251 /* ------------ classes and interfaces (body, functions) ------- */
2253 // non-vararg version
2256 memset(&$$,0,sizeof($$));
2258 MAYBE_PARAM_LIST: PARAM_LIST {
2264 MAYBE_PARAM_LIST: "..." PARAM {
2266 memset(&$$,0,sizeof($$));
2268 list_append($$.list, $2);
2270 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2274 list_append($$.list, $4);
2278 PARAM_LIST: PARAM_LIST ',' PARAM {
2281 list_append($$.list, $3);
2285 memset(&$$,0,sizeof($$));
2286 list_append($$.list, $1);
2289 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2292 $$ = malloc(sizeof(param_t));
2297 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2300 $$ = malloc(sizeof(param_t));
2302 $$->type = TYPE_ANY;
2305 GETSET : "get" {$$=$1;}
2309 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2310 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2312 PASS1 old_state();list_deep_free($6.list);
2314 if(!state->method->info) syntaxerror("internal error");
2316 code_t*c = method_header();
2317 c = wrap_function(c, 0, $11);
2319 endfunction(0,$1,$3,$4,&$6,$8,c);
2320 list_deep_free($6.list);
2324 MAYBE_IDENTIFIER: T_IDENTIFIER
2325 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2326 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2327 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2329 PASS1 old_state();list_deep_free($4.list);
2331 memberinfo_t*f = state->method->info;
2332 if(!f) syntaxerror("internal error");
2334 code_t*c = method_header();
2335 c = wrap_function(c, 0, $9);
2337 int index = state->method->var_index;
2338 endfunction(0,0,0,$2,&$4,$6,c);
2339 list_deep_free($4.list);
2341 $$.c = abc_getlocal(0, index);
2342 $$.t = TYPE_FUNCTION(f);
2346 /* ------------- package + class ids --------------- */
2348 CLASS: T_IDENTIFIER {
2351 /* try current package */
2352 $$ = find_class($1);
2353 if(!$$) syntaxerror("Could not find class %s\n", $1);
2356 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2359 $$ = registry_findclass($1, $3);
2360 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
2364 QNAME: PACKAGEANDCLASS
2367 QNAME_LIST : QNAME {PASS12 $$=list_new();list_append($$, $1);}
2368 QNAME_LIST : QNAME_LIST ',' QNAME {PASS12 $$=$1;list_append($$,$3);}
2370 TYPE : QNAME {$$=$1;}
2371 | '*' {$$=registry_getanytype();}
2372 | "void" {$$=registry_getanytype();}
2374 | "String" {$$=registry_getstringclass();}
2375 | "int" {$$=registry_getintclass();}
2376 | "uint" {$$=registry_getuintclass();}
2377 | "Boolean" {$$=registry_getbooleanclass();}
2378 | "Number" {$$=registry_getnumberclass();}
2381 MAYBETYPE: ':' TYPE {$$=$2;}
2384 /* ----------function calls, delete, constructor calls ------ */
2386 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2387 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2389 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2390 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2391 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2394 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
2396 $$.cc = code_append($1.cc, $3.c);
2400 NEW : "new" E XX MAYBE_PARAM_VALUES {
2402 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2404 code_t*paramcode = $4.cc;
2405 if($$.c->opcode == OPCODE_GETPROPERTY) {
2406 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2407 $$.c = code_cutlast($$.c);
2408 $$.c = code_append($$.c, paramcode);
2409 $$.c = abc_constructprop2($$.c, name, $4.len);
2410 multiname_destroy(name);
2411 } else if($$.c->opcode == OPCODE_GETSLOT) {
2412 int slot = (int)(ptroff_t)$$.c->data[0];
2413 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2414 multiname_t*name = t->name;
2415 $$.c = code_cutlast($$.c);
2416 $$.c = code_append($$.c, paramcode);
2417 $$.c = abc_constructprop2($$.c, name, $4.len);
2419 $$.c = code_append($$.c, paramcode);
2420 $$.c = abc_construct($$.c, $4.len);
2424 if(TYPE_IS_CLASS($2.t) && $2.t->cls) {
2427 $$.c = abc_coerce_a($$.c);
2432 /* TODO: use abc_call (for calling local variables),
2433 abc_callstatic (for calling own methods)
2436 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2439 if($$.c->opcode == OPCODE_COERCE_A) {
2440 $$.c = code_cutlast($$.c);
2442 code_t*paramcode = $3.cc;
2445 if($$.c->opcode == OPCODE_GETPROPERTY) {
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_callproperty2($$.c, name, $3.len);
2450 multiname_destroy(name);
2451 } else if($$.c->opcode == OPCODE_GETSLOT) {
2452 int slot = (int)(ptroff_t)$$.c->data[0];
2453 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2454 if(t->kind!=TRAIT_METHOD) {
2455 //ok: flash allows to assign closures to members.
2457 multiname_t*name = t->name;
2458 $$.c = code_cutlast($$.c);
2459 $$.c = code_append($$.c, paramcode);
2460 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2461 $$.c = abc_callproperty2($$.c, name, $3.len);
2462 } else if($$.c->opcode == OPCODE_GETSUPER) {
2463 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2464 $$.c = code_cutlast($$.c);
2465 $$.c = code_append($$.c, paramcode);
2466 $$.c = abc_callsuper2($$.c, name, $3.len);
2467 multiname_destroy(name);
2469 $$.c = abc_getglobalscope($$.c);
2470 $$.c = code_append($$.c, paramcode);
2471 $$.c = abc_call($$.c, $3.len);
2474 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
2475 $$.t = $1.t->function->return_type;
2477 $$.c = abc_coerce_a($$.c);
2482 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2483 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2484 if(!state->method) syntaxerror("super() not allowed outside of a function");
2485 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2488 $$.c = abc_getlocal_0($$.c);
2490 $$.c = code_append($$.c, $3.cc);
2492 this is dependent on the control path, check this somewhere else
2493 if(state->method->has_super)
2494 syntaxerror("constructor may call super() only once");
2496 state->method->has_super = 1;
2498 $$.c = abc_constructsuper($$.c, $3.len);
2499 $$.c = abc_pushundefined($$.c);
2503 DELETE: "delete" E {
2505 if($$.c->opcode == OPCODE_COERCE_A) {
2506 $$.c = code_cutlast($$.c);
2508 multiname_t*name = 0;
2509 if($$.c->opcode == OPCODE_GETPROPERTY) {
2510 $$.c->opcode = OPCODE_DELETEPROPERTY;
2511 } else if($$.c->opcode == OPCODE_GETSLOT) {
2512 int slot = (int)(ptroff_t)$$.c->data[0];
2513 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
2514 $$.c = code_cutlast($$.c);
2515 $$.c = abc_deleteproperty2($$.c, name);
2517 $$.c = abc_getlocal_0($$.c);
2518 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2519 $$.c = abc_deleteproperty2($$.c, &m);
2521 $$.t = TYPE_BOOLEAN;
2524 RETURN: "return" %prec prec_none {
2525 $$ = abc_returnvoid(0);
2527 RETURN: "return" EXPRESSION {
2529 $$ = abc_returnvalue($$);
2532 // ----------------------- expression types -------------------------------------
2534 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2535 EXPRESSION : E %prec below_minus {$$ = $1;}
2536 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2538 $$.c = cut_last_push($$.c);
2539 $$.c = code_append($$.c,$3.c);
2542 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2543 $$=cut_last_push($1.c);
2546 // ----------------------- expression evaluation -------------------------------------
2548 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2549 //V : CONSTANT {$$ = 0;}
2551 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2552 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2553 //V : NEW {$$ = $1.c;}
2555 //V : DELETE {$$ = $1.c;}
2556 E : DELETE {$$ = $1;}
2562 namespace_t ns = {ACCESS_PACKAGE, ""};
2563 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2565 $$.c = abc_getlex2($$.c, &m);
2566 $$.c = abc_pushstring($$.c, $1.pattern);
2567 $$.c = abc_construct($$.c, 1);
2569 $$.c = abc_getlex2($$.c, &m);
2570 $$.c = abc_pushstring($$.c, $1.pattern);
2571 $$.c = abc_pushstring($$.c, $1.options);
2572 $$.c = abc_construct($$.c, 2);
2577 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2578 //MULTINAME(m, registry_getintclass());
2579 //$$.c = abc_coerce2($$.c, &m); // FIXME
2582 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2585 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2588 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2591 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2594 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
2597 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2600 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2601 $$.t = TYPE_BOOLEAN;
2603 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2604 $$.t = TYPE_BOOLEAN;
2606 CONSTANT : "null" {$$.c = abc_pushnull(0);
2610 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2611 $$.t = TYPE_BOOLEAN;
2613 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2614 $$.t = TYPE_BOOLEAN;
2616 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2617 $$.t = TYPE_BOOLEAN;
2619 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2620 $$.t = TYPE_BOOLEAN;
2622 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2623 $$.t = TYPE_BOOLEAN;
2625 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2626 $$.t = TYPE_BOOLEAN;
2628 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2629 $$.t = TYPE_BOOLEAN;
2631 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2632 $$.t = TYPE_BOOLEAN;
2635 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2637 $$.c = converttype($$.c, $1.t, $$.t);
2638 $$.c = abc_dup($$.c);
2639 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2640 $$.c = cut_last_push($$.c);
2641 $$.c = code_append($$.c,$3.c);
2642 $$.c = converttype($$.c, $3.t, $$.t);
2643 code_t*label = $$.c = abc_label($$.c);
2644 jmp->branch = label;
2647 $$.t = join_types($1.t, $3.t, 'A');
2648 /*printf("%08x:\n",$1.t);
2649 code_dump($1.c, 0, 0, "", stdout);
2650 printf("%08x:\n",$3.t);
2651 code_dump($3.c, 0, 0, "", stdout);
2652 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2654 $$.c = converttype($$.c, $1.t, $$.t);
2655 $$.c = abc_dup($$.c);
2656 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2657 $$.c = cut_last_push($$.c);
2658 $$.c = code_append($$.c,$3.c);
2659 $$.c = converttype($$.c, $3.t, $$.t);
2660 code_t*label = $$.c = abc_label($$.c);
2661 jmp->branch = label;
2664 E : '!' E {$$.c=$2.c;
2665 $$.c = abc_not($$.c);
2666 $$.t = TYPE_BOOLEAN;
2669 E : '~' E {$$.c=$2.c;
2670 $$.c = abc_bitnot($$.c);
2674 E : E '&' E {$$.c = code_append($1.c,$3.c);
2675 $$.c = abc_bitand($$.c);
2679 E : E '^' E {$$.c = code_append($1.c,$3.c);
2680 $$.c = abc_bitxor($$.c);
2684 E : E '|' E {$$.c = code_append($1.c,$3.c);
2685 $$.c = abc_bitor($$.c);
2689 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2690 $$.c = abc_rshift($$.c);
2693 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2694 $$.c = abc_urshift($$.c);
2697 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2698 $$.c = abc_lshift($$.c);
2702 E : E '/' E {$$.c = code_append($1.c,$3.c);
2703 $$.c = abc_divide($$.c);
2706 E : E '%' E {$$.c = code_append($1.c,$3.c);
2707 $$.c = abc_modulo($$.c);
2710 E : E '+' E {$$.c = code_append($1.c,$3.c);
2711 if(BOTH_INT($1.t, $3.t)) {
2712 $$.c = abc_add_i($$.c);
2715 $$.c = abc_add($$.c);
2716 $$.t = join_types($1.t,$3.t,'+');
2719 E : E '-' E {$$.c = code_append($1.c,$3.c);
2720 if(BOTH_INT($1.t,$3.t)) {
2721 $$.c = abc_subtract_i($$.c);
2724 $$.c = abc_subtract($$.c);
2728 E : E '*' E {$$.c = code_append($1.c,$3.c);
2729 if(BOTH_INT($1.t,$3.t)) {
2730 $$.c = abc_multiply_i($$.c);
2733 $$.c = abc_multiply($$.c);
2738 E : E "in" E {$$.c = code_append($1.c,$3.c);
2739 $$.c = abc_in($$.c);
2740 $$.t = TYPE_BOOLEAN;
2743 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2744 if(use_astype && TYPE_IS_CLASS($3.t)) {
2745 MULTINAME(m,$3.t->cls);
2746 $$.c = abc_astype2($1.c, &m);
2749 $$.c = code_append($1.c, $3.c);
2750 $$.c = abc_astypelate($$.c);
2755 E : E "instanceof" E
2756 {$$.c = code_append($1.c, $3.c);
2757 $$.c = abc_instanceof($$.c);
2758 $$.t = TYPE_BOOLEAN;
2761 E : E "is" E {$$.c = code_append($1.c, $3.c);
2762 $$.c = abc_istypelate($$.c);
2763 $$.t = TYPE_BOOLEAN;
2766 E : "typeof" '(' E ')' {
2768 $$.c = abc_typeof($$.c);
2773 $$.c = cut_last_push($2.c);
2774 $$.c = abc_pushundefined($$.c);
2778 E : "void" { $$.c = abc_pushundefined(0);
2782 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
2787 $$.c=abc_negate_i($$.c);
2790 $$.c=abc_negate($$.c);
2797 $$.c = code_append($$.c, $3.c);
2799 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
2800 $$.c = abc_getproperty2($$.c, &m);
2801 $$.t = 0; // array elements have unknown type
2804 E : '[' MAYBE_EXPRESSION_LIST ']' {
2806 $$.c = code_append($$.c, $2.cc);
2807 $$.c = abc_newarray($$.c, $2.len);
2808 $$.t = registry_getarrayclass();
2811 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
2812 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
2814 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2816 $$.cc = code_append($$.cc, $1.c);
2817 $$.cc = code_append($$.cc, $3.c);
2820 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2823 $$.cc = code_append($$.cc, $3.c);
2824 $$.cc = code_append($$.cc, $5.c);
2829 E : '{' MAYBE_EXPRPAIR_LIST '}' {
2831 $$.c = code_append($$.c, $2.cc);
2832 $$.c = abc_newobject($$.c, $2.len/2);
2833 $$.t = registry_getobjectclass();
2838 if(BOTH_INT($1.t,$3.t)) {
2839 c=abc_multiply_i(c);
2843 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
2844 $$.c = toreadwrite($1.c, c, 0, 0);
2849 code_t*c = abc_modulo($3.c);
2850 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
2851 $$.c = toreadwrite($1.c, c, 0, 0);
2855 code_t*c = abc_lshift($3.c);
2856 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
2857 $$.c = toreadwrite($1.c, c, 0, 0);
2861 code_t*c = abc_rshift($3.c);
2862 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
2863 $$.c = toreadwrite($1.c, c, 0, 0);
2867 code_t*c = abc_urshift($3.c);
2868 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
2869 $$.c = toreadwrite($1.c, c, 0, 0);
2873 code_t*c = abc_divide($3.c);
2874 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
2875 $$.c = toreadwrite($1.c, c, 0, 0);
2879 code_t*c = abc_bitor($3.c);
2880 c=converttype(c, TYPE_INT, $1.t);
2881 $$.c = toreadwrite($1.c, c, 0, 0);
2887 if(TYPE_IS_INT($1.t)) {
2891 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
2894 $$.c = toreadwrite($1.c, c, 0, 0);
2897 E : E "-=" E { code_t*c = $3.c;
2898 if(TYPE_IS_INT($1.t)) {
2899 c=abc_subtract_i(c);
2902 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2905 $$.c = toreadwrite($1.c, c, 0, 0);
2908 E : E '=' E { code_t*c = 0;
2909 c = code_append(c, $3.c);
2910 c = converttype(c, $3.t, $1.t);
2911 $$.c = toreadwrite($1.c, c, 1, 0);
2915 E : E '?' E ':' E %prec below_assignment {
2916 $$.t = join_types($3.t,$5.t,'?');
2918 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2919 $$.c = code_append($$.c, $3.c);
2920 $$.c = converttype($$.c, $3.t, $$.t);
2921 code_t*j2 = $$.c = abc_jump($$.c, 0);
2922 $$.c = j1->branch = abc_label($$.c);
2923 $$.c = code_append($$.c, $5.c);
2924 $$.c = converttype($$.c, $3.t, $$.t);
2925 $$.c = j2->branch = abc_label($$.c);
2928 E : E "++" { code_t*c = 0;
2929 classinfo_t*type = $1.t;
2930 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
2931 int nr = getlocalnr($1.c);
2932 code_free($1.c);$1.c=0;
2933 if(TYPE_IS_INT($1.t)) {
2934 $$.c = abc_getlocal(0, nr);
2935 $$.c = abc_inclocal_i($$.c, nr);
2936 } else if(TYPE_IS_NUMBER($1.t)) {
2937 $$.c = abc_getlocal(0, nr);
2938 $$.c = abc_inclocal($$.c, nr);
2939 } else syntaxerror("internal error");
2941 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2942 c=abc_increment_i(c);
2948 c=converttype(c, type, $1.t);
2949 $$.c = toreadwrite($1.c, c, 0, 1);
2954 // TODO: use inclocal, like with ++
2955 E : E "--" { code_t*c = 0;
2956 classinfo_t*type = $1.t;
2957 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2958 c=abc_decrement_i(c);
2964 c=converttype(c, type, $1.t);
2965 $$.c = toreadwrite($1.c, c, 0, 1);
2969 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2970 classinfo_t*type = $2.t;
2971 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2972 c=abc_increment_i(c);
2978 c=converttype(c, type, $2.t);
2979 $$.c = toreadwrite($2.c, c, 0, 0);
2983 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2984 classinfo_t*type = $2.t;
2985 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2986 c=abc_decrement_i(c);
2992 c=converttype(c, type, $2.t);
2993 $$.c = toreadwrite($2.c, c, 0, 0);
2997 E : "super" '.' T_IDENTIFIER
2998 { if(!state->cls->info)
2999 syntaxerror("super keyword not allowed outside a class");
3000 classinfo_t*t = state->cls->info->superclass;
3001 if(!t) t = TYPE_OBJECT;
3003 memberinfo_t*f = registry_findmember(t, $3, 1);
3004 namespace_t ns = flags2namespace(f->flags, "");
3005 MEMBER_MULTINAME(m, f, $3);
3007 $$.c = abc_getlocal_0($$.c);
3008 $$.c = abc_getsuper2($$.c, &m);
3009 $$.t = memberinfo_gettype(f);
3012 E : E '.' T_IDENTIFIER
3014 classinfo_t*t = $1.t;
3016 if(TYPE_IS_CLASS(t) && t->cls) {
3021 memberinfo_t*f = registry_findmember(t, $3, 1);
3023 if(f && !is_static != !(f->flags&FLAG_STATIC))
3025 if(f && f->slot && !noslot) {
3026 $$.c = abc_getslot($$.c, f->slot);
3028 MEMBER_MULTINAME(m, f, $3);
3029 $$.c = abc_getproperty2($$.c, &m);
3031 /* determine type */
3032 $$.t = memberinfo_gettype(f);
3034 $$.c = abc_coerce_a($$.c);
3036 /* when resolving a property on an unknown type, we do know the
3037 name of the property (and don't seem to need the package), but
3038 we need to make avm2 try out all access modes */
3039 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3040 $$.c = abc_getproperty2($$.c, &m);
3041 $$.c = abc_coerce_a($$.c);
3042 $$.t = registry_getanytype();
3046 VAR_READ : T_IDENTIFIER {
3053 /* look at variables */
3054 if((v = find_variable($1))) {
3055 // $1 is a local variable
3056 $$.c = abc_getlocal($$.c, v->index);
3061 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3063 /* look at current class' members */
3064 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
3065 (f->flags&FLAG_STATIC) >= i_am_static) {
3066 // $1 is a function in this class
3067 int var_is_static = (f->flags&FLAG_STATIC);
3069 if(f->kind == MEMBER_METHOD) {
3070 $$.t = TYPE_FUNCTION(f);
3074 if(var_is_static && !i_am_static) {
3075 /* access to a static member from a non-static location.
3076 do this via findpropstrict:
3077 there doesn't seem to be any non-lookup way to access
3078 static properties of a class */
3079 state->method->late_binding = 1;
3081 namespace_t ns = {flags2access(f->flags), ""};
3082 multiname_t m = {QNAME, &ns, 0, $1};
3083 $$.c = abc_findpropstrict2($$.c, &m);
3084 $$.c = abc_getproperty2($$.c, &m);
3086 } else if(f->slot>0) {
3087 $$.c = abc_getlocal_0($$.c);
3088 $$.c = abc_getslot($$.c, f->slot);
3091 namespace_t ns = {flags2access(f->flags), ""};
3092 multiname_t m = {QNAME, &ns, 0, $1};
3093 $$.c = abc_getlocal_0($$.c);
3094 $$.c = abc_getproperty2($$.c, &m);
3099 /* look at actual classes, in the current package and imported */
3100 if((a = find_class($1))) {
3101 if(a->flags & FLAG_METHOD) {
3103 $$.c = abc_findpropstrict2($$.c, &m);
3104 $$.c = abc_getproperty2($$.c, &m);
3105 if(a->function->kind == MEMBER_METHOD) {
3106 $$.t = TYPE_FUNCTION(a->function);
3108 $$.t = a->function->type;
3112 $$.c = abc_getglobalscope($$.c);
3113 $$.c = abc_getslot($$.c, a->slot);
3116 $$.c = abc_getlex2($$.c, &m);
3118 $$.t = TYPE_CLASS(a);
3123 /* unknown object, let the avm2 resolve it */
3125 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3126 state->method->late_binding = 1;
3128 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3131 $$.c = abc_findpropstrict2($$.c, &m);
3132 $$.c = abc_getproperty2($$.c, &m);
3137 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
3138 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
3139 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3141 // ----------------- namespaces -------------------------------------------------
3143 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3144 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3145 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3147 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {$$=0;}