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"
45 enum yytokentype token;
48 classinfo_t*classinfo;
49 classinfo_list_t*classinfo_list;
52 unsigned int number_uint;
56 //typedcode_list_t*value_list;
57 codeandnumber_t value_list;
63 for_start_t for_start;
64 abc_exception_t *exception;
67 abc_exception_list_t *l;
73 %token<id> T_IDENTIFIER T_NAMESPACE
75 %token<regexp> T_REGEXP
77 %token<number_int> T_INT
78 %token<number_uint> T_UINT
79 %token<number_uint> T_BYTE
80 %token<number_uint> T_SHORT
81 %token<number_float> T_FLOAT
83 %token<id> T_FOR "for"
84 %token<id> T_WHILE "while"
86 %token<id> T_SWITCH "switch"
88 %token<token> KW_IMPLEMENTS "implements"
89 %token<token> KW_NAMESPACE "namespace"
90 %token<token> KW_PACKAGE "package"
91 %token<token> KW_PROTECTED "protected"
92 %token<token> KW_PUBLIC "public"
93 %token<token> KW_PRIVATE "private"
94 %token<token> KW_USE "use"
95 %token<token> KW_INTERNAL "internal"
96 %token<token> KW_NEW "new"
97 %token<token> KW_NATIVE "native"
98 %token<token> KW_FUNCTION "function"
99 %token<token> KW_FINALLY "finally"
100 %token<token> KW_UNDEFINED "undefined"
101 %token<token> KW_CONTINUE "continue"
102 %token<token> KW_CLASS "class"
103 %token<token> KW_CONST "const"
104 %token<token> KW_CATCH "catch"
105 %token<token> KW_CASE "case"
106 %token<token> KW_SET "set"
107 %token<token> KW_VOID "void"
108 %token<token> KW_THROW "throw"
109 %token<token> KW_STATIC "static"
110 %token<token> KW_WITH "with"
111 %token<token> KW_INSTANCEOF "instanceof"
112 %token<token> KW_IMPORT "import"
113 %token<token> KW_RETURN "return"
114 %token<token> KW_TYPEOF "typeof"
115 %token<token> KW_INTERFACE "interface"
116 %token<token> KW_NULL "null"
117 %token<token> KW_VAR "var"
118 %token<token> KW_DYNAMIC "dynamic"
119 %token<token> KW_OVERRIDE "override"
120 %token<token> KW_FINAL "final"
121 %token<token> KW_EACH "each"
122 %token<token> KW_GET "get"
123 %token<token> KW_TRY "try"
124 %token<token> KW_SUPER "super"
125 %token<token> KW_EXTENDS "extends"
126 %token<token> KW_FALSE "false"
127 %token<token> KW_TRUE "true"
128 %token<token> KW_BOOLEAN "Boolean"
129 %token<token> KW_UINT "uint"
130 %token<token> KW_INT "int"
131 %token<token> KW_NUMBER "Number"
132 %token<token> KW_STRING "String"
133 %token<token> KW_DEFAULT "default"
134 %token<token> KW_DELETE "delete"
135 %token<token> KW_IF "if"
136 %token<token> KW_ELSE "else"
137 %token<token> KW_BREAK "break"
138 %token<token> KW_IS "is"
139 %token<token> KW_IN "in"
140 %token<token> KW_AS "as"
142 %token<token> T_DICTSTART "{ (dictionary)"
143 %token<token> T_EQEQ "=="
144 %token<token> T_EQEQEQ "==="
145 %token<token> T_NE "!="
146 %token<token> T_NEE "!=="
147 %token<token> T_LE "<="
148 %token<token> T_GE ">="
149 %token<token> T_ORBY "|="
150 %token<token> T_DIVBY "/="
151 %token<token> T_MODBY "%="
152 %token<token> T_MULBY "*="
153 %token<token> T_PLUSBY "+="
154 %token<token> T_MINUSBY "-="
155 %token<token> T_XORBY "^="
156 %token<token> T_SHRBY ">>="
157 %token<token> T_SHLBY "<<="
158 %token<token> T_USHRBY ">>>="
159 %token<token> T_OROR "||"
160 %token<token> T_ANDAND "&&"
161 %token<token> T_COLONCOLON "::"
162 %token<token> T_MINUSMINUS "--"
163 %token<token> T_PLUSPLUS "++"
164 %token<token> T_DOTDOT ".."
165 %token<token> T_DOTDOTDOT "..."
166 %token<token> T_SHL "<<"
167 %token<token> T_USHR ">>>"
168 %token<token> T_SHR ">>"
170 %type <for_start> FOR_START
171 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
172 %type <token> VARCONST
174 %type <code> CODEPIECE CODE_STATEMENT
175 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
176 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION
177 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
178 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
179 %type <exception> CATCH FINALLY
180 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
181 %type <code> CLASS_DECLARATION
182 %type <code> NAMESPACE_DECLARATION
183 %type <code> INTERFACE_DECLARATION
184 %type <code> VOIDEXPRESSION
185 %type <value> EXPRESSION NONCOMMAEXPRESSION
186 %type <value> MAYBEEXPRESSION
187 %type <value> E DELETE
188 %type <value> CONSTANT
189 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
190 %type <value> INNERFUNCTION
191 %type <code> USE_NAMESPACE
192 %type <code> FOR_INIT
194 %type <classinfo> MAYBETYPE
197 %type <params> PARAM_LIST
198 %type <params> MAYBE_PARAM_LIST
199 %type <flags> MAYBE_MODIFIERS
200 %type <flags> MODIFIER_LIST
201 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
202 %type <classinfo_list> IMPLEMENTS_LIST
203 %type <classinfo> EXTENDS
204 %type <classinfo_list> EXTENDS_LIST
205 %type <classinfo> CLASS PACKAGEANDCLASS CLASS_SPEC
206 %type <classinfo_list> CLASS_SPEC_LIST
207 %type <classinfo> TYPE
208 //%type <token> VARIABLE
209 %type <value> VAR_READ
211 //%type <token> T_IDENTIFIER
212 %type <token> MODIFIER
213 %type <value> FUNCTIONCALL
214 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
216 // precedence: from low to high
220 %left below_semicolon
223 %nonassoc below_assignment // for ?:, contrary to spec
224 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
231 %nonassoc "==" "!=" "===" "!=="
232 %nonassoc "is" "as" "in"
233 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
234 %left "<<" ">>" ">>>"
238 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
240 %nonassoc below_curly
244 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
247 %left above_identifier
251 // needed for "return" precedence:
252 %nonassoc T_STRING T_REGEXP
253 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
254 %nonassoc "false" "true" "null" "undefined" "super" "function"
261 static int a3_error(char*s)
263 syntaxerror("%s", s);
264 return 0; //make gcc happy
268 static char* concat2(const char* t1, const char* t2)
272 char*text = malloc(l1+l2+1);
273 memcpy(text , t1, l1);
274 memcpy(text+l1, t2, l2);
278 static char* concat3(const char* t1, const char* t2, const char* t3)
283 char*text = malloc(l1+l2+l3+1);
284 memcpy(text , t1, l1);
285 memcpy(text+l1, t2, l2);
286 memcpy(text+l1+l2, t3, l3);
291 typedef struct _import {
295 DECLARE_LIST(import);
297 DECLARE(methodstate);
298 DECLARE_LIST(methodstate);
300 typedef struct _classstate {
306 methodstate_t*static_init;
308 //code_t*static_init;
310 char has_constructor;
313 struct _methodstate {
322 dict_t*unresolved_variables;
325 char uses_parent_function;
330 int var_index; // for inner methods
331 char is_a_slot; // for inner methods
334 abc_exception_list_t*exceptions;
336 methodstate_list_t*innerfunctions;
339 typedef struct _state {
344 import_list_t*wildcard_imports;
346 char has_own_imports;
347 char new_vars; // e.g. transition between two functions
350 methodstate_t*method;
357 typedef struct _global {
361 dict_t*file2token2info;
364 static global_t*global = 0;
365 static state_t* state = 0;
369 #define MULTINAME(m,x) \
373 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
375 #define MEMBER_MULTINAME(m,f,n) \
379 m##_ns.access = ((slotinfo_t*)(f))->access; \
383 m.namespace_set = 0; \
384 m.name = ((slotinfo_t*)(f))->name; \
386 m.type = MULTINAME; \
388 m.namespace_set = &nopackage_namespace_set; \
392 /* warning: list length of namespace set is undefined */
393 #define MULTINAME_LATE(m, access, package) \
394 namespace_t m##_ns = {access, package}; \
395 namespace_set_t m##_nsset; \
396 namespace_list_t m##_l;m##_l.next = 0; \
397 m##_nsset.namespaces = &m##_l; \
398 m##_nsset = m##_nsset; \
399 m##_l.namespace = &m##_ns; \
400 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
402 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
403 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
404 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
405 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
406 static namespace_list_t nl4 = {&ns4,0};
407 static namespace_list_t nl3 = {&ns3,&nl4};
408 static namespace_list_t nl2 = {&ns2,&nl3};
409 static namespace_list_t nl1 = {&ns1,&nl2};
410 static namespace_set_t nopackage_namespace_set = {&nl1};
412 static void new_state()
415 state_t*oldstate = state;
417 memcpy(s, state, sizeof(state_t)); //shallow copy
419 s->imports = dict_new();
423 state->has_own_imports = 0;
424 state->vars = dict_new();
425 state->old = oldstate;
428 static void state_has_imports()
430 state->wildcard_imports = list_clone(state->wildcard_imports);
431 state->imports = dict_clone(state->imports);
432 state->has_own_imports = 1;
435 static void state_destroy(state_t*state)
437 if(state->has_own_imports) {
438 list_free(state->wildcard_imports);
439 dict_destroy(state->imports);state->imports=0;
441 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
442 dict_destroy(state->imports);state->imports=0;
446 for(t=0;t<state->vars->hashsize;t++) {
447 dictentry_t*e =state->vars->slots[t];
449 free(e->data);e->data=0;
453 dict_destroy(state->vars);state->vars=0;
459 static void old_state()
461 if(!state || !state->old)
462 syntaxerror("invalid nesting");
463 state_t*leaving = state;
467 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
468 free(leaving->method);
471 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
476 state_destroy(leaving);
479 static code_t* method_header(methodstate_t*m);
480 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
481 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
484 static char* internal_filename_package = 0;
485 void initialize_file(char*filename)
488 syntaxerror("invalid call to initialize_file during parsing of another file");
491 state->package = internal_filename_package = strdup(filename);
493 global->token2info = dict_lookup(global->file2token2info,
494 current_filename // use long version
496 if(!global->token2info) {
497 global->token2info = dict_new2(&ptr_type);
498 dict_put(global->file2token2info, current_filename, global->token2info);
502 state->method = rfx_calloc(sizeof(methodstate_t));
503 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
505 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
506 function_initvars(state->method, 0, 0, 1);
507 global->init = abc_initscript(global->file);
508 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
514 if(!state || state->level!=1) {
515 syntaxerror("unexpected end of file in pass %d", as3_pass);
519 code_t*header = method_header(state->method);
520 code_t*c = wrap_function(header, 0, global->init->method->body->code);
521 global->init->method->body->code = c;
522 free(state->method);state->method=0;
525 //free(state->package);state->package=0; // used in registry
526 state_destroy(state);state=0;
529 void initialize_parser()
531 global = rfx_calloc(sizeof(global_t));
532 global->file = abc_file_new();
533 global->file->flags &= ~ABCFILE_LAZY;
534 global->file2token2info = dict_new();
535 global->token2info = 0;
538 void* finish_parser()
540 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
542 global->token2info=0;
548 static void xx_scopetest()
550 /* findpropstrict doesn't just return a scope object- it
551 also makes it "active" somehow. Push local_0 on the
552 scope stack and read it back with findpropstrict, it'll
553 contain properties like "trace". Trying to find the same
554 property on a "vanilla" local_0 yields only a "undefined" */
555 //c = abc_findpropstrict(c, "[package]::trace");
557 /*c = abc_getlocal_0(c);
558 c = abc_findpropstrict(c, "[package]::trace");
560 c = abc_setlocal_1(c);
562 c = abc_pushbyte(c, 0);
563 c = abc_setlocal_2(c);
565 code_t*xx = c = abc_label(c);
566 c = abc_findpropstrict(c, "[package]::trace");
567 c = abc_pushstring(c, "prop:");
568 c = abc_hasnext2(c, 1, 2);
570 c = abc_setlocal_3(c);
571 c = abc_callpropvoid(c, "[package]::trace", 2);
572 c = abc_getlocal_3(c);
574 c = abc_iftrue(c,xx);*/
577 typedef struct _variable {
581 methodstate_t*is_inner_method;
584 static variable_t* find_variable(state_t*s, char*name)
588 v = dict_lookup(s->vars, name);
590 if(s->new_vars) break;
595 static variable_t* find_slot(state_t*s, const char*name)
597 if(s->method && s->method->slots)
598 return dict_lookup(s->method->slots, name);
602 static variable_t* find_variable_safe(state_t*s, char*name)
604 variable_t* v = find_variable(s, name);
606 syntaxerror("undefined variable: %s", name);
609 static char variable_exists(char*name)
611 return dict_contains(state->vars, name);
613 code_t*defaultvalue(code_t*c, classinfo_t*type);
615 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
618 variable_t*v = find_slot(state, name);
624 v->index = state->method->variable_count++;
628 dict_put(state->vars, name, v);
632 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
634 return new_variable2(name, type, init, maybeslot)->index;
637 #define TEMPVARNAME "__as3_temp__"
638 static int gettempvar()
640 variable_t*v = find_variable(state, TEMPVARNAME);
643 return new_variable(TEMPVARNAME, 0, 0, 0);
646 code_t* var_block(code_t*body)
652 for(t=0;t<state->vars->hashsize;t++) {
653 dictentry_t*e = state->vars->slots[t];
655 variable_t*v = (variable_t*)e->data;
656 if(v->type && v->init) {
657 c = defaultvalue(c, v->type);
658 c = abc_setlocal(c, v->index);
659 k = abc_kill(k, v->index);
669 if(x->opcode== OPCODE___BREAK__ ||
670 x->opcode== OPCODE___CONTINUE__) {
671 /* link kill code before break/continue */
672 code_t*e = code_dup(k);
673 code_t*s = code_start(e);
685 c = code_append(c, body);
686 c = code_append(c, k);
690 void unknown_variable(char*name)
692 if(!state->method->unresolved_variables)
693 state->method->unresolved_variables = dict_new();
694 if(!dict_contains(state->method->unresolved_variables, name))
695 dict_put(state->method->unresolved_variables, name, 0);
698 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
700 static void parsererror(const char*file, int line, const char*f)
702 syntaxerror("internal error in %s, %s:%d", f, file, line);
706 static code_t* method_header(methodstate_t*m)
709 if(m->uses_slots || (m->late_binding && !m->inner)) {
710 c = abc_getlocal_0(c);
711 c = abc_pushscope(c);
714 c = abc_newactivation(c);
715 c = abc_pushscope(c);
717 methodstate_list_t*l = m->innerfunctions;
719 parserassert(l->methodstate->abc);
720 if(m->uses_slots && l->methodstate->is_a_slot) {
721 c = abc_getscopeobject(c, 1);
722 c = abc_newfunction(c, l->methodstate->abc);
724 c = abc_setlocal(c, l->methodstate->var_index);
725 c = abc_setslot(c, l->methodstate->var_index);
727 c = abc_newfunction(c, l->methodstate->abc);
728 c = abc_setlocal(c, l->methodstate->var_index);
730 free(l->methodstate);l->methodstate=0;
734 c = code_append(c, m->header);
737 if(m->is_constructor && !m->has_super) {
738 // call default constructor
739 c = abc_getlocal_0(c);
740 c = abc_constructsuper(c, 0);
742 list_free(m->innerfunctions);
743 m->innerfunctions = 0;
748 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
750 c = code_append(c, header);
751 c = code_append(c, var_block(body));
752 /* append return if necessary */
753 if(!c || (c->opcode != OPCODE_RETURNVOID &&
754 c->opcode != OPCODE_RETURNVALUE)) {
755 c = abc_returnvoid(c);
761 static void startpackage(char*name)
764 /*printf("entering package \"%s\"\n", name);*/
765 state->package = strdup(name);
767 static void endpackage()
769 /*printf("leaving package \"%s\"\n", state->package);*/
771 //used e.g. in classinfo_register:
772 //free(state->package);state->package=0;
777 #define FLAG_PUBLIC 256
778 #define FLAG_PROTECTED 512
779 #define FLAG_PRIVATE 1024
780 #define FLAG_PACKAGEINTERNAL 2048
781 #define FLAG_NAMESPACE 4096
783 static int flags2access(int flags)
786 if(flags&FLAG_PUBLIC) {
787 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
788 syntaxerror("invalid combination of access levels");
789 access = ACCESS_PACKAGE;
790 } else if(flags&FLAG_PRIVATE) {
791 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
792 syntaxerror("invalid combination of access levels");
793 access = ACCESS_PRIVATE;
794 } else if(flags&FLAG_PROTECTED) {
795 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
796 syntaxerror("invalid combination of access levels");
797 access = ACCESS_PROTECTED;
799 access = ACCESS_PACKAGEINTERNAL;
804 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
809 index = new_variable("this", 0, 0, 0);
810 else if(!m->is_global)
811 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
813 index = new_variable("globalscope", 0, 0, 0);
816 parserassert(!index);
819 /* as variables and slots share the same number, make sure
820 that those variable indices are reserved */
821 m->variable_count = m->uses_slots;
826 for(p=params->list;p;p=p->next) {
827 new_variable(p->param->name, p->param->type, 0, 1);
831 methodstate_list_t*l = m->innerfunctions;
833 methodstate_t*m = l->methodstate;
834 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
835 m->var_index = v->index;
836 v->is_inner_method = m;
842 char*as3_globalclass=0;
843 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements)
846 syntaxerror("inner classes now allowed");
850 classinfo_list_t*mlist=0;
852 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
853 syntaxerror("invalid modifier(s)");
855 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
856 syntaxerror("public and internal not supported at the same time.");
858 /* create the class name, together with the proper attributes */
862 if(!(flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
863 access = ACCESS_PRIVATE; package = internal_filename_package;
864 } else if(!(flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
865 access = ACCESS_PACKAGEINTERNAL; package = state->package;
866 } else if(state->package!=internal_filename_package) {
867 access = ACCESS_PACKAGE; package = state->package;
869 syntaxerror("public classes only allowed inside a package");
873 state->cls = rfx_calloc(sizeof(classstate_t));
874 state->cls->init = rfx_calloc(sizeof(methodstate_t));
875 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
876 /* notice: we make no effort to initialize the top variable (local0) here,
877 even though it has special meaning. We just rely on the facat
878 that pass 1 won't do anything with variables */
880 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
882 /* set current method to constructor- all code within the class-level (except
883 static variable initializations) will be executed during construction time */
884 state->method = state->cls->init;
886 if(registry_find(package, classname)) {
887 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
889 /* build info struct */
890 int num_interfaces = (list_length(implements));
891 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
892 state->cls->info->flags |= flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
896 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
898 state->method = state->cls->init;
899 parserassert(state->cls && state->cls->info);
901 function_initvars(state->cls->init, 0, 0, 1);
902 function_initvars(state->cls->static_init, 0, 0, 0);
904 if(extends && (extends->flags & FLAG_FINAL))
905 syntaxerror("Can't extend final class '%s'", extends->name);
907 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
908 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
910 classinfo_list_t*l = implements;
911 for(l=implements;l;l=l->next) {
912 if(!(l->classinfo->flags & FLAG_INTERFACE))
913 syntaxerror("'%s' is not an interface", l->classinfo->name);
914 state->cls->info->interfaces[pos++] = l->classinfo;
917 /* generate the abc code for this class */
918 MULTINAME(classname2,state->cls->info);
919 multiname_t*extends2 = sig2mname(extends);
921 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
922 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
923 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
924 if(state->cls->info->flags&FLAG_INTERFACE) {
925 abc_class_interface(state->cls->abc);
928 abc_class_protectedNS(state->cls->abc, classname);
930 for(mlist=implements;mlist;mlist=mlist->next) {
931 MULTINAME(m, mlist->classinfo);
932 abc_class_add_interface(state->cls->abc, &m);
935 /* write the construction code for this class to the global init
937 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
939 abc_method_body_t*m = global->init->method->body;
940 __ getglobalscope(m);
941 classinfo_t*s = extends;
946 //TODO: take a look at the current scope stack, maybe
947 // we can re-use something
952 multiname_t*s2 = sig2mname(s);
954 multiname_destroy(s2);
956 __ pushscope(m); count++;
957 m->code = m->code->prev->prev; // invert
959 /* continue appending after last op end */
960 while(m->code && m->code->next) m->code = m->code->next;
962 /* TODO: if this is one of *our* classes, we can also
963 do a getglobalscope/getslot <nr> (which references
964 the init function's slots) */
966 __ getlex2(m, extends2);
968 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
969 stack is not the superclass */
970 __ pushscope(m);count++;
973 /* notice: we get a verify error #1107 if the top element on the scope
974 stack is not the global object */
976 __ pushscope(m);count++;
978 __ newclass(m,state->cls->abc);
982 __ setslot(m, slotindex);
983 multiname_destroy(extends2);
985 /* flash.display.MovieClip handling */
987 if(!as3_globalclass && (flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
988 if(state->package && state->package[0]) {
989 as3_globalclass = concat3(state->package, ".", classname);
991 as3_globalclass = strdup(classname);
997 static void setstaticfunction(int x)
1001 state->method = state->cls->static_init;
1003 state->method = state->cls->init;
1006 parserassert(state->method);
1010 static void endclass()
1013 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1015 c = abc_getlocal_0(c);
1016 c = abc_constructsuper(c, 0);
1017 state->cls->init->header = code_append(state->cls->init->header, c);
1018 state->cls->has_constructor=1;
1020 if(state->cls->init) {
1021 if(state->cls->info->flags&FLAG_INTERFACE) {
1022 if(state->cls->init->header)
1023 syntaxerror("interface can not have class-level code");
1025 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1026 code_t*c = method_header(state->cls->init);
1027 m->body->code = wrap_function(c, 0, m->body->code);
1030 if(state->cls->static_init) {
1031 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1032 code_t*c = method_header(state->cls->static_init);
1033 m->body->code = wrap_function(c, 0, m->body->code);
1040 void check_code_for_break(code_t*c)
1043 if(c->opcode == OPCODE___BREAK__) {
1044 char*name = string_cstr(c->data[0]);
1045 syntaxerror("Unresolved \"break %s\"", name);
1047 if(c->opcode == OPCODE___CONTINUE__) {
1048 char*name = string_cstr(c->data[0]);
1049 syntaxerror("Unresolved \"continue %s\"", name);
1056 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1058 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1059 if(TYPE_IS_NUMBER(t)) {
1060 xassert(c->type == CONSTANT_FLOAT
1061 || c->type == CONSTANT_INT
1062 || c->type == CONSTANT_UINT);
1063 } else if(TYPE_IS_UINT(t)) {
1064 xassert(c->type == CONSTANT_UINT ||
1065 (c->type == CONSTANT_INT && c->i>=0));
1066 } else if(TYPE_IS_INT(t)) {
1067 xassert(c->type == CONSTANT_INT);
1068 } else if(TYPE_IS_BOOLEAN(t)) {
1069 xassert(c->type == CONSTANT_TRUE
1070 || c->type == CONSTANT_FALSE);
1074 static void check_override(memberinfo_t*m, int flags)
1078 if(m->parent == state->cls->info)
1079 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1081 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1082 if(m->access==ACCESS_PRIVATE)
1084 if(m->flags & FLAG_FINAL)
1085 syntaxerror("can't override final member %s", m->name);
1086 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1087 syntaxerror("can't override static member %s", m->name);
1088 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1089 syntaxerror("can't override non-static member %s with static declaration", m->name);
1091 if(!(flags&FLAG_OVERRIDE)) {
1092 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1093 if(m->kind == INFOTYPE_METHOD)
1094 syntaxerror("can't override without explicit 'override' declaration");
1096 syntaxerror("can't override '%s'", m->name);
1101 static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
1103 methodinfo_t*minfo = 0;
1104 U8 access = flags2access(flags);
1107 minfo = methodinfo_register_global(access, state->package, name);
1108 minfo->return_type = return_type;
1109 } else if(getset != KW_GET && getset != KW_SET) {
1111 memberinfo_t* m = registry_findmember(state->cls->info, name, 0);
1113 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1115 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1116 minfo->return_type = return_type;
1117 // getslot on a member slot only returns "undefined", so no need
1118 // to actually store these
1119 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1121 //class getter/setter
1122 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1124 if(getset == KW_GET)
1126 else if(params->list && params->list->param)
1127 type = params->list->param->type;
1128 // not sure wether to look into superclasses here, too
1129 minfo = (methodinfo_t*)registry_findmember(state->cls->info, name, 1);
1131 if(minfo->kind!=INFOTYPE_SLOT)
1132 syntaxerror("class already contains a method called '%s'", name);
1133 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1134 syntaxerror("class already contains a field called '%s'", name);
1135 if(minfo->subtype & gs)
1136 syntaxerror("getter/setter for '%s' already defined", name);
1137 /* make a setter or getter into a getset */
1138 minfo->subtype |= gs;
1139 if(!minfo->return_type) {
1140 minfo->return_type = type;
1142 if(minfo && minfo->return_type != type)
1143 syntaxerror("different type in getter and setter");
1146 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1147 minfo->kind = INFOTYPE_SLOT; //hack
1148 minfo->subtype = gs;
1149 minfo->return_type = type;
1151 /* can't assign a slot as getter and setter might have different slots */
1152 //minfo->slot = slot;
1154 if(flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1155 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1156 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1160 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1162 //parserassert(state->method && state->method->info);
1164 methodstate_t*parent_method = state->method;
1167 return_type = 0; // not valid in pass 1
1171 state->new_vars = 1;
1174 state->method = rfx_calloc(sizeof(methodstate_t));
1175 state->method->inner = 1;
1176 state->method->variable_count = 0;
1177 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1179 NEW(methodinfo_t,minfo);
1180 minfo->kind = INFOTYPE_METHOD;
1181 minfo->access = ACCESS_PACKAGEINTERNAL;
1183 state->method->info = minfo;
1186 list_append(parent_method->innerfunctions, state->method);
1188 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1190 function_initvars(state->method, params, 0, 1);
1194 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1195 state->method->variable_count = 0;
1196 parserassert(state->method);
1198 state->method->info->return_type = return_type;
1199 function_initvars(state->method, params, 0, 1);
1203 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1204 params_t*params, classinfo_t*return_type)
1206 if(state->method && state->method->info) {
1207 syntaxerror("not able to start another method scope");
1210 state->new_vars = 1;
1213 state->method = rfx_calloc(sizeof(methodstate_t));
1214 state->method->has_super = 0;
1217 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1219 state->method->is_global = 1;
1220 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1222 if(state->method->is_constructor)
1223 name = "__as3_constructor__";
1226 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1228 function_initvars(state->method, params, flags, 1);
1230 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1234 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1235 state->method->variable_count = 0;
1236 parserassert(state->method);
1239 memberinfo_t*m = registry_findmember(state->cls->info, name, 2);
1240 check_override(m, flags);
1244 state->cls->has_constructor |= state->method->is_constructor;
1247 state->method->info->return_type = return_type;
1248 function_initvars(state->method, params, flags, 1);
1252 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1253 params_t*params, classinfo_t*return_type, code_t*body)
1256 // store inner methods in variables
1257 function_initvars(state->method, 0, 0, 0);
1259 methodstate_list_t*ml = state->method->innerfunctions;
1260 dict_t*xvars = dict_new();
1262 methodstate_t*m = ml->methodstate;
1263 parserassert(m->inner);
1264 if(m->unresolved_variables) {
1265 dict_t*d = m->unresolved_variables;
1267 for(t=0;t<d->hashsize;t++) {
1268 dictentry_t*l = d->slots[t];
1270 /* check parent method's variables */
1271 if(find_variable(state, l->key)) {
1272 m->uses_parent_function = 1;
1273 state->method->uses_slots = 1;
1274 dict_put(xvars, l->key, 0);
1281 dict_destroy(m->unresolved_variables);
1282 m->unresolved_variables = 0;
1286 if(state->method->uses_slots) {
1287 state->method->slots = dict_new();
1289 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1290 if(v->index && dict_contains(xvars, name)) {
1293 if(v->is_inner_method) {
1294 v->is_inner_method->is_a_slot = 1;
1297 dict_put(state->method->slots, name, v);
1300 state->method->uses_slots = i;
1301 dict_destroy(state->vars);state->vars = 0;
1303 dict_destroy(xvars);
1310 /*if(state->method->uses_parent_function){
1311 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1316 multiname_t*type2 = sig2mname(return_type);
1318 if(state->method->inner) {
1319 f = state->method->abc;
1320 abc_method_init(f, global->file, type2, 1);
1321 } else if(state->method->is_constructor) {
1322 f = abc_class_getconstructor(state->cls->abc, type2);
1323 } else if(!state->method->is_global) {
1324 namespace_t mname_ns = {state->method->info->access, ""};
1325 multiname_t mname = {QNAME, &mname_ns, 0, name};
1327 if(flags&FLAG_STATIC)
1328 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1330 f = abc_class_method(state->cls->abc, type2, &mname);
1331 slot = f->trait->slot_id;
1333 namespace_t mname_ns = {state->method->info->access, state->package};
1334 multiname_t mname = {QNAME, &mname_ns, 0, name};
1336 f = abc_method_new(global->file, type2, 1);
1337 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1338 //abc_code_t*c = global->init->method->body->code;
1340 //flash doesn't seem to allow us to access function slots
1341 //state->method->info->slot = slot;
1343 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1344 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1345 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1346 if(params->varargs) f->flags |= METHOD_NEED_REST;
1350 for(p=params->list;p;p=p->next) {
1351 if(params->varargs && !p->next) {
1352 break; //varargs: omit last parameter in function signature
1354 multiname_t*m = sig2mname(p->param->type);
1355 list_append(f->parameters, m);
1356 if(p->param->value) {
1357 check_constant_against_type(p->param->type, p->param->value);
1358 opt=1;list_append(f->optional_parameters, p->param->value);
1360 syntaxerror("non-optional parameter not allowed after optional parameters");
1363 if(state->method->slots) {
1364 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1366 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1367 multiname_t*type = sig2mname(v->type);
1368 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1369 t->slot_id = v->index;
1374 check_code_for_break(body);
1377 f->body->code = body;
1378 f->body->exceptions = state->method->exceptions;
1379 } else { //interface
1381 syntaxerror("interface methods can't have a method body");
1391 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1396 void breakjumpsto(code_t*c, char*name, code_t*jump)
1399 if(c->opcode == OPCODE___BREAK__) {
1400 string_t*name2 = c->data[0];
1401 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1402 c->opcode = OPCODE_JUMP;
1409 void continuejumpsto(code_t*c, char*name, code_t*jump)
1412 if(c->opcode == OPCODE___CONTINUE__) {
1413 string_t*name2 = c->data[0];
1414 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1415 c->opcode = OPCODE_JUMP;
1423 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1424 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1425 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1427 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1429 if(!type1 || !type2)
1430 return registry_getanytype();
1431 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1432 return registry_getanytype();
1435 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1444 return registry_getanytype();
1446 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1451 return abc_coerce_a(c);
1455 // cast an "any" type to a specific type. subject to
1456 // runtime exceptions
1457 return abc_coerce2(c, &m);
1460 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1461 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1462 // allow conversion between number types
1463 return abc_coerce2(c, &m);
1465 //printf("%s.%s\n", from.package, from.name);
1466 //printf("%s.%s\n", to.package, to.name);
1468 classinfo_t*supertype = from;
1470 if(supertype == to) {
1471 // target type is one of from's superclasses
1472 return abc_coerce2(c, &m);
1475 while(supertype->interfaces[t]) {
1476 if(supertype->interfaces[t]==to) {
1477 // target type is one of from's interfaces
1478 return abc_coerce2(c, &m);
1482 supertype = supertype->superclass;
1484 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1486 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1488 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1491 as3_error("can't convert type %s%s%s to %s%s%s",
1492 from->package, from->package?".":"", from->name,
1493 to->package, to->package?".":"", to->name);
1497 code_t*defaultvalue(code_t*c, classinfo_t*type)
1499 if(TYPE_IS_INT(type)) {
1500 c = abc_pushbyte(c, 0);
1501 } else if(TYPE_IS_UINT(type)) {
1502 c = abc_pushuint(c, 0);
1503 } else if(TYPE_IS_FLOAT(type)) {
1505 } else if(TYPE_IS_BOOLEAN(type)) {
1506 c = abc_pushfalse(c);
1508 //c = abc_pushundefined(c);
1510 c = abc_pushnull(c);
1512 c = abc_coerce2(c, &m);
1517 char is_pushundefined(code_t*c)
1519 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1522 static slotinfo_t* find_class(char*name)
1526 c = registry_find(state->package, name);
1529 /* try explicit imports */
1530 dictentry_t* e = dict_get_slot(state->imports, name);
1533 if(!strcmp(e->key, name)) {
1534 c = (slotinfo_t*)e->data;
1540 /* try package.* imports */
1541 import_list_t*l = state->wildcard_imports;
1543 //printf("does package %s contain a class %s?\n", l->import->package, name);
1544 c = registry_find(l->import->package, name);
1549 /* try global package */
1550 c = registry_find("", name);
1553 /* try local "filename" package */
1554 c = registry_find(internal_filename_package, name);
1560 static char is_getlocal(code_t*c)
1562 if(!c || c->prev || c->next)
1564 return(c->opcode == OPCODE_GETLOCAL
1565 || c->opcode == OPCODE_GETLOCAL_0
1566 || c->opcode == OPCODE_GETLOCAL_1
1567 || c->opcode == OPCODE_GETLOCAL_2
1568 || c->opcode == OPCODE_GETLOCAL_3);
1570 static int getlocalnr(code_t*c)
1572 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1573 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1574 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1575 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1576 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1577 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1581 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1585 [prefix code] [read instruction]
1589 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1591 if(in && in->opcode == OPCODE_COERCE_A) {
1592 in = code_cutlast(in);
1595 syntaxerror("internal error");
1597 /* chop off read instruction */
1601 prefix = r->prev;r->prev = 0;
1607 char use_temp_var = readbefore;
1609 /* generate the write instruction, and maybe append a dup to the prefix code */
1610 code_t* write = abc_nop(0);
1611 if(r->opcode == OPCODE_GETPROPERTY) {
1612 write->opcode = OPCODE_SETPROPERTY;
1613 multiname_t*m = (multiname_t*)r->data[0];
1614 write->data[0] = multiname_clone(m);
1615 if(m->type == QNAME || m->type == MULTINAME) {
1617 prefix = abc_dup(prefix); // we need the object, too
1620 } else if(m->type == MULTINAMEL) {
1622 /* dupping two values on the stack requires 5 operations and one register-
1623 couldn't adobe just have given us a dup2? */
1624 int temp = gettempvar();
1625 prefix = abc_setlocal(prefix, temp);
1626 prefix = abc_dup(prefix);
1627 prefix = abc_getlocal(prefix, temp);
1628 prefix = abc_swap(prefix);
1629 prefix = abc_getlocal(prefix, temp);
1631 prefix = abc_kill(prefix, temp);
1635 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1637 } else if(r->opcode == OPCODE_GETSLOT) {
1638 write->opcode = OPCODE_SETSLOT;
1639 write->data[0] = r->data[0];
1641 prefix = abc_dup(prefix); // we need the object, too
1644 } else if(r->opcode == OPCODE_GETLOCAL) {
1645 write->opcode = OPCODE_SETLOCAL;
1646 write->data[0] = r->data[0];
1647 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1648 write->opcode = OPCODE_SETLOCAL_0;
1649 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1650 write->opcode = OPCODE_SETLOCAL_1;
1651 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1652 write->opcode = OPCODE_SETLOCAL_2;
1653 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1654 write->opcode = OPCODE_SETLOCAL_3;
1657 syntaxerror("illegal lvalue: can't assign a value to this expression");
1664 /* with getproperty/getslot, we have to be extra careful not
1665 to execute the read code twice, as it might have side-effects
1666 (e.g. if the property is in fact a setter/getter combination)
1668 So read the value, modify it, and write it again,
1669 using prefix only once and making sure (by using a temporary
1670 register) that the return value is what we just wrote */
1671 temp = gettempvar();
1672 c = code_append(c, prefix);
1673 c = code_append(c, r);
1676 c = abc_setlocal(c, temp);
1678 c = code_append(c, middlepart);
1681 c = abc_setlocal(c, temp);
1683 c = code_append(c, write);
1684 c = abc_getlocal(c, temp);
1685 c = abc_kill(c, temp);
1687 /* if we're allowed to execute the read code twice *and*
1688 the middlepart doesn't modify the code, things are easier.
1690 code_t* r2 = code_dup(r);
1691 //c = code_append(c, prefix);
1692 parserassert(!prefix);
1693 c = code_append(c, r);
1694 c = code_append(c, middlepart);
1695 c = code_append(c, write);
1696 c = code_append(c, r2);
1699 /* even smaller version: overwrite the value without reading
1703 c = code_append(c, prefix);
1706 c = code_append(c, middlepart);
1707 c = code_append(c, write);
1708 c = code_append(c, r);
1711 temp = gettempvar();
1713 c = code_append(c, prefix);
1715 c = code_append(c, middlepart);
1717 c = abc_setlocal(c, temp);
1718 c = code_append(c, write);
1719 c = abc_getlocal(c, temp);
1720 c = abc_kill(c, temp);
1726 char is_break_or_jump(code_t*c)
1730 if(c->opcode == OPCODE_JUMP ||
1731 c->opcode == OPCODE___BREAK__ ||
1732 c->opcode == OPCODE___CONTINUE__ ||
1733 c->opcode == OPCODE_THROW ||
1734 c->opcode == OPCODE_RETURNVOID ||
1735 c->opcode == OPCODE_RETURNVALUE) {
1742 #define IS_FINALLY_TARGET(op) \
1743 ((op) == OPCODE___CONTINUE__ || \
1744 (op) == OPCODE___BREAK__ || \
1745 (op) == OPCODE_RETURNVOID || \
1746 (op) == OPCODE_RETURNVALUE || \
1747 (op) == OPCODE___RETHROW__)
1749 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1751 #define NEED_EXTRA_STACK_ARG
1752 code_t*finally_label = abc_nop(0);
1753 NEW(lookupswitch_t, l);
1759 code_t*prev = i->prev;
1760 if(IS_FINALLY_TARGET(i->opcode)) {
1763 if(i->opcode == OPCODE___RETHROW__ ||
1764 i->opcode == OPCODE_RETURNVALUE) {
1765 if(i->opcode == OPCODE___RETHROW__)
1766 i->opcode = OPCODE_THROW;
1768 p = abc_coerce_a(p);
1769 p = abc_setlocal(p, tempvar);
1771 p = abc_pushbyte(p, count++);
1772 p = abc_jump(p, finally_label);
1773 code_t*target = p = abc_label(p);
1774 #ifdef NEED_EXTRA_STACK_ARG
1778 p = abc_getlocal(p, tempvar);
1781 p->next = i;i->prev = p;
1782 list_append(l->targets, target);
1788 c = abc_pushbyte(c, -1);
1789 c = code_append(c, finally_label);
1790 c = code_append(c, finally);
1792 #ifdef NEED_EXTRA_STACK_ARG
1795 c = abc_lookupswitch(c, l);
1796 c = l->def = abc_label(c);
1797 #ifdef NEED_EXTRA_STACK_ARG
1804 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1808 code_t*prev = i->prev;
1809 if(IS_FINALLY_TARGET(i->opcode)) {
1810 if(i->opcode == OPCODE___RETHROW__)
1811 i->opcode = OPCODE_THROW;
1812 code_t*end = code_dup(finally);
1813 code_t*start = code_start(end);
1814 if(prev) prev->next = start;
1821 return code_append(c, finally);
1824 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1830 int num_insertion_points=0;
1832 if(IS_FINALLY_TARGET(i->opcode))
1833 num_insertion_points++;
1840 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1845 int simple_version_cost = (1+num_insertion_points)*code_size;
1846 int lookup_version_cost = 4*num_insertion_points + 5;
1848 if(cantdup || simple_version_cost > lookup_version_cost) {
1849 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1850 return insert_finally_lookup(c, finally, tempvar);
1852 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1853 return insert_finally_simple(c, finally, tempvar);
1857 #define PASS1 }} if(as3_pass == 1) {{
1858 #define PASS1END }} if(as3_pass == 2) {{
1859 #define PASS2 }} if(as3_pass == 2) {{
1860 #define PASS12 }} {{
1861 #define PASS12END }} if(as3_pass == 2) {{
1867 /* ------------ code blocks / statements ---------------- */
1869 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1871 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1872 PROGRAM_CODE_LIST: PROGRAM_CODE
1873 | PROGRAM_CODE_LIST PROGRAM_CODE
1875 PROGRAM_CODE: PACKAGE_DECLARATION
1876 | INTERFACE_DECLARATION
1878 | FUNCTION_DECLARATION
1881 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
1884 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1885 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1886 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1888 INPACKAGE_CODE: INTERFACE_DECLARATION
1890 | FUNCTION_DECLARATION
1893 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
1896 MAYBECODE: CODE {$$=$1;}
1897 MAYBECODE: {$$=code_new();}
1899 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1900 CODE: CODEPIECE {$$=$1;}
1902 // code which also may appear outside a method
1903 CODE_STATEMENT: IMPORT
1905 CODE_STATEMENT: FOR_IN
1906 CODE_STATEMENT: WHILE
1907 CODE_STATEMENT: DO_WHILE
1908 CODE_STATEMENT: SWITCH
1910 CODE_STATEMENT: WITH
1912 CODE_STATEMENT: VOIDEXPRESSION
1913 CODE_STATEMENT: USE_NAMESPACE
1914 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1915 CODE_STATEMENT: '{' '}' {$$=0;}
1917 // code which may appear anywhere
1918 CODEPIECE: ';' {$$=0;}
1919 CODEPIECE: CODE_STATEMENT
1920 CODEPIECE: VARIABLE_DECLARATION
1925 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
1927 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1929 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1930 //CODEBLOCK : '{' '}' {$$=0;}
1931 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1932 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1934 /* ------------ package init code ------------------- */
1936 PACKAGE_INITCODE: CODE_STATEMENT {
1937 code_t**cc = &global->init->method->body->code;
1938 *cc = code_append(*cc, $1);
1941 /* ------------ conditional compilation ------------- */
1943 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
1945 /* ------------ variables --------------------------- */
1947 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1948 | {$$.c=abc_pushundefined(0);
1952 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1953 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1955 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1956 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1958 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1961 if(variable_exists($1))
1962 syntaxerror("Variable %s already defined", $1);
1964 new_variable($1, 0, 1, 0);
1967 if(!is_subtype_of($3.t, $2)) {
1968 syntaxerror("Can't convert %s to %s", $3.t->name,
1974 if(state->method->uses_slots) {
1975 variable_t* v = find_slot(state, $1);
1977 // this variable is stored in a slot
1985 index = new_variable($1, $2, 1, 0);
1988 $$ = slot?abc_getscopeobject(0, 1):0;
1991 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1992 $$ = code_append($$, $3.c);
1993 $$ = converttype($$, $3.t, $2);
1996 $$ = defaultvalue($$, $2);
1999 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2000 $$ = code_append($$, $3.c);
2001 $$ = abc_coerce_a($$);
2003 // don't do anything
2011 $$ = abc_setslot($$, index);
2013 $$ = abc_setlocal($$, index);
2017 /* ------------ control flow ------------------------- */
2019 MAYBEELSE: %prec below_else {$$ = code_new();}
2020 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2021 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2023 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2026 $$ = code_append($$, $4.c);
2027 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2029 $$ = code_append($$, $6);
2031 myjmp = $$ = abc_jump($$, 0);
2033 myif->branch = $$ = abc_nop($$);
2035 $$ = code_append($$, $7);
2036 myjmp->branch = $$ = abc_nop($$);
2042 FOR_INIT : {$$=code_new();}
2043 FOR_INIT : VARIABLE_DECLARATION
2044 FOR_INIT : VOIDEXPRESSION
2046 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2047 // (I don't see any easy way to revolve this conflict otherwise, as we
2048 // can't touch VAR_READ without upsetting the precedence about "return")
2049 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2050 PASS1 $$=$2;new_variable($2,0,1,0);
2051 PASS2 $$=$2;new_variable($2,$3,1,0);
2053 FOR_IN_INIT : T_IDENTIFIER {
2058 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2059 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2061 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2062 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2064 $$ = code_append($$, $2);
2065 code_t*loopstart = $$ = abc_label($$);
2066 $$ = code_append($$, $4.c);
2067 code_t*myif = $$ = abc_iffalse($$, 0);
2068 $$ = code_append($$, $8);
2069 code_t*cont = $$ = abc_nop($$);
2070 $$ = code_append($$, $6);
2071 $$ = abc_jump($$, loopstart);
2072 code_t*out = $$ = abc_nop($$);
2073 breakjumpsto($$, $1.name, out);
2074 continuejumpsto($$, $1.name, cont);
2081 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2082 variable_t*var = find_variable(state, $2);
2083 char*tmp1name = concat2($2, "__tmp1__");
2084 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2085 char*tmp2name = concat2($2, "__array__");
2086 int array = new_variable(tmp1name, 0, 0, 0);
2089 $$ = code_append($$, $4.c);
2090 $$ = abc_coerce_a($$);
2091 $$ = abc_setlocal($$, array);
2092 $$ = abc_pushbyte($$, 0);
2093 $$ = abc_setlocal($$, it);
2095 code_t*loopstart = $$ = abc_label($$);
2097 $$ = abc_hasnext2($$, array, it);
2098 code_t*myif = $$ = abc_iffalse($$, 0);
2099 $$ = abc_getlocal($$, array);
2100 $$ = abc_getlocal($$, it);
2102 $$ = abc_nextname($$);
2104 $$ = abc_nextvalue($$);
2105 $$ = converttype($$, 0, var->type);
2106 $$ = abc_setlocal($$, var->index);
2108 $$ = code_append($$, $6);
2109 $$ = abc_jump($$, loopstart);
2111 code_t*out = $$ = abc_nop($$);
2112 breakjumpsto($$, $1.name, out);
2113 continuejumpsto($$, $1.name, loopstart);
2125 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2129 code_t*myjmp = $$ = abc_jump($$, 0);
2130 code_t*loopstart = $$ = abc_label($$);
2131 $$ = code_append($$, $6);
2132 code_t*cont = $$ = abc_nop($$);
2133 myjmp->branch = cont;
2134 $$ = code_append($$, $4.c);
2135 $$ = abc_iftrue($$, loopstart);
2136 code_t*out = $$ = abc_nop($$);
2137 breakjumpsto($$, $1, out);
2138 continuejumpsto($$, $1, cont);
2144 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2146 code_t*loopstart = $$ = abc_label($$);
2147 $$ = code_append($$, $3);
2148 code_t*cont = $$ = abc_nop($$);
2149 $$ = code_append($$, $6.c);
2150 $$ = abc_iftrue($$, loopstart);
2151 code_t*out = $$ = abc_nop($$);
2152 breakjumpsto($$, $1, out);
2153 continuejumpsto($$, $1, cont);
2159 BREAK : "break" %prec prec_none {
2160 $$ = abc___break__(0, "");
2162 BREAK : "break" T_IDENTIFIER {
2163 $$ = abc___break__(0, $2);
2165 CONTINUE : "continue" %prec prec_none {
2166 $$ = abc___continue__(0, "");
2168 CONTINUE : "continue" T_IDENTIFIER {
2169 $$ = abc___continue__(0, $2);
2172 MAYBE_CASE_LIST : {$$=0;}
2173 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2174 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2175 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2176 CASE_LIST: CASE {$$=$1;}
2177 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2179 CASE: "case" E ':' MAYBECODE {
2181 $$ = code_append($$, $2.c);
2182 code_t*j = $$ = abc_ifne($$, 0);
2183 $$ = code_append($$, $4);
2184 if($$->opcode != OPCODE___BREAK__) {
2185 $$ = abc___fallthrough__($$, "");
2187 code_t*e = $$ = abc_nop($$);
2190 DEFAULT: "default" ':' MAYBECODE {
2193 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2195 $$ = code_append($$, $7);
2196 code_t*out = $$ = abc_pop($$);
2197 breakjumpsto($$, $1, out);
2199 code_t*c = $$,*lastblock=0;
2201 if(c->opcode == OPCODE_IFNE) {
2202 if(!c->next) syntaxerror("internal error in fallthrough handling");
2204 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2206 c->opcode = OPCODE_JUMP;
2207 c->branch = lastblock;
2209 /* fall through end of switch */
2210 c->opcode = OPCODE_NOP;
2220 /* ------------ try / catch /finally ---------------- */
2222 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2223 state->exception_name=$3;
2224 PASS1 new_variable($3, 0, 0, 0);
2225 PASS2 new_variable($3, $4, 0, 0);
2228 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2229 multiname_t name = {QNAME, &name_ns, 0, $3};
2231 NEW(abc_exception_t, e)
2232 e->exc_type = sig2mname($4);
2233 e->var_name = multiname_clone(&name);
2237 int i = find_variable_safe(state, $3)->index;
2238 e->target = c = abc_nop(0);
2239 c = abc_setlocal(c, i);
2240 c = code_append(c, $8);
2246 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2251 NEW(abc_exception_t, e)
2252 e->exc_type = 0; //all exceptions
2253 e->var_name = 0; //no name
2256 e->to = code_append(e->to, $4);
2262 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2263 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2264 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2265 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2269 list_append($$.l,$2);
2270 $$.finally = $2->to;$2->to=0;
2273 CATCH_FINALLY_LIST: FINALLY {
2277 list_append($$.l,$1);
2278 $$.finally = $1->to;$1->to=0;
2282 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2283 code_t*out = abc_nop(0);
2285 code_t*start = abc_nop(0);
2286 $$ = code_append(start, $4);
2287 if(!is_break_or_jump($4)) {
2288 $$ = abc_jump($$, out);
2290 code_t*end = $$ = abc_nop($$);
2294 tmp = new_variable("__finally__", 0, 0, 0);
2296 abc_exception_list_t*l = $6.l;
2299 abc_exception_t*e = l->abc_exception;
2301 $$ = code_append($$, e->target);
2302 $$ = abc_jump($$, out);
2304 parserassert((ptroff_t)$6.finally);
2306 e->target = $$ = abc_nop($$);
2307 $$ = abc___rethrow__($$);
2315 $$ = code_append($$, out);
2317 $$ = insert_finally($$, $6.finally, tmp);
2319 list_concat(state->method->exceptions, $6.l);
2325 /* ------------ throw ------------------------------- */
2327 THROW : "throw" EXPRESSION {
2331 THROW : "throw" %prec prec_none {
2332 if(!state->exception_name)
2333 syntaxerror("re-throw only possible within a catch block");
2334 variable_t*v = find_variable(state, state->exception_name);
2336 $$=abc_getlocal($$, v->index);
2340 /* ------------ with -------------------------------- */
2342 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2344 $$ = abc_pushscope($$);
2345 $$ = code_append($$, $5);
2346 $$ = abc_popscope($$);
2349 /* ------------ packages and imports ---------------- */
2351 X_IDENTIFIER: T_IDENTIFIER
2352 | "package" {PASS12 $$="package";}
2354 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2355 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2357 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2358 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2359 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2360 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2362 IMPORT : "import" PACKAGEANDCLASS {
2364 slotinfo_t*s = registry_find($2->package, $2->name);
2365 if(!s) {// || !(s->flags&FLAG_BUILTIN)) {
2366 as3_schedule_class($2->package, $2->name);
2372 syntaxerror("Couldn't import class\n");
2373 state_has_imports();
2374 dict_put(state->imports, c->name, c);
2377 IMPORT : "import" PACKAGE '.' '*' {
2379 if(strncmp("flash.", $2, 6)) {
2380 as3_schedule_package($2);
2386 state_has_imports();
2387 list_append(state->wildcard_imports, i);
2391 /* ------------ classes and interfaces (header) -------------- */
2393 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2394 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2395 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2396 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2398 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2399 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2400 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2401 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2402 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2403 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2404 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2405 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2406 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2407 | T_NAMESPACE {PASS12 $$=FLAG_NAMESPACE;}
2409 EXTENDS : {$$=registry_getobjectclass();}
2410 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2412 EXTENDS_LIST : {PASS12 $$=list_new();}
2413 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2415 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2416 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2418 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2419 EXTENDS IMPLEMENTS_LIST
2420 '{' {PASS12 startclass($1,$3,$4,$5);}
2422 '}' {PASS12 endclass();$$=0;}
2424 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2426 '{' {PASS12 startclass($1|FLAG_INTERFACE,$3,0,$4);}
2427 MAYBE_INTERFACE_BODY
2428 '}' {PASS12 endclass();$$=0;}
2430 /* ------------ classes and interfaces (body) -------------- */
2433 MAYBE_CLASS_BODY : CLASS_BODY
2434 CLASS_BODY : CLASS_BODY_ITEM
2435 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2436 CLASS_BODY_ITEM : ';'
2437 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2438 CLASS_BODY_ITEM : SLOT_DECLARATION
2439 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2441 CLASS_BODY_ITEM : CODE_STATEMENT {
2442 code_t*c = state->cls->static_init->header;
2443 c = code_append(c, $1);
2444 state->cls->static_init->header = c;
2447 MAYBE_INTERFACE_BODY :
2448 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2449 INTERFACE_BODY : IDECLARATION
2450 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2452 IDECLARATION : "var" T_IDENTIFIER {
2453 syntaxerror("variable declarations not allowed in interfaces");
2455 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2458 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2459 syntaxerror("invalid method modifiers: interface methods always need to be public");
2461 startfunction(0,$1,$3,$4,&$6,$8);
2462 endfunction(0,$1,$3,$4,&$6,$8, 0);
2463 list_deep_free($6.list);
2466 /* ------------ classes and interfaces (body, slots ) ------- */
2468 VARCONST: "var" | "const"
2470 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} MAYBETYPE MAYBEEXPRESSION {
2472 U8 access = flags2access($1);
2474 varinfo_t* info = 0;
2476 memberinfo_t*i = registry_findmember(state->cls->info, $3, 1);
2478 check_override(i, flags);
2480 info = varinfo_register_onclass(state->cls->info, access, $3);
2482 slotinfo_t*i = registry_find(state->package, $3);
2484 syntaxerror("package %s already contains '%s'", state->package, $3);
2486 info = varinfo_register_global(access, state->package, $3);
2490 info->flags = flags;
2493 namespace_t mname_ns = {access, ""};
2494 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2496 trait_list_t**traits;
2500 mname_ns.name = state->package;
2501 traits = &global->init->traits;
2502 code = &global->init->method->body->code;
2503 } else if(flags&FLAG_STATIC) {
2505 traits = &state->cls->abc->static_traits;
2506 code = &state->cls->static_init->header;
2508 // instance variable
2509 traits = &state->cls->abc->traits;
2510 code = &state->cls->init->header;
2516 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2518 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2520 info->slot = t->slot_id;
2522 /* initalization code (if needed) */
2524 if($6.c && !is_pushundefined($6.c)) {
2525 c = abc_getlocal_0(c);
2526 c = code_append(c, $6.c);
2527 c = converttype(c, $6.t, $5);
2528 c = abc_setslot(c, t->slot_id);
2531 *code = code_append(*code, c);
2534 t->kind= TRAIT_CONST;
2538 setstaticfunction(0);
2541 /* ------------ constants -------------------------------------- */
2543 MAYBESTATICCONSTANT: {$$=0;}
2544 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2546 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2547 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2548 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2549 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2550 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2551 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2552 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2553 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2554 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2555 STATICCONSTANT : T_IDENTIFIER {
2557 as3_warning("Couldn't resolve %s", $1);
2558 $$ = constant_new_null($1);
2561 /* ------------ classes and interfaces (body, functions) ------- */
2563 // non-vararg version
2566 memset(&$$,0,sizeof($$));
2568 MAYBE_PARAM_LIST: PARAM_LIST {
2574 MAYBE_PARAM_LIST: "..." PARAM {
2576 memset(&$$,0,sizeof($$));
2578 list_append($$.list, $2);
2580 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2584 list_append($$.list, $4);
2588 PARAM_LIST: PARAM_LIST ',' PARAM {
2591 list_append($$.list, $3);
2595 memset(&$$,0,sizeof($$));
2596 list_append($$.list, $1);
2599 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2601 $$ = rfx_calloc(sizeof(param_t));
2607 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2609 $$ = rfx_calloc(sizeof(param_t));
2611 $$->type = TYPE_ANY;
2619 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2620 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2623 endfunction(0,$1,$3,$4,&$6,0,0);
2625 if(!state->method->info) syntaxerror("internal error");
2627 code_t*c = method_header(state->method);
2628 c = wrap_function(c, 0, $11);
2630 endfunction(0,$1,$3,$4,&$6,$8,c);
2632 list_deep_free($6.list);
2636 MAYBE_IDENTIFIER: T_IDENTIFIER
2637 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2638 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2639 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2642 endfunction(0,0,0,$2,&$4,0,0);
2644 methodinfo_t*f = state->method->info;
2645 if(!f || !f->kind) syntaxerror("internal error");
2647 code_t*c = method_header(state->method);
2648 c = wrap_function(c, 0, $9);
2650 int index = state->method->var_index;
2651 endfunction(0,0,0,$2,&$4,$6,c);
2653 $$.c = abc_getlocal(0, index);
2654 $$.t = TYPE_FUNCTION(f);
2656 PASS12 list_deep_free($4.list);
2660 /* ------------- package + class ids --------------- */
2662 CLASS: T_IDENTIFIER {
2665 slotinfo_t*s = find_class($1);
2666 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2667 $$ = (classinfo_t*)s;
2670 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2671 PASS1 static classinfo_t c;
2672 memset(&c, 0, sizeof(c));
2677 slotinfo_t*s = registry_find($1, $3);
2678 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2680 $$ = (classinfo_t*)s;
2683 CLASS_SPEC: PACKAGEANDCLASS
2686 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2687 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2689 TYPE : CLASS_SPEC {$$=$1;}
2690 | '*' {$$=registry_getanytype();}
2691 | "void" {$$=registry_getanytype();}
2693 | "String" {$$=registry_getstringclass();}
2694 | "int" {$$=registry_getintclass();}
2695 | "uint" {$$=registry_getuintclass();}
2696 | "Boolean" {$$=registry_getbooleanclass();}
2697 | "Number" {$$=registry_getnumberclass();}
2700 MAYBETYPE: ':' TYPE {$$=$2;}
2703 /* ----------function calls, delete, constructor calls ------ */
2705 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2706 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2708 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2709 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2710 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2712 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2716 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2717 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2719 $$.cc = code_append($1.cc, $2.c);
2723 NEW : "new" E XX MAYBE_PARAM_VALUES {
2725 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2727 code_t*paramcode = $4.cc;
2728 if($$.c->opcode == OPCODE_GETPROPERTY) {
2729 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2730 $$.c = code_cutlast($$.c);
2731 $$.c = code_append($$.c, paramcode);
2732 $$.c = abc_constructprop2($$.c, name, $4.len);
2733 multiname_destroy(name);
2734 } else if($$.c->opcode == OPCODE_GETSLOT) {
2735 int slot = (int)(ptroff_t)$$.c->data[0];
2736 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2737 multiname_t*name = t->name;
2738 $$.c = code_cutlast($$.c);
2739 $$.c = code_append($$.c, paramcode);
2740 $$.c = abc_constructprop2($$.c, name, $4.len);
2742 $$.c = code_append($$.c, paramcode);
2743 $$.c = abc_construct($$.c, $4.len);
2747 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2750 $$.c = abc_coerce_a($$.c);
2755 /* TODO: use abc_call (for calling local variables),
2756 abc_callstatic (for calling own methods)
2759 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2762 if($$.c->opcode == OPCODE_COERCE_A) {
2763 $$.c = code_cutlast($$.c);
2765 code_t*paramcode = $3.cc;
2768 if($$.c->opcode == OPCODE_GETPROPERTY) {
2769 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2770 $$.c = code_cutlast($$.c);
2771 $$.c = code_append($$.c, paramcode);
2772 $$.c = abc_callproperty2($$.c, name, $3.len);
2773 multiname_destroy(name);
2774 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
2775 int slot = (int)(ptroff_t)$$.c->data[0];
2776 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
2777 if(t->kind!=TRAIT_METHOD) {
2778 //ok: flash allows to assign closures to members.
2780 multiname_t*name = t->name;
2781 $$.c = code_cutlast($$.c);
2782 $$.c = code_append($$.c, paramcode);
2783 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2784 $$.c = abc_callproperty2($$.c, name, $3.len);
2785 } else if($$.c->opcode == OPCODE_GETSUPER) {
2786 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2787 $$.c = code_cutlast($$.c);
2788 $$.c = code_append($$.c, paramcode);
2789 $$.c = abc_callsuper2($$.c, name, $3.len);
2790 multiname_destroy(name);
2792 $$.c = abc_getglobalscope($$.c);
2793 $$.c = code_append($$.c, paramcode);
2794 $$.c = abc_call($$.c, $3.len);
2797 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2798 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2800 $$.c = abc_coerce_a($$.c);
2805 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2806 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2807 if(!state->method) syntaxerror("super() not allowed outside of a function");
2808 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2811 $$.c = abc_getlocal_0($$.c);
2813 $$.c = code_append($$.c, $3.cc);
2815 this is dependent on the control path, check this somewhere else
2816 if(state->method->has_super)
2817 syntaxerror("constructor may call super() only once");
2819 state->method->has_super = 1;
2821 $$.c = abc_constructsuper($$.c, $3.len);
2822 $$.c = abc_pushundefined($$.c);
2826 DELETE: "delete" E {
2828 if($$.c->opcode == OPCODE_COERCE_A) {
2829 $$.c = code_cutlast($$.c);
2831 multiname_t*name = 0;
2832 if($$.c->opcode == OPCODE_GETPROPERTY) {
2833 $$.c->opcode = OPCODE_DELETEPROPERTY;
2834 } else if($$.c->opcode == OPCODE_GETSLOT) {
2835 int slot = (int)(ptroff_t)$$.c->data[0];
2836 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
2837 $$.c = code_cutlast($$.c);
2838 $$.c = abc_deleteproperty2($$.c, name);
2840 $$.c = abc_getlocal_0($$.c);
2841 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2842 $$.c = abc_deleteproperty2($$.c, &m);
2844 $$.t = TYPE_BOOLEAN;
2847 RETURN: "return" %prec prec_none {
2848 $$ = abc_returnvoid(0);
2850 RETURN: "return" EXPRESSION {
2852 $$ = abc_returnvalue($$);
2855 // ----------------------- expression types -------------------------------------
2857 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2858 EXPRESSION : E %prec below_minus {$$ = $1;}
2859 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2861 $$.c = cut_last_push($$.c);
2862 $$.c = code_append($$.c,$3.c);
2865 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2866 $$=cut_last_push($1.c);
2869 // ----------------------- expression evaluation -------------------------------------
2871 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2872 //V : CONSTANT {$$ = 0;}
2874 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2875 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2876 //V : NEW {$$ = $1.c;}
2878 //V : DELETE {$$ = $1.c;}
2879 E : DELETE {$$ = $1;}
2885 namespace_t ns = {ACCESS_PACKAGE, ""};
2886 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2888 $$.c = abc_getlex2($$.c, &m);
2889 $$.c = abc_pushstring($$.c, $1.pattern);
2890 $$.c = abc_construct($$.c, 1);
2892 $$.c = abc_getlex2($$.c, &m);
2893 $$.c = abc_pushstring($$.c, $1.pattern);
2894 $$.c = abc_pushstring($$.c, $1.options);
2895 $$.c = abc_construct($$.c, 2);
2900 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2901 //MULTINAME(m, registry_getintclass());
2902 //$$.c = abc_coerce2($$.c, &m); // FIXME
2905 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2908 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2911 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2914 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2917 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
2920 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2923 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2924 $$.t = TYPE_BOOLEAN;
2926 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2927 $$.t = TYPE_BOOLEAN;
2929 CONSTANT : "null" {$$.c = abc_pushnull(0);
2933 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2934 $$.t = TYPE_BOOLEAN;
2936 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2937 $$.t = TYPE_BOOLEAN;
2939 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2940 $$.t = TYPE_BOOLEAN;
2942 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2943 $$.t = TYPE_BOOLEAN;
2945 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2946 $$.t = TYPE_BOOLEAN;
2948 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2949 $$.t = TYPE_BOOLEAN;
2951 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2952 $$.t = TYPE_BOOLEAN;
2954 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2955 $$.t = TYPE_BOOLEAN;
2958 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2960 $$.c = converttype($$.c, $1.t, $$.t);
2961 $$.c = abc_dup($$.c);
2962 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2963 $$.c = cut_last_push($$.c);
2964 $$.c = code_append($$.c,$3.c);
2965 $$.c = converttype($$.c, $3.t, $$.t);
2966 code_t*label = $$.c = abc_label($$.c);
2967 jmp->branch = label;
2970 $$.t = join_types($1.t, $3.t, 'A');
2971 /*printf("%08x:\n",$1.t);
2972 code_dump($1.c, 0, 0, "", stdout);
2973 printf("%08x:\n",$3.t);
2974 code_dump($3.c, 0, 0, "", stdout);
2975 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2977 $$.c = converttype($$.c, $1.t, $$.t);
2978 $$.c = abc_dup($$.c);
2979 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2980 $$.c = cut_last_push($$.c);
2981 $$.c = code_append($$.c,$3.c);
2982 $$.c = converttype($$.c, $3.t, $$.t);
2983 code_t*label = $$.c = abc_label($$.c);
2984 jmp->branch = label;
2987 E : '!' E {$$.c=$2.c;
2988 $$.c = abc_not($$.c);
2989 $$.t = TYPE_BOOLEAN;
2992 E : '~' E {$$.c=$2.c;
2993 $$.c = abc_bitnot($$.c);
2997 E : E '&' E {$$.c = code_append($1.c,$3.c);
2998 $$.c = abc_bitand($$.c);
3002 E : E '^' E {$$.c = code_append($1.c,$3.c);
3003 $$.c = abc_bitxor($$.c);
3007 E : E '|' E {$$.c = code_append($1.c,$3.c);
3008 $$.c = abc_bitor($$.c);
3012 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3013 $$.c = abc_rshift($$.c);
3016 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3017 $$.c = abc_urshift($$.c);
3020 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3021 $$.c = abc_lshift($$.c);
3025 E : E '/' E {$$.c = code_append($1.c,$3.c);
3026 $$.c = abc_divide($$.c);
3029 E : E '%' E {$$.c = code_append($1.c,$3.c);
3030 $$.c = abc_modulo($$.c);
3033 E : E '+' E {$$.c = code_append($1.c,$3.c);
3034 if(BOTH_INT($1.t, $3.t)) {
3035 $$.c = abc_add_i($$.c);
3038 $$.c = abc_add($$.c);
3039 $$.t = join_types($1.t,$3.t,'+');
3042 E : E '-' E {$$.c = code_append($1.c,$3.c);
3043 if(BOTH_INT($1.t,$3.t)) {
3044 $$.c = abc_subtract_i($$.c);
3047 $$.c = abc_subtract($$.c);
3051 E : E '*' E {$$.c = code_append($1.c,$3.c);
3052 if(BOTH_INT($1.t,$3.t)) {
3053 $$.c = abc_multiply_i($$.c);
3056 $$.c = abc_multiply($$.c);
3061 E : E "in" E {$$.c = code_append($1.c,$3.c);
3062 $$.c = abc_in($$.c);
3063 $$.t = TYPE_BOOLEAN;
3066 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3067 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3068 MULTINAME(m, (classinfo_t*)($3.t->data));
3069 $$.c = abc_astype2($1.c, &m);
3072 $$.c = code_append($1.c, $3.c);
3073 $$.c = abc_astypelate($$.c);
3078 E : E "instanceof" E
3079 {$$.c = code_append($1.c, $3.c);
3080 $$.c = abc_instanceof($$.c);
3081 $$.t = TYPE_BOOLEAN;
3084 E : E "is" E {$$.c = code_append($1.c, $3.c);
3085 $$.c = abc_istypelate($$.c);
3086 $$.t = TYPE_BOOLEAN;
3089 E : "typeof" '(' E ')' {
3091 $$.c = abc_typeof($$.c);
3096 $$.c = cut_last_push($2.c);
3097 $$.c = abc_pushundefined($$.c);
3101 E : "void" { $$.c = abc_pushundefined(0);
3105 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3110 $$.c=abc_negate_i($$.c);
3113 $$.c=abc_negate($$.c);
3120 $$.c = code_append($$.c, $3.c);
3122 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3123 $$.c = abc_getproperty2($$.c, &m);
3124 $$.t = 0; // array elements have unknown type
3127 E : '[' MAYBE_EXPRESSION_LIST ']' {
3129 $$.c = code_append($$.c, $2.cc);
3130 $$.c = abc_newarray($$.c, $2.len);
3131 $$.t = registry_getarrayclass();
3134 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
3135 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3137 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3139 $$.cc = code_append($$.cc, $1.c);
3140 $$.cc = code_append($$.cc, $3.c);
3143 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3146 $$.cc = code_append($$.cc, $3.c);
3147 $$.cc = code_append($$.cc, $5.c);
3152 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3154 $$.c = code_append($$.c, $2.cc);
3155 $$.c = abc_newobject($$.c, $2.len/2);
3156 $$.t = registry_getobjectclass();
3161 if(BOTH_INT($1.t,$3.t)) {
3162 c=abc_multiply_i(c);
3166 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3167 $$.c = toreadwrite($1.c, c, 0, 0);
3172 code_t*c = abc_modulo($3.c);
3173 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3174 $$.c = toreadwrite($1.c, c, 0, 0);
3178 code_t*c = abc_lshift($3.c);
3179 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3180 $$.c = toreadwrite($1.c, c, 0, 0);
3184 code_t*c = abc_rshift($3.c);
3185 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3186 $$.c = toreadwrite($1.c, c, 0, 0);
3190 code_t*c = abc_urshift($3.c);
3191 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3192 $$.c = toreadwrite($1.c, c, 0, 0);
3196 code_t*c = abc_divide($3.c);
3197 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3198 $$.c = toreadwrite($1.c, c, 0, 0);
3202 code_t*c = abc_bitor($3.c);
3203 c=converttype(c, TYPE_INT, $1.t);
3204 $$.c = toreadwrite($1.c, c, 0, 0);
3208 code_t*c = abc_bitxor($3.c);
3209 c=converttype(c, TYPE_INT, $1.t);
3210 $$.c = toreadwrite($1.c, c, 0, 0);
3216 if(TYPE_IS_INT($1.t)) {
3220 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3223 $$.c = toreadwrite($1.c, c, 0, 0);
3226 E : E "-=" E { code_t*c = $3.c;
3227 if(TYPE_IS_INT($1.t)) {
3228 c=abc_subtract_i(c);
3231 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3234 $$.c = toreadwrite($1.c, c, 0, 0);
3237 E : E '=' E { code_t*c = 0;
3238 c = code_append(c, $3.c);
3239 c = converttype(c, $3.t, $1.t);
3240 $$.c = toreadwrite($1.c, c, 1, 0);
3244 E : E '?' E ':' E %prec below_assignment {
3245 $$.t = join_types($3.t,$5.t,'?');
3247 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3248 $$.c = code_append($$.c, $3.c);
3249 $$.c = converttype($$.c, $3.t, $$.t);
3250 code_t*j2 = $$.c = abc_jump($$.c, 0);
3251 $$.c = j1->branch = abc_label($$.c);
3252 $$.c = code_append($$.c, $5.c);
3253 $$.c = converttype($$.c, $3.t, $$.t);
3254 $$.c = j2->branch = abc_label($$.c);
3257 E : E "++" { code_t*c = 0;
3258 classinfo_t*type = $1.t;
3259 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
3260 int nr = getlocalnr($1.c);
3261 code_free($1.c);$1.c=0;
3262 if(TYPE_IS_INT($1.t)) {
3263 $$.c = abc_getlocal(0, nr);
3264 $$.c = abc_inclocal_i($$.c, nr);
3265 } else if(TYPE_IS_NUMBER($1.t)) {
3266 $$.c = abc_getlocal(0, nr);
3267 $$.c = abc_inclocal($$.c, nr);
3268 } else syntaxerror("internal error");
3270 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3271 c=abc_increment_i(c);
3277 c=converttype(c, type, $1.t);
3278 $$.c = toreadwrite($1.c, c, 0, 1);
3283 // TODO: use inclocal, like with ++
3284 E : E "--" { code_t*c = 0;
3285 classinfo_t*type = $1.t;
3286 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3287 c=abc_decrement_i(c);
3293 c=converttype(c, type, $1.t);
3294 $$.c = toreadwrite($1.c, c, 0, 1);
3298 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3299 classinfo_t*type = $2.t;
3300 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3301 c=abc_increment_i(c);
3307 c=converttype(c, type, $2.t);
3308 $$.c = toreadwrite($2.c, c, 0, 0);
3312 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3313 classinfo_t*type = $2.t;
3314 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3315 c=abc_decrement_i(c);
3321 c=converttype(c, type, $2.t);
3322 $$.c = toreadwrite($2.c, c, 0, 0);
3326 E : "super" '.' T_IDENTIFIER
3327 { if(!state->cls->info)
3328 syntaxerror("super keyword not allowed outside a class");
3329 classinfo_t*t = state->cls->info->superclass;
3330 if(!t) t = TYPE_OBJECT;
3332 memberinfo_t*f = registry_findmember(t, $3, 1);
3333 namespace_t ns = {f->access, ""};
3334 MEMBER_MULTINAME(m, f, $3);
3336 $$.c = abc_getlocal_0($$.c);
3337 $$.c = abc_getsuper2($$.c, &m);
3338 $$.t = slotinfo_gettype((slotinfo_t*)f);
3341 E : '@' T_IDENTIFIER {
3343 $$.c = abc_pushundefined(0);
3345 as3_warning("ignored @ operator");
3348 E : E '.' '@' T_IDENTIFIER {
3349 // child attribute TODO
3350 $$.c = abc_pushundefined(0);
3352 as3_warning("ignored .@ operator");
3355 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3356 // namespace declaration TODO
3357 $$.c = abc_pushundefined(0);
3359 as3_warning("ignored :: operator");
3362 E : E ".." T_IDENTIFIER {
3364 $$.c = abc_pushundefined(0);
3366 as3_warning("ignored .. operator");
3369 E : E '.' '(' E ')' {
3371 $$.c = abc_pushundefined(0);
3373 as3_warning("ignored .() operator");
3376 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3380 E : E '.' T_IDENTIFIER
3382 classinfo_t*t = $1.t;
3384 if(TYPE_IS_CLASS(t) && t->data) {
3389 memberinfo_t*f = registry_findmember(t, $3, 1);
3391 if(f && !is_static != !(f->flags&FLAG_STATIC))
3393 if(f && f->slot && !noslot) {
3394 $$.c = abc_getslot($$.c, f->slot);
3396 MEMBER_MULTINAME(m, f, $3);
3397 $$.c = abc_getproperty2($$.c, &m);
3399 /* determine type */
3400 $$.t = slotinfo_gettype((slotinfo_t*)f);
3402 $$.c = abc_coerce_a($$.c);
3404 /* when resolving a property on an unknown type, we do know the
3405 name of the property (and don't seem to need the package), but
3406 we need to make avm2 try out all access modes */
3407 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3408 $$.c = abc_getproperty2($$.c, &m);
3409 $$.c = abc_coerce_a($$.c);
3410 $$.t = registry_getanytype();
3414 VAR_READ : T_IDENTIFIER {
3416 /* Queue unresolved identifiers for checking against the parent
3417 function's variables.
3418 We consider everything which is not a local variable "unresolved".
3419 This encompasses class names, members of the surrounding class
3420 etc. which *correct* because local variables of the parent function
3423 if(state->method->inner && !find_variable(state, $1)) {
3424 unknown_variable($1);
3434 /* look at variables */
3435 if((v = find_variable(state, $1))) {
3436 // $1 is a local variable
3437 $$.c = abc_getlocal($$.c, v->index);
3441 if((v = find_slot(state, $1))) {
3442 $$.c = abc_getscopeobject($$.c, 1);
3443 $$.c = abc_getslot($$.c, v->index);
3448 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3450 /* look at current class' members */
3451 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
3452 (f->flags&FLAG_STATIC) >= i_am_static) {
3453 // $1 is a function in this class
3454 int var_is_static = (f->flags&FLAG_STATIC);
3456 if(f->kind == INFOTYPE_METHOD) {
3457 $$.t = TYPE_FUNCTION(f);
3461 if(var_is_static && !i_am_static) {
3462 /* access to a static member from a non-static location.
3463 do this via findpropstrict:
3464 there doesn't seem to be any non-lookup way to access
3465 static properties of a class */
3466 state->method->late_binding = 1;
3468 namespace_t ns = {f->access, ""};
3469 multiname_t m = {QNAME, &ns, 0, $1};
3470 $$.c = abc_findpropstrict2($$.c, &m);
3471 $$.c = abc_getproperty2($$.c, &m);
3473 } else if(f->slot>0) {
3474 $$.c = abc_getlocal_0($$.c);
3475 $$.c = abc_getslot($$.c, f->slot);
3478 namespace_t ns = {f->access, ""};
3479 multiname_t m = {QNAME, &ns, 0, $1};
3480 $$.c = abc_getlocal_0($$.c);
3481 $$.c = abc_getproperty2($$.c, &m);
3486 /* look at actual classes, in the current package and imported */
3487 if((a = find_class($1))) {
3488 if(a->access == ACCESS_PACKAGEINTERNAL &&
3489 strcmp(a->package, state->package) &&
3490 strcmp(a->package, internal_filename_package)
3492 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
3493 infotypename(a),$1, a->package, state->package);
3495 if(a->kind != INFOTYPE_CLASS) {
3497 $$.c = abc_findpropstrict2($$.c, &m);
3498 $$.c = abc_getproperty2($$.c, &m);
3499 if(a->kind == INFOTYPE_METHOD) {
3500 methodinfo_t*f = (methodinfo_t*)a;
3501 $$.t = TYPE_FUNCTION(f);
3503 varinfo_t*v = (varinfo_t*)a;
3507 classinfo_t*c = (classinfo_t*)a;
3509 $$.c = abc_getglobalscope($$.c);
3510 $$.c = abc_getslot($$.c, c->slot);
3513 $$.c = abc_getlex2($$.c, &m);
3515 $$.t = TYPE_CLASS(c);
3520 /* unknown object, let the avm2 resolve it */
3522 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3523 state->method->late_binding = 1;
3525 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3528 $$.c = abc_findpropstrict2($$.c, &m);
3529 $$.c = abc_getproperty2($$.c, &m);
3533 // ----------------- namespaces -------------------------------------------------
3535 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3536 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3537 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3539 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {
3541 tokenizer_register_namespace($3);