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;
47 classinfo_t*classinfo;
48 classinfo_list_t*classinfo_list;
50 slotinfo_list_t*slotinfo_list;
53 unsigned int number_uint;
57 //typedcode_list_t*value_list;
58 codeandnumber_t value_list;
64 for_start_t for_start;
65 abc_exception_t *exception;
69 abc_exception_list_t *l;
75 %token<id> T_IDENTIFIER T_NAMESPACE
77 %token<regexp> T_REGEXP
79 %token<number_int> T_INT
80 %token<number_uint> T_UINT
81 %token<number_uint> T_BYTE
82 %token<number_uint> T_SHORT
83 %token<number_float> T_FLOAT
85 %token<id> T_FOR "for"
86 %token<id> T_WHILE "while"
88 %token<id> T_SWITCH "switch"
90 %token<token> KW_IMPLEMENTS "implements"
91 %token<token> KW_NAMESPACE "namespace"
92 %token<token> KW_PACKAGE "package"
93 %token<token> KW_PROTECTED "protected"
94 %token<token> KW_PUBLIC "public"
95 %token<token> KW_PRIVATE "private"
96 %token<token> KW_USE "use"
97 %token<token> KW_INTERNAL "internal"
98 %token<token> KW_NEW "new"
99 %token<token> KW_NATIVE "native"
100 %token<token> KW_FUNCTION "function"
101 %token<token> KW_FINALLY "finally"
102 %token<token> KW_UNDEFINED "undefined"
103 %token<token> KW_CONTINUE "continue"
104 %token<token> KW_CLASS "class"
105 %token<token> KW_CONST "const"
106 %token<token> KW_CATCH "catch"
107 %token<token> KW_CASE "case"
108 %token<token> KW_SET "set"
109 %token<token> KW_VOID "void"
110 %token<token> KW_THROW "throw"
111 %token<token> KW_STATIC "static"
112 %token<token> KW_WITH "with"
113 %token<token> KW_INSTANCEOF "instanceof"
114 %token<token> KW_IMPORT "import"
115 %token<token> KW_RETURN "return"
116 %token<token> KW_TYPEOF "typeof"
117 %token<token> KW_INTERFACE "interface"
118 %token<token> KW_NULL "null"
119 %token<token> KW_VAR "var"
120 %token<token> KW_DYNAMIC "dynamic"
121 %token<token> KW_OVERRIDE "override"
122 %token<token> KW_FINAL "final"
123 %token<token> KW_EACH "each"
124 %token<token> KW_GET "get"
125 %token<token> KW_TRY "try"
126 %token<token> KW_SUPER "super"
127 %token<token> KW_EXTENDS "extends"
128 %token<token> KW_FALSE "false"
129 %token<token> KW_TRUE "true"
130 %token<token> KW_BOOLEAN "Boolean"
131 %token<token> KW_UINT "uint"
132 %token<token> KW_INT "int"
133 %token<token> KW_NUMBER "Number"
134 %token<token> KW_STRING "String"
135 %token<token> KW_DEFAULT "default"
136 %token<token> KW_DELETE "delete"
137 %token<token> KW_IF "if"
138 %token<token> KW_ELSE "else"
139 %token<token> KW_BREAK "break"
140 %token<token> KW_IS "is"
141 %token<token> KW_IN "in"
142 %token<token> KW_AS "as"
144 %token<token> T_DICTSTART "{ (dictionary)"
145 %token<token> T_EQEQ "=="
146 %token<token> T_EQEQEQ "==="
147 %token<token> T_NE "!="
148 %token<token> T_NEE "!=="
149 %token<token> T_LE "<="
150 %token<token> T_GE ">="
151 %token<token> T_ORBY "|="
152 %token<token> T_DIVBY "/="
153 %token<token> T_MODBY "%="
154 %token<token> T_MULBY "*="
155 %token<token> T_PLUSBY "+="
156 %token<token> T_MINUSBY "-="
157 %token<token> T_XORBY "^="
158 %token<token> T_SHRBY ">>="
159 %token<token> T_SHLBY "<<="
160 %token<token> T_USHRBY ">>>="
161 %token<token> T_OROR "||"
162 %token<token> T_ANDAND "&&"
163 %token<token> T_COLONCOLON "::"
164 %token<token> T_MINUSMINUS "--"
165 %token<token> T_PLUSPLUS "++"
166 %token<token> T_DOTDOT ".."
167 %token<token> T_DOTDOTDOT "..."
168 %token<token> T_SHL "<<"
169 %token<token> T_USHR ">>>"
170 %token<token> T_SHR ">>"
172 %type <for_start> FOR_START
173 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER NAMESPACE_ID
174 %type <token> VARCONST
176 %type <code> CODEPIECE CODE_STATEMENT
177 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
178 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
179 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
180 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
181 %type <exception> CATCH FINALLY
182 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
183 %type <code> CLASS_DECLARATION
184 %type <code> NAMESPACE_DECLARATION
185 %type <code> INTERFACE_DECLARATION
186 %type <code> VOIDEXPRESSION
187 %type <value> EXPRESSION NONCOMMAEXPRESSION
188 %type <value> MAYBEEXPRESSION
189 %type <value> E DELETE
190 %type <value> CONSTANT
191 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
192 %type <value> INNERFUNCTION
193 %type <code> USE_NAMESPACE
194 %type <code> FOR_INIT
196 %type <classinfo> MAYBETYPE
199 %type <params> PARAM_LIST
200 %type <params> MAYBE_PARAM_LIST
201 %type <flags> MAYBE_MODIFIERS
202 %type <flags> MODIFIER_LIST
203 %type <flags> MODIFIER
204 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
205 %type <classinfo_list> IMPLEMENTS_LIST
206 %type <classinfo> EXTENDS CLASS_SPEC
207 %type <classinfo_list> EXTENDS_LIST
209 %type <classinfo> CLASS PACKAGEANDCLASS
210 %type <classinfo_list> CLASS_SPEC_LIST
212 %type <classinfo> TYPE
213 //%type <token> VARIABLE
214 %type <value> VAR_READ
216 //%type <token> T_IDENTIFIER
217 %type <value> FUNCTIONCALL
218 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
220 // precedence: from low to high
224 %left below_semicolon
227 %nonassoc below_assignment // for ?:, contrary to spec
228 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
235 %nonassoc "==" "!=" "===" "!=="
236 %nonassoc "is" "as" "in"
237 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
238 %left "<<" ">>" ">>>"
242 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
244 %nonassoc below_curly
248 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
251 %left above_identifier
255 // needed for "return" precedence:
256 %nonassoc T_STRING T_REGEXP
257 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
258 %nonassoc "false" "true" "null" "undefined" "super" "function"
265 static int a3_error(char*s)
267 syntaxerror("%s", s);
268 return 0; //make gcc happy
272 static char* concat2(const char* t1, const char* t2)
276 char*text = malloc(l1+l2+1);
277 memcpy(text , t1, l1);
278 memcpy(text+l1, t2, l2);
282 static char* concat3(const char* t1, const char* t2, const char* t3)
287 char*text = malloc(l1+l2+l3+1);
288 memcpy(text , t1, l1);
289 memcpy(text+l1, t2, l2);
290 memcpy(text+l1+l2, t3, l3);
295 typedef struct _import {
299 DECLARE_LIST(import);
301 DECLARE(methodstate);
302 DECLARE_LIST(methodstate);
304 typedef struct _classstate {
310 methodstate_t*static_init;
312 //code_t*static_init;
314 char has_constructor;
317 struct _methodstate {
326 dict_t*unresolved_variables;
329 char uses_parent_function;
334 int var_index; // for inner methods
335 int slot_index; // for inner methods
336 char is_a_slot; // for inner methods
339 abc_exception_list_t*exceptions;
341 methodstate_list_t*innerfunctions;
344 typedef struct _state {
349 import_list_t*wildcard_imports;
350 dict_t*import_toplevel_packages;
352 namespace_list_t*active_namespaces;
353 char has_own_imports;
354 char new_vars; // e.g. transition between two functions
357 methodstate_t*method;
364 typedef struct _global {
368 dict_t*file2token2info;
371 static global_t*global = 0;
372 static state_t* state = 0;
376 #define MULTINAME(m,x) \
380 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
382 #define MEMBER_MULTINAME(m,f,n) \
386 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
387 m##_ns.name = ((slotinfo_t*)(f))->package; \
392 m.namespace_set = 0; \
393 m.name = ((slotinfo_t*)(f))->name; \
395 m.type = MULTINAME; \
397 m.namespace_set = &nopackage_namespace_set; \
401 /* warning: list length of namespace set is undefined */
402 #define MULTINAME_LATE(m, access, package) \
403 namespace_t m##_ns = {access, package}; \
404 namespace_set_t m##_nsset; \
405 namespace_list_t m##_l;m##_l.next = 0; \
406 m##_nsset.namespaces = &m##_l; \
407 m##_nsset = m##_nsset; \
408 m##_l.namespace = &m##_ns; \
409 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
411 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
412 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
413 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
414 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
415 static namespace_list_t nl4 = {&ns4,0};
416 static namespace_list_t nl3 = {&ns3,&nl4};
417 static namespace_list_t nl2 = {&ns2,&nl3};
418 static namespace_list_t nl1 = {&ns1,&nl2};
419 static namespace_set_t nopackage_namespace_set = {&nl1};
421 static void new_state()
424 state_t*oldstate = state;
426 memcpy(s, state, sizeof(state_t)); //shallow copy
428 s->imports = dict_new();
430 if(!s->import_toplevel_packages) {
431 s->import_toplevel_packages = dict_new();
435 state->has_own_imports = 0;
436 state->vars = dict_new();
437 state->old = oldstate;
440 static void state_has_imports()
442 state->wildcard_imports = list_clone(state->wildcard_imports);
443 state->imports = dict_clone(state->imports);
444 state->has_own_imports = 1;
446 static void import_toplevel(const char*package)
448 char* s = strdup(package);
450 dict_put(state->import_toplevel_packages, s, 0);
451 char*x = strrchr(s, '.');
459 static void state_destroy(state_t*state)
461 if(state->has_own_imports) {
462 list_free(state->wildcard_imports);
463 dict_destroy(state->imports);state->imports=0;
465 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
466 dict_destroy(state->imports);state->imports=0;
470 for(t=0;t<state->vars->hashsize;t++) {
471 dictentry_t*e =state->vars->slots[t];
473 free(e->data);e->data=0;
477 dict_destroy(state->vars);state->vars=0;
483 static void old_state()
485 if(!state || !state->old)
486 syntaxerror("invalid nesting");
487 state_t*leaving = state;
491 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
492 free(leaving->method);
495 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
500 state_destroy(leaving);
503 static code_t* method_header(methodstate_t*m);
504 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
505 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
508 static char* internal_filename_package = 0;
509 void initialize_file(char*filename)
512 syntaxerror("invalid call to initialize_file during parsing of another file");
515 state->package = internal_filename_package = strdup(filename);
517 global->token2info = dict_lookup(global->file2token2info,
518 current_filename // use long version
520 if(!global->token2info) {
521 global->token2info = dict_new2(&ptr_type);
522 dict_put(global->file2token2info, current_filename, global->token2info);
526 state->method = rfx_calloc(sizeof(methodstate_t));
527 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
529 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
530 function_initvars(state->method, 0, 0, 1);
531 global->init = abc_initscript(global->file);
532 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
538 if(!state || state->level!=1) {
539 syntaxerror("unexpected end of file in pass %d", as3_pass);
543 code_t*header = method_header(state->method);
544 code_t*c = wrap_function(header, 0, global->init->method->body->code);
545 global->init->method->body->code = c;
546 free(state->method);state->method=0;
549 //free(state->package);state->package=0; // used in registry
550 state_destroy(state);state=0;
553 void initialize_parser()
555 global = rfx_calloc(sizeof(global_t));
556 global->file = abc_file_new();
557 global->file->flags &= ~ABCFILE_LAZY;
558 global->file2token2info = dict_new();
559 global->token2info = 0;
562 void* finish_parser()
564 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
566 global->token2info=0;
572 static void xx_scopetest()
574 /* findpropstrict doesn't just return a scope object- it
575 also makes it "active" somehow. Push local_0 on the
576 scope stack and read it back with findpropstrict, it'll
577 contain properties like "trace". Trying to find the same
578 property on a "vanilla" local_0 yields only a "undefined" */
579 //c = abc_findpropstrict(c, "[package]::trace");
581 /*c = abc_getlocal_0(c);
582 c = abc_findpropstrict(c, "[package]::trace");
584 c = abc_setlocal_1(c);
586 c = abc_pushbyte(c, 0);
587 c = abc_setlocal_2(c);
589 code_t*xx = c = abc_label(c);
590 c = abc_findpropstrict(c, "[package]::trace");
591 c = abc_pushstring(c, "prop:");
592 c = abc_hasnext2(c, 1, 2);
594 c = abc_setlocal_3(c);
595 c = abc_callpropvoid(c, "[package]::trace", 2);
596 c = abc_getlocal_3(c);
598 c = abc_iftrue(c,xx);*/
601 typedef struct _variable {
605 methodstate_t*is_inner_method;
608 static variable_t* find_variable(state_t*s, char*name)
612 v = dict_lookup(s->vars, name);
614 if(s->new_vars) break;
619 static variable_t* find_slot(state_t*s, const char*name)
621 if(s->method && s->method->slots)
622 return dict_lookup(s->method->slots, name);
626 static variable_t* find_variable_safe(state_t*s, char*name)
628 variable_t* v = find_variable(s, name);
630 syntaxerror("undefined variable: %s", name);
633 static char variable_exists(char*name)
635 return dict_contains(state->vars, name);
637 code_t*defaultvalue(code_t*c, classinfo_t*type);
639 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
642 variable_t*v = find_slot(state, name);
648 v->index = state->method->variable_count++;
652 dict_put(state->vars, name, v);
656 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
658 return new_variable2(name, type, init, maybeslot)->index;
661 #define TEMPVARNAME "__as3_temp__"
662 static int gettempvar()
664 variable_t*v = find_variable(state, TEMPVARNAME);
667 return new_variable(TEMPVARNAME, 0, 0, 0);
670 code_t* var_block(code_t*body)
676 for(t=0;t<state->vars->hashsize;t++) {
677 dictentry_t*e = state->vars->slots[t];
679 variable_t*v = (variable_t*)e->data;
680 if(v->type && v->init) {
681 c = defaultvalue(c, v->type);
682 c = abc_setlocal(c, v->index);
683 k = abc_kill(k, v->index);
693 if(x->opcode== OPCODE___BREAK__ ||
694 x->opcode== OPCODE___CONTINUE__) {
695 /* link kill code before break/continue */
696 code_t*e = code_dup(k);
697 code_t*s = code_start(e);
709 c = code_append(c, body);
710 c = code_append(c, k);
714 void unknown_variable(char*name)
716 if(!state->method->unresolved_variables)
717 state->method->unresolved_variables = dict_new();
718 if(!dict_contains(state->method->unresolved_variables, name))
719 dict_put(state->method->unresolved_variables, name, 0);
722 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
724 static void parsererror(const char*file, int line, const char*f)
726 syntaxerror("internal error in %s, %s:%d", f, file, line);
730 static code_t* method_header(methodstate_t*m)
733 if(m->uses_slots || (m->late_binding && !m->inner)) {
734 c = abc_getlocal_0(c);
735 c = abc_pushscope(c);
738 c = abc_newactivation(c);
739 c = abc_pushscope(c);
741 methodstate_list_t*l = m->innerfunctions;
743 parserassert(l->methodstate->abc);
744 if(m->uses_slots && l->methodstate->is_a_slot) {
745 c = abc_getscopeobject(c, 1);
746 c = abc_newfunction(c, l->methodstate->abc);
748 c = abc_setlocal(c, l->methodstate->var_index);
749 c = abc_setslot(c, l->methodstate->slot_index);
751 c = abc_newfunction(c, l->methodstate->abc);
752 c = abc_setlocal(c, l->methodstate->var_index);
754 free(l->methodstate);l->methodstate=0;
758 c = code_append(c, m->header);
761 if(m->is_constructor && !m->has_super) {
762 // call default constructor
763 c = abc_getlocal_0(c);
764 c = abc_constructsuper(c, 0);
766 list_free(m->innerfunctions);
767 m->innerfunctions = 0;
772 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
774 c = code_append(c, header);
775 c = code_append(c, var_block(body));
776 /* append return if necessary */
777 if(!c || (c->opcode != OPCODE_RETURNVOID &&
778 c->opcode != OPCODE_RETURNVALUE)) {
779 c = abc_returnvoid(c);
785 static void startpackage(char*name)
788 /*printf("entering package \"%s\"\n", name);*/
789 state->package = strdup(name);
791 static void endpackage()
793 /*printf("leaving package \"%s\"\n", state->package);*/
795 //used e.g. in classinfo_register:
796 //free(state->package);state->package=0;
801 #define FLAG_PUBLIC 256
802 #define FLAG_PROTECTED 512
803 #define FLAG_PRIVATE 1024
804 #define FLAG_PACKAGEINTERNAL 2048
805 #define FLAG_NAMESPACE 4096
807 static namespace_t modifiers2access(modifiers_t*mod)
812 if(mod->flags&FLAG_NAMESPACE) {
813 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
814 syntaxerror("invalid combination of access levels and namespaces");
815 ns.access = ACCESS_NAMESPACE;
817 } else if(mod->flags&FLAG_PUBLIC) {
818 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
819 syntaxerror("invalid combination of access levels");
820 ns.access = ACCESS_PACKAGE;
821 } else if(mod->flags&FLAG_PRIVATE) {
822 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
823 syntaxerror("invalid combination of access levels");
824 ns.access = ACCESS_PRIVATE;
825 } else if(mod->flags&FLAG_PROTECTED) {
826 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
827 syntaxerror("invalid combination of access levels");
828 ns.access = ACCESS_PROTECTED;
830 ns.access = ACCESS_PACKAGEINTERNAL;
834 static slotinfo_t* find_class(const char*name);
836 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
841 index = new_variable("this", 0, 0, 0);
842 else if(!m->is_global)
843 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
845 index = new_variable("globalscope", 0, 0, 0);
848 parserassert(!index);
851 /* as variables and slots share the same number, make sure
852 that those variable indices are reserved. It's up to the
853 optimizer to later shuffle the variables down to lower
855 m->variable_count = m->uses_slots;
856 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
859 v->type = (classinfo_t*)registry_find(v->type->package, v->type->name);
861 v->type = (classinfo_t*)find_class(v->type->name);
862 if(!v->type || v->type->kind != INFOTYPE_CLASS) {
863 syntaxerror("Couldn't find class %s", v->type->name);
871 for(p=params->list;p;p=p->next) {
872 new_variable(p->param->name, p->param->type, 0, 1);
876 methodstate_list_t*l = m->innerfunctions;
878 methodstate_t*m = l->methodstate;
879 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
880 m->var_index = v->index;
881 m->slot_index = v->index;
882 v->is_inner_method = m;
888 char*as3_globalclass=0;
889 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
892 syntaxerror("inner classes now allowed");
897 classinfo_list_t*mlist=0;
899 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
900 syntaxerror("invalid modifier(s)");
902 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
903 syntaxerror("public and internal not supported at the same time.");
905 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
906 // all classes extend object
907 extends = registry_getobjectclass();
910 /* create the class name, together with the proper attributes */
914 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
915 access = ACCESS_PRIVATE; package = internal_filename_package;
916 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
917 access = ACCESS_PACKAGEINTERNAL; package = state->package;
918 } else if(state->package!=internal_filename_package) {
919 access = ACCESS_PACKAGE; package = state->package;
921 syntaxerror("public classes only allowed inside a package");
925 state->cls = rfx_calloc(sizeof(classstate_t));
926 state->cls->init = rfx_calloc(sizeof(methodstate_t));
927 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
928 /* notice: we make no effort to initialize the top variable (local0) here,
929 even though it has special meaning. We just rely on the facat
930 that pass 1 won't do anything with variables */
932 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
934 /* set current method to constructor- all code within the class-level (except
935 static variable initializations) will be executed during construction time */
936 state->method = state->cls->init;
938 if(registry_find(package, classname)) {
939 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
941 /* build info struct */
942 int num_interfaces = (list_length(implements));
943 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
944 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
948 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
950 state->method = state->cls->init;
951 parserassert(state->cls && state->cls->info);
953 function_initvars(state->cls->init, 0, 0, 1);
954 function_initvars(state->cls->static_init, 0, 0, 0);
956 if(extends && (extends->flags & FLAG_FINAL))
957 syntaxerror("Can't extend final class '%s'", extends->name);
959 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
960 state->cls->info->superclass = extends;
962 classinfo_list_t*l = implements;
963 for(l=implements;l;l=l->next) {
964 if(!(l->classinfo->flags & FLAG_INTERFACE))
965 syntaxerror("'%s' is not an interface", l->classinfo->name);
966 state->cls->info->interfaces[pos++] = l->classinfo;
969 /* generate the abc code for this class */
970 MULTINAME(classname2,state->cls->info);
971 multiname_t*extends2 = sig2mname(extends);
973 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
974 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
975 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
976 if(state->cls->info->flags&FLAG_INTERFACE) {
977 abc_class_interface(state->cls->abc);
980 abc_class_protectedNS(state->cls->abc, classname);
982 for(mlist=implements;mlist;mlist=mlist->next) {
983 MULTINAME(m, mlist->classinfo);
984 abc_class_add_interface(state->cls->abc, &m);
987 /* write the construction code for this class to the global init
989 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
991 abc_method_body_t*m = global->init->method->body;
992 __ getglobalscope(m);
993 classinfo_t*s = extends;
998 //TODO: take a look at the current scope stack, maybe
999 // we can re-use something
1004 multiname_t*s2 = sig2mname(s);
1006 multiname_destroy(s2);
1008 __ pushscope(m); count++;
1009 m->code = m->code->prev->prev; // invert
1011 /* continue appending after last op end */
1012 while(m->code && m->code->next) m->code = m->code->next;
1014 /* TODO: if this is one of *our* classes, we can also
1015 do a getglobalscope/getslot <nr> (which references
1016 the init function's slots) */
1018 __ getlex2(m, extends2);
1020 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1021 stack is not the superclass */
1022 __ pushscope(m);count++;
1025 /* notice: we get a verify error #1107 if the top element on the scope
1026 stack is not the global object */
1028 __ pushscope(m);count++;
1030 __ newclass(m,state->cls->abc);
1034 __ setslot(m, slotindex);
1035 multiname_destroy(extends2);
1037 /* flash.display.MovieClip handling */
1039 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1040 if(state->package && state->package[0]) {
1041 as3_globalclass = concat3(state->package, ".", classname);
1043 as3_globalclass = strdup(classname);
1049 static int slotstate_varconst = 0;
1050 static modifiers_t*slotstate_flags = 0;
1051 static void setslotstate(modifiers_t* flags, int varconst)
1053 slotstate_varconst = varconst;
1054 slotstate_flags = flags;
1056 if(flags && flags->flags&FLAG_STATIC) {
1057 state->method = state->cls->static_init;
1059 state->method = state->cls->init;
1062 parserassert(state->method);
1066 static void endclass()
1069 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1071 c = abc_getlocal_0(c);
1072 c = abc_constructsuper(c, 0);
1073 state->cls->init->header = code_append(state->cls->init->header, c);
1074 state->cls->has_constructor=1;
1076 if(state->cls->init) {
1077 if(state->cls->info->flags&FLAG_INTERFACE) {
1078 if(state->cls->init->header)
1079 syntaxerror("interface can not have class-level code");
1081 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1082 code_t*c = method_header(state->cls->init);
1083 m->body->code = wrap_function(c, 0, m->body->code);
1086 if(state->cls->static_init) {
1087 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1088 code_t*c = method_header(state->cls->static_init);
1089 m->body->code = wrap_function(c, 0, m->body->code);
1096 void check_code_for_break(code_t*c)
1099 if(c->opcode == OPCODE___BREAK__) {
1100 char*name = string_cstr(c->data[0]);
1101 syntaxerror("Unresolved \"break %s\"", name);
1103 if(c->opcode == OPCODE___CONTINUE__) {
1104 char*name = string_cstr(c->data[0]);
1105 syntaxerror("Unresolved \"continue %s\"", name);
1107 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1108 char*name = string_cstr(c->data[0]);
1109 syntaxerror("Can't reference a package (%s) as such", name);
1116 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1118 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1119 if(TYPE_IS_NUMBER(t)) {
1120 xassert(c->type == CONSTANT_FLOAT
1121 || c->type == CONSTANT_INT
1122 || c->type == CONSTANT_UINT);
1123 } else if(TYPE_IS_UINT(t)) {
1124 xassert(c->type == CONSTANT_UINT ||
1125 (c->type == CONSTANT_INT && c->i>=0));
1126 } else if(TYPE_IS_INT(t)) {
1127 xassert(c->type == CONSTANT_INT);
1128 } else if(TYPE_IS_BOOLEAN(t)) {
1129 xassert(c->type == CONSTANT_TRUE
1130 || c->type == CONSTANT_FALSE);
1134 static void check_override(memberinfo_t*m, int flags)
1138 if(m->parent == state->cls->info)
1139 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1141 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1142 if(m->access==ACCESS_PRIVATE)
1144 if(m->flags & FLAG_FINAL)
1145 syntaxerror("can't override final member %s", m->name);
1147 /* allow this. it's no issue.
1148 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1149 syntaxerror("can't override static member %s", m->name);*/
1151 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1152 syntaxerror("can't override non-static member %s with static declaration", m->name);
1154 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1155 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1156 if(m->kind == INFOTYPE_METHOD)
1157 syntaxerror("can't override without explicit 'override' declaration");
1159 syntaxerror("can't override '%s'", m->name);
1164 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1166 methodinfo_t*minfo = 0;
1167 namespace_t ns = modifiers2access(mod);
1170 minfo = methodinfo_register_global(ns.access, state->package, name);
1171 minfo->return_type = 0; // save this for pass 2
1172 } else if(getset != KW_GET && getset != KW_SET) {
1174 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1176 printf("%s.%s | %s.%s\n",
1177 m->package, m->name,
1179 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1181 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1182 minfo->return_type = 0; // save this for pass 2
1183 // getslot on a member slot only returns "undefined", so no need
1184 // to actually store these
1185 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1187 //class getter/setter
1188 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1190 if(getset == KW_GET) {
1192 } else if(params->list && params->list->param && !params->list->next) {
1193 type = params->list->param->type;
1195 syntaxerror("setter function needs to take exactly one argument");
1196 // not sure wether to look into superclasses here, too
1197 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1199 if(minfo->kind!=INFOTYPE_SLOT)
1200 syntaxerror("class already contains a method called '%s'", name);
1201 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1202 syntaxerror("class already contains a field called '%s'", name);
1203 if(minfo->subtype & gs)
1204 syntaxerror("getter/setter for '%s' already defined", name);
1205 /* make a setter or getter into a getset */
1206 minfo->subtype |= gs;
1209 FIXME: this check needs to be done in pass 2
1211 if((!minfo->return_type != !type) ||
1212 (minfo->return_type && type &&
1213 !strcmp(minfo->return_type->name, type->name))) {
1214 syntaxerror("different type in getter and setter: %s and %s",
1215 minfo->return_type?minfo->return_type->name:"*",
1216 type?type->name:"*");
1219 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1220 minfo->kind = INFOTYPE_SLOT; //hack
1221 minfo->subtype = gs;
1222 minfo->return_type = 0;
1224 /* can't assign a slot as getter and setter might have different slots */
1225 //minfo->slot = slot;
1227 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1228 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1229 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1234 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1236 //parserassert(state->method && state->method->info);
1238 methodstate_t*parent_method = state->method;
1241 return_type = 0; // not valid in pass 1
1245 state->new_vars = 1;
1248 state->method = rfx_calloc(sizeof(methodstate_t));
1249 state->method->inner = 1;
1250 state->method->variable_count = 0;
1251 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1253 NEW(methodinfo_t,minfo);
1254 minfo->kind = INFOTYPE_METHOD;
1255 minfo->access = ACCESS_PACKAGEINTERNAL;
1257 state->method->info = minfo;
1260 list_append(parent_method->innerfunctions, state->method);
1262 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1264 function_initvars(state->method, params, 0, 1);
1268 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1269 state->method->variable_count = 0;
1270 parserassert(state->method);
1272 state->method->info->return_type = return_type;
1273 function_initvars(state->method, params, 0, 1);
1277 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1278 params_t*params, classinfo_t*return_type)
1280 if(state->method && state->method->info) {
1281 syntaxerror("not able to start another method scope");
1284 state->new_vars = 1;
1287 state->method = rfx_calloc(sizeof(methodstate_t));
1288 state->method->has_super = 0;
1291 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1293 state->method->is_global = 1;
1294 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1296 if(state->method->is_constructor)
1297 name = "__as3_constructor__";
1299 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1301 function_initvars(state->method, params, mod->flags, 1);
1303 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1307 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1308 state->method->variable_count = 0;
1309 parserassert(state->method);
1312 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1313 check_override(m, mod->flags);
1317 state->cls->has_constructor |= state->method->is_constructor;
1320 state->method->info->return_type = return_type;
1321 function_initvars(state->method, params, mod->flags, 1);
1325 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1326 params_t*params, classinfo_t*return_type, code_t*body)
1328 int flags = mod?mod->flags:0;
1331 // store inner methods in variables
1332 function_initvars(state->method, 0, 0, 0);
1334 methodstate_list_t*ml = state->method->innerfunctions;
1336 dict_t*xvars = dict_new();
1339 methodstate_t*m = ml->methodstate;
1340 parserassert(m->inner);
1341 if(m->unresolved_variables) {
1342 dict_t*d = m->unresolved_variables;
1344 for(t=0;t<d->hashsize;t++) {
1345 dictentry_t*l = d->slots[t];
1347 /* check parent method's variables */
1349 if((v=find_variable(state, l->key))) {
1350 m->uses_parent_function = 1;
1351 state->method->uses_slots = 1;
1352 dict_put(xvars, l->key, 0);
1359 dict_destroy(m->unresolved_variables);
1360 m->unresolved_variables = 0;
1365 if(state->method->uses_slots) {
1366 state->method->slots = dict_new();
1368 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1369 if(v->index && dict_contains(xvars, name)) {
1372 if(v->is_inner_method) {
1373 v->is_inner_method->is_a_slot = 1;
1376 dict_put(state->method->slots, name, v);
1379 state->method->uses_slots = i;
1380 dict_destroy(state->vars);state->vars = 0;
1387 /*if(state->method->uses_parent_function){
1388 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1393 multiname_t*type2 = sig2mname(return_type);
1395 if(state->method->inner) {
1396 f = state->method->abc;
1397 abc_method_init(f, global->file, type2, 1);
1398 } else if(state->method->is_constructor) {
1399 f = abc_class_getconstructor(state->cls->abc, type2);
1400 } else if(!state->method->is_global) {
1401 namespace_t mname_ns = {state->method->info->access, ""};
1402 multiname_t mname = {QNAME, &mname_ns, 0, name};
1404 if(flags&FLAG_STATIC)
1405 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1407 f = abc_class_method(state->cls->abc, type2, &mname);
1408 slot = f->trait->slot_id;
1410 namespace_t mname_ns = {state->method->info->access, state->package};
1411 multiname_t mname = {QNAME, &mname_ns, 0, name};
1413 f = abc_method_new(global->file, type2, 1);
1414 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1415 //abc_code_t*c = global->init->method->body->code;
1417 //flash doesn't seem to allow us to access function slots
1418 //state->method->info->slot = slot;
1420 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1421 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1422 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1423 if(params->varargs) f->flags |= METHOD_NEED_REST;
1427 for(p=params->list;p;p=p->next) {
1428 if(params->varargs && !p->next) {
1429 break; //varargs: omit last parameter in function signature
1431 multiname_t*m = sig2mname(p->param->type);
1432 list_append(f->parameters, m);
1433 if(p->param->value) {
1434 check_constant_against_type(p->param->type, p->param->value);
1435 opt=1;list_append(f->optional_parameters, p->param->value);
1437 syntaxerror("non-optional parameter not allowed after optional parameters");
1440 if(state->method->slots) {
1441 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1443 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1444 multiname_t*type = sig2mname(v->type);
1445 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1446 t->slot_id = v->index;
1451 check_code_for_break(body);
1453 if(state->method->exceptions &&
1454 (state->method->late_binding || state->method->uses_slots)) {
1455 //syntaxerror("try/catch and activation or late binding not supported yet within the same method");
1456 as3_warning("try/catch and activation or late binding not supported yet within the same method");
1460 f->body->code = body;
1461 f->body->exceptions = state->method->exceptions;
1462 } else { //interface
1464 syntaxerror("interface methods can't have a method body");
1474 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1479 void breakjumpsto(code_t*c, char*name, code_t*jump)
1482 if(c->opcode == OPCODE___BREAK__) {
1483 string_t*name2 = c->data[0];
1484 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1485 c->opcode = OPCODE_JUMP;
1492 void continuejumpsto(code_t*c, char*name, code_t*jump)
1495 if(c->opcode == OPCODE___CONTINUE__) {
1496 string_t*name2 = c->data[0];
1497 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1498 c->opcode = OPCODE_JUMP;
1506 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1507 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1508 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1510 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1512 if(!type1 || !type2)
1513 return registry_getanytype();
1514 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1515 return registry_getanytype();
1518 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1527 return registry_getanytype();
1529 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1534 return abc_coerce_a(c);
1538 // cast an "any" type to a specific type. subject to
1539 // runtime exceptions
1540 return abc_coerce2(c, &m);
1543 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1544 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1545 // allow conversion between number types
1546 return abc_coerce2(c, &m);
1548 //printf("%s.%s\n", from.package, from.name);
1549 //printf("%s.%s\n", to.package, to.name);
1551 classinfo_t*supertype = from;
1553 if(supertype == to) {
1554 // target type is one of from's superclasses
1555 return abc_coerce2(c, &m);
1558 while(supertype->interfaces[t]) {
1559 if(supertype->interfaces[t]==to) {
1560 // target type is one of from's interfaces
1561 return abc_coerce2(c, &m);
1565 supertype = supertype->superclass;
1567 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1569 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1571 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1574 as3_error("can't convert type %s%s%s to %s%s%s",
1575 from->package, from->package?".":"", from->name,
1576 to->package, to->package?".":"", to->name);
1580 code_t*defaultvalue(code_t*c, classinfo_t*type)
1582 if(TYPE_IS_INT(type)) {
1583 c = abc_pushbyte(c, 0);
1584 } else if(TYPE_IS_UINT(type)) {
1585 c = abc_pushuint(c, 0);
1586 } else if(TYPE_IS_FLOAT(type)) {
1588 } else if(TYPE_IS_BOOLEAN(type)) {
1589 c = abc_pushfalse(c);
1591 //c = abc_pushundefined(c);
1593 c = abc_pushnull(c);
1595 c = abc_coerce2(c, &m);
1600 char is_pushundefined(code_t*c)
1602 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1605 static slotinfo_t* find_class(const char*name)
1609 c = registry_find(state->package, name);
1612 /* try explicit imports */
1613 dictentry_t* e = dict_get_slot(state->imports, name);
1616 if(!strcmp(e->key, name)) {
1617 c = (slotinfo_t*)e->data;
1623 /* try package.* imports */
1624 import_list_t*l = state->wildcard_imports;
1626 //printf("does package %s contain a class %s?\n", l->import->package, name);
1627 c = registry_find(l->import->package, name);
1632 /* try global package */
1633 c = registry_find("", name);
1636 /* try local "filename" package */
1637 c = registry_find(internal_filename_package, name);
1642 typedcode_t push_class(slotinfo_t*a)
1647 if(a->access == ACCESS_PACKAGEINTERNAL &&
1648 strcmp(a->package, state->package) &&
1649 strcmp(a->package, internal_filename_package)
1651 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1652 infotypename(a), a->name, a->package, state->package);
1655 if(a->kind != INFOTYPE_CLASS) {
1657 x.c = abc_findpropstrict2(x.c, &m);
1658 x.c = abc_getproperty2(x.c, &m);
1659 if(a->kind == INFOTYPE_METHOD) {
1660 methodinfo_t*f = (methodinfo_t*)a;
1661 x.t = TYPE_FUNCTION(f);
1663 varinfo_t*v = (varinfo_t*)a;
1667 classinfo_t*c = (classinfo_t*)a;
1669 x.c = abc_getglobalscope(x.c);
1670 x.c = abc_getslot(x.c, c->slot);
1673 x.c = abc_getlex2(x.c, &m);
1675 x.t = TYPE_CLASS(c);
1680 static char is_getlocal(code_t*c)
1682 if(!c || c->prev || c->next)
1684 return(c->opcode == OPCODE_GETLOCAL
1685 || c->opcode == OPCODE_GETLOCAL_0
1686 || c->opcode == OPCODE_GETLOCAL_1
1687 || c->opcode == OPCODE_GETLOCAL_2
1688 || c->opcode == OPCODE_GETLOCAL_3);
1690 static int getlocalnr(code_t*c)
1692 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1693 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1694 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1695 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1696 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1697 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1701 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1705 [prefix code] [read instruction]
1709 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1711 if(in && in->opcode == OPCODE_COERCE_A) {
1712 in = code_cutlast(in);
1715 syntaxerror("internal error");
1717 /* chop off read instruction */
1721 prefix = r->prev;r->prev = 0;
1727 char use_temp_var = readbefore;
1729 /* generate the write instruction, and maybe append a dup to the prefix code */
1730 code_t* write = abc_nop(0);
1731 if(r->opcode == OPCODE_GETPROPERTY) {
1732 write->opcode = OPCODE_SETPROPERTY;
1733 multiname_t*m = (multiname_t*)r->data[0];
1734 write->data[0] = multiname_clone(m);
1735 if(m->type == QNAME || m->type == MULTINAME) {
1737 prefix = abc_dup(prefix); // we need the object, too
1740 } else if(m->type == MULTINAMEL) {
1742 /* dupping two values on the stack requires 5 operations and one register-
1743 couldn't adobe just have given us a dup2? */
1744 int temp = gettempvar();
1745 prefix = abc_setlocal(prefix, temp);
1746 prefix = abc_dup(prefix);
1747 prefix = abc_getlocal(prefix, temp);
1748 prefix = abc_swap(prefix);
1749 prefix = abc_getlocal(prefix, temp);
1751 prefix = abc_kill(prefix, temp);
1755 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1757 } else if(r->opcode == OPCODE_GETSLOT) {
1758 write->opcode = OPCODE_SETSLOT;
1759 write->data[0] = r->data[0];
1761 prefix = abc_dup(prefix); // we need the object, too
1764 } else if(r->opcode == OPCODE_GETLOCAL) {
1765 write->opcode = OPCODE_SETLOCAL;
1766 write->data[0] = r->data[0];
1767 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1768 write->opcode = OPCODE_SETLOCAL_0;
1769 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1770 write->opcode = OPCODE_SETLOCAL_1;
1771 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1772 write->opcode = OPCODE_SETLOCAL_2;
1773 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1774 write->opcode = OPCODE_SETLOCAL_3;
1775 } else if(r->opcode == OPCODE_GETSUPER) {
1776 write->opcode = OPCODE_SETSUPER;
1777 multiname_t*m = (multiname_t*)r->data[0];
1778 write->data[0] = multiname_clone(m);
1781 syntaxerror("illegal lvalue: can't assign a value to this expression");
1788 /* with getproperty/getslot, we have to be extra careful not
1789 to execute the read code twice, as it might have side-effects
1790 (e.g. if the property is in fact a setter/getter combination)
1792 So read the value, modify it, and write it again,
1793 using prefix only once and making sure (by using a temporary
1794 register) that the return value is what we just wrote */
1795 temp = gettempvar();
1796 c = code_append(c, prefix);
1797 c = code_append(c, r);
1800 c = abc_setlocal(c, temp);
1802 c = code_append(c, middlepart);
1805 c = abc_setlocal(c, temp);
1807 c = code_append(c, write);
1808 c = abc_getlocal(c, temp);
1809 c = abc_kill(c, temp);
1811 /* if we're allowed to execute the read code twice *and*
1812 the middlepart doesn't modify the code, things are easier.
1814 code_t* r2 = code_dup(r);
1815 //c = code_append(c, prefix);
1816 parserassert(!prefix);
1817 c = code_append(c, r);
1818 c = code_append(c, middlepart);
1819 c = code_append(c, write);
1820 c = code_append(c, r2);
1823 /* even smaller version: overwrite the value without reading
1827 c = code_append(c, prefix);
1830 c = code_append(c, middlepart);
1831 c = code_append(c, write);
1832 c = code_append(c, r);
1835 temp = gettempvar();
1837 c = code_append(c, prefix);
1839 c = code_append(c, middlepart);
1841 c = abc_setlocal(c, temp);
1842 c = code_append(c, write);
1843 c = abc_getlocal(c, temp);
1844 c = abc_kill(c, temp);
1850 char is_break_or_jump(code_t*c)
1854 if(c->opcode == OPCODE_JUMP ||
1855 c->opcode == OPCODE___BREAK__ ||
1856 c->opcode == OPCODE___CONTINUE__ ||
1857 c->opcode == OPCODE_THROW ||
1858 c->opcode == OPCODE_RETURNVOID ||
1859 c->opcode == OPCODE_RETURNVALUE) {
1866 #define IS_FINALLY_TARGET(op) \
1867 ((op) == OPCODE___CONTINUE__ || \
1868 (op) == OPCODE___BREAK__ || \
1869 (op) == OPCODE_RETURNVOID || \
1870 (op) == OPCODE_RETURNVALUE || \
1871 (op) == OPCODE___RETHROW__)
1873 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1875 #define NEED_EXTRA_STACK_ARG
1876 code_t*finally_label = abc_nop(0);
1877 NEW(lookupswitch_t, l);
1883 code_t*prev = i->prev;
1884 if(IS_FINALLY_TARGET(i->opcode)) {
1887 if(i->opcode == OPCODE___RETHROW__ ||
1888 i->opcode == OPCODE_RETURNVALUE) {
1889 if(i->opcode == OPCODE___RETHROW__)
1890 i->opcode = OPCODE_THROW;
1892 p = abc_coerce_a(p);
1893 p = abc_setlocal(p, tempvar);
1895 p = abc_pushbyte(p, count++);
1896 p = abc_jump(p, finally_label);
1897 code_t*target = p = abc_label(p);
1898 #ifdef NEED_EXTRA_STACK_ARG
1902 p = abc_getlocal(p, tempvar);
1905 p->next = i;i->prev = p;
1906 list_append(l->targets, target);
1912 c = abc_pushbyte(c, -1);
1913 c = code_append(c, finally_label);
1914 c = code_append(c, finally);
1916 #ifdef NEED_EXTRA_STACK_ARG
1919 c = abc_lookupswitch(c, l);
1920 c = l->def = abc_label(c);
1921 #ifdef NEED_EXTRA_STACK_ARG
1928 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1932 code_t*prev = i->prev;
1933 if(IS_FINALLY_TARGET(i->opcode)) {
1934 if(i->opcode == OPCODE___RETHROW__)
1935 i->opcode = OPCODE_THROW;
1936 code_t*end = code_dup(finally);
1937 code_t*start = code_start(end);
1938 if(prev) prev->next = start;
1945 return code_append(c, finally);
1948 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1954 int num_insertion_points=0;
1956 if(IS_FINALLY_TARGET(i->opcode))
1957 num_insertion_points++;
1964 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1969 int simple_version_cost = (1+num_insertion_points)*code_size;
1970 int lookup_version_cost = 4*num_insertion_points + 5;
1972 if(cantdup || simple_version_cost > lookup_version_cost) {
1973 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1974 return insert_finally_lookup(c, finally, tempvar);
1976 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1977 return insert_finally_simple(c, finally, tempvar);
1981 #define PASS1 }} if(as3_pass == 1) {{
1982 #define PASS1END }} if(as3_pass == 2) {{
1983 #define PASS2 }} if(as3_pass == 2) {{
1984 #define PASS12 }} {{
1985 #define PASS12END }} if(as3_pass == 2) {{
1991 /* ------------ code blocks / statements ---------------- */
1993 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1995 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1996 PROGRAM_CODE_LIST: PROGRAM_CODE
1997 | PROGRAM_CODE_LIST PROGRAM_CODE
1999 PROGRAM_CODE: PACKAGE_DECLARATION
2000 | INTERFACE_DECLARATION
2002 | FUNCTION_DECLARATION
2005 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
2008 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2009 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2010 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2012 INPACKAGE_CODE: INTERFACE_DECLARATION
2014 | FUNCTION_DECLARATION
2017 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
2020 MAYBECODE: CODE {$$=$1;}
2021 MAYBECODE: {$$=code_new();}
2023 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2024 CODE: CODEPIECE {$$=$1;}
2026 // code which may appear outside of methods
2027 CODE_STATEMENT: IMPORT
2029 CODE_STATEMENT: FOR_IN
2030 CODE_STATEMENT: WHILE
2031 CODE_STATEMENT: DO_WHILE
2032 CODE_STATEMENT: SWITCH
2034 CODE_STATEMENT: WITH
2036 CODE_STATEMENT: VOIDEXPRESSION
2037 CODE_STATEMENT: USE_NAMESPACE
2038 CODE_STATEMENT: NAMESPACE_DECLARATION
2039 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2040 CODE_STATEMENT: '{' '}' {$$=0;}
2042 // code which may appear in methods
2043 CODEPIECE: ';' {$$=0;}
2044 CODEPIECE: CODE_STATEMENT
2045 CODEPIECE: VARIABLE_DECLARATION
2050 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
2052 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2053 //CODEBLOCK : '{' '}' {$$=0;}
2054 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2055 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2057 /* ------------ package init code ------------------- */
2059 PACKAGE_INITCODE: CODE_STATEMENT {
2060 code_t**cc = &global->init->method->body->code;
2061 *cc = code_append(*cc, $1);
2064 /* ------------ conditional compilation ------------- */
2066 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
2068 /* ------------ variables --------------------------- */
2070 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2071 | {$$.c=abc_pushundefined(0);
2075 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2076 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2078 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2079 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2081 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2084 if(variable_exists($1))
2085 syntaxerror("Variable %s already defined", $1);
2087 new_variable($1, 0, 1, 0);
2090 if(!is_subtype_of($3.t, $2)) {
2091 syntaxerror("Can't convert %s to %s", $3.t->name,
2097 if(state->method->uses_slots) {
2098 variable_t* v = find_slot(state, $1);
2100 // this variable is stored in a slot
2108 index = new_variable($1, $2, 1, 0);
2111 $$ = slot?abc_getscopeobject(0, 1):0;
2114 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2115 $$ = code_append($$, $3.c);
2116 $$ = converttype($$, $3.t, $2);
2119 $$ = defaultvalue($$, $2);
2122 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2123 $$ = code_append($$, $3.c);
2124 $$ = abc_coerce_a($$);
2126 // don't do anything
2134 $$ = abc_setslot($$, index);
2136 $$ = abc_setlocal($$, index);
2140 /* ------------ control flow ------------------------- */
2142 MAYBEELSE: %prec below_else {$$ = code_new();}
2143 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2144 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2146 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2149 $$ = code_append($$, $4.c);
2150 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2152 $$ = code_append($$, $6);
2154 myjmp = $$ = abc_jump($$, 0);
2156 myif->branch = $$ = abc_nop($$);
2158 $$ = code_append($$, $7);
2159 myjmp->branch = $$ = abc_nop($$);
2165 FOR_INIT : {$$=code_new();}
2166 FOR_INIT : VARIABLE_DECLARATION
2167 FOR_INIT : VOIDEXPRESSION
2169 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2170 // (I don't see any easy way to revolve this conflict otherwise, as we
2171 // can't touch VAR_READ without upsetting the precedence about "return")
2172 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2173 PASS1 $$=$2;new_variable($2,0,1,0);
2174 PASS2 $$=$2;new_variable($2,$3,1,0);
2176 FOR_IN_INIT : T_IDENTIFIER {
2181 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2182 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2184 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2185 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2187 $$ = code_append($$, $2);
2188 code_t*loopstart = $$ = abc_label($$);
2189 $$ = code_append($$, $4.c);
2190 code_t*myif = $$ = abc_iffalse($$, 0);
2191 $$ = code_append($$, $8);
2192 code_t*cont = $$ = abc_nop($$);
2193 $$ = code_append($$, $6);
2194 $$ = abc_jump($$, loopstart);
2195 code_t*out = $$ = abc_nop($$);
2196 breakjumpsto($$, $1.name, out);
2197 continuejumpsto($$, $1.name, cont);
2204 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2205 variable_t*var = find_variable(state, $2);
2207 syntaxerror("variable %s not known in this scope", $2);
2210 char*tmp1name = concat2($2, "__tmp1__");
2211 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2212 char*tmp2name = concat2($2, "__array__");
2213 int array = new_variable(tmp1name, 0, 0, 0);
2216 $$ = code_append($$, $4.c);
2217 $$ = abc_coerce_a($$);
2218 $$ = abc_setlocal($$, array);
2219 $$ = abc_pushbyte($$, 0);
2220 $$ = abc_setlocal($$, it);
2222 code_t*loopstart = $$ = abc_label($$);
2224 $$ = abc_hasnext2($$, array, it);
2225 code_t*myif = $$ = abc_iffalse($$, 0);
2226 $$ = abc_getlocal($$, array);
2227 $$ = abc_getlocal($$, it);
2229 $$ = abc_nextname($$);
2231 $$ = abc_nextvalue($$);
2232 $$ = converttype($$, 0, var->type);
2233 $$ = abc_setlocal($$, var->index);
2235 $$ = code_append($$, $6);
2236 $$ = abc_jump($$, loopstart);
2238 code_t*out = $$ = abc_nop($$);
2239 breakjumpsto($$, $1.name, out);
2240 continuejumpsto($$, $1.name, loopstart);
2252 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2256 code_t*myjmp = $$ = abc_jump($$, 0);
2257 code_t*loopstart = $$ = abc_label($$);
2258 $$ = code_append($$, $6);
2259 code_t*cont = $$ = abc_nop($$);
2260 myjmp->branch = cont;
2261 $$ = code_append($$, $4.c);
2262 $$ = abc_iftrue($$, loopstart);
2263 code_t*out = $$ = abc_nop($$);
2264 breakjumpsto($$, $1, out);
2265 continuejumpsto($$, $1, cont);
2271 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2273 code_t*loopstart = $$ = abc_label($$);
2274 $$ = code_append($$, $3);
2275 code_t*cont = $$ = abc_nop($$);
2276 $$ = code_append($$, $6.c);
2277 $$ = abc_iftrue($$, loopstart);
2278 code_t*out = $$ = abc_nop($$);
2279 breakjumpsto($$, $1, out);
2280 continuejumpsto($$, $1, cont);
2286 BREAK : "break" %prec prec_none {
2287 $$ = abc___break__(0, "");
2289 BREAK : "break" T_IDENTIFIER {
2290 $$ = abc___break__(0, $2);
2292 CONTINUE : "continue" %prec prec_none {
2293 $$ = abc___continue__(0, "");
2295 CONTINUE : "continue" T_IDENTIFIER {
2296 $$ = abc___continue__(0, $2);
2299 MAYBE_CASE_LIST : {$$=0;}
2300 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2301 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2302 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2303 CASE_LIST: CASE {$$=$1;}
2304 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2306 CASE: "case" E ':' MAYBECODE {
2308 $$ = code_append($$, $2.c);
2309 code_t*j = $$ = abc_ifne($$, 0);
2310 $$ = code_append($$, $4);
2311 if($$->opcode != OPCODE___BREAK__) {
2312 $$ = abc___fallthrough__($$, "");
2314 code_t*e = $$ = abc_nop($$);
2317 DEFAULT: "default" ':' MAYBECODE {
2320 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2322 $$ = code_append($$, $7);
2323 code_t*out = $$ = abc_pop($$);
2324 breakjumpsto($$, $1, out);
2326 code_t*c = $$,*lastblock=0;
2328 if(c->opcode == OPCODE_IFNE) {
2329 if(!c->next) syntaxerror("internal error in fallthrough handling");
2331 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2333 c->opcode = OPCODE_JUMP;
2334 c->branch = lastblock;
2336 /* fall through end of switch */
2337 c->opcode = OPCODE_NOP;
2347 /* ------------ try / catch /finally ---------------- */
2349 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2350 state->exception_name=$3;
2351 PASS1 new_variable($3, 0, 0, 0);
2352 PASS2 new_variable($3, $4, 0, 0);
2355 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2356 multiname_t name = {QNAME, &name_ns, 0, $3};
2358 NEW(abc_exception_t, e)
2359 e->exc_type = sig2mname($4);
2360 e->var_name = multiname_clone(&name);
2364 int i = find_variable_safe(state, $3)->index;
2365 e->target = c = abc_nop(0);
2366 c = abc_setlocal(c, i);
2367 c = code_append(c, $8);
2373 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2378 NEW(abc_exception_t, e)
2379 e->exc_type = 0; //all exceptions
2380 e->var_name = 0; //no name
2383 e->to = code_append(e->to, $4);
2389 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2390 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2391 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2392 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2396 list_append($$.l,$2);
2397 $$.finally = $2->to;$2->to=0;
2400 CATCH_FINALLY_LIST: FINALLY {
2404 list_append($$.l,$1);
2405 $$.finally = $1->to;$1->to=0;
2409 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2410 code_t*out = abc_nop(0);
2412 code_t*start = abc_nop(0);
2413 $$ = code_append(start, $4);
2414 if(!is_break_or_jump($4)) {
2415 $$ = abc_jump($$, out);
2417 code_t*end = $$ = abc_nop($$);
2421 tmp = new_variable("__finally__", 0, 0, 0);
2423 abc_exception_list_t*l = $6.l;
2426 abc_exception_t*e = l->abc_exception;
2428 $$ = code_append($$, e->target);
2429 $$ = abc_jump($$, out);
2431 parserassert((ptroff_t)$6.finally);
2433 e->target = $$ = abc_nop($$);
2434 $$ = abc___rethrow__($$);
2442 $$ = code_append($$, out);
2444 $$ = insert_finally($$, $6.finally, tmp);
2446 list_concat(state->method->exceptions, $6.l);
2452 /* ------------ throw ------------------------------- */
2454 THROW : "throw" EXPRESSION {
2458 THROW : "throw" %prec prec_none {
2459 if(!state->exception_name)
2460 syntaxerror("re-throw only possible within a catch block");
2461 variable_t*v = find_variable(state, state->exception_name);
2463 $$=abc_getlocal($$, v->index);
2467 /* ------------ with -------------------------------- */
2469 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2471 $$ = abc_pushwith($$);
2472 $$ = code_append($$, $5);
2473 $$ = abc_popscope($$);
2476 /* ------------ packages and imports ---------------- */
2478 X_IDENTIFIER: T_IDENTIFIER
2479 | "package" {PASS12 $$="package";}
2481 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2482 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2484 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2485 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2486 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2487 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2489 IMPORT : "import" PACKAGEANDCLASS {
2491 slotinfo_t*s = registry_find($2->package, $2->name);
2492 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2493 as3_schedule_class($2->package, $2->name);
2499 syntaxerror("Couldn't import class\n");
2500 state_has_imports();
2501 dict_put(state->imports, c->name, c);
2502 import_toplevel(c->package);
2505 IMPORT : "import" PACKAGE '.' '*' {
2507 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2508 as3_schedule_package($2);
2514 state_has_imports();
2515 list_append(state->wildcard_imports, i);
2516 import_toplevel(i->package);
2520 /* ------------ classes and interfaces (header) -------------- */
2522 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2523 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2524 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2525 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2527 $$.flags=$1.flags|$2.flags;
2528 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2529 $$.ns=$1.ns?$1.ns:$2.ns;
2533 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2534 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2535 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2536 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2537 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2538 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2539 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2540 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2541 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2542 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2547 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2549 EXTENDS_LIST : {PASS12 $$=list_new();}
2550 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2552 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2553 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2555 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2556 EXTENDS IMPLEMENTS_LIST
2557 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2559 '}' {PASS12 endclass();$$=0;}
2561 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2563 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2564 startclass(&$1,$3,0,$4);}
2565 MAYBE_INTERFACE_BODY
2566 '}' {PASS12 endclass();$$=0;}
2568 /* ------------ classes and interfaces (body) -------------- */
2571 MAYBE_CLASS_BODY : CLASS_BODY
2572 CLASS_BODY : CLASS_BODY_ITEM
2573 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2574 CLASS_BODY_ITEM : ';'
2575 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2576 CLASS_BODY_ITEM : SLOT_DECLARATION
2577 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2579 CLASS_BODY_ITEM : CODE_STATEMENT {
2580 code_t*c = state->cls->static_init->header;
2581 c = code_append(c, $1);
2582 state->cls->static_init->header = c;
2585 MAYBE_INTERFACE_BODY :
2586 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2587 INTERFACE_BODY : IDECLARATION
2588 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2590 IDECLARATION : "var" T_IDENTIFIER {
2591 syntaxerror("variable declarations not allowed in interfaces");
2593 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2595 $1.flags |= FLAG_PUBLIC;
2596 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2597 syntaxerror("invalid method modifiers: interface methods always need to be public");
2599 startfunction(&$1,$3,$4,&$6,$8);
2600 endfunction(&$1,$3,$4,&$6,$8, 0);
2601 list_deep_free($6.list);
2604 /* ------------ classes and interfaces (body, slots ) ------- */
2606 VARCONST: "var" | "const"
2608 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2610 SLOT_LIST: ONE_SLOT {$$ = $1;}
2611 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2613 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2615 int flags = slotstate_flags->flags;
2616 namespace_t ns = modifiers2access(slotstate_flags);
2618 varinfo_t* info = 0;
2620 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2622 check_override(i, flags);
2624 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2626 slotinfo_t*i = registry_find(state->package, $1);
2628 syntaxerror("package %s already contains '%s'", state->package, $1);
2630 if(ns.name && ns.name[0]) {
2631 syntaxerror("namespaces not allowed on package-level variables");
2633 info = varinfo_register_global(ns.access, state->package, $1);
2637 info->flags = flags;
2640 multiname_t mname = {QNAME, &ns, 0, $1};
2642 trait_list_t**traits;
2646 ns.name = state->package;
2647 traits = &global->init->traits;
2648 code = &global->init->method->body->code;
2649 } else if(flags&FLAG_STATIC) {
2651 traits = &state->cls->abc->static_traits;
2652 code = &state->cls->static_init->header;
2654 // instance variable
2655 traits = &state->cls->abc->traits;
2656 code = &state->cls->init->header;
2662 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2664 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2666 info->slot = t->slot_id;
2668 /* initalization code (if needed) */
2670 if($3.c && !is_pushundefined($3.c)) {
2671 c = abc_getlocal_0(c);
2672 c = code_append(c, $3.c);
2673 c = converttype(c, $3.t, $2);
2674 c = abc_setslot(c, t->slot_id);
2677 *code = code_append(*code, c);
2679 if(slotstate_varconst==KW_CONST) {
2680 t->kind= TRAIT_CONST;
2686 /* ------------ constants -------------------------------------- */
2688 MAYBESTATICCONSTANT: {$$=0;}
2689 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2691 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2692 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2693 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2694 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2695 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2696 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2697 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2698 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2699 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2700 STATICCONSTANT : T_IDENTIFIER {
2701 if(!strcmp($1, "NaN")) {
2702 $$ = constant_new_float(__builtin_nan(""));
2704 as3_warning("Couldn't evaluate constant value of %s", $1);
2705 $$ = constant_new_null($1);
2709 /* ------------ classes and interfaces (body, functions) ------- */
2711 // non-vararg version
2714 memset(&$$,0,sizeof($$));
2716 MAYBE_PARAM_LIST: PARAM_LIST {
2722 MAYBE_PARAM_LIST: "..." PARAM {
2724 memset(&$$,0,sizeof($$));
2726 list_append($$.list, $2);
2728 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2732 list_append($$.list, $4);
2736 PARAM_LIST: PARAM_LIST ',' PARAM {
2739 list_append($$.list, $3);
2743 memset(&$$,0,sizeof($$));
2744 list_append($$.list, $1);
2747 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2749 $$ = rfx_calloc(sizeof(param_t));
2755 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2757 $$ = rfx_calloc(sizeof(param_t));
2759 $$->type = TYPE_ANY;
2767 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2768 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2771 endfunction(&$1,$3,$4,&$6,0,0);
2773 if(!state->method->info) syntaxerror("internal error");
2775 code_t*c = method_header(state->method);
2776 c = wrap_function(c, 0, $11);
2778 endfunction(&$1,$3,$4,&$6,$8,c);
2780 list_deep_free($6.list);
2784 MAYBE_IDENTIFIER: T_IDENTIFIER
2785 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2786 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2787 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2790 endfunction(0,0,$2,&$4,0,0);
2792 methodinfo_t*f = state->method->info;
2793 if(!f || !f->kind) syntaxerror("internal error");
2795 code_t*c = method_header(state->method);
2796 c = wrap_function(c, 0, $9);
2798 int index = state->method->var_index;
2799 endfunction(0,0,$2,&$4,$6,c);
2801 $$.c = abc_getlocal(0, index);
2802 $$.t = TYPE_FUNCTION(f);
2804 PASS12 list_deep_free($4.list);
2808 /* ------------- package + class ids --------------- */
2810 CLASS: T_IDENTIFIER {
2811 PASS1 static classinfo_t c;
2812 memset(&c, 0, sizeof(c));
2813 c.kind = INFOTYPE_CLASS;
2818 /* let the compiler know that we might be looking for this soon */
2819 as3_schedule_class_noerror(state->package, $1);
2821 slotinfo_t*s = find_class($1);
2822 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2823 $$ = (classinfo_t*)s;
2826 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2827 PASS1 static classinfo_t c;
2828 memset(&c, 0, sizeof(c));
2829 c.kind = INFOTYPE_CLASS;
2835 slotinfo_t*s = registry_find($1, $3);
2836 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2838 $$ = (classinfo_t*)s;
2841 CLASS_SPEC: PACKAGEANDCLASS
2844 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2845 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2847 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2848 | '*' {PASS12 $$=registry_getanytype();}
2849 | "void" {PASS12 $$=registry_getanytype();}
2851 | "String" {$$=registry_getstringclass();}
2852 | "int" {$$=registry_getintclass();}
2853 | "uint" {$$=registry_getuintclass();}
2854 | "Boolean" {$$=registry_getbooleanclass();}
2855 | "Number" {$$=registry_getnumberclass();}
2858 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2859 MAYBETYPE: {PASS12 $$=0;}
2861 /* ----------function calls, delete, constructor calls ------ */
2863 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2864 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2866 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2867 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2868 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2870 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2874 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2875 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2877 $$.cc = code_append($1.cc, $2.c);
2881 NEW : "new" E XX MAYBE_PARAM_VALUES {
2883 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2885 code_t*paramcode = $4.cc;
2886 if($$.c->opcode == OPCODE_GETPROPERTY) {
2887 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2888 $$.c = code_cutlast($$.c);
2889 $$.c = code_append($$.c, paramcode);
2890 $$.c = abc_constructprop2($$.c, name, $4.len);
2891 multiname_destroy(name);
2892 } else if($$.c->opcode == OPCODE_GETSLOT) {
2893 int slot = (int)(ptroff_t)$$.c->data[0];
2894 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2895 multiname_t*name = t->name;
2896 $$.c = code_cutlast($$.c);
2897 $$.c = code_append($$.c, paramcode);
2898 $$.c = abc_constructprop2($$.c, name, $4.len);
2900 $$.c = code_append($$.c, paramcode);
2901 $$.c = abc_construct($$.c, $4.len);
2905 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2908 $$.c = abc_coerce_a($$.c);
2913 /* TODO: use abc_call (for calling local variables),
2914 abc_callstatic (for calling own methods)
2917 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2920 if($$.c->opcode == OPCODE_COERCE_A) {
2921 $$.c = code_cutlast($$.c);
2923 code_t*paramcode = $3.cc;
2926 if($$.c->opcode == OPCODE_GETPROPERTY) {
2927 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2928 $$.c = code_cutlast($$.c);
2929 $$.c = code_append($$.c, paramcode);
2930 $$.c = abc_callproperty2($$.c, name, $3.len);
2931 multiname_destroy(name);
2932 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
2933 int slot = (int)(ptroff_t)$$.c->data[0];
2934 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
2935 if(t->kind!=TRAIT_METHOD) {
2936 //ok: flash allows to assign closures to members.
2938 multiname_t*name = t->name;
2939 $$.c = code_cutlast($$.c);
2940 $$.c = code_append($$.c, paramcode);
2941 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2942 $$.c = abc_callproperty2($$.c, name, $3.len);
2943 } else if($$.c->opcode == OPCODE_GETSUPER) {
2944 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2945 $$.c = code_cutlast($$.c);
2946 $$.c = code_append($$.c, paramcode);
2947 $$.c = abc_callsuper2($$.c, name, $3.len);
2948 multiname_destroy(name);
2950 $$.c = abc_getglobalscope($$.c);
2951 $$.c = code_append($$.c, paramcode);
2952 $$.c = abc_call($$.c, $3.len);
2955 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2956 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2958 $$.c = abc_coerce_a($$.c);
2963 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2964 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2965 if(!state->method) syntaxerror("super() not allowed outside of a function");
2966 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2969 $$.c = abc_getlocal_0($$.c);
2971 $$.c = code_append($$.c, $3.cc);
2973 this is dependent on the control path, check this somewhere else
2974 if(state->method->has_super)
2975 syntaxerror("constructor may call super() only once");
2977 state->method->has_super = 1;
2979 $$.c = abc_constructsuper($$.c, $3.len);
2980 $$.c = abc_pushundefined($$.c);
2984 DELETE: "delete" E {
2986 if($$.c->opcode == OPCODE_COERCE_A) {
2987 $$.c = code_cutlast($$.c);
2989 multiname_t*name = 0;
2990 if($$.c->opcode == OPCODE_GETPROPERTY) {
2991 $$.c->opcode = OPCODE_DELETEPROPERTY;
2992 } else if($$.c->opcode == OPCODE_GETSLOT) {
2993 int slot = (int)(ptroff_t)$$.c->data[0];
2994 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
2995 $$.c = code_cutlast($$.c);
2996 $$.c = abc_deleteproperty2($$.c, name);
2998 $$.c = abc_getlocal_0($$.c);
2999 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3000 $$.c = abc_deleteproperty2($$.c, &m);
3002 $$.t = TYPE_BOOLEAN;
3005 RETURN: "return" %prec prec_none {
3006 $$ = abc_returnvoid(0);
3008 RETURN: "return" EXPRESSION {
3010 $$ = abc_returnvalue($$);
3013 // ----------------------- expression types -------------------------------------
3015 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3016 EXPRESSION : E %prec below_minus {$$ = $1;}
3017 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3019 $$.c = cut_last_push($$.c);
3020 $$.c = code_append($$.c,$3.c);
3023 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3024 $$=cut_last_push($1.c);
3027 // ----------------------- expression evaluation -------------------------------------
3029 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3030 //V : CONSTANT {$$ = 0;}
3032 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
3033 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3034 //V : NEW {$$ = $1.c;}
3036 //V : DELETE {$$ = $1.c;}
3037 E : DELETE {$$ = $1;}
3043 namespace_t ns = {ACCESS_PACKAGE, ""};
3044 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3046 $$.c = abc_getlex2($$.c, &m);
3047 $$.c = abc_pushstring($$.c, $1.pattern);
3048 $$.c = abc_construct($$.c, 1);
3050 $$.c = abc_getlex2($$.c, &m);
3051 $$.c = abc_pushstring($$.c, $1.pattern);
3052 $$.c = abc_pushstring($$.c, $1.options);
3053 $$.c = abc_construct($$.c, 2);
3058 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3059 //MULTINAME(m, registry_getintclass());
3060 //$$.c = abc_coerce2($$.c, &m); // FIXME
3063 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3066 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3069 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3072 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3075 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3078 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3081 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3082 $$.t = TYPE_BOOLEAN;
3084 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3085 $$.t = TYPE_BOOLEAN;
3087 CONSTANT : "null" {$$.c = abc_pushnull(0);
3091 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3092 $$.t = TYPE_BOOLEAN;
3094 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3095 $$.t = TYPE_BOOLEAN;
3097 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3098 $$.t = TYPE_BOOLEAN;
3100 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3101 $$.t = TYPE_BOOLEAN;
3103 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3104 $$.t = TYPE_BOOLEAN;
3106 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3107 $$.t = TYPE_BOOLEAN;
3109 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3110 $$.t = TYPE_BOOLEAN;
3112 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3113 $$.t = TYPE_BOOLEAN;
3116 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3118 $$.c = converttype($$.c, $1.t, $$.t);
3119 $$.c = abc_dup($$.c);
3120 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3121 $$.c = cut_last_push($$.c);
3122 $$.c = code_append($$.c,$3.c);
3123 $$.c = converttype($$.c, $3.t, $$.t);
3124 code_t*label = $$.c = abc_label($$.c);
3125 jmp->branch = label;
3128 $$.t = join_types($1.t, $3.t, 'A');
3129 /*printf("%08x:\n",$1.t);
3130 code_dump($1.c, 0, 0, "", stdout);
3131 printf("%08x:\n",$3.t);
3132 code_dump($3.c, 0, 0, "", stdout);
3133 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3135 $$.c = converttype($$.c, $1.t, $$.t);
3136 $$.c = abc_dup($$.c);
3137 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3138 $$.c = cut_last_push($$.c);
3139 $$.c = code_append($$.c,$3.c);
3140 $$.c = converttype($$.c, $3.t, $$.t);
3141 code_t*label = $$.c = abc_label($$.c);
3142 jmp->branch = label;
3145 E : '!' E {$$.c=$2.c;
3146 $$.c = abc_not($$.c);
3147 $$.t = TYPE_BOOLEAN;
3150 E : '~' E {$$.c=$2.c;
3151 $$.c = abc_bitnot($$.c);
3155 E : E '&' E {$$.c = code_append($1.c,$3.c);
3156 $$.c = abc_bitand($$.c);
3160 E : E '^' E {$$.c = code_append($1.c,$3.c);
3161 $$.c = abc_bitxor($$.c);
3165 E : E '|' E {$$.c = code_append($1.c,$3.c);
3166 $$.c = abc_bitor($$.c);
3170 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3171 $$.c = abc_rshift($$.c);
3174 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3175 $$.c = abc_urshift($$.c);
3178 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3179 $$.c = abc_lshift($$.c);
3183 E : E '/' E {$$.c = code_append($1.c,$3.c);
3184 $$.c = abc_divide($$.c);
3187 E : E '%' E {$$.c = code_append($1.c,$3.c);
3188 $$.c = abc_modulo($$.c);
3191 E : E '+' E {$$.c = code_append($1.c,$3.c);
3192 if(BOTH_INT($1.t, $3.t)) {
3193 $$.c = abc_add_i($$.c);
3196 $$.c = abc_add($$.c);
3197 $$.t = join_types($1.t,$3.t,'+');
3200 E : E '-' E {$$.c = code_append($1.c,$3.c);
3201 if(BOTH_INT($1.t,$3.t)) {
3202 $$.c = abc_subtract_i($$.c);
3205 $$.c = abc_subtract($$.c);
3209 E : E '*' E {$$.c = code_append($1.c,$3.c);
3210 if(BOTH_INT($1.t,$3.t)) {
3211 $$.c = abc_multiply_i($$.c);
3214 $$.c = abc_multiply($$.c);
3219 E : E "in" E {$$.c = code_append($1.c,$3.c);
3220 $$.c = abc_in($$.c);
3221 $$.t = TYPE_BOOLEAN;
3224 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3225 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3226 MULTINAME(m, (classinfo_t*)($3.t->data));
3227 $$.c = abc_astype2($1.c, &m);
3230 $$.c = code_append($1.c, $3.c);
3231 $$.c = abc_astypelate($$.c);
3236 E : E "instanceof" E
3237 {$$.c = code_append($1.c, $3.c);
3238 $$.c = abc_instanceof($$.c);
3239 $$.t = TYPE_BOOLEAN;
3242 E : E "is" E {$$.c = code_append($1.c, $3.c);
3243 $$.c = abc_istypelate($$.c);
3244 $$.t = TYPE_BOOLEAN;
3247 E : "typeof" '(' E ')' {
3249 $$.c = abc_typeof($$.c);
3254 $$.c = cut_last_push($2.c);
3255 $$.c = abc_pushundefined($$.c);
3259 E : "void" { $$.c = abc_pushundefined(0);
3263 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3268 $$.c=abc_negate_i($$.c);
3271 $$.c=abc_negate($$.c);
3278 $$.c = code_append($$.c, $3.c);
3280 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3281 $$.c = abc_getproperty2($$.c, &m);
3282 $$.t = 0; // array elements have unknown type
3285 E : '[' MAYBE_EXPRESSION_LIST ']' {
3287 $$.c = code_append($$.c, $2.cc);
3288 $$.c = abc_newarray($$.c, $2.len);
3289 $$.t = registry_getarrayclass();
3292 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
3293 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3295 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3297 $$.cc = code_append($$.cc, $1.c);
3298 $$.cc = code_append($$.cc, $3.c);
3301 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3304 $$.cc = code_append($$.cc, $3.c);
3305 $$.cc = code_append($$.cc, $5.c);
3310 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3312 $$.c = code_append($$.c, $2.cc);
3313 $$.c = abc_newobject($$.c, $2.len/2);
3314 $$.t = registry_getobjectclass();
3319 if(BOTH_INT($1.t,$3.t)) {
3320 c=abc_multiply_i(c);
3324 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3325 $$.c = toreadwrite($1.c, c, 0, 0);
3330 code_t*c = abc_modulo($3.c);
3331 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3332 $$.c = toreadwrite($1.c, c, 0, 0);
3336 code_t*c = abc_lshift($3.c);
3337 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3338 $$.c = toreadwrite($1.c, c, 0, 0);
3342 code_t*c = abc_rshift($3.c);
3343 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3344 $$.c = toreadwrite($1.c, c, 0, 0);
3348 code_t*c = abc_urshift($3.c);
3349 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3350 $$.c = toreadwrite($1.c, c, 0, 0);
3354 code_t*c = abc_divide($3.c);
3355 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3356 $$.c = toreadwrite($1.c, c, 0, 0);
3360 code_t*c = abc_bitor($3.c);
3361 c=converttype(c, TYPE_INT, $1.t);
3362 $$.c = toreadwrite($1.c, c, 0, 0);
3366 code_t*c = abc_bitxor($3.c);
3367 c=converttype(c, TYPE_INT, $1.t);
3368 $$.c = toreadwrite($1.c, c, 0, 0);
3374 if(TYPE_IS_INT($1.t)) {
3378 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3381 $$.c = toreadwrite($1.c, c, 0, 0);
3384 E : E "-=" E { code_t*c = $3.c;
3385 if(TYPE_IS_INT($1.t)) {
3386 c=abc_subtract_i(c);
3389 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3392 $$.c = toreadwrite($1.c, c, 0, 0);
3395 E : E '=' E { code_t*c = 0;
3396 c = code_append(c, $3.c);
3397 c = converttype(c, $3.t, $1.t);
3398 $$.c = toreadwrite($1.c, c, 1, 0);
3402 E : E '?' E ':' E %prec below_assignment {
3403 $$.t = join_types($3.t,$5.t,'?');
3405 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3406 $$.c = code_append($$.c, $3.c);
3407 $$.c = converttype($$.c, $3.t, $$.t);
3408 code_t*j2 = $$.c = abc_jump($$.c, 0);
3409 $$.c = j1->branch = abc_label($$.c);
3410 $$.c = code_append($$.c, $5.c);
3411 $$.c = converttype($$.c, $5.t, $$.t);
3412 $$.c = j2->branch = abc_label($$.c);
3415 E : E "++" { code_t*c = 0;
3416 classinfo_t*type = $1.t;
3417 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3418 int nr = getlocalnr($1.c);
3419 code_free($1.c);$1.c=0;
3420 if(TYPE_IS_INT($1.t)) {
3421 $$.c = abc_getlocal(0, nr);
3422 $$.c = abc_inclocal_i($$.c, nr);
3423 } else if(TYPE_IS_NUMBER($1.t)) {
3424 $$.c = abc_getlocal(0, nr);
3425 $$.c = abc_inclocal($$.c, nr);
3426 } else syntaxerror("internal error");
3428 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3429 c=abc_increment_i(c);
3435 c=converttype(c, type, $1.t);
3436 $$.c = toreadwrite($1.c, c, 0, 1);
3441 // TODO: use inclocal, like with ++
3442 E : E "--" { code_t*c = 0;
3443 classinfo_t*type = $1.t;
3444 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3445 c=abc_decrement_i(c);
3451 c=converttype(c, type, $1.t);
3452 $$.c = toreadwrite($1.c, c, 0, 1);
3456 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3457 classinfo_t*type = $2.t;
3458 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3459 c=abc_increment_i(c);
3465 c=converttype(c, type, $2.t);
3466 $$.c = toreadwrite($2.c, c, 0, 0);
3470 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3471 classinfo_t*type = $2.t;
3472 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3473 c=abc_decrement_i(c);
3479 c=converttype(c, type, $2.t);
3480 $$.c = toreadwrite($2.c, c, 0, 0);
3484 E : "super" '.' T_IDENTIFIER
3485 { if(!state->cls->info)
3486 syntaxerror("super keyword not allowed outside a class");
3487 classinfo_t*t = state->cls->info->superclass;
3488 if(!t) t = TYPE_OBJECT;
3490 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3492 MEMBER_MULTINAME(m, f, $3);
3494 $$.c = abc_getlocal_0($$.c);
3495 $$.c = abc_getsuper2($$.c, &m);
3496 $$.t = slotinfo_gettype((slotinfo_t*)f);
3499 E : '@' T_IDENTIFIER {
3501 $$.c = abc_pushundefined(0);
3503 as3_warning("ignored @ operator");
3506 E : E '.' '@' T_IDENTIFIER {
3507 // child attribute TODO
3508 $$.c = abc_pushundefined(0);
3510 as3_warning("ignored .@ operator");
3513 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3514 // namespace declaration TODO
3515 $$.c = abc_pushundefined(0);
3517 as3_warning("ignored :: operator");
3520 E : E ".." T_IDENTIFIER {
3522 $$.c = abc_pushundefined(0);
3524 as3_warning("ignored .. operator");
3527 E : E '.' '(' E ')' {
3529 $$.c = abc_pushundefined(0);
3531 as3_warning("ignored .() operator");
3534 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3538 E : E '.' T_IDENTIFIER {
3540 classinfo_t*t = $1.t;
3542 if(TYPE_IS_CLASS(t) && t->data) {
3547 if(t->subtype==0xff) {
3548 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3550 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3552 if(f && !is_static != !(f->flags&FLAG_STATIC))
3554 if(f && f->slot && !noslot) {
3555 $$.c = abc_getslot($$.c, f->slot);
3557 MEMBER_MULTINAME(m, f, $3);
3558 $$.c = abc_getproperty2($$.c, &m);
3560 /* determine type */
3561 $$.t = slotinfo_gettype((slotinfo_t*)f);
3563 $$.c = abc_coerce_a($$.c);
3564 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3565 string_t*package = $1.c->data[0];
3566 char*package2 = concat3(package->str, ".", $3);
3567 if(dict_contains(state->import_toplevel_packages, package2)) {
3569 $$.c->data[0] = string_new4(package2);
3572 slotinfo_t*a = registry_find(package->str, $3);
3574 syntaxerror("couldn't resolve %s", package2);
3578 /* when resolving a property on an unknown type, we do know the
3579 name of the property (and don't seem to need the package), but
3580 we need to make avm2 try out all access modes */
3581 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3582 $$.c = abc_getproperty2($$.c, &m);
3583 $$.c = abc_coerce_a($$.c);
3584 $$.t = registry_getanytype();
3588 VAR_READ : T_IDENTIFIER {
3590 /* Queue unresolved identifiers for checking against the parent
3591 function's variables.
3592 We consider everything which is not a local variable "unresolved".
3593 This encompasses class names, members of the surrounding class
3594 etc. which is *correct* because local variables of the parent function
3597 if(state->method->inner && !find_variable(state, $1)) {
3598 unknown_variable($1);
3601 /* let the compiler know that it might check the current directory/package
3602 for this identifier- maybe there's a file $1.as defining $1. */
3603 as3_schedule_class_noerror(state->package, $1);
3612 /* look at variables */
3613 if((v = find_variable(state, $1))) {
3614 // $1 is a local variable
3615 $$.c = abc_getlocal($$.c, v->index);
3619 if((v = find_slot(state, $1))) {
3620 $$.c = abc_getscopeobject($$.c, 1);
3621 $$.c = abc_getslot($$.c, v->index);
3626 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3628 /* look at current class' members */
3629 if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) &&
3630 (f->flags&FLAG_STATIC) >= i_am_static) {
3631 // $1 is a function in this class
3632 int var_is_static = (f->flags&FLAG_STATIC);
3634 if(f->kind == INFOTYPE_METHOD) {
3635 $$.t = TYPE_FUNCTION(f);
3639 if(var_is_static && !i_am_static) {
3640 /* access to a static member from a non-static location.
3641 do this via findpropstrict:
3642 there doesn't seem to be any non-lookup way to access
3643 static properties of a class */
3644 state->method->late_binding = 1;
3646 namespace_t ns = {f->access, ""};
3647 multiname_t m = {QNAME, &ns, 0, $1};
3648 $$.c = abc_findpropstrict2($$.c, &m);
3649 $$.c = abc_getproperty2($$.c, &m);
3651 } else if(f->slot>0) {
3652 $$.c = abc_getlocal_0($$.c);
3653 $$.c = abc_getslot($$.c, f->slot);
3656 namespace_t ns = {f->access, ""};
3657 multiname_t m = {QNAME, &ns, 0, $1};
3658 $$.c = abc_getlocal_0($$.c);
3659 $$.c = abc_getproperty2($$.c, &m);
3664 /* look at actual classes, in the current package and imported */
3665 if((a = find_class($1))) {
3670 /* look through package prefixes */
3671 if(dict_contains(state->import_toplevel_packages, $1)) {
3672 $$.c = abc___pushpackage__($$.c, $1);
3677 /* unknown object, let the avm2 resolve it */
3679 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3680 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3681 state->method->late_binding = 1;
3683 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3686 $$.c = abc_findpropstrict2($$.c, &m);
3687 $$.c = abc_getproperty2($$.c, &m);
3691 // ----------------- namespaces -------------------------------------------------
3693 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3695 tokenizer_register_namespace($2);
3699 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3702 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID '=' T_IDENTIFIER {
3705 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID '=' T_STRING {
3708 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3710 tokenizer_register_namespace($3->name);