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 = 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 state_destroy(state);state=0;
476 void initialize_parser()
478 global = rfx_calloc(sizeof(global_t));
479 global->file = abc_file_new();
480 global->file->flags &= ~ABCFILE_LAZY;
481 global->token2info = dict_new2(&ptr_type);
483 global->init = abc_initscript(global->file);
484 code_t*c = global->init->method->body->code;
485 c = abc_getlocal_0(c);
486 c = abc_pushscope(c);
487 global->init->method->body->code = c;
490 void* finish_parser()
492 code_t*c = global->init->method->body->code;
493 /*c = abc_findpropstrict(c, "[package]::trace");
494 c = abc_pushstring(c, "[leaving global init function]");
495 c = abc_callpropvoid(c, "[package]::trace", 1);*/
496 c = abc_returnvoid(c);
497 global->init->method->body->code = c;
498 dict_destroy(global->token2info);global->token2info=0;
503 static void xx_scopetest()
505 /* findpropstrict doesn't just return a scope object- it
506 also makes it "active" somehow. Push local_0 on the
507 scope stack and read it back with findpropstrict, it'll
508 contain properties like "trace". Trying to find the same
509 property on a "vanilla" local_0 yields only a "undefined" */
510 //c = abc_findpropstrict(c, "[package]::trace");
512 /*c = abc_getlocal_0(c);
513 c = abc_findpropstrict(c, "[package]::trace");
515 c = abc_setlocal_1(c);
517 c = abc_pushbyte(c, 0);
518 c = abc_setlocal_2(c);
520 code_t*xx = c = abc_label(c);
521 c = abc_findpropstrict(c, "[package]::trace");
522 c = abc_pushstring(c, "prop:");
523 c = abc_hasnext2(c, 1, 2);
525 c = abc_setlocal_3(c);
526 c = abc_callpropvoid(c, "[package]::trace", 2);
527 c = abc_getlocal_3(c);
529 c = abc_iftrue(c,xx);*/
533 typedef struct _variable {
539 static variable_t* find_variable(char*name)
545 v = dict_lookup(s->vars, name);
555 static variable_t* find_variable_safe(char*name)
557 variable_t* v = find_variable(name);
559 syntaxerror("undefined variable: %s", name);
562 static char variable_exists(char*name)
564 return dict_lookup(state->vars, name)!=0;
566 code_t*defaultvalue(code_t*c, classinfo_t*type);
567 static int new_variable(const char*name, classinfo_t*type, char init)
570 v->index = state->method->variable_count;
574 dict_put(state->vars, name, v);
576 return state->method->variable_count++;
578 #define TEMPVARNAME "__as3_temp__"
579 static int gettempvar()
581 variable_t*v = find_variable(TEMPVARNAME);
584 return new_variable(TEMPVARNAME, 0, 0);
587 code_t* var_block(code_t*body)
593 for(t=0;t<state->vars->hashsize;t++) {
594 dictentry_t*e = state->vars->slots[t];
596 variable_t*v = (variable_t*)e->data;
597 if(v->type && v->init) {
598 c = defaultvalue(c, v->type);
599 c = abc_setlocal(c, v->index);
600 k = abc_kill(k, v->index);
610 if(x->opcode== OPCODE___BREAK__ ||
611 x->opcode== OPCODE___CONTINUE__) {
612 /* link kill code before break/continue */
613 code_t*e = code_dup(k);
614 code_t*s = code_start(e);
626 c = code_append(c, body);
627 c = code_append(c, k);
631 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
633 static void parsererror(const char*file, int line, const char*f)
635 syntaxerror("internal error in %s, %s:%d", f, file, line);
639 code_t* method_header()
642 if(state->method->late_binding && !state->method->inner) {
643 c = abc_getlocal_0(c);
644 c = abc_pushscope(c);
646 /*if(state->method->innerfunctions) {
647 c = abc_newactivation(c);
648 c = abc_pushscope(c);
650 if(state->method->is_constructor && !state->method->has_super) {
651 // call default constructor
652 c = abc_getlocal_0(c);
653 c = abc_constructsuper(c, 0);
655 methodstate_list_t*l = state->method->innerfunctions;
657 parserassert(l->methodstate->abc);
658 c = abc_newfunction(c, l->methodstate->abc);
659 c = abc_setlocal(c, l->methodstate->var_index);
660 free(l->methodstate);l->methodstate=0;
663 list_free(state->method->innerfunctions);
664 state->method->innerfunctions = 0;
669 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
671 c = code_append(c, header);
672 c = code_append(c, var_block(body));
673 /* append return if necessary */
674 if(!c || (c->opcode != OPCODE_RETURNVOID &&
675 c->opcode != OPCODE_RETURNVALUE)) {
676 c = abc_returnvoid(c);
682 static void startpackage(char*name)
685 /*printf("entering package \"%s\"\n", name);*/
686 state->package = strdup(name);
688 static void endpackage()
690 /*printf("leaving package \"%s\"\n", state->package);*/
692 //used e.g. in classinfo_register:
693 //free(state->package);state->package=0;
699 char*as3_globalclass=0;
700 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
703 syntaxerror("inner classes now allowed");
706 state->cls = rfx_calloc(sizeof(classstate_t));
707 state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor
708 state->method->variable_count = 1;
711 classinfo_list_t*mlist=0;
713 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
714 syntaxerror("invalid modifier(s)");
716 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
717 syntaxerror("public and internal not supported at the same time.");
719 /* create the class name, together with the proper attributes */
723 if(!(flags&FLAG_PUBLIC) && !state->package) {
724 access = ACCESS_PRIVATE; package = current_filename;
725 } else if(!(flags&FLAG_PUBLIC) && state->package) {
726 access = ACCESS_PACKAGEINTERNAL; package = state->package;
727 } else if(state->package) {
728 access = ACCESS_PACKAGE; package = state->package;
730 syntaxerror("public classes only allowed inside a package");
734 if(registry_findclass(package, classname)) {
735 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
737 /* build info struct */
738 int num_interfaces = (list_length(implements));
739 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
743 state->cls->info = registry_findclass(package, classname);
744 parserassert((int)state->cls->info);
746 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
747 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
749 classinfo_list_t*l = implements;
750 for(l=implements;l;l=l->next) {
751 state->cls->info->interfaces[pos++] = l->classinfo;
754 /* generate the abc code for this class */
755 MULTINAME(classname2,state->cls->info);
756 multiname_t*extends2 = sig2mname(extends);
758 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
759 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
760 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
762 state->cls->info->flags |= CLASS_INTERFACE;
763 abc_class_interface(state->cls->abc);
766 abc_class_protectedNS(state->cls->abc, classname);
768 for(mlist=implements;mlist;mlist=mlist->next) {
769 MULTINAME(m, mlist->classinfo);
770 abc_class_add_interface(state->cls->abc, &m);
773 /* write the construction code for this class to the global init
775 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
777 abc_method_body_t*m = global->init->method->body;
778 __ getglobalscope(m);
779 classinfo_t*s = extends;
784 //TODO: take a look at the current scope stack, maybe
785 // we can re-use something
790 multiname_t*s2 = sig2mname(s);
792 multiname_destroy(s2);
794 __ pushscope(m); count++;
795 m->code = m->code->prev->prev; // invert
797 /* continue appending after last op end */
798 while(m->code && m->code->next) m->code = m->code->next;
800 /* TODO: if this is one of *our* classes, we can also
801 do a getglobalscope/getslot <nr> (which references
802 the init function's slots) */
804 __ getlex2(m, extends2);
806 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
807 stack is not the superclass */
808 __ pushscope(m);count++;
811 /* notice: we get a verify error #1107 if the top element on the scope
812 stack is not the global object */
814 __ pushscope(m);count++;
816 __ newclass(m,state->cls->abc);
820 __ setslot(m, slotindex);
821 multiname_destroy(extends2);
823 /* flash.display.MovieClip handling */
825 if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
826 if(state->package && state->package[0]) {
827 as3_globalclass = concat3(state->package, ".", classname);
829 as3_globalclass = strdup(classname);
835 static void endclass()
838 if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
840 c = abc_getlocal_0(c);
841 c = abc_constructsuper(c, 0);
842 state->cls->init = code_append(state->cls->init, c);
844 if(!state->method->late_binding) {
845 // class initialization code uses late binding
847 c = abc_getlocal_0(c);
848 c = abc_pushscope(c);
849 state->cls->static_init = code_append(c, state->cls->static_init);
852 if(state->cls->init) {
853 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
854 m->body->code = wrap_function(0, state->cls->init, m->body->code);
856 if(state->cls->static_init) {
857 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
858 m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
865 void check_code_for_break(code_t*c)
868 if(c->opcode == OPCODE___BREAK__) {
869 char*name = string_cstr(c->data[0]);
870 syntaxerror("Unresolved \"break %s\"", name);
872 if(c->opcode == OPCODE___CONTINUE__) {
873 char*name = string_cstr(c->data[0]);
874 syntaxerror("Unresolved \"continue %s\"", name);
881 static void check_constant_against_type(classinfo_t*t, constant_t*c)
883 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
884 if(TYPE_IS_NUMBER(t)) {
885 xassert(c->type == CONSTANT_FLOAT
886 || c->type == CONSTANT_INT
887 || c->type == CONSTANT_UINT);
888 } else if(TYPE_IS_UINT(t)) {
889 xassert(c->type == CONSTANT_UINT ||
890 (c->type == CONSTANT_INT && c->i>0));
891 } else if(TYPE_IS_INT(t)) {
892 xassert(c->type == CONSTANT_INT);
893 } else if(TYPE_IS_BOOLEAN(t)) {
894 xassert(c->type == CONSTANT_TRUE
895 || c->type == CONSTANT_FALSE);
900 static int flags2access(int flags)
903 if(flags&FLAG_PUBLIC) {
904 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
905 syntaxerror("invalid combination of access levels");
906 access = ACCESS_PACKAGE;
907 } else if(flags&FLAG_PRIVATE) {
908 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
909 syntaxerror("invalid combination of access levels");
910 access = ACCESS_PRIVATE;
911 } else if(flags&FLAG_PROTECTED) {
912 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
913 syntaxerror("invalid combination of access levels");
914 access = ACCESS_PROTECTED;
916 access = ACCESS_PACKAGEINTERNAL;
922 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
924 memberinfo_t*minfo = 0;
927 minfo = memberinfo_register_global(flags2access(flags), state->package, name, MEMBER_METHOD);
928 minfo->return_type = return_type;
929 } else if(getset != KW_GET && getset != KW_SET) {
931 if((minfo = registry_findmember(state->cls->info, name, 0))) {
932 if(minfo->parent == state->cls->info) {
933 syntaxerror("class already contains a member/method called '%s'", name);
934 } else if(!minfo->parent) {
935 syntaxerror("internal error: overriding method %s, which doesn't have parent", name);
937 if(!(minfo->flags&(FLAG_STATIC|FLAG_PRIVATE)))
938 syntaxerror("function %s already exists in superclass. Did you forget the 'override' keyword?");
941 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
942 minfo->return_type = return_type;
943 // getslot on a member slot only returns "undefined", so no need
944 // to actually store these
945 //state->minfo->slot = state->method->abc->method->trait->slot_id;
947 //class getter/setter
948 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
952 else if(params->list && params->list->param)
953 type = params->list->param->type;
954 // not sure wether to look into superclasses here, too
955 if((minfo=registry_findmember(state->cls->info, name, 0))) {
956 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
957 syntaxerror("class already contains a member or method called '%s'", name);
959 syntaxerror("getter/setter for '%s' already defined", name);
960 /* make a setter or getter into a getset */
965 if(type && minfo->type != type)
966 syntaxerror("different type in getter and setter");
968 minfo = memberinfo_register(state->cls->info, name, gs);
971 /* can't assign a slot as getter and setter might have different slots */
972 //minfo->slot = slot;
974 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
975 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
976 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
977 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
978 if(flags&FLAG_PACKAGEINTERNAL) minfo->flags |= FLAG_PACKAGEINTERNAL;
979 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
983 static void function_initvars(params_t*params, int flags)
985 if(state->method->inner)
986 new_variable("this", 0, 0);
987 else if(!state->method->is_global)
988 new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0);
990 new_variable("globalscope", 0, 0);
993 for(p=params->list;p;p=p->next) {
994 new_variable(p->param->name, p->param->type, 0);
997 methodstate_list_t*l = state->method->innerfunctions;
999 methodstate_t*m = l->methodstate;
1000 m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0);
1005 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1007 parserassert(state->method && state->method->info);
1009 methodstate_t*parent_method = state->method;
1018 state->new_vars = 1;
1021 state->method = rfx_calloc(sizeof(methodstate_t));
1022 state->method->inner = 1;
1023 state->method->variable_count = 0;
1024 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1026 NEW(memberinfo_t,minfo);
1028 state->method->info = minfo;
1030 list_append(parent_method->innerfunctions, state->method);
1032 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1036 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1037 parserassert(state->method);
1039 state->method->info->return_type = return_type;
1040 function_initvars(params, 0);
1044 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1045 params_t*params, classinfo_t*return_type)
1047 if(state->method && state->method->info) {
1048 syntaxerror("not able to start another method scope");
1053 state->method = rfx_calloc(sizeof(methodstate_t));
1054 state->method->has_super = 0;
1055 state->method->variable_count = 0;
1058 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1060 state->method->is_global = 1;
1061 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1063 if(state->method->is_constructor)
1064 name = "__as3_constructor__";
1067 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1069 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1073 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1074 parserassert(state->method);
1077 state->cls->has_constructor |= state->method->is_constructor;
1080 state->method->info->return_type = return_type;
1081 function_initvars(params, flags);
1085 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1086 params_t*params, classinfo_t*return_type, code_t*body)
1095 multiname_t*type2 = sig2mname(return_type);
1097 if(state->method->inner) {
1098 f = state->method->abc;
1099 abc_method_init(f, global->file, type2, 1);
1100 } else if(state->method->is_constructor) {
1101 f = abc_class_getconstructor(state->cls->abc, type2);
1102 } else if(!state->method->is_global) {
1103 namespace_t mname_ns = flags2namespace(flags, "");
1104 multiname_t mname = {QNAME, &mname_ns, 0, name};
1106 if(flags&FLAG_STATIC)
1107 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1109 f = abc_class_method(state->cls->abc, type2, &mname);
1110 slot = f->trait->slot_id;
1112 namespace_t mname_ns = flags2namespace(flags, state->package);
1113 multiname_t mname = {QNAME, &mname_ns, 0, name};
1115 f = abc_method_new(global->file, type2, 1);
1116 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1117 //abc_code_t*c = global->init->method->body->code;
1119 //flash doesn't seem to allow us to access function slots
1120 //state->method->info->slot = slot;
1122 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1123 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1124 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1125 if(params->varargs) f->flags |= METHOD_NEED_REST;
1129 for(p=params->list;p;p=p->next) {
1130 if(params->varargs && !p->next) {
1131 break; //varargs: omit last parameter in function signature
1133 multiname_t*m = sig2mname(p->param->type);
1134 list_append(f->parameters, m);
1135 if(p->param->value) {
1136 check_constant_against_type(p->param->type, p->param->value);
1137 opt=1;list_append(f->optional_parameters, p->param->value);
1139 syntaxerror("non-optional parameter not allowed after optional parameters");
1142 check_code_for_break(body);
1145 f->body->code = body;
1146 f->body->exceptions = state->method->exceptions;
1147 } else { //interface
1149 syntaxerror("interface methods can't have a method body");
1156 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1161 void breakjumpsto(code_t*c, char*name, code_t*jump)
1164 if(c->opcode == OPCODE___BREAK__) {
1165 string_t*name2 = c->data[0];
1166 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1167 c->opcode = OPCODE_JUMP;
1174 void continuejumpsto(code_t*c, char*name, code_t*jump)
1177 if(c->opcode == OPCODE___CONTINUE__) {
1178 string_t*name2 = c->data[0];
1179 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1180 c->opcode = OPCODE_JUMP;
1188 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1189 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1190 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1192 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1194 if(!type1 || !type2)
1195 return registry_getanytype();
1196 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1197 return registry_getanytype();
1200 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1209 return registry_getanytype();
1211 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1216 return abc_coerce_a(c);
1220 // cast an "any" type to a specific type. subject to
1221 // runtime exceptions
1222 return abc_coerce2(c, &m);
1225 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1226 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1227 // allow conversion between number types
1228 return abc_coerce2(c, &m);
1230 //printf("%s.%s\n", from.package, from.name);
1231 //printf("%s.%s\n", to.package, to.name);
1233 classinfo_t*supertype = from;
1235 if(supertype == to) {
1236 // target type is one of from's superclasses
1237 return abc_coerce2(c, &m);
1240 while(supertype->interfaces[t]) {
1241 if(supertype->interfaces[t]==to) {
1242 // target type is one of from's interfaces
1243 return abc_coerce2(c, &m);
1247 supertype = supertype->superclass;
1249 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1251 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1253 syntaxerror("can't convert type %s to %s", from->name, to->name);
1254 return 0; // make gcc happy
1257 code_t*defaultvalue(code_t*c, classinfo_t*type)
1259 if(TYPE_IS_INT(type)) {
1260 c = abc_pushbyte(c, 0);
1261 } else if(TYPE_IS_UINT(type)) {
1262 c = abc_pushuint(c, 0);
1263 } else if(TYPE_IS_FLOAT(type)) {
1265 } else if(TYPE_IS_BOOLEAN(type)) {
1266 c = abc_pushfalse(c);
1268 //c = abc_pushundefined(c);
1270 c = abc_pushnull(c);
1272 c = abc_coerce2(c, &m);
1277 char is_pushundefined(code_t*c)
1279 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1282 static classinfo_t* find_class(char*name)
1286 c = registry_findclass(state->package, name);
1289 /* try explicit imports */
1290 dictentry_t* e = dict_get_slot(state->imports, name);
1293 if(!strcmp(e->key, name)) {
1294 c = (classinfo_t*)e->data;
1300 /* try package.* imports */
1301 import_list_t*l = state->wildcard_imports;
1303 //printf("does package %s contain a class %s?\n", l->import->package, name);
1304 c = registry_findclass(l->import->package, name);
1309 /* try global package */
1310 c = registry_findclass("", name);
1313 /* try local "filename" package */
1314 c = registry_findclass(current_filename_short, name);
1320 static char is_getlocal(code_t*c)
1322 if(!c || c->prev || c->next)
1324 return(c->opcode == OPCODE_GETLOCAL
1325 || c->opcode == OPCODE_GETLOCAL_0
1326 || c->opcode == OPCODE_GETLOCAL_1
1327 || c->opcode == OPCODE_GETLOCAL_2
1328 || c->opcode == OPCODE_GETLOCAL_3);
1330 static int getlocalnr(code_t*c)
1332 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1333 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1334 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1335 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1336 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1337 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1341 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1345 [prefix code] [read instruction]
1349 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1352 if(in && in->opcode == OPCODE_COERCE_A) {
1353 in = code_cutlast(in);
1356 syntaxerror("internal error");
1358 /* chop off read instruction */
1362 prefix = r->prev;r->prev = 0;
1368 char use_temp_var = readbefore;
1370 /* generate the write instruction, and maybe append a dup to the prefix code */
1371 code_t* write = abc_nop(0);
1372 if(r->opcode == OPCODE_GETPROPERTY) {
1373 write->opcode = OPCODE_SETPROPERTY;
1374 multiname_t*m = (multiname_t*)r->data[0];
1375 write->data[0] = multiname_clone(m);
1376 if(m->type == QNAME || m->type == MULTINAME) {
1378 prefix = abc_dup(prefix); // we need the object, too
1381 } else if(m->type == MULTINAMEL) {
1383 /* dupping two values on the stack requires 5 operations and one register-
1384 couldn't adobe just have given us a dup2? */
1385 int temp = gettempvar();
1386 prefix = abc_setlocal(prefix, temp);
1387 prefix = abc_dup(prefix);
1388 prefix = abc_getlocal(prefix, temp);
1389 prefix = abc_swap(prefix);
1390 prefix = abc_getlocal(prefix, temp);
1392 prefix = abc_kill(prefix, temp);
1396 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1398 } else if(r->opcode == OPCODE_GETSLOT) {
1399 write->opcode = OPCODE_SETSLOT;
1400 write->data[0] = r->data[0];
1402 prefix = abc_dup(prefix); // we need the object, too
1405 } else if(r->opcode == OPCODE_GETLOCAL) {
1406 write->opcode = OPCODE_SETLOCAL;
1407 write->data[0] = r->data[0];
1408 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1409 write->opcode = OPCODE_SETLOCAL_0;
1410 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1411 write->opcode = OPCODE_SETLOCAL_1;
1412 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1413 write->opcode = OPCODE_SETLOCAL_2;
1414 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1415 write->opcode = OPCODE_SETLOCAL_3;
1418 syntaxerror("illegal lvalue: can't assign a value to this expression");
1425 /* with getproperty/getslot, we have to be extra careful not
1426 to execute the read code twice, as it might have side-effects
1427 (e.g. if the property is in fact a setter/getter combination)
1429 So read the value, modify it, and write it again,
1430 using prefix only once and making sure (by using a temporary
1431 register) that the return value is what we just wrote */
1432 temp = gettempvar();
1433 c = code_append(c, prefix);
1434 c = code_append(c, r);
1437 c = abc_setlocal(c, temp);
1439 c = code_append(c, middlepart);
1442 c = abc_setlocal(c, temp);
1444 c = code_append(c, write);
1445 c = abc_getlocal(c, temp);
1446 c = abc_kill(c, temp);
1448 /* if we're allowed to execute the read code twice *and*
1449 the middlepart doesn't modify the code, things are easier.
1451 code_t* r2 = code_dup(r);
1452 //c = code_append(c, prefix);
1453 parserassert(!prefix);
1454 c = code_append(c, r);
1455 c = code_append(c, middlepart);
1456 c = code_append(c, write);
1457 c = code_append(c, r2);
1460 /* even smaller version: overwrite the value without reading
1464 c = code_append(c, prefix);
1467 c = code_append(c, middlepart);
1468 c = code_append(c, write);
1469 c = code_append(c, r);
1471 temp = gettempvar();
1473 c = code_append(c, prefix);
1475 c = code_append(c, middlepart);
1477 c = abc_setlocal(c, temp);
1478 c = code_append(c, write);
1479 c = abc_getlocal(c, temp);
1480 c = abc_kill(c, temp);
1486 char is_break_or_jump(code_t*c)
1490 if(c->opcode == OPCODE_JUMP ||
1491 c->opcode == OPCODE___BREAK__ ||
1492 c->opcode == OPCODE___CONTINUE__ ||
1493 c->opcode == OPCODE_THROW ||
1494 c->opcode == OPCODE_RETURNVOID ||
1495 c->opcode == OPCODE_RETURNVALUE) {
1502 #define IS_FINALLY_TARGET(op) \
1503 ((op) == OPCODE___CONTINUE__ || \
1504 (op) == OPCODE___BREAK__ || \
1505 (op) == OPCODE_RETURNVOID || \
1506 (op) == OPCODE_RETURNVALUE || \
1507 (op) == OPCODE___RETHROW__)
1509 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1511 #define NEED_EXTRA_STACK_ARG
1512 code_t*finally_label = abc_nop(0);
1513 NEW(lookupswitch_t, l);
1519 code_t*prev = i->prev;
1520 if(IS_FINALLY_TARGET(i->opcode)) {
1523 if(i->opcode == OPCODE___RETHROW__ ||
1524 i->opcode == OPCODE_RETURNVALUE) {
1525 if(i->opcode == OPCODE___RETHROW__)
1526 i->opcode = OPCODE_THROW;
1528 p = abc_coerce_a(p);
1529 p = abc_setlocal(p, tempvar);
1531 p = abc_pushbyte(p, count++);
1532 p = abc_jump(p, finally_label);
1533 code_t*target = p = abc_label(p);
1534 #ifdef NEED_EXTRA_STACK_ARG
1538 p = abc_getlocal(p, tempvar);
1541 p->next = i;i->prev = p;
1542 list_append(l->targets, target);
1548 c = abc_pushbyte(c, -1);
1549 c = code_append(c, finally_label);
1550 c = code_append(c, finally);
1552 #ifdef NEED_EXTRA_STACK_ARG
1555 c = abc_lookupswitch(c, l);
1556 c = l->def = abc_label(c);
1557 #ifdef NEED_EXTRA_STACK_ARG
1564 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1568 code_t*prev = i->prev;
1569 if(IS_FINALLY_TARGET(i->opcode)) {
1570 if(i->opcode == OPCODE___RETHROW__)
1571 i->opcode = OPCODE_THROW;
1572 code_t*end = code_dup(finally);
1573 code_t*start = code_start(end);
1574 if(prev) prev->next = start;
1581 return code_append(c, finally);
1584 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1590 int num_insertion_points=0;
1592 if(IS_FINALLY_TARGET(i->opcode))
1593 num_insertion_points++;
1600 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1605 int simple_version_cost = (1+num_insertion_points)*code_size;
1606 int lookup_version_cost = 4*num_insertion_points + 5;
1608 if(cantdup || simple_version_cost > lookup_version_cost) {
1609 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1610 return insert_finally_lookup(c, finally, tempvar);
1612 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1613 return insert_finally_simple(c, finally, tempvar);
1617 #define PASS1 }} if(as3_pass == 1) {{
1618 #define PASS1END }} if(as3_pass == 2) {{
1619 #define PASS2 }} if(as3_pass == 2) {{
1620 #define PASS12 }} {{
1621 #define PASS12END }} if(as3_pass == 2) {{
1627 /* ------------ code blocks / statements ---------------- */
1629 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1631 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1632 PROGRAM_CODE_LIST: PROGRAM_CODE
1633 | PROGRAM_CODE_LIST PROGRAM_CODE
1635 PROGRAM_CODE: PACKAGE_DECLARATION
1636 | INTERFACE_DECLARATION
1638 | FUNCTION_DECLARATION
1643 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1644 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1645 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1647 INPACKAGE_CODE: INTERFACE_DECLARATION
1649 | FUNCTION_DECLARATION
1654 MAYBECODE: CODE {$$=$1;}
1655 MAYBECODE: {$$=code_new();}
1657 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1658 CODE: CODEPIECE {$$=$1;}
1660 // code which also may appear outside a method
1661 CODE_STATEMENT: IMPORT
1663 CODE_STATEMENT: FOR_IN
1664 CODE_STATEMENT: WHILE
1665 CODE_STATEMENT: DO_WHILE
1666 CODE_STATEMENT: SWITCH
1668 CODE_STATEMENT: WITH
1670 CODE_STATEMENT: VOIDEXPRESSION
1672 // code which may appear anywhere
1673 CODEPIECE: ';' {$$=0;}
1674 CODEPIECE: CODE_STATEMENT
1675 CODEPIECE: VARIABLE_DECLARATION
1681 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1682 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;}
1684 CODEBLOCK : '{' CODE '}' {$$=$2;}
1685 CODEBLOCK : '{' '}' {$$=0;}
1686 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1687 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1689 /* ------------ package init code ------------------- */
1691 PACKAGE_INITCODE: CODE_STATEMENT {
1692 code_t**cc = &global->init->method->body->code;
1693 *cc = code_append(*cc, $1);
1696 /* ------------ variables --------------------------- */
1698 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1699 | {$$.c=abc_pushundefined(0);
1703 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1704 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1706 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1707 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1709 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1711 if(variable_exists($1))
1712 syntaxerror("Variable %s already defined", $1);
1714 if(!is_subtype_of($3.t, $2)) {
1715 syntaxerror("Can't convert %s to %s", $3.t->name,
1719 int index = new_variable($1, $2, 1);
1722 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1724 $$ = converttype($$, $3.t, $2);
1725 $$ = abc_setlocal($$, index);
1727 $$ = defaultvalue(0, $2);
1728 $$ = abc_setlocal($$, index);
1731 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1733 $$ = abc_coerce_a($$);
1734 $$ = abc_setlocal($$, index);
1740 /* that's the default for a local register, anyway
1742 state->method->initcode = abc_pushundefined(state->method->initcode);
1743 state->method->initcode = abc_setlocal(state->method->initcode, index);
1745 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1748 /* ------------ control flow ------------------------- */
1750 MAYBEELSE: %prec below_else {$$ = code_new();}
1751 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1752 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1754 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1757 $$ = code_append($$, $4.c);
1758 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1760 $$ = code_append($$, $6);
1762 myjmp = $$ = abc_jump($$, 0);
1764 myif->branch = $$ = abc_nop($$);
1766 $$ = code_append($$, $7);
1767 myjmp->branch = $$ = abc_nop($$);
1773 FOR_INIT : {$$=code_new();}
1774 FOR_INIT : VARIABLE_DECLARATION
1775 FOR_INIT : VOIDEXPRESSION
1777 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
1778 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1779 $$=$2;new_variable($2,$3,1);
1781 FOR_IN_INIT : T_IDENTIFIER {
1785 FOR_START : T_FOR '(' {new_state();$$.name=$1;$$.each=0;}
1786 FOR_START : T_FOR "each" '(' {new_state();$$.name=$1;$$.each=1;}
1788 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1789 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1791 $$ = code_append($$, $2);
1792 code_t*loopstart = $$ = abc_label($$);
1793 $$ = code_append($$, $4.c);
1794 code_t*myif = $$ = abc_iffalse($$, 0);
1795 $$ = code_append($$, $8);
1796 code_t*cont = $$ = abc_nop($$);
1797 $$ = code_append($$, $6);
1798 $$ = abc_jump($$, loopstart);
1799 code_t*out = $$ = abc_nop($$);
1800 breakjumpsto($$, $1.name, out);
1801 continuejumpsto($$, $1.name, cont);
1808 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
1809 variable_t*var = find_variable($2);
1810 char*tmp1name = concat2($2, "__tmp1__");
1811 int it = new_variable(tmp1name, TYPE_INT, 0);
1812 char*tmp2name = concat2($2, "__array__");
1813 int array = new_variable(tmp1name, 0, 0);
1816 $$ = code_append($$, $4.c);
1817 $$ = abc_coerce_a($$);
1818 $$ = abc_setlocal($$, array);
1819 $$ = abc_pushbyte($$, 0);
1820 $$ = abc_setlocal($$, it);
1822 code_t*loopstart = $$ = abc_label($$);
1824 $$ = abc_hasnext2($$, array, it);
1825 code_t*myif = $$ = abc_iffalse($$, 0);
1826 $$ = abc_getlocal($$, array);
1827 $$ = abc_getlocal($$, it);
1829 $$ = abc_nextname($$);
1831 $$ = abc_nextvalue($$);
1832 $$ = converttype($$, 0, var->type);
1833 $$ = abc_setlocal($$, var->index);
1835 $$ = code_append($$, $6);
1836 $$ = abc_jump($$, loopstart);
1838 code_t*out = $$ = abc_nop($$);
1839 breakjumpsto($$, $1.name, out);
1840 continuejumpsto($$, $1.name, loopstart);
1851 WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1855 code_t*myjmp = $$ = abc_jump($$, 0);
1856 code_t*loopstart = $$ = abc_label($$);
1857 $$ = code_append($$, $6);
1858 code_t*cont = $$ = abc_nop($$);
1859 myjmp->branch = cont;
1860 $$ = code_append($$, $4.c);
1861 $$ = abc_iftrue($$, loopstart);
1862 code_t*out = $$ = abc_nop($$);
1863 breakjumpsto($$, $1, out);
1864 continuejumpsto($$, $1, cont);
1870 DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
1872 code_t*loopstart = $$ = abc_label($$);
1873 $$ = code_append($$, $3);
1874 code_t*cont = $$ = abc_nop($$);
1875 $$ = code_append($$, $6.c);
1876 $$ = abc_iftrue($$, loopstart);
1877 code_t*out = $$ = abc_nop($$);
1878 breakjumpsto($$, $1, out);
1879 continuejumpsto($$, $1, cont);
1885 BREAK : "break" %prec prec_none {
1886 $$ = abc___break__(0, "");
1888 BREAK : "break" T_IDENTIFIER {
1889 $$ = abc___break__(0, $2);
1891 CONTINUE : "continue" %prec prec_none {
1892 $$ = abc___continue__(0, "");
1894 CONTINUE : "continue" T_IDENTIFIER {
1895 $$ = abc___continue__(0, $2);
1898 MAYBE_CASE_LIST : {$$=0;}
1899 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
1900 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
1901 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
1902 CASE_LIST: CASE {$$=$1;}
1903 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
1905 CASE: "case" E ':' MAYBECODE {
1907 $$ = code_append($$, $2.c);
1908 code_t*j = $$ = abc_ifne($$, 0);
1909 $$ = code_append($$, $4);
1910 if($$->opcode != OPCODE___BREAK__) {
1911 $$ = abc___fallthrough__($$, "");
1913 code_t*e = $$ = abc_nop($$);
1916 DEFAULT: "default" ':' MAYBECODE {
1919 SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
1921 $$ = code_append($$, $7);
1922 code_t*out = $$ = abc_pop($$);
1923 breakjumpsto($$, $1, out);
1925 code_t*c = $$,*lastblock=0;
1927 if(c->opcode == OPCODE_IFNE) {
1928 if(!c->next) syntaxerror("internal error in fallthrough handling");
1930 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
1932 c->opcode = OPCODE_JUMP;
1933 c->branch = lastblock;
1935 /* fall through end of switch */
1936 c->opcode = OPCODE_NOP;
1946 /* ------------ try / catch /finally ---------------- */
1948 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);}
1950 namespace_t name_ns = {ACCESS_PACKAGE, ""};
1951 multiname_t name = {QNAME, &name_ns, 0, $3};
1953 NEW(abc_exception_t, e)
1954 e->exc_type = sig2mname($4);
1955 e->var_name = multiname_clone(&name);
1959 int i = find_variable_safe($3)->index;
1960 e->target = c = abc_nop(0);
1961 c = abc_setlocal(c, i);
1962 c = code_append(c, $8);
1968 FINALLY: "finally" '{' {new_state();state->exception_name=0;} MAYBECODE '}' {
1974 NEW(abc_exception_t, e)
1975 e->exc_type = 0; //all exceptions
1976 e->var_name = 0; //no name
1979 e->to = code_append(e->to, $4);
1985 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
1986 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
1987 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
1988 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
1992 list_append($$.l,$2);
1993 $$.finally = $2->to;$2->to=0;
1996 CATCH_FINALLY_LIST: FINALLY {
2000 list_append($$.l,$1);
2001 $$.finally = $1->to;$1->to=0;
2005 TRY : "try" '{' {new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2006 code_t*out = abc_nop(0);
2008 code_t*start = abc_nop(0);
2009 $$ = code_append(start, $4);
2010 if(!is_break_or_jump($4)) {
2011 $$ = abc_jump($$, out);
2013 code_t*end = $$ = abc_nop($$);
2017 tmp = new_variable("__finally__", 0, 0);
2019 abc_exception_list_t*l = $6.l;
2022 abc_exception_t*e = l->abc_exception;
2024 $$ = code_append($$, e->target);
2025 $$ = abc_jump($$, out);
2027 parserassert((ptroff_t)$6.finally);
2029 e->target = $$ = abc_nop($$);
2030 $$ = abc___rethrow__($$);
2038 $$ = code_append($$, out);
2040 $$ = insert_finally($$, $6.finally, tmp);
2042 list_concat(state->method->exceptions, $6.l);
2048 /* ------------ throw ------------------------------- */
2050 THROW : "throw" EXPRESSION {
2054 THROW : "throw" %prec prec_none {
2055 if(!state->exception_name)
2056 syntaxerror("re-throw only possible within a catch block");
2057 variable_t*v = find_variable(state->exception_name);
2059 $$=abc_getlocal($$, v->index);
2063 /* ------------ with -------------------------------- */
2065 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2067 $$ = abc_pushscope($$);
2068 $$ = code_append($$, $5);
2069 $$ = abc_popscope($$);
2072 /* ------------ packages and imports ---------------- */
2074 X_IDENTIFIER: T_IDENTIFIER
2075 | "package" {PASS12 $$="package";}
2077 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2078 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2080 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2081 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2082 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2083 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2085 IMPORT : "import" QNAME {
2088 syntaxerror("Couldn't import class\n");
2089 state_has_imports();
2090 dict_put(state->imports, c->name, c);
2093 IMPORT : "import" PACKAGE '.' '*' {
2096 state_has_imports();
2097 list_append(state->wildcard_imports, i);
2101 /* ------------ classes and interfaces (header) -------------- */
2103 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2104 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2105 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2106 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2108 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2109 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2110 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2111 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2112 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2113 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2114 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2115 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2116 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2118 EXTENDS : {$$=registry_getobjectclass();}
2119 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
2121 EXTENDS_LIST : {PASS12 $$=list_new();}
2122 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {PASS12 $$=$2;}
2124 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2125 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {PASS12 $$=$2;}
2127 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2128 EXTENDS IMPLEMENTS_LIST
2129 '{' {PASS12 startclass($1,$3,$4,$5, 0);}
2131 '}' {PASS12 endclass();$$=0;}
2133 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2135 '{' {PASS12 startclass($1,$3,0,$4,1);}
2136 MAYBE_INTERFACE_BODY
2137 '}' {PASS12 endclass();$$=0;}
2139 /* ------------ classes and interfaces (body) -------------- */
2142 MAYBE_CLASS_BODY : CLASS_BODY
2143 CLASS_BODY : CLASS_BODY_ITEM
2144 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2145 CLASS_BODY_ITEM : ';'
2146 CLASS_BODY_ITEM : SLOT_DECLARATION
2147 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2149 CLASS_BODY_ITEM : CODE_STATEMENT {
2150 code_t*c = state->cls->static_init;
2151 c = code_append(c, $1);
2152 state->cls->static_init = c;
2155 MAYBE_INTERFACE_BODY :
2156 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2157 INTERFACE_BODY : IDECLARATION
2158 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2160 IDECLARATION : "var" T_IDENTIFIER {
2161 syntaxerror("variable declarations not allowed in interfaces");
2163 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2166 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2167 syntaxerror("invalid method modifiers: interface methods always need to be public");
2169 startfunction(0,$1,$3,$4,&$6,$8);
2170 endfunction(0,$1,$3,$4,&$6,$8, 0);
2173 /* ------------ classes and interfaces (body, slots ) ------- */
2175 VARCONST: "var" | "const"
2177 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
2179 memberinfo_t* info = state->cls?
2180 memberinfo_register(state->cls->info, $3, MEMBER_SLOT):
2181 memberinfo_register_global(flags2access($1), state->package, $3, MEMBER_SLOT);
2184 info->flags = flags;
2187 namespace_t mname_ns = {flags2access(flags), ""};
2188 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2190 trait_list_t**traits;
2194 mname_ns.name = state->package;
2195 traits = &global->init->traits;
2196 code = &global->init->method->body->code;
2197 } else if(flags&FLAG_STATIC) {
2199 traits = &state->cls->abc->static_traits;
2200 code = &state->cls->static_init;
2202 // instance variable
2203 traits = &state->cls->abc->traits;
2204 code = &state->cls->init;
2210 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2212 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2214 info->slot = t->slot_id;
2216 /* initalization code (if needed) */
2218 if($5.c && !is_pushundefined($5.c)) {
2219 c = abc_getlocal_0(c);
2220 c = code_append(c, $5.c);
2221 c = converttype(c, $5.t, $4);
2222 c = abc_setslot(c, t->slot_id);
2225 *code = code_append(*code, c);
2228 t->kind= TRAIT_CONST;
2234 /* ------------ constants -------------------------------------- */
2236 MAYBESTATICCONSTANT: {$$=0;}
2237 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2239 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2240 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2241 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2242 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2243 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
2244 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2245 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2246 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2247 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2249 /* ------------ classes and interfaces (body, functions) ------- */
2251 // non-vararg version
2254 memset(&$$,0,sizeof($$));
2256 MAYBE_PARAM_LIST: PARAM_LIST {
2262 MAYBE_PARAM_LIST: "..." PARAM {
2264 memset(&$$,0,sizeof($$));
2266 list_append($$.list, $2);
2268 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2272 list_append($$.list, $4);
2276 PARAM_LIST: PARAM_LIST ',' PARAM {
2279 list_append($$.list, $3);
2283 memset(&$$,0,sizeof($$));
2284 list_append($$.list, $1);
2287 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2290 $$ = malloc(sizeof(param_t));
2295 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2298 $$ = malloc(sizeof(param_t));
2300 $$->type = TYPE_ANY;
2303 GETSET : "get" {$$=$1;}
2307 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2308 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2312 if(!state->method->info) syntaxerror("internal error");
2314 code_t*c = method_header();
2315 c = wrap_function(c, 0, $11);
2317 endfunction(0,$1,$3,$4,&$6,$8,c);
2321 MAYBE_IDENTIFIER: T_IDENTIFIER
2322 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2323 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2324 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2328 memberinfo_t*f = state->method->info;
2329 if(!f) syntaxerror("internal error");
2331 code_t*c = method_header();
2332 c = wrap_function(c, 0, $9);
2334 int index = state->method->var_index;
2335 endfunction(0,0,0,$2,&$4,$6,c);
2337 $$.c = abc_getlocal(0, index);
2338 $$.t = TYPE_FUNCTION(f);
2342 /* ------------- package + class ids --------------- */
2344 CLASS: T_IDENTIFIER {
2347 /* try current package */
2348 $$ = find_class($1);
2349 if(!$$) syntaxerror("Could not find class %s\n", $1);
2352 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2355 $$ = registry_findclass($1, $3);
2356 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
2360 QNAME: PACKAGEANDCLASS
2363 QNAME_LIST : QNAME {PASS12 $$=list_new();list_append($$, $1);}
2364 QNAME_LIST : QNAME_LIST ',' QNAME {PASS12 $$=$1;list_append($$,$3);}
2366 TYPE : QNAME {$$=$1;}
2367 | '*' {$$=registry_getanytype();}
2368 | "void" {$$=registry_getanytype();}
2370 | "String" {$$=registry_getstringclass();}
2371 | "int" {$$=registry_getintclass();}
2372 | "uint" {$$=registry_getuintclass();}
2373 | "Boolean" {$$=registry_getbooleanclass();}
2374 | "Number" {$$=registry_getnumberclass();}
2377 MAYBETYPE: ':' TYPE {$$=$2;}
2380 /* ----------function calls, delete, constructor calls ------ */
2382 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2383 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2385 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2386 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2387 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2390 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {
2392 $$.cc = code_append($1.cc, $3.c);
2396 NEW : "new" E XX MAYBE_PARAM_VALUES {
2398 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2400 code_t*paramcode = $4.cc;
2401 if($$.c->opcode == OPCODE_GETPROPERTY) {
2402 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2403 $$.c = code_cutlast($$.c);
2404 $$.c = code_append($$.c, paramcode);
2405 $$.c = abc_constructprop2($$.c, name, $4.len);
2406 multiname_destroy(name);
2407 } else if($$.c->opcode == OPCODE_GETSLOT) {
2408 int slot = (int)(ptroff_t)$$.c->data[0];
2409 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2410 multiname_t*name = t->name;
2411 $$.c = code_cutlast($$.c);
2412 $$.c = code_append($$.c, paramcode);
2413 $$.c = abc_constructprop2($$.c, name, $4.len);
2415 $$.c = code_append($$.c, paramcode);
2416 $$.c = abc_construct($$.c, $4.len);
2420 if(TYPE_IS_CLASS($2.t) && $2.t->cls) {
2423 $$.c = abc_coerce_a($$.c);
2428 /* TODO: use abc_call (for calling local variables),
2429 abc_callstatic (for calling own methods)
2432 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2435 if($$.c->opcode == OPCODE_COERCE_A) {
2436 $$.c = code_cutlast($$.c);
2438 code_t*paramcode = $3.cc;
2441 if($$.c->opcode == OPCODE_GETPROPERTY) {
2442 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2443 $$.c = code_cutlast($$.c);
2444 $$.c = code_append($$.c, paramcode);
2445 $$.c = abc_callproperty2($$.c, name, $3.len);
2446 multiname_destroy(name);
2447 } else if($$.c->opcode == OPCODE_GETSLOT) {
2448 int slot = (int)(ptroff_t)$$.c->data[0];
2449 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2450 if(t->kind!=TRAIT_METHOD) {
2451 //ok: flash allows to assign closures to members.
2453 multiname_t*name = t->name;
2454 $$.c = code_cutlast($$.c);
2455 $$.c = code_append($$.c, paramcode);
2456 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2457 $$.c = abc_callproperty2($$.c, name, $3.len);
2458 } else if($$.c->opcode == OPCODE_GETSUPER) {
2459 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2460 $$.c = code_cutlast($$.c);
2461 $$.c = code_append($$.c, paramcode);
2462 $$.c = abc_callsuper2($$.c, name, $3.len);
2463 multiname_destroy(name);
2465 $$.c = abc_getglobalscope($$.c);
2466 $$.c = code_append($$.c, paramcode);
2467 $$.c = abc_call($$.c, $3.len);
2470 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
2471 $$.t = $1.t->function->return_type;
2473 $$.c = abc_coerce_a($$.c);
2478 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2479 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2480 if(!state->method) syntaxerror("super() not allowed outside of a function");
2481 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2484 $$.c = abc_getlocal_0($$.c);
2486 $$.c = code_append($$.c, $3.cc);
2488 this is dependent on the control path, check this somewhere else
2489 if(state->method->has_super)
2490 syntaxerror("constructor may call super() only once");
2492 state->method->has_super = 1;
2494 $$.c = abc_constructsuper($$.c, $3.len);
2495 $$.c = abc_pushundefined($$.c);
2499 DELETE: "delete" E {
2501 if($$.c->opcode == OPCODE_COERCE_A) {
2502 $$.c = code_cutlast($$.c);
2504 multiname_t*name = 0;
2505 if($$.c->opcode == OPCODE_GETPROPERTY) {
2506 $$.c->opcode = OPCODE_DELETEPROPERTY;
2507 } else if($$.c->opcode == OPCODE_GETSLOT) {
2508 int slot = (int)(ptroff_t)$$.c->data[0];
2509 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
2510 $$.c = code_cutlast($$.c);
2511 $$.c = abc_deleteproperty2($$.c, name);
2513 $$.c = abc_getlocal_0($$.c);
2514 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2515 $$.c = abc_deleteproperty2($$.c, &m);
2517 $$.t = TYPE_BOOLEAN;
2520 RETURN: "return" %prec prec_none {
2521 $$ = abc_returnvoid(0);
2523 RETURN: "return" EXPRESSION {
2525 $$ = abc_returnvalue($$);
2528 // ----------------------- expression types -------------------------------------
2530 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2531 EXPRESSION : E %prec below_minus {$$ = $1;}
2532 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2534 $$.c = cut_last_push($$.c);
2535 $$.c = code_append($$.c,$3.c);
2538 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2539 $$=cut_last_push($1.c);
2542 // ----------------------- expression evaluation -------------------------------------
2544 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2545 //V : CONSTANT {$$ = 0;}
2547 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2548 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2549 //V : NEW {$$ = $1.c;}
2551 //V : DELETE {$$ = $1.c;}
2552 E : DELETE {$$ = $1;}
2558 namespace_t ns = {ACCESS_PACKAGE, ""};
2559 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2561 $$.c = abc_getlex2($$.c, &m);
2562 $$.c = abc_pushstring($$.c, $1.pattern);
2563 $$.c = abc_construct($$.c, 1);
2565 $$.c = abc_getlex2($$.c, &m);
2566 $$.c = abc_pushstring($$.c, $1.pattern);
2567 $$.c = abc_pushstring($$.c, $1.options);
2568 $$.c = abc_construct($$.c, 2);
2573 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2574 //MULTINAME(m, registry_getintclass());
2575 //$$.c = abc_coerce2($$.c, &m); // FIXME
2578 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2581 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2584 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2587 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2590 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
2593 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2596 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2597 $$.t = TYPE_BOOLEAN;
2599 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2600 $$.t = TYPE_BOOLEAN;
2602 CONSTANT : "null" {$$.c = abc_pushnull(0);
2606 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2607 $$.t = TYPE_BOOLEAN;
2609 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2610 $$.t = TYPE_BOOLEAN;
2612 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2613 $$.t = TYPE_BOOLEAN;
2615 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2616 $$.t = TYPE_BOOLEAN;
2618 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2619 $$.t = TYPE_BOOLEAN;
2621 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2622 $$.t = TYPE_BOOLEAN;
2624 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2625 $$.t = TYPE_BOOLEAN;
2627 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2628 $$.t = TYPE_BOOLEAN;
2631 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2633 $$.c = converttype($$.c, $1.t, $$.t);
2634 $$.c = abc_dup($$.c);
2635 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2636 $$.c = cut_last_push($$.c);
2637 $$.c = code_append($$.c,$3.c);
2638 $$.c = converttype($$.c, $3.t, $$.t);
2639 code_t*label = $$.c = abc_label($$.c);
2640 jmp->branch = label;
2643 $$.t = join_types($1.t, $3.t, 'A');
2644 /*printf("%08x:\n",$1.t);
2645 code_dump($1.c, 0, 0, "", stdout);
2646 printf("%08x:\n",$3.t);
2647 code_dump($3.c, 0, 0, "", stdout);
2648 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2650 $$.c = converttype($$.c, $1.t, $$.t);
2651 $$.c = abc_dup($$.c);
2652 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2653 $$.c = cut_last_push($$.c);
2654 $$.c = code_append($$.c,$3.c);
2655 $$.c = converttype($$.c, $3.t, $$.t);
2656 code_t*label = $$.c = abc_label($$.c);
2657 jmp->branch = label;
2660 E : '!' E {$$.c=$2.c;
2661 $$.c = abc_not($$.c);
2662 $$.t = TYPE_BOOLEAN;
2665 E : '~' E {$$.c=$2.c;
2666 $$.c = abc_bitnot($$.c);
2670 E : E '&' E {$$.c = code_append($1.c,$3.c);
2671 $$.c = abc_bitand($$.c);
2675 E : E '^' E {$$.c = code_append($1.c,$3.c);
2676 $$.c = abc_bitxor($$.c);
2680 E : E '|' E {$$.c = code_append($1.c,$3.c);
2681 $$.c = abc_bitor($$.c);
2685 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2686 $$.c = abc_rshift($$.c);
2689 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2690 $$.c = abc_urshift($$.c);
2693 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2694 $$.c = abc_lshift($$.c);
2698 E : E '/' E {$$.c = code_append($1.c,$3.c);
2699 $$.c = abc_divide($$.c);
2702 E : E '%' E {$$.c = code_append($1.c,$3.c);
2703 $$.c = abc_modulo($$.c);
2706 E : E '+' E {$$.c = code_append($1.c,$3.c);
2707 if(BOTH_INT($1.t, $3.t)) {
2708 $$.c = abc_add_i($$.c);
2711 $$.c = abc_add($$.c);
2712 $$.t = join_types($1.t,$3.t,'+');
2715 E : E '-' E {$$.c = code_append($1.c,$3.c);
2716 if(BOTH_INT($1.t,$3.t)) {
2717 $$.c = abc_subtract_i($$.c);
2720 $$.c = abc_subtract($$.c);
2724 E : E '*' E {$$.c = code_append($1.c,$3.c);
2725 if(BOTH_INT($1.t,$3.t)) {
2726 $$.c = abc_multiply_i($$.c);
2729 $$.c = abc_multiply($$.c);
2734 E : E "in" E {$$.c = code_append($1.c,$3.c);
2735 $$.c = abc_in($$.c);
2736 $$.t = TYPE_BOOLEAN;
2739 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2740 if(use_astype && TYPE_IS_CLASS($3.t)) {
2741 MULTINAME(m,$3.t->cls);
2742 $$.c = abc_astype2($1.c, &m);
2745 $$.c = code_append($1.c, $3.c);
2746 $$.c = abc_astypelate($$.c);
2751 E : E "instanceof" E
2752 {$$.c = code_append($1.c, $3.c);
2753 $$.c = abc_instanceof($$.c);
2754 $$.t = TYPE_BOOLEAN;
2757 E : E "is" E {$$.c = code_append($1.c, $3.c);
2758 $$.c = abc_istypelate($$.c);
2759 $$.t = TYPE_BOOLEAN;
2762 E : "typeof" '(' E ')' {
2764 $$.c = abc_typeof($$.c);
2769 $$.c = cut_last_push($2.c);
2770 $$.c = abc_pushundefined($$.c);
2774 E : "void" { $$.c = abc_pushundefined(0);
2778 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
2783 $$.c=abc_negate_i($$.c);
2786 $$.c=abc_negate($$.c);
2793 $$.c = code_append($$.c, $3.c);
2795 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
2796 $$.c = abc_getproperty2($$.c, &m);
2797 $$.t = 0; // array elements have unknown type
2800 E : '[' MAYBE_EXPRESSION_LIST ']' {
2802 $$.c = code_append($$.c, $2.cc);
2803 $$.c = abc_newarray($$.c, $2.len);
2804 $$.t = registry_getarrayclass();
2807 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
2808 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
2810 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2812 $$.cc = code_append($$.cc, $1.c);
2813 $$.cc = code_append($$.cc, $3.c);
2816 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
2819 $$.cc = code_append($$.cc, $3.c);
2820 $$.cc = code_append($$.cc, $5.c);
2825 E : '{' MAYBE_EXPRPAIR_LIST '}' {
2827 $$.c = code_append($$.c, $2.cc);
2828 $$.c = abc_newobject($$.c, $2.len/2);
2829 $$.t = registry_getobjectclass();
2834 if(BOTH_INT($1.t,$3.t)) {
2835 c=abc_multiply_i(c);
2839 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
2840 $$.c = toreadwrite($1.c, c, 0, 0);
2845 code_t*c = abc_modulo($3.c);
2846 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
2847 $$.c = toreadwrite($1.c, c, 0, 0);
2851 code_t*c = abc_lshift($3.c);
2852 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
2853 $$.c = toreadwrite($1.c, c, 0, 0);
2857 code_t*c = abc_rshift($3.c);
2858 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
2859 $$.c = toreadwrite($1.c, c, 0, 0);
2863 code_t*c = abc_urshift($3.c);
2864 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
2865 $$.c = toreadwrite($1.c, c, 0, 0);
2869 code_t*c = abc_divide($3.c);
2870 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
2871 $$.c = toreadwrite($1.c, c, 0, 0);
2875 code_t*c = abc_bitor($3.c);
2876 c=converttype(c, TYPE_INT, $1.t);
2877 $$.c = toreadwrite($1.c, c, 0, 0);
2883 if(TYPE_IS_INT($1.t)) {
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 = $3.c;
2894 if(TYPE_IS_INT($1.t)) {
2895 c=abc_subtract_i(c);
2898 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2901 $$.c = toreadwrite($1.c, c, 0, 0);
2904 E : E '=' E { code_t*c = 0;
2905 c = code_append(c, $3.c);
2906 c = converttype(c, $3.t, $1.t);
2907 $$.c = toreadwrite($1.c, c, 1, 0);
2911 E : E '?' E ':' E %prec below_assignment {
2912 $$.t = join_types($3.t,$5.t,'?');
2914 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2915 $$.c = code_append($$.c, $3.c);
2916 $$.c = converttype($$.c, $3.t, $$.t);
2917 code_t*j2 = $$.c = abc_jump($$.c, 0);
2918 $$.c = j1->branch = abc_label($$.c);
2919 $$.c = code_append($$.c, $5.c);
2920 $$.c = converttype($$.c, $3.t, $$.t);
2921 $$.c = j2->branch = abc_label($$.c);
2924 E : E "++" { code_t*c = 0;
2925 classinfo_t*type = $1.t;
2926 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
2927 int nr = getlocalnr($1.c);
2928 code_free($1.c);$1.c=0;
2929 if(TYPE_IS_INT($1.t)) {
2930 $$.c = abc_getlocal(0, nr);
2931 $$.c = abc_inclocal_i($$.c, nr);
2932 } else if(TYPE_IS_NUMBER($1.t)) {
2933 $$.c = abc_getlocal(0, nr);
2934 $$.c = abc_inclocal($$.c, nr);
2935 } else syntaxerror("internal error");
2937 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2938 c=abc_increment_i(c);
2944 c=converttype(c, type, $1.t);
2945 $$.c = toreadwrite($1.c, c, 0, 1);
2950 // TODO: use inclocal, like with ++
2951 E : E "--" { code_t*c = 0;
2952 classinfo_t*type = $1.t;
2953 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2954 c=abc_decrement_i(c);
2960 c=converttype(c, type, $1.t);
2961 $$.c = toreadwrite($1.c, c, 0, 1);
2965 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2966 classinfo_t*type = $2.t;
2967 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2968 c=abc_increment_i(c);
2974 c=converttype(c, type, $2.t);
2975 $$.c = toreadwrite($2.c, c, 0, 0);
2979 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2980 classinfo_t*type = $2.t;
2981 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2982 c=abc_decrement_i(c);
2988 c=converttype(c, type, $2.t);
2989 $$.c = toreadwrite($2.c, c, 0, 0);
2993 E : "super" '.' T_IDENTIFIER
2994 { if(!state->cls->info)
2995 syntaxerror("super keyword not allowed outside a class");
2996 classinfo_t*t = state->cls->info->superclass;
2997 if(!t) t = TYPE_OBJECT;
2999 memberinfo_t*f = registry_findmember(t, $3, 1);
3000 namespace_t ns = flags2namespace(f->flags, "");
3001 MEMBER_MULTINAME(m, f, $3);
3003 $$.c = abc_getlocal_0($$.c);
3004 $$.c = abc_getsuper2($$.c, &m);
3005 $$.t = memberinfo_gettype(f);
3008 E : E '.' T_IDENTIFIER
3010 classinfo_t*t = $1.t;
3012 if(TYPE_IS_CLASS(t) && t->cls) {
3017 memberinfo_t*f = registry_findmember(t, $3, 1);
3019 if(f && !is_static != !(f->flags&FLAG_STATIC))
3021 if(f && f->slot && !noslot) {
3022 $$.c = abc_getslot($$.c, f->slot);
3024 MEMBER_MULTINAME(m, f, $3);
3025 $$.c = abc_getproperty2($$.c, &m);
3027 /* determine type */
3028 $$.t = memberinfo_gettype(f);
3030 $$.c = abc_coerce_a($$.c);
3032 /* when resolving a property on an unknown type, we do know the
3033 name of the property (and don't seem to need the package), but
3034 we need to make avm2 try out all access modes */
3035 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3036 $$.c = abc_getproperty2($$.c, &m);
3037 $$.c = abc_coerce_a($$.c);
3038 $$.t = registry_getanytype();
3042 VAR_READ : T_IDENTIFIER {
3049 /* look at variables */
3050 if((v = find_variable($1))) {
3051 // $1 is a local variable
3052 $$.c = abc_getlocal($$.c, v->index);
3057 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3059 /* look at current class' members */
3060 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
3061 (f->flags&FLAG_STATIC) >= i_am_static) {
3062 // $1 is a function in this class
3063 int var_is_static = (f->flags&FLAG_STATIC);
3065 if(f->kind == MEMBER_METHOD) {
3066 $$.t = TYPE_FUNCTION(f);
3070 if(var_is_static && !i_am_static) {
3071 /* access to a static member from a non-static location.
3072 do this via findpropstrict:
3073 there doesn't seem to be any non-lookup way to access
3074 static properties of a class */
3075 state->method->late_binding = 1;
3077 namespace_t ns = {flags2access(f->flags), ""};
3078 multiname_t m = {QNAME, &ns, 0, $1};
3079 $$.c = abc_findpropstrict2($$.c, &m);
3080 $$.c = abc_getproperty2($$.c, &m);
3082 } else if(f->slot>0) {
3083 $$.c = abc_getlocal_0($$.c);
3084 $$.c = abc_getslot($$.c, f->slot);
3087 namespace_t ns = {flags2access(f->flags), ""};
3088 multiname_t m = {QNAME, &ns, 0, $1};
3089 $$.c = abc_getlocal_0($$.c);
3090 $$.c = abc_getproperty2($$.c, &m);
3095 /* look at actual classes, in the current package and imported */
3096 if((a = find_class($1))) {
3097 if(a->flags & FLAG_METHOD) {
3099 $$.c = abc_findpropstrict2($$.c, &m);
3100 $$.c = abc_getproperty2($$.c, &m);
3101 if(a->function->kind == MEMBER_METHOD) {
3102 $$.t = TYPE_FUNCTION(a->function);
3104 $$.t = a->function->type;
3108 $$.c = abc_getglobalscope($$.c);
3109 $$.c = abc_getslot($$.c, a->slot);
3112 $$.c = abc_getlex2($$.c, &m);
3114 $$.t = TYPE_CLASS(a);
3119 /* unknown object, let the avm2 resolve it */
3121 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3122 state->method->late_binding = 1;
3124 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3127 $$.c = abc_findpropstrict2($$.c, &m);
3128 $$.c = abc_getproperty2($$.c, &m);
3133 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
3134 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
3135 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3137 // ----------------- namespaces -------------------------------------------------
3139 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3140 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3141 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3143 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {$$=0;}