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 int slot_index; // for inner methods
332 char is_a_slot; // for inner methods
335 abc_exception_list_t*exceptions;
337 methodstate_list_t*innerfunctions;
340 typedef struct _state {
345 import_list_t*wildcard_imports;
347 char has_own_imports;
348 char new_vars; // e.g. transition between two functions
351 methodstate_t*method;
358 typedef struct _global {
362 dict_t*file2token2info;
365 static global_t*global = 0;
366 static state_t* state = 0;
370 #define MULTINAME(m,x) \
374 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
376 #define MEMBER_MULTINAME(m,f,n) \
380 m##_ns.access = ((slotinfo_t*)(f))->access; \
384 m.namespace_set = 0; \
385 m.name = ((slotinfo_t*)(f))->name; \
387 m.type = MULTINAME; \
389 m.namespace_set = &nopackage_namespace_set; \
393 /* warning: list length of namespace set is undefined */
394 #define MULTINAME_LATE(m, access, package) \
395 namespace_t m##_ns = {access, package}; \
396 namespace_set_t m##_nsset; \
397 namespace_list_t m##_l;m##_l.next = 0; \
398 m##_nsset.namespaces = &m##_l; \
399 m##_nsset = m##_nsset; \
400 m##_l.namespace = &m##_ns; \
401 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
403 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
404 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
405 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
406 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
407 static namespace_list_t nl4 = {&ns4,0};
408 static namespace_list_t nl3 = {&ns3,&nl4};
409 static namespace_list_t nl2 = {&ns2,&nl3};
410 static namespace_list_t nl1 = {&ns1,&nl2};
411 static namespace_set_t nopackage_namespace_set = {&nl1};
413 static void new_state()
416 state_t*oldstate = state;
418 memcpy(s, state, sizeof(state_t)); //shallow copy
420 s->imports = dict_new();
424 state->has_own_imports = 0;
425 state->vars = dict_new();
426 state->old = oldstate;
429 static void state_has_imports()
431 state->wildcard_imports = list_clone(state->wildcard_imports);
432 state->imports = dict_clone(state->imports);
433 state->has_own_imports = 1;
436 static void state_destroy(state_t*state)
438 if(state->has_own_imports) {
439 list_free(state->wildcard_imports);
440 dict_destroy(state->imports);state->imports=0;
442 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
443 dict_destroy(state->imports);state->imports=0;
447 for(t=0;t<state->vars->hashsize;t++) {
448 dictentry_t*e =state->vars->slots[t];
450 free(e->data);e->data=0;
454 dict_destroy(state->vars);state->vars=0;
460 static void old_state()
462 if(!state || !state->old)
463 syntaxerror("invalid nesting");
464 state_t*leaving = state;
468 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
469 free(leaving->method);
472 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
477 state_destroy(leaving);
480 static code_t* method_header(methodstate_t*m);
481 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
482 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
485 static char* internal_filename_package = 0;
486 void initialize_file(char*filename)
489 syntaxerror("invalid call to initialize_file during parsing of another file");
492 state->package = internal_filename_package = strdup(filename);
494 global->token2info = dict_lookup(global->file2token2info,
495 current_filename // use long version
497 if(!global->token2info) {
498 global->token2info = dict_new2(&ptr_type);
499 dict_put(global->file2token2info, current_filename, global->token2info);
503 state->method = rfx_calloc(sizeof(methodstate_t));
504 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
506 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
507 function_initvars(state->method, 0, 0, 1);
508 global->init = abc_initscript(global->file);
509 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
515 if(!state || state->level!=1) {
516 syntaxerror("unexpected end of file in pass %d", as3_pass);
520 code_t*header = method_header(state->method);
521 code_t*c = wrap_function(header, 0, global->init->method->body->code);
522 global->init->method->body->code = c;
523 free(state->method);state->method=0;
526 //free(state->package);state->package=0; // used in registry
527 state_destroy(state);state=0;
530 void initialize_parser()
532 global = rfx_calloc(sizeof(global_t));
533 global->file = abc_file_new();
534 global->file->flags &= ~ABCFILE_LAZY;
535 global->file2token2info = dict_new();
536 global->token2info = 0;
539 void* finish_parser()
541 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
543 global->token2info=0;
549 static void xx_scopetest()
551 /* findpropstrict doesn't just return a scope object- it
552 also makes it "active" somehow. Push local_0 on the
553 scope stack and read it back with findpropstrict, it'll
554 contain properties like "trace". Trying to find the same
555 property on a "vanilla" local_0 yields only a "undefined" */
556 //c = abc_findpropstrict(c, "[package]::trace");
558 /*c = abc_getlocal_0(c);
559 c = abc_findpropstrict(c, "[package]::trace");
561 c = abc_setlocal_1(c);
563 c = abc_pushbyte(c, 0);
564 c = abc_setlocal_2(c);
566 code_t*xx = c = abc_label(c);
567 c = abc_findpropstrict(c, "[package]::trace");
568 c = abc_pushstring(c, "prop:");
569 c = abc_hasnext2(c, 1, 2);
571 c = abc_setlocal_3(c);
572 c = abc_callpropvoid(c, "[package]::trace", 2);
573 c = abc_getlocal_3(c);
575 c = abc_iftrue(c,xx);*/
578 typedef struct _variable {
582 methodstate_t*is_inner_method;
585 static variable_t* find_variable(state_t*s, char*name)
589 v = dict_lookup(s->vars, name);
591 if(s->new_vars) break;
596 static variable_t* find_slot(state_t*s, const char*name)
598 if(s->method && s->method->slots)
599 return dict_lookup(s->method->slots, name);
603 static variable_t* find_variable_safe(state_t*s, char*name)
605 variable_t* v = find_variable(s, name);
607 syntaxerror("undefined variable: %s", name);
610 static char variable_exists(char*name)
612 return dict_contains(state->vars, name);
614 code_t*defaultvalue(code_t*c, classinfo_t*type);
616 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
619 variable_t*v = find_slot(state, name);
625 v->index = state->method->variable_count++;
629 dict_put(state->vars, name, v);
633 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
635 return new_variable2(name, type, init, maybeslot)->index;
638 #define TEMPVARNAME "__as3_temp__"
639 static int gettempvar()
641 variable_t*v = find_variable(state, TEMPVARNAME);
644 return new_variable(TEMPVARNAME, 0, 0, 0);
647 code_t* var_block(code_t*body)
653 for(t=0;t<state->vars->hashsize;t++) {
654 dictentry_t*e = state->vars->slots[t];
656 variable_t*v = (variable_t*)e->data;
657 if(v->type && v->init) {
658 c = defaultvalue(c, v->type);
659 c = abc_setlocal(c, v->index);
660 k = abc_kill(k, v->index);
670 if(x->opcode== OPCODE___BREAK__ ||
671 x->opcode== OPCODE___CONTINUE__) {
672 /* link kill code before break/continue */
673 code_t*e = code_dup(k);
674 code_t*s = code_start(e);
686 c = code_append(c, body);
687 c = code_append(c, k);
691 void unknown_variable(char*name)
693 if(!state->method->unresolved_variables)
694 state->method->unresolved_variables = dict_new();
695 if(!dict_contains(state->method->unresolved_variables, name))
696 dict_put(state->method->unresolved_variables, name, 0);
699 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
701 static void parsererror(const char*file, int line, const char*f)
703 syntaxerror("internal error in %s, %s:%d", f, file, line);
707 static code_t* method_header(methodstate_t*m)
710 if(m->uses_slots || (m->late_binding && !m->inner)) {
711 c = abc_getlocal_0(c);
712 c = abc_pushscope(c);
715 c = abc_newactivation(c);
716 c = abc_pushscope(c);
718 methodstate_list_t*l = m->innerfunctions;
720 parserassert(l->methodstate->abc);
721 if(m->uses_slots && l->methodstate->is_a_slot) {
722 c = abc_getscopeobject(c, 1);
723 c = abc_newfunction(c, l->methodstate->abc);
725 c = abc_setlocal(c, l->methodstate->var_index);
726 c = abc_setslot(c, l->methodstate->slot_index);
728 c = abc_newfunction(c, l->methodstate->abc);
729 c = abc_setlocal(c, l->methodstate->var_index);
731 free(l->methodstate);l->methodstate=0;
735 c = code_append(c, m->header);
738 if(m->is_constructor && !m->has_super) {
739 // call default constructor
740 c = abc_getlocal_0(c);
741 c = abc_constructsuper(c, 0);
743 list_free(m->innerfunctions);
744 m->innerfunctions = 0;
749 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
751 c = code_append(c, header);
752 c = code_append(c, var_block(body));
753 /* append return if necessary */
754 if(!c || (c->opcode != OPCODE_RETURNVOID &&
755 c->opcode != OPCODE_RETURNVALUE)) {
756 c = abc_returnvoid(c);
762 static void startpackage(char*name)
765 /*printf("entering package \"%s\"\n", name);*/
766 state->package = strdup(name);
768 static void endpackage()
770 /*printf("leaving package \"%s\"\n", state->package);*/
772 //used e.g. in classinfo_register:
773 //free(state->package);state->package=0;
778 #define FLAG_PUBLIC 256
779 #define FLAG_PROTECTED 512
780 #define FLAG_PRIVATE 1024
781 #define FLAG_PACKAGEINTERNAL 2048
782 #define FLAG_NAMESPACE 4096
784 static int flags2access(int flags)
787 if(flags&FLAG_PUBLIC) {
788 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
789 syntaxerror("invalid combination of access levels");
790 access = ACCESS_PACKAGE;
791 } else if(flags&FLAG_PRIVATE) {
792 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
793 syntaxerror("invalid combination of access levels");
794 access = ACCESS_PRIVATE;
795 } else if(flags&FLAG_PROTECTED) {
796 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
797 syntaxerror("invalid combination of access levels");
798 access = ACCESS_PROTECTED;
800 access = ACCESS_PACKAGEINTERNAL;
805 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
810 index = new_variable("this", 0, 0, 0);
811 else if(!m->is_global)
812 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
814 index = new_variable("globalscope", 0, 0, 0);
817 parserassert(!index);
820 /* as variables and slots share the same number, make sure
821 that those variable indices are reserved. It's up to the
822 optimizer to later shuffle the variables down to lower
824 m->variable_count = m->uses_slots;
829 for(p=params->list;p;p=p->next) {
830 new_variable(p->param->name, p->param->type, 0, 1);
834 methodstate_list_t*l = m->innerfunctions;
836 methodstate_t*m = l->methodstate;
837 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
838 m->var_index = v->index;
839 m->slot_index = v->index;
840 v->is_inner_method = m;
846 char*as3_globalclass=0;
847 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements)
850 syntaxerror("inner classes now allowed");
854 classinfo_list_t*mlist=0;
856 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
857 syntaxerror("invalid modifier(s)");
859 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
860 syntaxerror("public and internal not supported at the same time.");
862 /* create the class name, together with the proper attributes */
866 if(!(flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
867 access = ACCESS_PRIVATE; package = internal_filename_package;
868 } else if(!(flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
869 access = ACCESS_PACKAGEINTERNAL; package = state->package;
870 } else if(state->package!=internal_filename_package) {
871 access = ACCESS_PACKAGE; package = state->package;
873 syntaxerror("public classes only allowed inside a package");
877 state->cls = rfx_calloc(sizeof(classstate_t));
878 state->cls->init = rfx_calloc(sizeof(methodstate_t));
879 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
880 /* notice: we make no effort to initialize the top variable (local0) here,
881 even though it has special meaning. We just rely on the facat
882 that pass 1 won't do anything with variables */
884 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
886 /* set current method to constructor- all code within the class-level (except
887 static variable initializations) will be executed during construction time */
888 state->method = state->cls->init;
890 if(registry_find(package, classname)) {
891 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
893 /* build info struct */
894 int num_interfaces = (list_length(implements));
895 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
896 state->cls->info->flags |= flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
900 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
902 state->method = state->cls->init;
903 parserassert(state->cls && state->cls->info);
905 function_initvars(state->cls->init, 0, 0, 1);
906 function_initvars(state->cls->static_init, 0, 0, 0);
908 if(extends && (extends->flags & FLAG_FINAL))
909 syntaxerror("Can't extend final class '%s'", extends->name);
911 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
912 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
914 classinfo_list_t*l = implements;
915 for(l=implements;l;l=l->next) {
916 if(!(l->classinfo->flags & FLAG_INTERFACE))
917 syntaxerror("'%s' is not an interface", l->classinfo->name);
918 state->cls->info->interfaces[pos++] = l->classinfo;
921 /* generate the abc code for this class */
922 MULTINAME(classname2,state->cls->info);
923 multiname_t*extends2 = sig2mname(extends);
925 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
926 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
927 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
928 if(state->cls->info->flags&FLAG_INTERFACE) {
929 abc_class_interface(state->cls->abc);
932 abc_class_protectedNS(state->cls->abc, classname);
934 for(mlist=implements;mlist;mlist=mlist->next) {
935 MULTINAME(m, mlist->classinfo);
936 abc_class_add_interface(state->cls->abc, &m);
939 /* write the construction code for this class to the global init
941 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
943 abc_method_body_t*m = global->init->method->body;
944 __ getglobalscope(m);
945 classinfo_t*s = extends;
950 //TODO: take a look at the current scope stack, maybe
951 // we can re-use something
956 multiname_t*s2 = sig2mname(s);
958 multiname_destroy(s2);
960 __ pushscope(m); count++;
961 m->code = m->code->prev->prev; // invert
963 /* continue appending after last op end */
964 while(m->code && m->code->next) m->code = m->code->next;
966 /* TODO: if this is one of *our* classes, we can also
967 do a getglobalscope/getslot <nr> (which references
968 the init function's slots) */
970 __ getlex2(m, extends2);
972 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
973 stack is not the superclass */
974 __ pushscope(m);count++;
977 /* notice: we get a verify error #1107 if the top element on the scope
978 stack is not the global object */
980 __ pushscope(m);count++;
982 __ newclass(m,state->cls->abc);
986 __ setslot(m, slotindex);
987 multiname_destroy(extends2);
989 /* flash.display.MovieClip handling */
991 if(!as3_globalclass && (flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
992 if(state->package && state->package[0]) {
993 as3_globalclass = concat3(state->package, ".", classname);
995 as3_globalclass = strdup(classname);
1001 static void setstaticfunction(int x)
1005 state->method = state->cls->static_init;
1007 state->method = state->cls->init;
1010 parserassert(state->method);
1014 static void endclass()
1017 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1019 c = abc_getlocal_0(c);
1020 c = abc_constructsuper(c, 0);
1021 state->cls->init->header = code_append(state->cls->init->header, c);
1022 state->cls->has_constructor=1;
1024 if(state->cls->init) {
1025 if(state->cls->info->flags&FLAG_INTERFACE) {
1026 if(state->cls->init->header)
1027 syntaxerror("interface can not have class-level code");
1029 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1030 code_t*c = method_header(state->cls->init);
1031 m->body->code = wrap_function(c, 0, m->body->code);
1034 if(state->cls->static_init) {
1035 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1036 code_t*c = method_header(state->cls->static_init);
1037 m->body->code = wrap_function(c, 0, m->body->code);
1044 void check_code_for_break(code_t*c)
1047 if(c->opcode == OPCODE___BREAK__) {
1048 char*name = string_cstr(c->data[0]);
1049 syntaxerror("Unresolved \"break %s\"", name);
1051 if(c->opcode == OPCODE___CONTINUE__) {
1052 char*name = string_cstr(c->data[0]);
1053 syntaxerror("Unresolved \"continue %s\"", name);
1060 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1062 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1063 if(TYPE_IS_NUMBER(t)) {
1064 xassert(c->type == CONSTANT_FLOAT
1065 || c->type == CONSTANT_INT
1066 || c->type == CONSTANT_UINT);
1067 } else if(TYPE_IS_UINT(t)) {
1068 xassert(c->type == CONSTANT_UINT ||
1069 (c->type == CONSTANT_INT && c->i>=0));
1070 } else if(TYPE_IS_INT(t)) {
1071 xassert(c->type == CONSTANT_INT);
1072 } else if(TYPE_IS_BOOLEAN(t)) {
1073 xassert(c->type == CONSTANT_TRUE
1074 || c->type == CONSTANT_FALSE);
1078 static void check_override(memberinfo_t*m, int flags)
1082 if(m->parent == state->cls->info)
1083 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1085 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1086 if(m->access==ACCESS_PRIVATE)
1088 if(m->flags & FLAG_FINAL)
1089 syntaxerror("can't override final member %s", m->name);
1090 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1091 syntaxerror("can't override static member %s", m->name);
1092 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1093 syntaxerror("can't override non-static member %s with static declaration", m->name);
1095 if(!(flags&FLAG_OVERRIDE)) {
1096 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1097 if(m->kind == INFOTYPE_METHOD)
1098 syntaxerror("can't override without explicit 'override' declaration");
1100 syntaxerror("can't override '%s'", m->name);
1105 static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
1107 methodinfo_t*minfo = 0;
1108 U8 access = flags2access(flags);
1111 minfo = methodinfo_register_global(access, state->package, name);
1112 minfo->return_type = return_type;
1113 } else if(getset != KW_GET && getset != KW_SET) {
1115 memberinfo_t* m = registry_findmember(state->cls->info, name, 0);
1117 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1119 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1120 minfo->return_type = return_type;
1121 // getslot on a member slot only returns "undefined", so no need
1122 // to actually store these
1123 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1125 //class getter/setter
1126 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1128 if(getset == KW_GET)
1130 else if(params->list && params->list->param)
1131 type = params->list->param->type;
1132 // not sure wether to look into superclasses here, too
1133 minfo = (methodinfo_t*)registry_findmember(state->cls->info, name, 1);
1135 if(minfo->kind!=INFOTYPE_SLOT)
1136 syntaxerror("class already contains a method called '%s'", name);
1137 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1138 syntaxerror("class already contains a field called '%s'", name);
1139 if(minfo->subtype & gs)
1140 syntaxerror("getter/setter for '%s' already defined", name);
1141 /* make a setter or getter into a getset */
1142 minfo->subtype |= gs;
1143 if(!minfo->return_type) {
1144 minfo->return_type = type;
1146 if(minfo && minfo->return_type != type)
1147 syntaxerror("different type in getter and setter");
1150 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1151 minfo->kind = INFOTYPE_SLOT; //hack
1152 minfo->subtype = gs;
1153 minfo->return_type = type;
1155 /* can't assign a slot as getter and setter might have different slots */
1156 //minfo->slot = slot;
1158 if(flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1159 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1160 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1164 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1166 //parserassert(state->method && state->method->info);
1168 methodstate_t*parent_method = state->method;
1171 return_type = 0; // not valid in pass 1
1175 state->new_vars = 1;
1178 state->method = rfx_calloc(sizeof(methodstate_t));
1179 state->method->inner = 1;
1180 state->method->variable_count = 0;
1181 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1183 NEW(methodinfo_t,minfo);
1184 minfo->kind = INFOTYPE_METHOD;
1185 minfo->access = ACCESS_PACKAGEINTERNAL;
1187 state->method->info = minfo;
1190 list_append(parent_method->innerfunctions, state->method);
1192 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1194 function_initvars(state->method, params, 0, 1);
1198 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1199 state->method->variable_count = 0;
1200 parserassert(state->method);
1202 state->method->info->return_type = return_type;
1203 function_initvars(state->method, params, 0, 1);
1207 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1208 params_t*params, classinfo_t*return_type)
1210 if(state->method && state->method->info) {
1211 syntaxerror("not able to start another method scope");
1214 state->new_vars = 1;
1217 state->method = rfx_calloc(sizeof(methodstate_t));
1218 state->method->has_super = 0;
1221 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1223 state->method->is_global = 1;
1224 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1226 if(state->method->is_constructor)
1227 name = "__as3_constructor__";
1230 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1232 function_initvars(state->method, params, flags, 1);
1234 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1238 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1239 state->method->variable_count = 0;
1240 parserassert(state->method);
1243 memberinfo_t*m = registry_findmember(state->cls->info, name, 2);
1244 check_override(m, flags);
1248 state->cls->has_constructor |= state->method->is_constructor;
1251 state->method->info->return_type = return_type;
1252 function_initvars(state->method, params, flags, 1);
1256 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1257 params_t*params, classinfo_t*return_type, code_t*body)
1260 // store inner methods in variables
1261 function_initvars(state->method, 0, 0, 0);
1263 methodstate_list_t*ml = state->method->innerfunctions;
1264 dict_t*xvars = dict_new();
1266 methodstate_t*m = ml->methodstate;
1267 parserassert(m->inner);
1268 if(m->unresolved_variables) {
1269 dict_t*d = m->unresolved_variables;
1271 for(t=0;t<d->hashsize;t++) {
1272 dictentry_t*l = d->slots[t];
1274 /* check parent method's variables */
1275 if(find_variable(state, l->key)) {
1276 m->uses_parent_function = 1;
1277 state->method->uses_slots = 1;
1278 dict_put(xvars, l->key, 0);
1285 dict_destroy(m->unresolved_variables);
1286 m->unresolved_variables = 0;
1290 if(state->method->uses_slots) {
1291 state->method->slots = dict_new();
1293 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1294 if(v->index && dict_contains(xvars, name)) {
1297 if(v->is_inner_method) {
1298 v->is_inner_method->is_a_slot = 1;
1301 dict_put(state->method->slots, name, v);
1304 state->method->uses_slots = i;
1305 dict_destroy(state->vars);state->vars = 0;
1307 dict_destroy(xvars);
1314 /*if(state->method->uses_parent_function){
1315 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1320 multiname_t*type2 = sig2mname(return_type);
1322 if(state->method->inner) {
1323 f = state->method->abc;
1324 abc_method_init(f, global->file, type2, 1);
1325 } else if(state->method->is_constructor) {
1326 f = abc_class_getconstructor(state->cls->abc, type2);
1327 } else if(!state->method->is_global) {
1328 namespace_t mname_ns = {state->method->info->access, ""};
1329 multiname_t mname = {QNAME, &mname_ns, 0, name};
1331 if(flags&FLAG_STATIC)
1332 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1334 f = abc_class_method(state->cls->abc, type2, &mname);
1335 slot = f->trait->slot_id;
1337 namespace_t mname_ns = {state->method->info->access, state->package};
1338 multiname_t mname = {QNAME, &mname_ns, 0, name};
1340 f = abc_method_new(global->file, type2, 1);
1341 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1342 //abc_code_t*c = global->init->method->body->code;
1344 //flash doesn't seem to allow us to access function slots
1345 //state->method->info->slot = slot;
1347 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1348 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1349 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1350 if(params->varargs) f->flags |= METHOD_NEED_REST;
1354 for(p=params->list;p;p=p->next) {
1355 if(params->varargs && !p->next) {
1356 break; //varargs: omit last parameter in function signature
1358 multiname_t*m = sig2mname(p->param->type);
1359 list_append(f->parameters, m);
1360 if(p->param->value) {
1361 check_constant_against_type(p->param->type, p->param->value);
1362 opt=1;list_append(f->optional_parameters, p->param->value);
1364 syntaxerror("non-optional parameter not allowed after optional parameters");
1367 if(state->method->slots) {
1368 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1370 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1371 multiname_t*type = sig2mname(v->type);
1372 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1373 t->slot_id = v->index;
1378 check_code_for_break(body);
1381 f->body->code = body;
1382 f->body->exceptions = state->method->exceptions;
1383 } else { //interface
1385 syntaxerror("interface methods can't have a method body");
1395 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1400 void breakjumpsto(code_t*c, char*name, code_t*jump)
1403 if(c->opcode == OPCODE___BREAK__) {
1404 string_t*name2 = c->data[0];
1405 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1406 c->opcode = OPCODE_JUMP;
1413 void continuejumpsto(code_t*c, char*name, code_t*jump)
1416 if(c->opcode == OPCODE___CONTINUE__) {
1417 string_t*name2 = c->data[0];
1418 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1419 c->opcode = OPCODE_JUMP;
1427 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1428 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1429 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1431 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1433 if(!type1 || !type2)
1434 return registry_getanytype();
1435 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1436 return registry_getanytype();
1439 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1448 return registry_getanytype();
1450 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1455 return abc_coerce_a(c);
1459 // cast an "any" type to a specific type. subject to
1460 // runtime exceptions
1461 return abc_coerce2(c, &m);
1464 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1465 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1466 // allow conversion between number types
1467 return abc_coerce2(c, &m);
1469 //printf("%s.%s\n", from.package, from.name);
1470 //printf("%s.%s\n", to.package, to.name);
1472 classinfo_t*supertype = from;
1474 if(supertype == to) {
1475 // target type is one of from's superclasses
1476 return abc_coerce2(c, &m);
1479 while(supertype->interfaces[t]) {
1480 if(supertype->interfaces[t]==to) {
1481 // target type is one of from's interfaces
1482 return abc_coerce2(c, &m);
1486 supertype = supertype->superclass;
1488 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1490 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1492 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1495 as3_error("can't convert type %s%s%s to %s%s%s",
1496 from->package, from->package?".":"", from->name,
1497 to->package, to->package?".":"", to->name);
1501 code_t*defaultvalue(code_t*c, classinfo_t*type)
1503 if(TYPE_IS_INT(type)) {
1504 c = abc_pushbyte(c, 0);
1505 } else if(TYPE_IS_UINT(type)) {
1506 c = abc_pushuint(c, 0);
1507 } else if(TYPE_IS_FLOAT(type)) {
1509 } else if(TYPE_IS_BOOLEAN(type)) {
1510 c = abc_pushfalse(c);
1512 //c = abc_pushundefined(c);
1514 c = abc_pushnull(c);
1516 c = abc_coerce2(c, &m);
1521 char is_pushundefined(code_t*c)
1523 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1526 static slotinfo_t* find_class(char*name)
1530 c = registry_find(state->package, name);
1533 /* try explicit imports */
1534 dictentry_t* e = dict_get_slot(state->imports, name);
1537 if(!strcmp(e->key, name)) {
1538 c = (slotinfo_t*)e->data;
1544 /* try package.* imports */
1545 import_list_t*l = state->wildcard_imports;
1547 //printf("does package %s contain a class %s?\n", l->import->package, name);
1548 c = registry_find(l->import->package, name);
1553 /* try global package */
1554 c = registry_find("", name);
1557 /* try local "filename" package */
1558 c = registry_find(internal_filename_package, name);
1564 static char is_getlocal(code_t*c)
1566 if(!c || c->prev || c->next)
1568 return(c->opcode == OPCODE_GETLOCAL
1569 || c->opcode == OPCODE_GETLOCAL_0
1570 || c->opcode == OPCODE_GETLOCAL_1
1571 || c->opcode == OPCODE_GETLOCAL_2
1572 || c->opcode == OPCODE_GETLOCAL_3);
1574 static int getlocalnr(code_t*c)
1576 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1577 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1578 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1579 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1580 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1581 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1585 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1589 [prefix code] [read instruction]
1593 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1595 if(in && in->opcode == OPCODE_COERCE_A) {
1596 in = code_cutlast(in);
1599 syntaxerror("internal error");
1601 /* chop off read instruction */
1605 prefix = r->prev;r->prev = 0;
1611 char use_temp_var = readbefore;
1613 /* generate the write instruction, and maybe append a dup to the prefix code */
1614 code_t* write = abc_nop(0);
1615 if(r->opcode == OPCODE_GETPROPERTY) {
1616 write->opcode = OPCODE_SETPROPERTY;
1617 multiname_t*m = (multiname_t*)r->data[0];
1618 write->data[0] = multiname_clone(m);
1619 if(m->type == QNAME || m->type == MULTINAME) {
1621 prefix = abc_dup(prefix); // we need the object, too
1624 } else if(m->type == MULTINAMEL) {
1626 /* dupping two values on the stack requires 5 operations and one register-
1627 couldn't adobe just have given us a dup2? */
1628 int temp = gettempvar();
1629 prefix = abc_setlocal(prefix, temp);
1630 prefix = abc_dup(prefix);
1631 prefix = abc_getlocal(prefix, temp);
1632 prefix = abc_swap(prefix);
1633 prefix = abc_getlocal(prefix, temp);
1635 prefix = abc_kill(prefix, temp);
1639 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1641 } else if(r->opcode == OPCODE_GETSLOT) {
1642 write->opcode = OPCODE_SETSLOT;
1643 write->data[0] = r->data[0];
1645 prefix = abc_dup(prefix); // we need the object, too
1648 } else if(r->opcode == OPCODE_GETLOCAL) {
1649 write->opcode = OPCODE_SETLOCAL;
1650 write->data[0] = r->data[0];
1651 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1652 write->opcode = OPCODE_SETLOCAL_0;
1653 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1654 write->opcode = OPCODE_SETLOCAL_1;
1655 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1656 write->opcode = OPCODE_SETLOCAL_2;
1657 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1658 write->opcode = OPCODE_SETLOCAL_3;
1661 syntaxerror("illegal lvalue: can't assign a value to this expression");
1668 /* with getproperty/getslot, we have to be extra careful not
1669 to execute the read code twice, as it might have side-effects
1670 (e.g. if the property is in fact a setter/getter combination)
1672 So read the value, modify it, and write it again,
1673 using prefix only once and making sure (by using a temporary
1674 register) that the return value is what we just wrote */
1675 temp = gettempvar();
1676 c = code_append(c, prefix);
1677 c = code_append(c, r);
1680 c = abc_setlocal(c, temp);
1682 c = code_append(c, middlepart);
1685 c = abc_setlocal(c, temp);
1687 c = code_append(c, write);
1688 c = abc_getlocal(c, temp);
1689 c = abc_kill(c, temp);
1691 /* if we're allowed to execute the read code twice *and*
1692 the middlepart doesn't modify the code, things are easier.
1694 code_t* r2 = code_dup(r);
1695 //c = code_append(c, prefix);
1696 parserassert(!prefix);
1697 c = code_append(c, r);
1698 c = code_append(c, middlepart);
1699 c = code_append(c, write);
1700 c = code_append(c, r2);
1703 /* even smaller version: overwrite the value without reading
1707 c = code_append(c, prefix);
1710 c = code_append(c, middlepart);
1711 c = code_append(c, write);
1712 c = code_append(c, r);
1715 temp = gettempvar();
1717 c = code_append(c, prefix);
1719 c = code_append(c, middlepart);
1721 c = abc_setlocal(c, temp);
1722 c = code_append(c, write);
1723 c = abc_getlocal(c, temp);
1724 c = abc_kill(c, temp);
1730 char is_break_or_jump(code_t*c)
1734 if(c->opcode == OPCODE_JUMP ||
1735 c->opcode == OPCODE___BREAK__ ||
1736 c->opcode == OPCODE___CONTINUE__ ||
1737 c->opcode == OPCODE_THROW ||
1738 c->opcode == OPCODE_RETURNVOID ||
1739 c->opcode == OPCODE_RETURNVALUE) {
1746 #define IS_FINALLY_TARGET(op) \
1747 ((op) == OPCODE___CONTINUE__ || \
1748 (op) == OPCODE___BREAK__ || \
1749 (op) == OPCODE_RETURNVOID || \
1750 (op) == OPCODE_RETURNVALUE || \
1751 (op) == OPCODE___RETHROW__)
1753 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1755 #define NEED_EXTRA_STACK_ARG
1756 code_t*finally_label = abc_nop(0);
1757 NEW(lookupswitch_t, l);
1763 code_t*prev = i->prev;
1764 if(IS_FINALLY_TARGET(i->opcode)) {
1767 if(i->opcode == OPCODE___RETHROW__ ||
1768 i->opcode == OPCODE_RETURNVALUE) {
1769 if(i->opcode == OPCODE___RETHROW__)
1770 i->opcode = OPCODE_THROW;
1772 p = abc_coerce_a(p);
1773 p = abc_setlocal(p, tempvar);
1775 p = abc_pushbyte(p, count++);
1776 p = abc_jump(p, finally_label);
1777 code_t*target = p = abc_label(p);
1778 #ifdef NEED_EXTRA_STACK_ARG
1782 p = abc_getlocal(p, tempvar);
1785 p->next = i;i->prev = p;
1786 list_append(l->targets, target);
1792 c = abc_pushbyte(c, -1);
1793 c = code_append(c, finally_label);
1794 c = code_append(c, finally);
1796 #ifdef NEED_EXTRA_STACK_ARG
1799 c = abc_lookupswitch(c, l);
1800 c = l->def = abc_label(c);
1801 #ifdef NEED_EXTRA_STACK_ARG
1808 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1812 code_t*prev = i->prev;
1813 if(IS_FINALLY_TARGET(i->opcode)) {
1814 if(i->opcode == OPCODE___RETHROW__)
1815 i->opcode = OPCODE_THROW;
1816 code_t*end = code_dup(finally);
1817 code_t*start = code_start(end);
1818 if(prev) prev->next = start;
1825 return code_append(c, finally);
1828 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1834 int num_insertion_points=0;
1836 if(IS_FINALLY_TARGET(i->opcode))
1837 num_insertion_points++;
1844 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1849 int simple_version_cost = (1+num_insertion_points)*code_size;
1850 int lookup_version_cost = 4*num_insertion_points + 5;
1852 if(cantdup || simple_version_cost > lookup_version_cost) {
1853 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1854 return insert_finally_lookup(c, finally, tempvar);
1856 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1857 return insert_finally_simple(c, finally, tempvar);
1861 #define PASS1 }} if(as3_pass == 1) {{
1862 #define PASS1END }} if(as3_pass == 2) {{
1863 #define PASS2 }} if(as3_pass == 2) {{
1864 #define PASS12 }} {{
1865 #define PASS12END }} if(as3_pass == 2) {{
1871 /* ------------ code blocks / statements ---------------- */
1873 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1875 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1876 PROGRAM_CODE_LIST: PROGRAM_CODE
1877 | PROGRAM_CODE_LIST PROGRAM_CODE
1879 PROGRAM_CODE: PACKAGE_DECLARATION
1880 | INTERFACE_DECLARATION
1882 | FUNCTION_DECLARATION
1885 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
1888 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1889 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1890 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1892 INPACKAGE_CODE: INTERFACE_DECLARATION
1894 | FUNCTION_DECLARATION
1897 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
1900 MAYBECODE: CODE {$$=$1;}
1901 MAYBECODE: {$$=code_new();}
1903 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1904 CODE: CODEPIECE {$$=$1;}
1906 // code which also may appear outside a method
1907 CODE_STATEMENT: IMPORT
1909 CODE_STATEMENT: FOR_IN
1910 CODE_STATEMENT: WHILE
1911 CODE_STATEMENT: DO_WHILE
1912 CODE_STATEMENT: SWITCH
1914 CODE_STATEMENT: WITH
1916 CODE_STATEMENT: VOIDEXPRESSION
1917 CODE_STATEMENT: USE_NAMESPACE
1918 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1919 CODE_STATEMENT: '{' '}' {$$=0;}
1921 // code which may appear anywhere
1922 CODEPIECE: ';' {$$=0;}
1923 CODEPIECE: CODE_STATEMENT
1924 CODEPIECE: VARIABLE_DECLARATION
1929 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
1931 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1933 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1934 //CODEBLOCK : '{' '}' {$$=0;}
1935 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1936 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1938 /* ------------ package init code ------------------- */
1940 PACKAGE_INITCODE: CODE_STATEMENT {
1941 code_t**cc = &global->init->method->body->code;
1942 *cc = code_append(*cc, $1);
1945 /* ------------ conditional compilation ------------- */
1947 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
1949 /* ------------ variables --------------------------- */
1951 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1952 | {$$.c=abc_pushundefined(0);
1956 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1957 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1959 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1960 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1962 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1965 if(variable_exists($1))
1966 syntaxerror("Variable %s already defined", $1);
1968 new_variable($1, 0, 1, 0);
1971 if(!is_subtype_of($3.t, $2)) {
1972 syntaxerror("Can't convert %s to %s", $3.t->name,
1978 if(state->method->uses_slots) {
1979 variable_t* v = find_slot(state, $1);
1981 // this variable is stored in a slot
1989 index = new_variable($1, $2, 1, 0);
1992 $$ = slot?abc_getscopeobject(0, 1):0;
1995 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1996 $$ = code_append($$, $3.c);
1997 $$ = converttype($$, $3.t, $2);
2000 $$ = defaultvalue($$, $2);
2003 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2004 $$ = code_append($$, $3.c);
2005 $$ = abc_coerce_a($$);
2007 // don't do anything
2015 $$ = abc_setslot($$, index);
2017 $$ = abc_setlocal($$, index);
2021 /* ------------ control flow ------------------------- */
2023 MAYBEELSE: %prec below_else {$$ = code_new();}
2024 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2025 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2027 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2030 $$ = code_append($$, $4.c);
2031 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2033 $$ = code_append($$, $6);
2035 myjmp = $$ = abc_jump($$, 0);
2037 myif->branch = $$ = abc_nop($$);
2039 $$ = code_append($$, $7);
2040 myjmp->branch = $$ = abc_nop($$);
2046 FOR_INIT : {$$=code_new();}
2047 FOR_INIT : VARIABLE_DECLARATION
2048 FOR_INIT : VOIDEXPRESSION
2050 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2051 // (I don't see any easy way to revolve this conflict otherwise, as we
2052 // can't touch VAR_READ without upsetting the precedence about "return")
2053 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2054 PASS1 $$=$2;new_variable($2,0,1,0);
2055 PASS2 $$=$2;new_variable($2,$3,1,0);
2057 FOR_IN_INIT : T_IDENTIFIER {
2062 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2063 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2065 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2066 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2068 $$ = code_append($$, $2);
2069 code_t*loopstart = $$ = abc_label($$);
2070 $$ = code_append($$, $4.c);
2071 code_t*myif = $$ = abc_iffalse($$, 0);
2072 $$ = code_append($$, $8);
2073 code_t*cont = $$ = abc_nop($$);
2074 $$ = code_append($$, $6);
2075 $$ = abc_jump($$, loopstart);
2076 code_t*out = $$ = abc_nop($$);
2077 breakjumpsto($$, $1.name, out);
2078 continuejumpsto($$, $1.name, cont);
2085 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2086 variable_t*var = find_variable(state, $2);
2087 char*tmp1name = concat2($2, "__tmp1__");
2088 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2089 char*tmp2name = concat2($2, "__array__");
2090 int array = new_variable(tmp1name, 0, 0, 0);
2093 $$ = code_append($$, $4.c);
2094 $$ = abc_coerce_a($$);
2095 $$ = abc_setlocal($$, array);
2096 $$ = abc_pushbyte($$, 0);
2097 $$ = abc_setlocal($$, it);
2099 code_t*loopstart = $$ = abc_label($$);
2101 $$ = abc_hasnext2($$, array, it);
2102 code_t*myif = $$ = abc_iffalse($$, 0);
2103 $$ = abc_getlocal($$, array);
2104 $$ = abc_getlocal($$, it);
2106 $$ = abc_nextname($$);
2108 $$ = abc_nextvalue($$);
2109 $$ = converttype($$, 0, var->type);
2110 $$ = abc_setlocal($$, var->index);
2112 $$ = code_append($$, $6);
2113 $$ = abc_jump($$, loopstart);
2115 code_t*out = $$ = abc_nop($$);
2116 breakjumpsto($$, $1.name, out);
2117 continuejumpsto($$, $1.name, loopstart);
2129 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2133 code_t*myjmp = $$ = abc_jump($$, 0);
2134 code_t*loopstart = $$ = abc_label($$);
2135 $$ = code_append($$, $6);
2136 code_t*cont = $$ = abc_nop($$);
2137 myjmp->branch = cont;
2138 $$ = code_append($$, $4.c);
2139 $$ = abc_iftrue($$, loopstart);
2140 code_t*out = $$ = abc_nop($$);
2141 breakjumpsto($$, $1, out);
2142 continuejumpsto($$, $1, cont);
2148 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2150 code_t*loopstart = $$ = abc_label($$);
2151 $$ = code_append($$, $3);
2152 code_t*cont = $$ = abc_nop($$);
2153 $$ = code_append($$, $6.c);
2154 $$ = abc_iftrue($$, loopstart);
2155 code_t*out = $$ = abc_nop($$);
2156 breakjumpsto($$, $1, out);
2157 continuejumpsto($$, $1, cont);
2163 BREAK : "break" %prec prec_none {
2164 $$ = abc___break__(0, "");
2166 BREAK : "break" T_IDENTIFIER {
2167 $$ = abc___break__(0, $2);
2169 CONTINUE : "continue" %prec prec_none {
2170 $$ = abc___continue__(0, "");
2172 CONTINUE : "continue" T_IDENTIFIER {
2173 $$ = abc___continue__(0, $2);
2176 MAYBE_CASE_LIST : {$$=0;}
2177 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2178 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2179 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2180 CASE_LIST: CASE {$$=$1;}
2181 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2183 CASE: "case" E ':' MAYBECODE {
2185 $$ = code_append($$, $2.c);
2186 code_t*j = $$ = abc_ifne($$, 0);
2187 $$ = code_append($$, $4);
2188 if($$->opcode != OPCODE___BREAK__) {
2189 $$ = abc___fallthrough__($$, "");
2191 code_t*e = $$ = abc_nop($$);
2194 DEFAULT: "default" ':' MAYBECODE {
2197 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2199 $$ = code_append($$, $7);
2200 code_t*out = $$ = abc_pop($$);
2201 breakjumpsto($$, $1, out);
2203 code_t*c = $$,*lastblock=0;
2205 if(c->opcode == OPCODE_IFNE) {
2206 if(!c->next) syntaxerror("internal error in fallthrough handling");
2208 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2210 c->opcode = OPCODE_JUMP;
2211 c->branch = lastblock;
2213 /* fall through end of switch */
2214 c->opcode = OPCODE_NOP;
2224 /* ------------ try / catch /finally ---------------- */
2226 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2227 state->exception_name=$3;
2228 PASS1 new_variable($3, 0, 0, 0);
2229 PASS2 new_variable($3, $4, 0, 0);
2232 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2233 multiname_t name = {QNAME, &name_ns, 0, $3};
2235 NEW(abc_exception_t, e)
2236 e->exc_type = sig2mname($4);
2237 e->var_name = multiname_clone(&name);
2241 int i = find_variable_safe(state, $3)->index;
2242 e->target = c = abc_nop(0);
2243 c = abc_setlocal(c, i);
2244 c = code_append(c, $8);
2250 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2255 NEW(abc_exception_t, e)
2256 e->exc_type = 0; //all exceptions
2257 e->var_name = 0; //no name
2260 e->to = code_append(e->to, $4);
2266 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2267 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2268 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2269 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2273 list_append($$.l,$2);
2274 $$.finally = $2->to;$2->to=0;
2277 CATCH_FINALLY_LIST: FINALLY {
2281 list_append($$.l,$1);
2282 $$.finally = $1->to;$1->to=0;
2286 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2287 code_t*out = abc_nop(0);
2289 code_t*start = abc_nop(0);
2290 $$ = code_append(start, $4);
2291 if(!is_break_or_jump($4)) {
2292 $$ = abc_jump($$, out);
2294 code_t*end = $$ = abc_nop($$);
2298 tmp = new_variable("__finally__", 0, 0, 0);
2300 abc_exception_list_t*l = $6.l;
2303 abc_exception_t*e = l->abc_exception;
2305 $$ = code_append($$, e->target);
2306 $$ = abc_jump($$, out);
2308 parserassert((ptroff_t)$6.finally);
2310 e->target = $$ = abc_nop($$);
2311 $$ = abc___rethrow__($$);
2319 $$ = code_append($$, out);
2321 $$ = insert_finally($$, $6.finally, tmp);
2323 list_concat(state->method->exceptions, $6.l);
2329 /* ------------ throw ------------------------------- */
2331 THROW : "throw" EXPRESSION {
2335 THROW : "throw" %prec prec_none {
2336 if(!state->exception_name)
2337 syntaxerror("re-throw only possible within a catch block");
2338 variable_t*v = find_variable(state, state->exception_name);
2340 $$=abc_getlocal($$, v->index);
2344 /* ------------ with -------------------------------- */
2346 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2348 $$ = abc_pushscope($$);
2349 $$ = code_append($$, $5);
2350 $$ = abc_popscope($$);
2353 /* ------------ packages and imports ---------------- */
2355 X_IDENTIFIER: T_IDENTIFIER
2356 | "package" {PASS12 $$="package";}
2358 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2359 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2361 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2362 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2363 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2364 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2366 IMPORT : "import" PACKAGEANDCLASS {
2368 slotinfo_t*s = registry_find($2->package, $2->name);
2369 if(!s) {// || !(s->flags&FLAG_BUILTIN)) {
2370 as3_schedule_class($2->package, $2->name);
2376 syntaxerror("Couldn't import class\n");
2377 state_has_imports();
2378 dict_put(state->imports, c->name, c);
2381 IMPORT : "import" PACKAGE '.' '*' {
2383 if(strncmp("flash.", $2, 6)) {
2384 as3_schedule_package($2);
2390 state_has_imports();
2391 list_append(state->wildcard_imports, i);
2395 /* ------------ classes and interfaces (header) -------------- */
2397 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2398 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2399 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2400 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2402 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2403 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2404 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2405 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2406 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2407 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2408 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2409 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2410 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2411 | T_NAMESPACE {PASS12 $$=FLAG_NAMESPACE;}
2413 EXTENDS : {$$=registry_getobjectclass();}
2414 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2416 EXTENDS_LIST : {PASS12 $$=list_new();}
2417 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2419 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2420 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2422 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2423 EXTENDS IMPLEMENTS_LIST
2424 '{' {PASS12 startclass($1,$3,$4,$5);}
2426 '}' {PASS12 endclass();$$=0;}
2428 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2430 '{' {PASS12 startclass($1|FLAG_INTERFACE,$3,0,$4);}
2431 MAYBE_INTERFACE_BODY
2432 '}' {PASS12 endclass();$$=0;}
2434 /* ------------ classes and interfaces (body) -------------- */
2437 MAYBE_CLASS_BODY : CLASS_BODY
2438 CLASS_BODY : CLASS_BODY_ITEM
2439 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2440 CLASS_BODY_ITEM : ';'
2441 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2442 CLASS_BODY_ITEM : SLOT_DECLARATION
2443 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2445 CLASS_BODY_ITEM : CODE_STATEMENT {
2446 code_t*c = state->cls->static_init->header;
2447 c = code_append(c, $1);
2448 state->cls->static_init->header = c;
2451 MAYBE_INTERFACE_BODY :
2452 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2453 INTERFACE_BODY : IDECLARATION
2454 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2456 IDECLARATION : "var" T_IDENTIFIER {
2457 syntaxerror("variable declarations not allowed in interfaces");
2459 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2462 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2463 syntaxerror("invalid method modifiers: interface methods always need to be public");
2465 startfunction(0,$1,$3,$4,&$6,$8);
2466 endfunction(0,$1,$3,$4,&$6,$8, 0);
2467 list_deep_free($6.list);
2470 /* ------------ classes and interfaces (body, slots ) ------- */
2472 VARCONST: "var" | "const"
2474 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} MAYBETYPE MAYBEEXPRESSION {
2476 U8 access = flags2access($1);
2478 varinfo_t* info = 0;
2480 memberinfo_t*i = registry_findmember(state->cls->info, $3, 1);
2482 check_override(i, flags);
2484 info = varinfo_register_onclass(state->cls->info, access, $3);
2486 slotinfo_t*i = registry_find(state->package, $3);
2488 syntaxerror("package %s already contains '%s'", state->package, $3);
2490 info = varinfo_register_global(access, state->package, $3);
2494 info->flags = flags;
2497 namespace_t mname_ns = {access, ""};
2498 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2500 trait_list_t**traits;
2504 mname_ns.name = state->package;
2505 traits = &global->init->traits;
2506 code = &global->init->method->body->code;
2507 } else if(flags&FLAG_STATIC) {
2509 traits = &state->cls->abc->static_traits;
2510 code = &state->cls->static_init->header;
2512 // instance variable
2513 traits = &state->cls->abc->traits;
2514 code = &state->cls->init->header;
2520 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2522 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2524 info->slot = t->slot_id;
2526 /* initalization code (if needed) */
2528 if($6.c && !is_pushundefined($6.c)) {
2529 c = abc_getlocal_0(c);
2530 c = code_append(c, $6.c);
2531 c = converttype(c, $6.t, $5);
2532 c = abc_setslot(c, t->slot_id);
2535 *code = code_append(*code, c);
2538 t->kind= TRAIT_CONST;
2542 setstaticfunction(0);
2545 /* ------------ constants -------------------------------------- */
2547 MAYBESTATICCONSTANT: {$$=0;}
2548 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2550 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2551 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2552 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2553 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2554 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2555 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2556 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2557 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2558 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2559 STATICCONSTANT : T_IDENTIFIER {
2561 as3_warning("Couldn't resolve %s", $1);
2562 $$ = constant_new_null($1);
2565 /* ------------ classes and interfaces (body, functions) ------- */
2567 // non-vararg version
2570 memset(&$$,0,sizeof($$));
2572 MAYBE_PARAM_LIST: PARAM_LIST {
2578 MAYBE_PARAM_LIST: "..." PARAM {
2580 memset(&$$,0,sizeof($$));
2582 list_append($$.list, $2);
2584 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2588 list_append($$.list, $4);
2592 PARAM_LIST: PARAM_LIST ',' PARAM {
2595 list_append($$.list, $3);
2599 memset(&$$,0,sizeof($$));
2600 list_append($$.list, $1);
2603 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2605 $$ = rfx_calloc(sizeof(param_t));
2611 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2613 $$ = rfx_calloc(sizeof(param_t));
2615 $$->type = TYPE_ANY;
2623 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2624 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2627 endfunction(0,$1,$3,$4,&$6,0,0);
2629 if(!state->method->info) syntaxerror("internal error");
2631 code_t*c = method_header(state->method);
2632 c = wrap_function(c, 0, $11);
2634 endfunction(0,$1,$3,$4,&$6,$8,c);
2636 list_deep_free($6.list);
2640 MAYBE_IDENTIFIER: T_IDENTIFIER
2641 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2642 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2643 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2646 endfunction(0,0,0,$2,&$4,0,0);
2648 methodinfo_t*f = state->method->info;
2649 if(!f || !f->kind) syntaxerror("internal error");
2651 code_t*c = method_header(state->method);
2652 c = wrap_function(c, 0, $9);
2654 int index = state->method->var_index;
2655 endfunction(0,0,0,$2,&$4,$6,c);
2657 $$.c = abc_getlocal(0, index);
2658 $$.t = TYPE_FUNCTION(f);
2660 PASS12 list_deep_free($4.list);
2664 /* ------------- package + class ids --------------- */
2666 CLASS: T_IDENTIFIER {
2669 slotinfo_t*s = find_class($1);
2670 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2671 $$ = (classinfo_t*)s;
2674 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2675 PASS1 static classinfo_t c;
2676 memset(&c, 0, sizeof(c));
2681 slotinfo_t*s = registry_find($1, $3);
2682 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2684 $$ = (classinfo_t*)s;
2687 CLASS_SPEC: PACKAGEANDCLASS
2690 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2691 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2693 TYPE : CLASS_SPEC {$$=$1;}
2694 | '*' {$$=registry_getanytype();}
2695 | "void" {$$=registry_getanytype();}
2697 | "String" {$$=registry_getstringclass();}
2698 | "int" {$$=registry_getintclass();}
2699 | "uint" {$$=registry_getuintclass();}
2700 | "Boolean" {$$=registry_getbooleanclass();}
2701 | "Number" {$$=registry_getnumberclass();}
2704 MAYBETYPE: ':' TYPE {$$=$2;}
2707 /* ----------function calls, delete, constructor calls ------ */
2709 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2710 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2712 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2713 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2714 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2716 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2720 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2721 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2723 $$.cc = code_append($1.cc, $2.c);
2727 NEW : "new" E XX MAYBE_PARAM_VALUES {
2729 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2731 code_t*paramcode = $4.cc;
2732 if($$.c->opcode == OPCODE_GETPROPERTY) {
2733 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2734 $$.c = code_cutlast($$.c);
2735 $$.c = code_append($$.c, paramcode);
2736 $$.c = abc_constructprop2($$.c, name, $4.len);
2737 multiname_destroy(name);
2738 } else if($$.c->opcode == OPCODE_GETSLOT) {
2739 int slot = (int)(ptroff_t)$$.c->data[0];
2740 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2741 multiname_t*name = t->name;
2742 $$.c = code_cutlast($$.c);
2743 $$.c = code_append($$.c, paramcode);
2744 $$.c = abc_constructprop2($$.c, name, $4.len);
2746 $$.c = code_append($$.c, paramcode);
2747 $$.c = abc_construct($$.c, $4.len);
2751 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2754 $$.c = abc_coerce_a($$.c);
2759 /* TODO: use abc_call (for calling local variables),
2760 abc_callstatic (for calling own methods)
2763 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2766 if($$.c->opcode == OPCODE_COERCE_A) {
2767 $$.c = code_cutlast($$.c);
2769 code_t*paramcode = $3.cc;
2772 if($$.c->opcode == OPCODE_GETPROPERTY) {
2773 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2774 $$.c = code_cutlast($$.c);
2775 $$.c = code_append($$.c, paramcode);
2776 $$.c = abc_callproperty2($$.c, name, $3.len);
2777 multiname_destroy(name);
2778 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
2779 int slot = (int)(ptroff_t)$$.c->data[0];
2780 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
2781 if(t->kind!=TRAIT_METHOD) {
2782 //ok: flash allows to assign closures to members.
2784 multiname_t*name = t->name;
2785 $$.c = code_cutlast($$.c);
2786 $$.c = code_append($$.c, paramcode);
2787 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2788 $$.c = abc_callproperty2($$.c, name, $3.len);
2789 } else if($$.c->opcode == OPCODE_GETSUPER) {
2790 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2791 $$.c = code_cutlast($$.c);
2792 $$.c = code_append($$.c, paramcode);
2793 $$.c = abc_callsuper2($$.c, name, $3.len);
2794 multiname_destroy(name);
2796 $$.c = abc_getglobalscope($$.c);
2797 $$.c = code_append($$.c, paramcode);
2798 $$.c = abc_call($$.c, $3.len);
2801 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2802 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2804 $$.c = abc_coerce_a($$.c);
2809 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2810 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2811 if(!state->method) syntaxerror("super() not allowed outside of a function");
2812 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2815 $$.c = abc_getlocal_0($$.c);
2817 $$.c = code_append($$.c, $3.cc);
2819 this is dependent on the control path, check this somewhere else
2820 if(state->method->has_super)
2821 syntaxerror("constructor may call super() only once");
2823 state->method->has_super = 1;
2825 $$.c = abc_constructsuper($$.c, $3.len);
2826 $$.c = abc_pushundefined($$.c);
2830 DELETE: "delete" E {
2832 if($$.c->opcode == OPCODE_COERCE_A) {
2833 $$.c = code_cutlast($$.c);
2835 multiname_t*name = 0;
2836 if($$.c->opcode == OPCODE_GETPROPERTY) {
2837 $$.c->opcode = OPCODE_DELETEPROPERTY;
2838 } else if($$.c->opcode == OPCODE_GETSLOT) {
2839 int slot = (int)(ptroff_t)$$.c->data[0];
2840 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
2841 $$.c = code_cutlast($$.c);
2842 $$.c = abc_deleteproperty2($$.c, name);
2844 $$.c = abc_getlocal_0($$.c);
2845 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2846 $$.c = abc_deleteproperty2($$.c, &m);
2848 $$.t = TYPE_BOOLEAN;
2851 RETURN: "return" %prec prec_none {
2852 $$ = abc_returnvoid(0);
2854 RETURN: "return" EXPRESSION {
2856 $$ = abc_returnvalue($$);
2859 // ----------------------- expression types -------------------------------------
2861 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2862 EXPRESSION : E %prec below_minus {$$ = $1;}
2863 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2865 $$.c = cut_last_push($$.c);
2866 $$.c = code_append($$.c,$3.c);
2869 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2870 $$=cut_last_push($1.c);
2873 // ----------------------- expression evaluation -------------------------------------
2875 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2876 //V : CONSTANT {$$ = 0;}
2878 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2879 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2880 //V : NEW {$$ = $1.c;}
2882 //V : DELETE {$$ = $1.c;}
2883 E : DELETE {$$ = $1;}
2889 namespace_t ns = {ACCESS_PACKAGE, ""};
2890 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2892 $$.c = abc_getlex2($$.c, &m);
2893 $$.c = abc_pushstring($$.c, $1.pattern);
2894 $$.c = abc_construct($$.c, 1);
2896 $$.c = abc_getlex2($$.c, &m);
2897 $$.c = abc_pushstring($$.c, $1.pattern);
2898 $$.c = abc_pushstring($$.c, $1.options);
2899 $$.c = abc_construct($$.c, 2);
2904 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2905 //MULTINAME(m, registry_getintclass());
2906 //$$.c = abc_coerce2($$.c, &m); // FIXME
2909 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2912 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2915 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2918 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2921 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
2924 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2927 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2928 $$.t = TYPE_BOOLEAN;
2930 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2931 $$.t = TYPE_BOOLEAN;
2933 CONSTANT : "null" {$$.c = abc_pushnull(0);
2937 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2938 $$.t = TYPE_BOOLEAN;
2940 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2941 $$.t = TYPE_BOOLEAN;
2943 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2944 $$.t = TYPE_BOOLEAN;
2946 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2947 $$.t = TYPE_BOOLEAN;
2949 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2950 $$.t = TYPE_BOOLEAN;
2952 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2953 $$.t = TYPE_BOOLEAN;
2955 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2956 $$.t = TYPE_BOOLEAN;
2958 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2959 $$.t = TYPE_BOOLEAN;
2962 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2964 $$.c = converttype($$.c, $1.t, $$.t);
2965 $$.c = abc_dup($$.c);
2966 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2967 $$.c = cut_last_push($$.c);
2968 $$.c = code_append($$.c,$3.c);
2969 $$.c = converttype($$.c, $3.t, $$.t);
2970 code_t*label = $$.c = abc_label($$.c);
2971 jmp->branch = label;
2974 $$.t = join_types($1.t, $3.t, 'A');
2975 /*printf("%08x:\n",$1.t);
2976 code_dump($1.c, 0, 0, "", stdout);
2977 printf("%08x:\n",$3.t);
2978 code_dump($3.c, 0, 0, "", stdout);
2979 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2981 $$.c = converttype($$.c, $1.t, $$.t);
2982 $$.c = abc_dup($$.c);
2983 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2984 $$.c = cut_last_push($$.c);
2985 $$.c = code_append($$.c,$3.c);
2986 $$.c = converttype($$.c, $3.t, $$.t);
2987 code_t*label = $$.c = abc_label($$.c);
2988 jmp->branch = label;
2991 E : '!' E {$$.c=$2.c;
2992 $$.c = abc_not($$.c);
2993 $$.t = TYPE_BOOLEAN;
2996 E : '~' E {$$.c=$2.c;
2997 $$.c = abc_bitnot($$.c);
3001 E : E '&' E {$$.c = code_append($1.c,$3.c);
3002 $$.c = abc_bitand($$.c);
3006 E : E '^' E {$$.c = code_append($1.c,$3.c);
3007 $$.c = abc_bitxor($$.c);
3011 E : E '|' E {$$.c = code_append($1.c,$3.c);
3012 $$.c = abc_bitor($$.c);
3016 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3017 $$.c = abc_rshift($$.c);
3020 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3021 $$.c = abc_urshift($$.c);
3024 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3025 $$.c = abc_lshift($$.c);
3029 E : E '/' E {$$.c = code_append($1.c,$3.c);
3030 $$.c = abc_divide($$.c);
3033 E : E '%' E {$$.c = code_append($1.c,$3.c);
3034 $$.c = abc_modulo($$.c);
3037 E : E '+' E {$$.c = code_append($1.c,$3.c);
3038 if(BOTH_INT($1.t, $3.t)) {
3039 $$.c = abc_add_i($$.c);
3042 $$.c = abc_add($$.c);
3043 $$.t = join_types($1.t,$3.t,'+');
3046 E : E '-' E {$$.c = code_append($1.c,$3.c);
3047 if(BOTH_INT($1.t,$3.t)) {
3048 $$.c = abc_subtract_i($$.c);
3051 $$.c = abc_subtract($$.c);
3055 E : E '*' E {$$.c = code_append($1.c,$3.c);
3056 if(BOTH_INT($1.t,$3.t)) {
3057 $$.c = abc_multiply_i($$.c);
3060 $$.c = abc_multiply($$.c);
3065 E : E "in" E {$$.c = code_append($1.c,$3.c);
3066 $$.c = abc_in($$.c);
3067 $$.t = TYPE_BOOLEAN;
3070 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3071 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3072 MULTINAME(m, (classinfo_t*)($3.t->data));
3073 $$.c = abc_astype2($1.c, &m);
3076 $$.c = code_append($1.c, $3.c);
3077 $$.c = abc_astypelate($$.c);
3082 E : E "instanceof" E
3083 {$$.c = code_append($1.c, $3.c);
3084 $$.c = abc_instanceof($$.c);
3085 $$.t = TYPE_BOOLEAN;
3088 E : E "is" E {$$.c = code_append($1.c, $3.c);
3089 $$.c = abc_istypelate($$.c);
3090 $$.t = TYPE_BOOLEAN;
3093 E : "typeof" '(' E ')' {
3095 $$.c = abc_typeof($$.c);
3100 $$.c = cut_last_push($2.c);
3101 $$.c = abc_pushundefined($$.c);
3105 E : "void" { $$.c = abc_pushundefined(0);
3109 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3114 $$.c=abc_negate_i($$.c);
3117 $$.c=abc_negate($$.c);
3124 $$.c = code_append($$.c, $3.c);
3126 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3127 $$.c = abc_getproperty2($$.c, &m);
3128 $$.t = 0; // array elements have unknown type
3131 E : '[' MAYBE_EXPRESSION_LIST ']' {
3133 $$.c = code_append($$.c, $2.cc);
3134 $$.c = abc_newarray($$.c, $2.len);
3135 $$.t = registry_getarrayclass();
3138 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
3139 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3141 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3143 $$.cc = code_append($$.cc, $1.c);
3144 $$.cc = code_append($$.cc, $3.c);
3147 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3150 $$.cc = code_append($$.cc, $3.c);
3151 $$.cc = code_append($$.cc, $5.c);
3156 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3158 $$.c = code_append($$.c, $2.cc);
3159 $$.c = abc_newobject($$.c, $2.len/2);
3160 $$.t = registry_getobjectclass();
3165 if(BOTH_INT($1.t,$3.t)) {
3166 c=abc_multiply_i(c);
3170 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3171 $$.c = toreadwrite($1.c, c, 0, 0);
3176 code_t*c = abc_modulo($3.c);
3177 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3178 $$.c = toreadwrite($1.c, c, 0, 0);
3182 code_t*c = abc_lshift($3.c);
3183 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3184 $$.c = toreadwrite($1.c, c, 0, 0);
3188 code_t*c = abc_rshift($3.c);
3189 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3190 $$.c = toreadwrite($1.c, c, 0, 0);
3194 code_t*c = abc_urshift($3.c);
3195 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3196 $$.c = toreadwrite($1.c, c, 0, 0);
3200 code_t*c = abc_divide($3.c);
3201 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3202 $$.c = toreadwrite($1.c, c, 0, 0);
3206 code_t*c = abc_bitor($3.c);
3207 c=converttype(c, TYPE_INT, $1.t);
3208 $$.c = toreadwrite($1.c, c, 0, 0);
3212 code_t*c = abc_bitxor($3.c);
3213 c=converttype(c, TYPE_INT, $1.t);
3214 $$.c = toreadwrite($1.c, c, 0, 0);
3220 if(TYPE_IS_INT($1.t)) {
3224 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3227 $$.c = toreadwrite($1.c, c, 0, 0);
3230 E : E "-=" E { code_t*c = $3.c;
3231 if(TYPE_IS_INT($1.t)) {
3232 c=abc_subtract_i(c);
3235 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3238 $$.c = toreadwrite($1.c, c, 0, 0);
3241 E : E '=' E { code_t*c = 0;
3242 c = code_append(c, $3.c);
3243 c = converttype(c, $3.t, $1.t);
3244 $$.c = toreadwrite($1.c, c, 1, 0);
3248 E : E '?' E ':' E %prec below_assignment {
3249 $$.t = join_types($3.t,$5.t,'?');
3251 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3252 $$.c = code_append($$.c, $3.c);
3253 $$.c = converttype($$.c, $3.t, $$.t);
3254 code_t*j2 = $$.c = abc_jump($$.c, 0);
3255 $$.c = j1->branch = abc_label($$.c);
3256 $$.c = code_append($$.c, $5.c);
3257 $$.c = converttype($$.c, $5.t, $$.t);
3258 $$.c = j2->branch = abc_label($$.c);
3261 E : E "++" { code_t*c = 0;
3262 classinfo_t*type = $1.t;
3263 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
3264 int nr = getlocalnr($1.c);
3265 code_free($1.c);$1.c=0;
3266 if(TYPE_IS_INT($1.t)) {
3267 $$.c = abc_getlocal(0, nr);
3268 $$.c = abc_inclocal_i($$.c, nr);
3269 } else if(TYPE_IS_NUMBER($1.t)) {
3270 $$.c = abc_getlocal(0, nr);
3271 $$.c = abc_inclocal($$.c, nr);
3272 } else syntaxerror("internal error");
3274 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3275 c=abc_increment_i(c);
3281 c=converttype(c, type, $1.t);
3282 $$.c = toreadwrite($1.c, c, 0, 1);
3287 // TODO: use inclocal, like with ++
3288 E : E "--" { code_t*c = 0;
3289 classinfo_t*type = $1.t;
3290 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3291 c=abc_decrement_i(c);
3297 c=converttype(c, type, $1.t);
3298 $$.c = toreadwrite($1.c, c, 0, 1);
3302 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3303 classinfo_t*type = $2.t;
3304 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3305 c=abc_increment_i(c);
3311 c=converttype(c, type, $2.t);
3312 $$.c = toreadwrite($2.c, c, 0, 0);
3316 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3317 classinfo_t*type = $2.t;
3318 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3319 c=abc_decrement_i(c);
3325 c=converttype(c, type, $2.t);
3326 $$.c = toreadwrite($2.c, c, 0, 0);
3330 E : "super" '.' T_IDENTIFIER
3331 { if(!state->cls->info)
3332 syntaxerror("super keyword not allowed outside a class");
3333 classinfo_t*t = state->cls->info->superclass;
3334 if(!t) t = TYPE_OBJECT;
3336 memberinfo_t*f = registry_findmember(t, $3, 1);
3337 namespace_t ns = {f->access, ""};
3338 MEMBER_MULTINAME(m, f, $3);
3340 $$.c = abc_getlocal_0($$.c);
3341 $$.c = abc_getsuper2($$.c, &m);
3342 $$.t = slotinfo_gettype((slotinfo_t*)f);
3345 E : '@' T_IDENTIFIER {
3347 $$.c = abc_pushundefined(0);
3349 as3_warning("ignored @ operator");
3352 E : E '.' '@' T_IDENTIFIER {
3353 // child attribute TODO
3354 $$.c = abc_pushundefined(0);
3356 as3_warning("ignored .@ operator");
3359 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3360 // namespace declaration TODO
3361 $$.c = abc_pushundefined(0);
3363 as3_warning("ignored :: operator");
3366 E : E ".." T_IDENTIFIER {
3368 $$.c = abc_pushundefined(0);
3370 as3_warning("ignored .. operator");
3373 E : E '.' '(' E ')' {
3375 $$.c = abc_pushundefined(0);
3377 as3_warning("ignored .() operator");
3380 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3384 E : E '.' T_IDENTIFIER
3386 classinfo_t*t = $1.t;
3388 if(TYPE_IS_CLASS(t) && t->data) {
3393 memberinfo_t*f = registry_findmember(t, $3, 1);
3395 if(f && !is_static != !(f->flags&FLAG_STATIC))
3397 if(f && f->slot && !noslot) {
3398 $$.c = abc_getslot($$.c, f->slot);
3400 MEMBER_MULTINAME(m, f, $3);
3401 $$.c = abc_getproperty2($$.c, &m);
3403 /* determine type */
3404 $$.t = slotinfo_gettype((slotinfo_t*)f);
3406 $$.c = abc_coerce_a($$.c);
3408 /* when resolving a property on an unknown type, we do know the
3409 name of the property (and don't seem to need the package), but
3410 we need to make avm2 try out all access modes */
3411 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3412 $$.c = abc_getproperty2($$.c, &m);
3413 $$.c = abc_coerce_a($$.c);
3414 $$.t = registry_getanytype();
3418 VAR_READ : T_IDENTIFIER {
3420 /* Queue unresolved identifiers for checking against the parent
3421 function's variables.
3422 We consider everything which is not a local variable "unresolved".
3423 This encompasses class names, members of the surrounding class
3424 etc. which *correct* because local variables of the parent function
3427 if(state->method->inner && !find_variable(state, $1)) {
3428 unknown_variable($1);
3438 /* look at variables */
3439 if((v = find_variable(state, $1))) {
3440 // $1 is a local variable
3441 $$.c = abc_getlocal($$.c, v->index);
3445 if((v = find_slot(state, $1))) {
3446 $$.c = abc_getscopeobject($$.c, 1);
3447 $$.c = abc_getslot($$.c, v->index);
3452 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3454 /* look at current class' members */
3455 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
3456 (f->flags&FLAG_STATIC) >= i_am_static) {
3457 // $1 is a function in this class
3458 int var_is_static = (f->flags&FLAG_STATIC);
3460 if(f->kind == INFOTYPE_METHOD) {
3461 $$.t = TYPE_FUNCTION(f);
3465 if(var_is_static && !i_am_static) {
3466 /* access to a static member from a non-static location.
3467 do this via findpropstrict:
3468 there doesn't seem to be any non-lookup way to access
3469 static properties of a class */
3470 state->method->late_binding = 1;
3472 namespace_t ns = {f->access, ""};
3473 multiname_t m = {QNAME, &ns, 0, $1};
3474 $$.c = abc_findpropstrict2($$.c, &m);
3475 $$.c = abc_getproperty2($$.c, &m);
3477 } else if(f->slot>0) {
3478 $$.c = abc_getlocal_0($$.c);
3479 $$.c = abc_getslot($$.c, f->slot);
3482 namespace_t ns = {f->access, ""};
3483 multiname_t m = {QNAME, &ns, 0, $1};
3484 $$.c = abc_getlocal_0($$.c);
3485 $$.c = abc_getproperty2($$.c, &m);
3490 /* look at actual classes, in the current package and imported */
3491 if((a = find_class($1))) {
3492 if(a->access == ACCESS_PACKAGEINTERNAL &&
3493 strcmp(a->package, state->package) &&
3494 strcmp(a->package, internal_filename_package)
3496 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
3497 infotypename(a),$1, a->package, state->package);
3499 if(a->kind != INFOTYPE_CLASS) {
3501 $$.c = abc_findpropstrict2($$.c, &m);
3502 $$.c = abc_getproperty2($$.c, &m);
3503 if(a->kind == INFOTYPE_METHOD) {
3504 methodinfo_t*f = (methodinfo_t*)a;
3505 $$.t = TYPE_FUNCTION(f);
3507 varinfo_t*v = (varinfo_t*)a;
3511 classinfo_t*c = (classinfo_t*)a;
3513 $$.c = abc_getglobalscope($$.c);
3514 $$.c = abc_getslot($$.c, c->slot);
3517 $$.c = abc_getlex2($$.c, &m);
3519 $$.t = TYPE_CLASS(c);
3524 /* unknown object, let the avm2 resolve it */
3526 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3527 state->method->late_binding = 1;
3529 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3532 $$.c = abc_findpropstrict2($$.c, &m);
3533 $$.c = abc_getproperty2($$.c, &m);
3537 // ----------------- namespaces -------------------------------------------------
3539 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3540 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3541 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3543 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {
3545 tokenizer_register_namespace($3);