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;
68 namespace_decl_t* namespace_decl;
70 abc_exception_list_t *l;
76 %token<id> T_IDENTIFIER T_NAMESPACE
78 %token<regexp> T_REGEXP
80 %token<number_int> T_INT
81 %token<number_uint> T_UINT
82 %token<number_uint> T_BYTE
83 %token<number_uint> T_SHORT
84 %token<number_float> T_FLOAT
86 %token<id> T_FOR "for"
87 %token<id> T_WHILE "while"
89 %token<id> T_SWITCH "switch"
91 %token<token> KW_IMPLEMENTS "implements"
92 %token<token> KW_NAMESPACE "namespace"
93 %token<token> KW_PACKAGE "package"
94 %token<token> KW_PROTECTED "protected"
95 %token<token> KW_PUBLIC "public"
96 %token<token> KW_PRIVATE "private"
97 %token<token> KW_USE "use"
98 %token<token> KW_INTERNAL "internal"
99 %token<token> KW_NEW "new"
100 %token<token> KW_NATIVE "native"
101 %token<token> KW_FUNCTION "function"
102 %token<token> KW_FINALLY "finally"
103 %token<token> KW_UNDEFINED "undefined"
104 %token<token> KW_CONTINUE "continue"
105 %token<token> KW_CLASS "class"
106 %token<token> KW_CONST "const"
107 %token<token> KW_CATCH "catch"
108 %token<token> KW_CASE "case"
109 %token<token> KW_SET "set"
110 %token<token> KW_VOID "void"
111 %token<token> KW_THROW "throw"
112 %token<token> KW_STATIC "static"
113 %token<token> KW_WITH "with"
114 %token<token> KW_INSTANCEOF "instanceof"
115 %token<token> KW_IMPORT "import"
116 %token<token> KW_RETURN "return"
117 %token<token> KW_TYPEOF "typeof"
118 %token<token> KW_INTERFACE "interface"
119 %token<token> KW_NULL "null"
120 %token<token> KW_VAR "var"
121 %token<token> KW_DYNAMIC "dynamic"
122 %token<token> KW_OVERRIDE "override"
123 %token<token> KW_FINAL "final"
124 %token<token> KW_EACH "each"
125 %token<token> KW_GET "get"
126 %token<token> KW_TRY "try"
127 %token<token> KW_SUPER "super"
128 %token<token> KW_EXTENDS "extends"
129 %token<token> KW_FALSE "false"
130 %token<token> KW_TRUE "true"
131 %token<token> KW_BOOLEAN "Boolean"
132 %token<token> KW_UINT "uint"
133 %token<token> KW_INT "int"
134 %token<token> KW_NUMBER "Number"
135 %token<token> KW_STRING "String"
136 %token<token> KW_DEFAULT "default"
137 %token<token> KW_DELETE "delete"
138 %token<token> KW_IF "if"
139 %token<token> KW_ELSE "else"
140 %token<token> KW_BREAK "break"
141 %token<token> KW_IS "is"
142 %token<token> KW_IN "in"
143 %token<token> KW_AS "as"
145 %token<token> T_DICTSTART "{ (dictionary)"
146 %token<token> T_EQEQ "=="
147 %token<token> T_EQEQEQ "==="
148 %token<token> T_NE "!="
149 %token<token> T_NEE "!=="
150 %token<token> T_LE "<="
151 %token<token> T_GE ">="
152 %token<token> T_ORBY "|="
153 %token<token> T_DIVBY "/="
154 %token<token> T_MODBY "%="
155 %token<token> T_MULBY "*="
156 %token<token> T_PLUSBY "+="
157 %token<token> T_MINUSBY "-="
158 %token<token> T_XORBY "^="
159 %token<token> T_SHRBY ">>="
160 %token<token> T_SHLBY "<<="
161 %token<token> T_USHRBY ">>>="
162 %token<token> T_OROR "||"
163 %token<token> T_ANDAND "&&"
164 %token<token> T_COLONCOLON "::"
165 %token<token> T_MINUSMINUS "--"
166 %token<token> T_PLUSPLUS "++"
167 %token<token> T_DOTDOT ".."
168 %token<token> T_DOTDOTDOT "..."
169 %token<token> T_SHL "<<"
170 %token<token> T_USHR ">>>"
171 %token<token> T_SHR ">>"
173 %type <for_start> FOR_START
174 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
175 %type <namespace_decl> NAMESPACE_ID
176 %type <token> VARCONST
178 %type <code> CODEPIECE CODE_STATEMENT
179 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
180 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
181 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
182 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
183 %type <exception> CATCH FINALLY
184 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
185 %type <code> CLASS_DECLARATION
186 %type <code> NAMESPACE_DECLARATION
187 %type <code> INTERFACE_DECLARATION
188 %type <code> VOIDEXPRESSION
189 %type <value> EXPRESSION NONCOMMAEXPRESSION
190 %type <value> MAYBEEXPRESSION
191 %type <value> E DELETE
192 %type <value> CONSTANT
193 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
194 %type <value> INNERFUNCTION
195 %type <code> USE_NAMESPACE
196 %type <code> FOR_INIT
198 %type <classinfo> MAYBETYPE
201 %type <params> PARAM_LIST
202 %type <params> MAYBE_PARAM_LIST
203 %type <flags> MAYBE_MODIFIERS
204 %type <flags> MODIFIER_LIST
205 %type <flags> MODIFIER
206 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
207 %type <classinfo_list> IMPLEMENTS_LIST
208 %type <classinfo> EXTENDS CLASS_SPEC
209 %type <classinfo_list> EXTENDS_LIST
211 %type <classinfo> CLASS PACKAGEANDCLASS
212 %type <classinfo_list> CLASS_SPEC_LIST
214 %type <classinfo> TYPE
215 //%type <token> VARIABLE
216 %type <value> VAR_READ
218 //%type <token> T_IDENTIFIER
219 %type <value> FUNCTIONCALL
220 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD
222 // precedence: from low to high
226 %left below_semicolon
229 %nonassoc below_assignment // for ?:, contrary to spec
230 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
237 %nonassoc "==" "!=" "===" "!=="
238 %nonassoc "is" "as" "in"
239 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
240 %left "<<" ">>" ">>>"
244 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
246 %nonassoc below_curly
250 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
253 %left above_identifier
257 // needed for "return" precedence:
258 %nonassoc T_STRING T_REGEXP
259 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
260 %nonassoc "false" "true" "null" "undefined" "super" "function"
267 static int a3_error(char*s)
269 syntaxerror("%s", s);
270 return 0; //make gcc happy
274 static char* concat2(const char* t1, const char* t2)
278 char*text = malloc(l1+l2+1);
279 memcpy(text , t1, l1);
280 memcpy(text+l1, t2, l2);
284 static char* concat3(const char* t1, const char* t2, const char* t3)
289 char*text = malloc(l1+l2+l3+1);
290 memcpy(text , t1, l1);
291 memcpy(text+l1, t2, l2);
292 memcpy(text+l1+l2, t3, l3);
297 typedef struct _import {
301 DECLARE_LIST(import);
303 DECLARE(methodstate);
304 DECLARE_LIST(methodstate);
306 typedef struct _classstate {
312 methodstate_t*static_init;
314 //code_t*static_init;
316 char has_constructor;
319 struct _methodstate {
329 dict_t*unresolved_variables;
332 char uses_parent_function;
338 int var_index; // for inner methods
339 int slot_index; // for inner methods
340 char is_a_slot; // for inner methods
345 abc_exception_list_t*exceptions;
347 methodstate_list_t*innerfunctions;
350 typedef struct _state {
355 import_list_t*wildcard_imports;
356 dict_t*import_toplevel_packages;
359 namespace_list_t*active_namespace_urls;
361 char has_own_imports;
362 char new_vars; // e.g. transition between two functions
365 methodstate_t*method;
374 typedef struct _global {
378 dict_t*file2token2info;
381 static global_t*global = 0;
382 static state_t* state = 0;
386 #define MULTINAME(m,x) \
390 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
392 #define MEMBER_MULTINAME(m,f,n) \
396 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
397 m##_ns.name = ((slotinfo_t*)(f))->package; \
402 m.namespace_set = 0; \
403 m.name = ((slotinfo_t*)(f))->name; \
405 m.type = MULTINAME; \
407 m.namespace_set = &nopackage_namespace_set; \
411 /* warning: list length of namespace set is undefined */
412 #define MULTINAME_LATE(m, access, package) \
413 namespace_t m##_ns = {access, package}; \
414 namespace_set_t m##_nsset; \
415 namespace_list_t m##_l;m##_l.next = 0; \
416 m##_nsset.namespaces = &m##_l; \
417 m##_nsset = m##_nsset; \
418 m##_l.namespace = &m##_ns; \
419 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
421 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
422 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
423 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
424 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
425 static namespace_list_t nl4 = {&ns4,0};
426 static namespace_list_t nl3 = {&ns3,&nl4};
427 static namespace_list_t nl2 = {&ns2,&nl3};
428 static namespace_list_t nl1 = {&ns1,&nl2};
429 static namespace_set_t nopackage_namespace_set = {&nl1};
431 static void new_state()
434 state_t*oldstate = state;
436 memcpy(s, state, sizeof(state_t)); //shallow copy
438 s->imports = dict_new();
440 if(!s->import_toplevel_packages) {
441 s->import_toplevel_packages = dict_new();
445 state->has_own_imports = 0;
446 state->vars = dict_new();
447 state->old = oldstate;
450 trie_remember(active_namespaces);
453 state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
455 static void state_has_imports()
457 state->wildcard_imports = list_clone(state->wildcard_imports);
458 state->imports = dict_clone(state->imports);
459 state->has_own_imports = 1;
461 static void import_toplevel(const char*package)
463 char* s = strdup(package);
465 dict_put(state->import_toplevel_packages, s, 0);
466 char*x = strrchr(s, '.');
474 static void state_destroy(state_t*state)
476 if(state->has_own_imports) {
477 list_free(state->wildcard_imports);
478 dict_destroy(state->imports);state->imports=0;
480 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
481 dict_destroy(state->imports);state->imports=0;
485 for(t=0;t<state->vars->hashsize;t++) {
486 dictentry_t*e =state->vars->slots[t];
488 free(e->data);e->data=0;
492 dict_destroy(state->vars);state->vars=0;
495 list_free(state->active_namespace_urls)
496 state->active_namespace_urls = 0;
501 static void old_state()
503 trie_rollback(active_namespaces);
505 if(!state || !state->old)
506 syntaxerror("invalid nesting");
507 state_t*leaving = state;
511 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
512 free(leaving->method);
515 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
520 state_destroy(leaving);
523 static code_t* method_header(methodstate_t*m);
524 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
525 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
528 static char* internal_filename_package = 0;
529 void initialize_file(char*filename)
532 syntaxerror("invalid call to initialize_file during parsing of another file");
535 active_namespaces = trie_new();
538 state->package = internal_filename_package = strdup(filename);
540 global->token2info = dict_lookup(global->file2token2info,
541 current_filename // use long version
543 if(!global->token2info) {
544 global->token2info = dict_new2(&ptr_type);
545 dict_put(global->file2token2info, current_filename, global->token2info);
549 state->method = rfx_calloc(sizeof(methodstate_t));
550 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
551 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
553 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
554 function_initvars(state->method, 0, 0, 1);
555 global->init = abc_initscript(global->file);
561 if(!state || state->level!=1) {
562 syntaxerror("unexpected end of file in pass %d", as3_pass);
566 code_t*header = method_header(state->method);
567 code_t*c = wrap_function(header, 0, global->init->method->body->code);
568 global->init->method->body->code = c;
569 free(state->method);state->method=0;
572 //free(state->package);state->package=0; // used in registry
573 state_destroy(state);state=0;
576 void initialize_parser()
578 global = rfx_calloc(sizeof(global_t));
579 global->file = abc_file_new();
580 global->file->flags &= ~ABCFILE_LAZY;
581 global->file2token2info = dict_new();
582 global->token2info = 0;
585 void* finish_parser()
587 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
589 global->token2info=0;
595 static void xx_scopetest()
597 /* findpropstrict doesn't just return a scope object- it
598 also makes it "active" somehow. Push local_0 on the
599 scope stack and read it back with findpropstrict, it'll
600 contain properties like "trace". Trying to find the same
601 property on a "vanilla" local_0 yields only a "undefined" */
602 //c = abc_findpropstrict(c, "[package]::trace");
604 /*c = abc_getlocal_0(c);
605 c = abc_findpropstrict(c, "[package]::trace");
607 c = abc_setlocal_1(c);
609 c = abc_pushbyte(c, 0);
610 c = abc_setlocal_2(c);
612 code_t*xx = c = abc_label(c);
613 c = abc_findpropstrict(c, "[package]::trace");
614 c = abc_pushstring(c, "prop:");
615 c = abc_hasnext2(c, 1, 2);
617 c = abc_setlocal_3(c);
618 c = abc_callpropvoid(c, "[package]::trace", 2);
619 c = abc_getlocal_3(c);
621 c = abc_iftrue(c,xx);*/
624 typedef struct _variable {
629 methodstate_t*is_inner_method;
632 static variable_t* find_variable(state_t*s, char*name)
636 v = dict_lookup(s->vars, name);
638 if(s->new_vars) break;
643 static variable_t* find_slot(state_t*s, const char*name)
645 if(s->method && s->method->slots)
646 return dict_lookup(s->method->slots, name);
650 static variable_t* find_variable_safe(state_t*s, char*name)
652 variable_t* v = find_variable(s, name);
654 syntaxerror("undefined variable: %s", name);
657 static char variable_exists(char*name)
659 return dict_contains(state->vars, name);
661 code_t*defaultvalue(code_t*c, classinfo_t*type);
663 static int alloc_local()
665 return state->method->variable_count++;
668 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
671 variable_t*v = find_slot(state, name);
677 v->index = alloc_local();
682 dict_put(state->vars, name, v);
686 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
688 return new_variable2(name, type, init, maybeslot)->index;
691 #define TEMPVARNAME "__as3_temp__"
692 static int gettempvar()
694 variable_t*v = find_variable(state, TEMPVARNAME);
697 return new_variable(TEMPVARNAME, 0, 0, 0);
700 code_t* var_block(code_t*body)
706 for(t=0;t<state->vars->hashsize;t++) {
707 dictentry_t*e = state->vars->slots[t];
709 variable_t*v = (variable_t*)e->data;
710 if(v->type && v->init) {
711 c = defaultvalue(c, v->type);
712 c = abc_setlocal(c, v->index);
713 k = abc_kill(k, v->index);
723 if(x->opcode== OPCODE___BREAK__ ||
724 x->opcode== OPCODE___CONTINUE__) {
725 /* link kill code before break/continue */
726 code_t*e = code_dup(k);
727 code_t*s = code_start(e);
739 c = code_append(c, body);
740 c = code_append(c, k);
744 void unknown_variable(char*name)
746 if(!state->method->unresolved_variables)
747 state->method->unresolved_variables = dict_new();
748 if(!dict_contains(state->method->unresolved_variables, name))
749 dict_put(state->method->unresolved_variables, name, 0);
752 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
754 static void parsererror(const char*file, int line, const char*f)
756 syntaxerror("internal error in %s, %s:%d", f, file, line);
760 static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
762 if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
763 c = abc_getlocal_0(c);
764 c = abc_pushscope(c);
767 /* FIXME: this alloc_local() causes variable indexes to be
768 different in pass2 than in pass1 */
769 if(!m->activation_var)
770 m->activation_var = alloc_local();
772 c = abc_newactivation(c);
774 c = abc_pushscope(c);
775 c = abc_setlocal(c, m->activation_var);
777 c = abc_getlocal(c, m->activation_var);
778 c = abc_pushscope(c);
784 static code_t* method_header(methodstate_t*m)
788 c = add_scope_code(c, m, 1);
790 methodstate_list_t*l = m->innerfunctions;
792 parserassert(l->methodstate->abc);
793 if(m->uses_slots && l->methodstate->is_a_slot) {
794 c = abc_getscopeobject(c, 1);
795 c = abc_newfunction(c, l->methodstate->abc);
797 c = abc_setlocal(c, l->methodstate->var_index);
798 c = abc_setslot(c, l->methodstate->slot_index);
800 c = abc_newfunction(c, l->methodstate->abc);
801 c = abc_setlocal(c, l->methodstate->var_index);
803 free(l->methodstate);l->methodstate=0;
807 c = code_append(c, m->header);
810 if(m->is_constructor && !m->has_super) {
811 // call default constructor
812 c = abc_getlocal_0(c);
813 c = abc_constructsuper(c, 0);
817 /* all parameters that are used by inner functions
818 need to be copied from local to slot */
819 parserassert(m->activation_var);
820 DICT_ITERATE_ITEMS(m->slots,char*,name,variable_t*,v) {
821 if(v->is_parameter) {
822 c = abc_getlocal(c, m->activation_var);
823 c = abc_getlocal(c, v->index);
824 c = abc_setslot(c, v->index);
828 list_free(m->innerfunctions);
829 m->innerfunctions = 0;
834 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
836 c = code_append(c, header);
837 c = code_append(c, var_block(body));
838 /* append return if necessary */
839 if(!c || (c->opcode != OPCODE_RETURNVOID &&
840 c->opcode != OPCODE_RETURNVALUE)) {
841 c = abc_returnvoid(c);
847 static void startpackage(char*name)
850 /*printf("entering package \"%s\"\n", name);*/
851 state->package = strdup(name);
853 static void endpackage()
855 /*printf("leaving package \"%s\"\n", state->package);*/
857 //used e.g. in classinfo_register:
858 //free(state->package);state->package=0;
863 #define FLAG_PUBLIC 256
864 #define FLAG_PROTECTED 512
865 #define FLAG_PRIVATE 1024
866 #define FLAG_PACKAGEINTERNAL 2048
867 #define FLAG_NAMESPACE 4096
869 static namespace_t modifiers2access(modifiers_t*mod)
874 if(mod->flags&FLAG_NAMESPACE) {
875 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
876 syntaxerror("invalid combination of access levels and namespaces");
877 ns.access = ACCESS_NAMESPACE;
879 const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
881 /* shouldn't happen- the tokenizer only reports something as a namespace
882 if it was already registered */
883 trie_dump(active_namespaces);
884 syntaxerror("unknown namespace: %s", mod->ns);
887 } else if(mod->flags&FLAG_PUBLIC) {
888 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
889 syntaxerror("invalid combination of access levels");
890 ns.access = ACCESS_PACKAGE;
891 } else if(mod->flags&FLAG_PRIVATE) {
892 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
893 syntaxerror("invalid combination of access levels");
894 ns.access = ACCESS_PRIVATE;
895 } else if(mod->flags&FLAG_PROTECTED) {
896 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
897 syntaxerror("invalid combination of access levels");
898 ns.access = ACCESS_PROTECTED;
900 ns.access = ACCESS_PACKAGEINTERNAL;
904 static slotinfo_t* find_class(const char*name);
906 static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
908 return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
911 void add_active_url(const char*url)
915 list_append(state->active_namespace_urls, n);
918 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
923 index = new_variable("this", 0, 0, 0);
924 else if(!m->is_global)
925 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
927 index = new_variable("globalscope", 0, 0, 0);
930 parserassert(!index);
934 /* as variables and slots share the same number, make sure
935 that those variable indices are reserved. It's up to the
936 optimizer to later shuffle the variables down to lower
938 m->variable_count = m->uses_slots;
943 for(p=params->list;p;p=p->next) {
944 variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1);
949 dict_dump(m->slots, stdout, "");
952 methodstate_list_t*l = m->innerfunctions;
954 methodstate_t*m = l->methodstate;
956 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
957 m->var_index = v->index;
958 m->slot_index = v->index;
959 v->is_inner_method = m;
965 m->scope_code = add_scope_code(m->scope_code, m, 0);
968 if(as3_pass==2 && m->slots) {
969 /* exchange unresolved identifiers with the actual objects */
970 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
971 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
972 classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
973 if(!type || type->kind != INFOTYPE_CLASS) {
974 syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name);
983 char*as3_globalclass=0;
984 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
987 syntaxerror("inner classes now allowed");
992 classinfo_list_t*mlist=0;
994 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
995 syntaxerror("invalid modifier(s)");
997 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
998 syntaxerror("public and internal not supported at the same time.");
1000 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
1001 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
1002 // all classes extend object
1003 extends = registry_getobjectclass();
1006 /* create the class name, together with the proper attributes */
1010 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
1011 access = ACCESS_PRIVATE; package = internal_filename_package;
1012 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
1013 access = ACCESS_PACKAGEINTERNAL; package = state->package;
1014 } else if(state->package!=internal_filename_package) {
1015 access = ACCESS_PACKAGE; package = state->package;
1017 syntaxerror("public classes only allowed inside a package");
1021 state->cls = rfx_calloc(sizeof(classstate_t));
1022 state->cls->init = rfx_calloc(sizeof(methodstate_t));
1023 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
1024 /* notice: we make no effort to initialize the top variable (local0) here,
1025 even though it has special meaning. We just rely on the facat
1026 that pass 1 won't do anything with variables */
1028 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
1030 /* set current method to constructor- all code within the class-level (except
1031 static variable initializations) will be executed during construction time */
1032 state->method = state->cls->init;
1034 if(registry_find(package, classname)) {
1035 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
1037 /* build info struct */
1038 int num_interfaces = (list_length(implements));
1039 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
1040 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
1043 classinfo_list_t*l = implements;
1044 for(l=implements;l;l=l->next) {
1045 state->cls->info->interfaces[pos++] = l->classinfo;
1050 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1052 state->method = state->cls->init;
1053 parserassert(state->cls && state->cls->info);
1055 function_initvars(state->cls->init, 0, 0, 1);
1056 function_initvars(state->cls->static_init, 0, 0, 0);
1058 if(extends && (extends->flags & FLAG_FINAL))
1059 syntaxerror("Can't extend final class '%s'", extends->name);
1062 while(state->cls->info->interfaces[pos]) {
1063 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1064 syntaxerror("'%s' is not an interface",
1065 state->cls->info->interfaces[pos]->name);
1069 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
1070 state->cls->info->superclass = extends;
1072 /* generate the abc code for this class */
1073 MULTINAME(classname2,state->cls->info);
1074 multiname_t*extends2 = sig2mname(extends);
1076 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1077 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1078 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1079 if(state->cls->info->flags&FLAG_INTERFACE) {
1080 abc_class_interface(state->cls->abc);
1083 abc_class_protectedNS(state->cls->abc, classname);
1085 for(mlist=implements;mlist;mlist=mlist->next) {
1086 MULTINAME(m, mlist->classinfo);
1087 abc_class_add_interface(state->cls->abc, &m);
1090 /* write the construction code for this class to the global init
1092 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1094 abc_method_body_t*m = global->init->method->body;
1095 __ getglobalscope(m);
1096 classinfo_t*s = extends;
1101 //TODO: take a look at the current scope stack, maybe
1102 // we can re-use something
1107 multiname_t*s2 = sig2mname(s);
1109 multiname_destroy(s2);
1111 __ pushscope(m); count++;
1112 m->code = m->code->prev->prev; // invert
1114 /* continue appending after last op end */
1115 while(m->code && m->code->next) m->code = m->code->next;
1117 /* TODO: if this is one of *our* classes, we can also
1118 do a getglobalscope/getslot <nr> (which references
1119 the init function's slots) */
1121 __ getlex2(m, extends2);
1123 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1124 stack is not the superclass */
1125 __ pushscope(m);count++;
1128 /* notice: we get a verify error #1107 if the top element on the scope
1129 stack is not the global object */
1131 __ pushscope(m);count++;
1133 __ newclass(m,state->cls->abc);
1137 __ setslot(m, slotindex);
1138 multiname_destroy(extends2);
1140 /* flash.display.MovieClip handling */
1142 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1143 if(state->package && state->package[0]) {
1144 as3_globalclass = concat3(state->package, ".", classname);
1146 as3_globalclass = strdup(classname);
1152 static int slotstate_varconst = 0;
1153 static modifiers_t*slotstate_flags = 0;
1154 static void setslotstate(modifiers_t* flags, int varconst)
1156 slotstate_varconst = varconst;
1157 slotstate_flags = flags;
1159 if(flags && flags->flags&FLAG_STATIC) {
1160 state->method = state->cls->static_init;
1162 state->method = state->cls->init;
1165 parserassert(state->method);
1169 static void endclass()
1172 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1174 c = abc_getlocal_0(c);
1175 c = abc_constructsuper(c, 0);
1176 state->cls->init->header = code_append(state->cls->init->header, c);
1177 state->cls->has_constructor=1;
1179 if(state->cls->init) {
1180 if(state->cls->info->flags&FLAG_INTERFACE) {
1181 if(state->cls->init->header)
1182 syntaxerror("interface can not have class-level code");
1184 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1185 code_t*c = method_header(state->cls->init);
1186 m->body->code = wrap_function(c, 0, m->body->code);
1189 if(state->cls->static_init) {
1190 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1191 code_t*c = method_header(state->cls->static_init);
1192 m->body->code = wrap_function(c, 0, m->body->code);
1199 void check_code_for_break(code_t*c)
1202 if(c->opcode == OPCODE___BREAK__) {
1203 char*name = string_cstr(c->data[0]);
1204 syntaxerror("Unresolved \"break %s\"", name);
1206 if(c->opcode == OPCODE___CONTINUE__) {
1207 char*name = string_cstr(c->data[0]);
1208 syntaxerror("Unresolved \"continue %s\"", name);
1210 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1211 char*name = string_cstr(c->data[0]);
1212 syntaxerror("Can't reference a package (%s) as such", name);
1219 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1222 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1223 if(TYPE_IS_NUMBER(t)) {
1224 xassert(c->type == CONSTANT_FLOAT
1225 || c->type == CONSTANT_INT
1226 || c->type == CONSTANT_UINT);
1227 } else if(TYPE_IS_UINT(t)) {
1228 xassert(c->type == CONSTANT_UINT ||
1229 (c->type == CONSTANT_INT && c->i>=0));
1230 } else if(TYPE_IS_INT(t)) {
1231 xassert(c->type == CONSTANT_INT);
1232 } else if(TYPE_IS_BOOLEAN(t)) {
1233 xassert(c->type == CONSTANT_TRUE
1234 || c->type == CONSTANT_FALSE);
1238 static void check_override(memberinfo_t*m, int flags)
1242 if(m->parent == state->cls->info)
1243 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1245 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1246 if(m->access==ACCESS_PRIVATE)
1248 if(m->flags & FLAG_FINAL)
1249 syntaxerror("can't override final member %s", m->name);
1251 /* allow this. it's no issue.
1252 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1253 syntaxerror("can't override static member %s", m->name);*/
1255 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1256 syntaxerror("can't override non-static member %s with static declaration", m->name);
1258 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1259 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1260 if(m->kind == INFOTYPE_METHOD)
1261 syntaxerror("can't override without explicit 'override' declaration");
1263 syntaxerror("can't override '%s'", m->name);
1268 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1270 methodinfo_t*minfo = 0;
1271 namespace_t ns = modifiers2access(mod);
1274 minfo = methodinfo_register_global(ns.access, state->package, name);
1275 minfo->return_type = 0; // save this for pass 2
1276 } else if(getset != KW_GET && getset != KW_SET) {
1278 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1280 printf("%s.%s | %s.%s\n",
1281 m->package, m->name,
1283 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1285 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1286 minfo->return_type = 0; // save this for pass 2
1287 // getslot on a member slot only returns "undefined", so no need
1288 // to actually store these
1289 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1291 //class getter/setter
1292 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1294 if(getset == KW_GET) {
1296 } else if(params->list && params->list->param && !params->list->next) {
1297 type = params->list->param->type;
1299 syntaxerror("setter function needs to take exactly one argument");
1300 // not sure wether to look into superclasses here, too
1301 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1303 if(minfo->kind!=INFOTYPE_SLOT)
1304 syntaxerror("class already contains a method called '%s'", name);
1305 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1306 syntaxerror("class already contains a field called '%s'", name);
1307 if(minfo->subtype & gs)
1308 syntaxerror("getter/setter for '%s' already defined", name);
1309 /* make a setter or getter into a getset */
1310 minfo->subtype |= gs;
1313 FIXME: this check needs to be done in pass 2
1315 if((!minfo->return_type != !type) ||
1316 (minfo->return_type && type &&
1317 !strcmp(minfo->return_type->name, type->name))) {
1318 syntaxerror("different type in getter and setter: %s and %s",
1319 minfo->return_type?minfo->return_type->name:"*",
1320 type?type->name:"*");
1323 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1324 minfo->kind = INFOTYPE_SLOT; //hack
1325 minfo->subtype = gs;
1326 minfo->return_type = 0;
1328 /* can't assign a slot as getter and setter might have different slots */
1329 //minfo->slot = slot;
1331 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1332 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1333 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1338 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1340 //parserassert(state->method && state->method->info);
1342 methodstate_t*parent_method = state->method;
1345 return_type = 0; // not valid in pass 1
1349 state->new_vars = 1;
1352 state->method = rfx_calloc(sizeof(methodstate_t));
1353 state->method->inner = 1;
1354 state->method->variable_count = 0;
1355 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1357 NEW(methodinfo_t,minfo);
1358 minfo->kind = INFOTYPE_METHOD;
1359 minfo->access = ACCESS_PACKAGEINTERNAL;
1361 state->method->info = minfo;
1364 list_append(parent_method->innerfunctions, state->method);
1366 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1368 function_initvars(state->method, params, 0, 1);
1372 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1373 state->method->variable_count = 0;
1374 parserassert(state->method);
1376 state->method->info->return_type = return_type;
1377 function_initvars(state->method, params, 0, 1);
1381 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1382 params_t*params, classinfo_t*return_type)
1384 if(state->method && state->method->info) {
1385 syntaxerror("not able to start another method scope");
1388 state->new_vars = 1;
1391 state->method = rfx_calloc(sizeof(methodstate_t));
1392 state->method->has_super = 0;
1395 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1397 state->method->is_global = 1;
1398 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1400 if(state->method->is_constructor)
1401 name = "__as3_constructor__";
1403 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1405 function_initvars(state->method, params, mod->flags, 1);
1407 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1411 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1412 state->method->variable_count = 0;
1413 parserassert(state->method);
1416 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1417 check_override(m, mod->flags);
1421 state->cls->has_constructor |= state->method->is_constructor;
1424 state->method->info->return_type = return_type;
1425 function_initvars(state->method, params, mod->flags, 1);
1429 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1430 params_t*params, classinfo_t*return_type, code_t*body)
1433 // store inner methods in variables
1434 function_initvars(state->method, 0, 0, 0);
1436 methodstate_list_t*ml = state->method->innerfunctions;
1438 dict_t*xvars = dict_new();
1441 methodstate_t*m = ml->methodstate;
1442 parserassert(m->inner);
1443 if(m->unresolved_variables) {
1444 dict_t*d = m->unresolved_variables;
1446 for(t=0;t<d->hashsize;t++) {
1447 dictentry_t*l = d->slots[t];
1449 /* check parent method's variables */
1451 if((v=find_variable(state, l->key))) {
1452 m->uses_parent_function = 1;
1453 state->method->uses_slots = 1;
1454 dict_put(xvars, l->key, 0);
1461 dict_destroy(m->unresolved_variables);
1462 m->unresolved_variables = 0;
1467 if(state->method->uses_slots) {
1468 state->method->slots = dict_new();
1470 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1471 if(!name) syntaxerror("internal error");
1472 if(v->index && dict_contains(xvars, name)) {
1475 if(v->is_inner_method) {
1476 v->is_inner_method->is_a_slot = 1;
1479 dict_put(state->method->slots, name, v);
1482 state->method->uses_slots = i;
1483 dict_destroy(state->vars);state->vars = 0;
1490 /*if(state->method->uses_parent_function){
1491 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1496 multiname_t*type2 = sig2mname(return_type);
1498 if(state->method->inner) {
1499 f = state->method->abc;
1500 abc_method_init(f, global->file, type2, 1);
1501 } else if(state->method->is_constructor) {
1502 f = abc_class_getconstructor(state->cls->abc, type2);
1503 } else if(!state->method->is_global) {
1504 namespace_t mname_ns = modifiers2access(mod);
1505 multiname_t mname = {QNAME, &mname_ns, 0, name};
1507 if(mod->flags&FLAG_STATIC)
1508 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1510 f = abc_class_method(state->cls->abc, type2, &mname);
1511 slot = f->trait->slot_id;
1513 namespace_t mname_ns = {state->method->info->access, state->package};
1514 multiname_t mname = {QNAME, &mname_ns, 0, name};
1516 f = abc_method_new(global->file, type2, 1);
1517 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1518 //abc_code_t*c = global->init->method->body->code;
1520 //flash doesn't seem to allow us to access function slots
1521 //state->method->info->slot = slot;
1523 if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1524 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1525 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1526 if(params->varargs) f->flags |= METHOD_NEED_REST;
1530 for(p=params->list;p;p=p->next) {
1531 if(params->varargs && !p->next) {
1532 break; //varargs: omit last parameter in function signature
1534 multiname_t*m = sig2mname(p->param->type);
1535 list_append(f->parameters, m);
1536 if(p->param->value) {
1537 check_constant_against_type(p->param->type, p->param->value);
1538 opt=1;list_append(f->optional_parameters, p->param->value);
1540 syntaxerror("non-optional parameter not allowed after optional parameters");
1543 if(state->method->slots) {
1544 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1546 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1547 multiname_t*type = sig2mname(v->type);
1548 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1549 t->slot_id = v->index;
1554 check_code_for_break(body);
1556 /* Seems this works now.
1557 if(state->method->exceptions && state->method->uses_slots) {
1558 as3_warning("try/catch and activation not supported yet within the same method");
1562 f->body->code = body;
1563 f->body->exceptions = state->method->exceptions;
1564 } else { //interface
1566 syntaxerror("interface methods can't have a method body");
1576 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1581 void breakjumpsto(code_t*c, char*name, code_t*jump)
1584 if(c->opcode == OPCODE___BREAK__) {
1585 string_t*name2 = c->data[0];
1586 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1587 c->opcode = OPCODE_JUMP;
1594 void continuejumpsto(code_t*c, char*name, code_t*jump)
1597 if(c->opcode == OPCODE___CONTINUE__) {
1598 string_t*name2 = c->data[0];
1599 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1600 c->opcode = OPCODE_JUMP;
1608 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1609 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1610 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1612 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1614 if(!type1 || !type2)
1615 return registry_getanytype();
1616 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1617 return registry_getanytype();
1620 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1629 return registry_getanytype();
1631 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1636 return abc_coerce_a(c);
1640 // cast an "any" type to a specific type. subject to
1641 // runtime exceptions
1642 return abc_coerce2(c, &m);
1645 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1646 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1647 // allow conversion between number types
1648 return abc_coerce2(c, &m);
1650 //printf("%s.%s\n", from.package, from.name);
1651 //printf("%s.%s\n", to.package, to.name);
1653 classinfo_t*supertype = from;
1655 if(supertype == to) {
1656 // target type is one of from's superclasses
1657 return abc_coerce2(c, &m);
1660 while(supertype->interfaces[t]) {
1661 if(supertype->interfaces[t]==to) {
1662 // target type is one of from's interfaces
1663 return abc_coerce2(c, &m);
1667 supertype = supertype->superclass;
1669 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1671 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1673 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1676 as3_error("can't convert type %s%s%s to %s%s%s",
1677 from->package, from->package?".":"", from->name,
1678 to->package, to->package?".":"", to->name);
1682 code_t*defaultvalue(code_t*c, classinfo_t*type)
1684 if(TYPE_IS_INT(type)) {
1685 c = abc_pushbyte(c, 0);
1686 } else if(TYPE_IS_UINT(type)) {
1687 c = abc_pushuint(c, 0);
1688 } else if(TYPE_IS_FLOAT(type)) {
1690 } else if(TYPE_IS_BOOLEAN(type)) {
1691 c = abc_pushfalse(c);
1693 //c = abc_pushundefined(c);
1695 c = abc_pushnull(c);
1697 c = abc_coerce2(c, &m);
1702 char is_pushundefined(code_t*c)
1704 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1707 static const char* get_package_from_name(const char*name)
1709 /* try explicit imports */
1710 dictentry_t* e = dict_get_slot(state->imports, name);
1712 if(!strcmp(e->key, name)) {
1713 slotinfo_t*c = (slotinfo_t*)e->data;
1714 if(c) return c->package;
1720 static namespace_list_t*get_current_imports()
1722 namespace_list_t*searchlist = 0;
1724 list_append(searchlist, namespace_new_package(state->package));
1726 import_list_t*l = state->wildcard_imports;
1728 namespace_t*ns = namespace_new_package(l->import->package);
1729 list_append(searchlist, ns);
1732 list_append(searchlist, namespace_new_package(""));
1733 list_append(searchlist, namespace_new_package(internal_filename_package));
1737 static slotinfo_t* find_class(const char*name)
1741 c = registry_find(state->package, name);
1744 /* try explicit imports */
1745 dictentry_t* e = dict_get_slot(state->imports, name);
1748 if(!strcmp(e->key, name)) {
1749 c = (slotinfo_t*)e->data;
1755 /* try package.* imports */
1756 import_list_t*l = state->wildcard_imports;
1758 //printf("does package %s contain a class %s?\n", l->import->package, name);
1759 c = registry_find(l->import->package, name);
1764 /* try global package */
1765 c = registry_find("", name);
1768 /* try local "filename" package */
1769 c = registry_find(internal_filename_package, name);
1774 typedcode_t push_class(slotinfo_t*a)
1779 if(a->access == ACCESS_PACKAGEINTERNAL &&
1780 strcmp(a->package, state->package) &&
1781 strcmp(a->package, internal_filename_package)
1783 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1784 infotypename(a), a->name, a->package, state->package);
1787 if(a->kind != INFOTYPE_CLASS) {
1789 x.c = abc_findpropstrict2(x.c, &m);
1790 x.c = abc_getproperty2(x.c, &m);
1791 if(a->kind == INFOTYPE_METHOD) {
1792 methodinfo_t*f = (methodinfo_t*)a;
1793 x.t = TYPE_FUNCTION(f);
1795 varinfo_t*v = (varinfo_t*)a;
1799 classinfo_t*c = (classinfo_t*)a;
1801 x.c = abc_getglobalscope(x.c);
1802 x.c = abc_getslot(x.c, c->slot);
1805 x.c = abc_getlex2(x.c, &m);
1807 x.t = TYPE_CLASS(c);
1812 static char is_getlocal(code_t*c)
1814 if(!c || c->prev || c->next)
1816 return(c->opcode == OPCODE_GETLOCAL
1817 || c->opcode == OPCODE_GETLOCAL_0
1818 || c->opcode == OPCODE_GETLOCAL_1
1819 || c->opcode == OPCODE_GETLOCAL_2
1820 || c->opcode == OPCODE_GETLOCAL_3);
1822 static int getlocalnr(code_t*c)
1824 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1825 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1826 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1827 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1828 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1829 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1833 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1837 [prefix code] [read instruction]
1841 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1843 if(in && in->opcode == OPCODE_COERCE_A) {
1844 in = code_cutlast(in);
1847 syntaxerror("internal error");
1849 /* chop off read instruction */
1853 prefix = r->prev;r->prev = 0;
1859 char use_temp_var = readbefore;
1861 /* generate the write instruction, and maybe append a dup to the prefix code */
1862 code_t* write = abc_nop(0);
1863 if(r->opcode == OPCODE_GETPROPERTY) {
1864 write->opcode = OPCODE_SETPROPERTY;
1865 multiname_t*m = (multiname_t*)r->data[0];
1866 write->data[0] = multiname_clone(m);
1867 if(m->type == QNAME || m->type == MULTINAME) {
1869 prefix = abc_dup(prefix); // we need the object, too
1872 } else if(m->type == MULTINAMEL) {
1874 /* dupping two values on the stack requires 5 operations and one register-
1875 couldn't adobe just have given us a dup2? */
1876 int temp = gettempvar();
1877 prefix = abc_setlocal(prefix, temp);
1878 prefix = abc_dup(prefix);
1879 prefix = abc_getlocal(prefix, temp);
1880 prefix = abc_swap(prefix);
1881 prefix = abc_getlocal(prefix, temp);
1883 prefix = abc_kill(prefix, temp);
1887 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1889 } else if(r->opcode == OPCODE_GETSLOT) {
1890 write->opcode = OPCODE_SETSLOT;
1891 write->data[0] = r->data[0];
1893 prefix = abc_dup(prefix); // we need the object, too
1896 } else if(r->opcode == OPCODE_GETLOCAL) {
1897 write->opcode = OPCODE_SETLOCAL;
1898 write->data[0] = r->data[0];
1899 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1900 write->opcode = OPCODE_SETLOCAL_0;
1901 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1902 write->opcode = OPCODE_SETLOCAL_1;
1903 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1904 write->opcode = OPCODE_SETLOCAL_2;
1905 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1906 write->opcode = OPCODE_SETLOCAL_3;
1907 } else if(r->opcode == OPCODE_GETSUPER) {
1908 write->opcode = OPCODE_SETSUPER;
1909 multiname_t*m = (multiname_t*)r->data[0];
1910 write->data[0] = multiname_clone(m);
1913 syntaxerror("illegal lvalue: can't assign a value to this expression");
1920 /* with getproperty/getslot, we have to be extra careful not
1921 to execute the read code twice, as it might have side-effects
1922 (e.g. if the property is in fact a setter/getter combination)
1924 So read the value, modify it, and write it again,
1925 using prefix only once and making sure (by using a temporary
1926 register) that the return value is what we just wrote */
1927 temp = gettempvar();
1928 c = code_append(c, prefix);
1929 c = code_append(c, r);
1932 c = abc_setlocal(c, temp);
1934 c = code_append(c, middlepart);
1937 c = abc_setlocal(c, temp);
1939 c = code_append(c, write);
1940 c = abc_getlocal(c, temp);
1941 c = abc_kill(c, temp);
1943 /* if we're allowed to execute the read code twice *and*
1944 the middlepart doesn't modify the code, things are easier.
1946 code_t* r2 = code_dup(r);
1947 //c = code_append(c, prefix);
1948 parserassert(!prefix);
1949 c = code_append(c, r);
1950 c = code_append(c, middlepart);
1951 c = code_append(c, write);
1952 c = code_append(c, r2);
1955 /* even smaller version: overwrite the value without reading
1959 c = code_append(c, prefix);
1962 c = code_append(c, middlepart);
1963 c = code_append(c, write);
1964 c = code_append(c, r);
1967 temp = gettempvar();
1969 c = code_append(c, prefix);
1971 c = code_append(c, middlepart);
1973 c = abc_setlocal(c, temp);
1974 c = code_append(c, write);
1975 c = abc_getlocal(c, temp);
1976 c = abc_kill(c, temp);
1982 char is_break_or_jump(code_t*c)
1986 if(c->opcode == OPCODE_JUMP ||
1987 c->opcode == OPCODE___BREAK__ ||
1988 c->opcode == OPCODE___CONTINUE__ ||
1989 c->opcode == OPCODE_THROW ||
1990 c->opcode == OPCODE_RETURNVOID ||
1991 c->opcode == OPCODE_RETURNVALUE) {
1997 #define IS_FINALLY_TARGET(op) \
1998 ((op) == OPCODE___CONTINUE__ || \
1999 (op) == OPCODE___BREAK__ || \
2000 (op) == OPCODE_RETURNVOID || \
2001 (op) == OPCODE_RETURNVALUE || \
2002 (op) == OPCODE___RETHROW__)
2004 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
2006 #define NEED_EXTRA_STACK_ARG
2007 code_t*finally_label = abc_nop(0);
2008 NEW(lookupswitch_t, l);
2014 code_t*prev = i->prev;
2015 if(IS_FINALLY_TARGET(i->opcode)) {
2018 if(i->opcode == OPCODE___RETHROW__ ||
2019 i->opcode == OPCODE_RETURNVALUE) {
2020 if(i->opcode == OPCODE___RETHROW__)
2021 i->opcode = OPCODE_THROW;
2023 p = abc_coerce_a(p);
2024 p = abc_setlocal(p, tempvar);
2026 p = abc_pushbyte(p, count++);
2027 p = abc_jump(p, finally_label);
2028 code_t*target = p = abc_label(p);
2029 #ifdef NEED_EXTRA_STACK_ARG
2033 p = abc_getlocal(p, tempvar);
2036 p->next = i;i->prev = p;
2037 list_append(l->targets, target);
2043 c = abc_pushbyte(c, -1);
2044 c = code_append(c, finally_label);
2045 c = code_append(c, finally);
2047 #ifdef NEED_EXTRA_STACK_ARG
2050 c = abc_lookupswitch(c, l);
2051 c = l->def = abc_label(c);
2052 #ifdef NEED_EXTRA_STACK_ARG
2059 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
2063 code_t*prev = i->prev;
2064 if(IS_FINALLY_TARGET(i->opcode)) {
2065 if(i->opcode == OPCODE___RETHROW__)
2066 i->opcode = OPCODE_THROW;
2067 code_t*end = code_dup(finally);
2068 code_t*start = code_start(end);
2069 if(prev) prev->next = start;
2076 return code_append(c, finally);
2079 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
2085 int num_insertion_points=0;
2087 if(IS_FINALLY_TARGET(i->opcode))
2088 num_insertion_points++;
2095 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
2100 int simple_version_cost = (1+num_insertion_points)*code_size;
2101 int lookup_version_cost = 4*num_insertion_points + 5;
2103 if(cantdup || simple_version_cost > lookup_version_cost) {
2104 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
2105 return insert_finally_lookup(c, finally, tempvar);
2107 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
2108 return insert_finally_simple(c, finally, tempvar);
2112 #define PASS1 }} if(as3_pass == 1) {{
2113 #define PASS1END }} if(as3_pass == 2) {{
2114 #define PASS2 }} if(as3_pass == 2) {{
2115 #define PASS12 }} {{
2116 #define PASS12END }} if(as3_pass == 2) {{
2122 /* ------------ code blocks / statements ---------------- */
2124 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2126 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2127 PROGRAM_CODE_LIST: PROGRAM_CODE
2128 | PROGRAM_CODE_LIST PROGRAM_CODE
2130 PROGRAM_CODE: PACKAGE_DECLARATION
2131 | INTERFACE_DECLARATION
2133 | FUNCTION_DECLARATION
2136 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
2139 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2140 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2141 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2143 INPACKAGE_CODE: INTERFACE_DECLARATION
2145 | FUNCTION_DECLARATION
2148 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
2151 MAYBECODE: CODE {$$=$1;}
2152 MAYBECODE: {$$=code_new();}
2154 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2155 CODE: CODEPIECE {$$=$1;}
2157 // code which may appear outside of methods
2158 CODE_STATEMENT: IMPORT
2160 CODE_STATEMENT: FOR_IN
2161 CODE_STATEMENT: WHILE
2162 CODE_STATEMENT: DO_WHILE
2163 CODE_STATEMENT: SWITCH
2165 CODE_STATEMENT: WITH
2167 CODE_STATEMENT: VOIDEXPRESSION
2168 CODE_STATEMENT: USE_NAMESPACE
2169 CODE_STATEMENT: NAMESPACE_DECLARATION
2170 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2171 CODE_STATEMENT: '{' '}' {$$=0;}
2173 // code which may appear in methods
2174 CODEPIECE: ';' {$$=0;}
2175 CODEPIECE: CODE_STATEMENT
2176 CODEPIECE: VARIABLE_DECLARATION
2181 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
2183 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2184 //CODEBLOCK : '{' '}' {$$=0;}
2185 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2186 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2188 /* ------------ package init code ------------------- */
2190 PACKAGE_INITCODE: CODE_STATEMENT {
2191 code_t**cc = &global->init->method->body->code;
2192 *cc = code_append(*cc, $1);
2195 /* ------------ conditional compilation ------------- */
2197 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
2199 /* ------------ variables --------------------------- */
2201 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2202 | {$$.c=abc_pushundefined(0);
2206 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2207 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2209 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2210 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2212 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2215 if(variable_exists($1))
2216 syntaxerror("Variable %s already defined", $1);
2218 new_variable($1, 0, 1, 0);
2221 if(!is_subtype_of($3.t, $2)) {
2222 syntaxerror("Can't convert %s to %s", $3.t->name,
2228 if(state->method->uses_slots) {
2229 variable_t* v = find_slot(state, $1);
2231 // this variable is stored in a slot
2239 index = new_variable($1, $2, 1, 0);
2242 $$ = slot?abc_getscopeobject(0, 1):0;
2245 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2246 $$ = code_append($$, $3.c);
2247 $$ = converttype($$, $3.t, $2);
2250 $$ = defaultvalue($$, $2);
2253 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2254 $$ = code_append($$, $3.c);
2255 $$ = abc_coerce_a($$);
2257 // don't do anything
2265 $$ = abc_setslot($$, index);
2267 $$ = abc_setlocal($$, index);
2271 /* ------------ control flow ------------------------- */
2273 MAYBEELSE: %prec below_else {$$ = code_new();}
2274 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2275 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2277 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2280 $$ = code_append($$, $4.c);
2281 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2283 $$ = code_append($$, $6);
2285 myjmp = $$ = abc_jump($$, 0);
2287 myif->branch = $$ = abc_nop($$);
2289 $$ = code_append($$, $7);
2290 myjmp->branch = $$ = abc_nop($$);
2296 FOR_INIT : {$$=code_new();}
2297 FOR_INIT : VARIABLE_DECLARATION
2298 FOR_INIT : VOIDEXPRESSION
2300 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2301 // (I don't see any easy way to revolve this conflict otherwise, as we
2302 // can't touch VAR_READ without upsetting the precedence about "return")
2303 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2304 PASS1 $$=$2;new_variable($2,0,1,0);
2305 PASS2 $$=$2;new_variable($2,$3,1,0);
2307 FOR_IN_INIT : T_IDENTIFIER {
2312 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2313 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2315 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2316 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2318 $$ = code_append($$, $2);
2319 code_t*loopstart = $$ = abc_label($$);
2320 $$ = code_append($$, $4.c);
2321 code_t*myif = $$ = abc_iffalse($$, 0);
2322 $$ = code_append($$, $8);
2323 code_t*cont = $$ = abc_nop($$);
2324 $$ = code_append($$, $6);
2325 $$ = abc_jump($$, loopstart);
2326 code_t*out = $$ = abc_nop($$);
2327 breakjumpsto($$, $1.name, out);
2328 continuejumpsto($$, $1.name, cont);
2335 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2336 variable_t*var = find_variable(state, $2);
2338 syntaxerror("variable %s not known in this scope", $2);
2341 char*tmp1name = concat2($2, "__tmp1__");
2342 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2343 char*tmp2name = concat2($2, "__array__");
2344 int array = new_variable(tmp1name, 0, 0, 0);
2347 $$ = code_append($$, $4.c);
2348 $$ = abc_coerce_a($$);
2349 $$ = abc_setlocal($$, array);
2350 $$ = abc_pushbyte($$, 0);
2351 $$ = abc_setlocal($$, it);
2353 code_t*loopstart = $$ = abc_label($$);
2355 $$ = abc_hasnext2($$, array, it);
2356 code_t*myif = $$ = abc_iffalse($$, 0);
2357 $$ = abc_getlocal($$, array);
2358 $$ = abc_getlocal($$, it);
2360 $$ = abc_nextname($$);
2362 $$ = abc_nextvalue($$);
2363 $$ = converttype($$, 0, var->type);
2364 $$ = abc_setlocal($$, var->index);
2366 $$ = code_append($$, $6);
2367 $$ = abc_jump($$, loopstart);
2369 code_t*out = $$ = abc_nop($$);
2370 breakjumpsto($$, $1.name, out);
2371 continuejumpsto($$, $1.name, loopstart);
2383 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2387 code_t*myjmp = $$ = abc_jump($$, 0);
2388 code_t*loopstart = $$ = abc_label($$);
2389 $$ = code_append($$, $6);
2390 code_t*cont = $$ = abc_nop($$);
2391 myjmp->branch = cont;
2392 $$ = code_append($$, $4.c);
2393 $$ = abc_iftrue($$, loopstart);
2394 code_t*out = $$ = abc_nop($$);
2395 breakjumpsto($$, $1, out);
2396 continuejumpsto($$, $1, cont);
2402 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2404 code_t*loopstart = $$ = abc_label($$);
2405 $$ = code_append($$, $3);
2406 code_t*cont = $$ = abc_nop($$);
2407 $$ = code_append($$, $6.c);
2408 $$ = abc_iftrue($$, loopstart);
2409 code_t*out = $$ = abc_nop($$);
2410 breakjumpsto($$, $1, out);
2411 continuejumpsto($$, $1, cont);
2417 BREAK : "break" %prec prec_none {
2418 $$ = abc___break__(0, "");
2420 BREAK : "break" T_IDENTIFIER {
2421 $$ = abc___break__(0, $2);
2423 CONTINUE : "continue" %prec prec_none {
2424 $$ = abc___continue__(0, "");
2426 CONTINUE : "continue" T_IDENTIFIER {
2427 $$ = abc___continue__(0, $2);
2430 MAYBE_CASE_LIST : {$$=0;}
2431 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2432 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2433 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2434 CASE_LIST: CASE {$$=$1;}
2435 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2437 CASE: "case" E ':' MAYBECODE {
2438 $$ = abc_getlocal(0, state->switch_var);
2439 $$ = code_append($$, $2.c);
2440 code_t*j = $$ = abc_ifne($$, 0);
2441 $$ = code_append($$, $4);
2442 if($$->opcode != OPCODE___BREAK__) {
2443 $$ = abc___fallthrough__($$, "");
2445 code_t*e = $$ = abc_nop($$);
2448 DEFAULT: "default" ':' MAYBECODE {
2451 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2453 $$ = abc_setlocal($$, state->switch_var);
2454 $$ = code_append($$, $7);
2456 code_t*out = $$ = abc_kill($$, state->switch_var);
2457 breakjumpsto($$, $1, out);
2459 code_t*c = $$,*lastblock=0;
2461 if(c->opcode == OPCODE_IFNE) {
2462 if(!c->next) syntaxerror("internal error in fallthrough handling");
2464 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2466 c->opcode = OPCODE_JUMP;
2467 c->branch = lastblock;
2469 /* fall through end of switch */
2470 c->opcode = OPCODE_NOP;
2480 /* ------------ try / catch /finally ---------------- */
2482 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2483 state->exception_name=$3;
2484 PASS1 new_variable($3, 0, 0, 0);
2485 PASS2 new_variable($3, $4, 0, 0);
2488 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2489 multiname_t name = {QNAME, &name_ns, 0, $3};
2491 NEW(abc_exception_t, e)
2492 e->exc_type = sig2mname($4);
2493 e->var_name = multiname_clone(&name);
2497 int i = find_variable_safe(state, $3)->index;
2498 e->target = c = abc_nop(0);
2499 c = abc_setlocal(c, i);
2500 c = code_append(c, code_dup(state->method->scope_code));
2501 c = code_append(c, $8);
2507 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2512 NEW(abc_exception_t, e)
2513 e->exc_type = 0; //all exceptions
2514 e->var_name = 0; //no name
2517 e->to = code_append(e->to, $4);
2523 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2524 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2525 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2526 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2530 list_append($$.l,$2);
2531 $$.finally = $2->to;$2->to=0;
2534 CATCH_FINALLY_LIST: FINALLY {
2538 list_append($$.l,$1);
2539 $$.finally = $1->to;$1->to=0;
2543 TRY : "try" '{' {PASS12 new_state();
2544 state->method->has_exceptions=1;
2545 state->method->late_binding=1;//for invariant scope_code
2546 } MAYBECODE '}' CATCH_FINALLY_LIST {
2547 code_t*out = abc_nop(0);
2549 code_t*start = abc_nop(0);
2550 $$ = code_append(start, $4);
2551 if(!is_break_or_jump($4)) {
2552 $$ = abc_jump($$, out);
2554 code_t*end = $$ = abc_nop($$);
2558 tmp = new_variable("__finally__", 0, 0, 0);
2560 abc_exception_list_t*l = $6.l;
2563 abc_exception_t*e = l->abc_exception;
2565 $$ = code_append($$, e->target);
2566 $$ = abc_jump($$, out);
2568 parserassert((ptroff_t)$6.finally);
2570 e->target = $$ = abc_nop($$);
2571 $$ = code_append($$, code_dup(state->method->scope_code));
2572 $$ = abc___rethrow__($$);
2580 $$ = code_append($$, out);
2582 $$ = insert_finally($$, $6.finally, tmp);
2584 list_concat(state->method->exceptions, $6.l);
2590 /* ------------ throw ------------------------------- */
2592 THROW : "throw" EXPRESSION {
2596 THROW : "throw" %prec prec_none {
2597 if(!state->exception_name)
2598 syntaxerror("re-throw only possible within a catch block");
2599 variable_t*v = find_variable(state, state->exception_name);
2601 $$=abc_getlocal($$, v->index);
2605 /* ------------ with -------------------------------- */
2607 WITH_HEAD : "with" '(' EXPRESSION ')' {
2609 if(state->method->has_exceptions) {
2610 int v = alloc_local();
2611 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2612 state->method->scope_code = abc_pushwith(state->method->scope_code);
2617 WITH : WITH_HEAD CODEBLOCK {
2618 /* remove getlocal;pushwith from scope code again */
2619 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2622 if(state->method->has_exceptions) {
2624 $$ = abc_setlocal($$, $1.number);
2626 $$ = abc_pushwith($$);
2627 $$ = code_append($$, $2);
2628 $$ = abc_popscope($$);
2632 /* ------------ packages and imports ---------------- */
2634 X_IDENTIFIER: T_IDENTIFIER
2635 | "package" {PASS12 $$="package";}
2636 | T_NAMESPACE {PASS12 $$=$1;}
2638 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2639 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2641 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2642 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2643 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2644 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2646 IMPORT : "import" PACKAGEANDCLASS {
2648 slotinfo_t*s = registry_find($2->package, $2->name);
2649 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2650 as3_schedule_class($2->package, $2->name);
2654 syntaxerror("Couldn't import class\n");
2655 state_has_imports();
2656 dict_put(state->imports, c->name, c);
2657 import_toplevel(c->package);
2660 IMPORT : "import" PACKAGE '.' '*' {
2662 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2663 as3_schedule_package($2);
2668 state_has_imports();
2669 list_append(state->wildcard_imports, i);
2670 import_toplevel(i->package);
2674 /* ------------ classes and interfaces (header) -------------- */
2676 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2677 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2678 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2679 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2681 $$.flags=$1.flags|$2.flags;
2682 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2683 $$.ns=$1.ns?$1.ns:$2.ns;
2686 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2687 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2688 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2689 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2690 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2691 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2692 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2693 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2694 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2695 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2699 EXTENDS : {PASS12 $$=0;}
2700 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2702 EXTENDS_LIST : {PASS12 $$=list_new();}
2703 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2705 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2706 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2708 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2709 EXTENDS IMPLEMENTS_LIST
2710 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2712 '}' {PASS12 endclass();$$=0;}
2714 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2716 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2717 startclass(&$1,$3,0,$4);}
2718 MAYBE_INTERFACE_BODY
2719 '}' {PASS12 endclass();$$=0;}
2721 /* ------------ classes and interfaces (body) -------------- */
2724 MAYBE_CLASS_BODY : CLASS_BODY
2725 CLASS_BODY : CLASS_BODY_ITEM
2726 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2727 CLASS_BODY_ITEM : ';'
2728 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2729 CLASS_BODY_ITEM : SLOT_DECLARATION
2730 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2732 CLASS_BODY_ITEM : CODE_STATEMENT {
2733 code_t*c = state->cls->static_init->header;
2734 c = code_append(c, $1);
2735 state->cls->static_init->header = c;
2738 MAYBE_INTERFACE_BODY :
2739 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2740 INTERFACE_BODY : IDECLARATION
2741 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2743 IDECLARATION : "var" T_IDENTIFIER {
2744 syntaxerror("variable declarations not allowed in interfaces");
2746 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2748 $1.flags |= FLAG_PUBLIC;
2749 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2750 syntaxerror("invalid method modifiers: interface methods always need to be public");
2752 startfunction(&$1,$3,$4,&$6,$8);
2753 endfunction(&$1,$3,$4,&$6,$8, 0);
2754 list_deep_free($6.list);
2757 /* ------------ classes and interfaces (body, slots ) ------- */
2759 VARCONST: "var" | "const"
2761 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2763 SLOT_LIST: ONE_SLOT {$$ = $1;}
2764 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2766 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2768 int flags = slotstate_flags->flags;
2769 namespace_t ns = modifiers2access(slotstate_flags);
2771 varinfo_t* info = 0;
2773 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2775 check_override(i, flags);
2777 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2779 slotinfo_t*i = registry_find(state->package, $1);
2781 syntaxerror("package %s already contains '%s'", state->package, $1);
2783 if(ns.name && ns.name[0]) {
2784 syntaxerror("namespaces not allowed on package-level variables");
2786 info = varinfo_register_global(ns.access, state->package, $1);
2790 info->flags = flags;
2793 multiname_t mname = {QNAME, &ns, 0, $1};
2795 trait_list_t**traits;
2799 ns.name = state->package;
2800 traits = &global->init->traits;
2801 code = &global->init->method->body->code;
2802 } else if(flags&FLAG_STATIC) {
2804 traits = &state->cls->abc->static_traits;
2805 code = &state->cls->static_init->header;
2807 // instance variable
2808 traits = &state->cls->abc->traits;
2809 code = &state->cls->init->header;
2815 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2817 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2819 info->slot = t->slot_id;
2821 /* initalization code (if needed) */
2823 if($3.c && !is_pushundefined($3.c)) {
2824 c = abc_getlocal_0(c);
2825 c = code_append(c, $3.c);
2826 c = converttype(c, $3.t, $2);
2827 c = abc_setslot(c, t->slot_id);
2830 *code = code_append(*code, c);
2832 if(slotstate_varconst==KW_CONST) {
2833 t->kind= TRAIT_CONST;
2839 /* ------------ constants -------------------------------------- */
2841 MAYBESTATICCONSTANT: {$$=0;}
2842 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2844 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2845 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2846 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2847 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2848 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2849 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2850 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2851 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2852 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2853 STATICCONSTANT : T_IDENTIFIER {
2854 if(!strcmp($1, "NaN")) {
2855 $$ = constant_new_float(__builtin_nan(""));
2857 as3_warning("Couldn't evaluate constant value of %s", $1);
2858 $$ = constant_new_null($1);
2862 /* ------------ classes and interfaces (body, functions) ------- */
2864 // non-vararg version
2867 memset(&$$,0,sizeof($$));
2869 MAYBE_PARAM_LIST: PARAM_LIST {
2875 MAYBE_PARAM_LIST: "..." PARAM {
2877 memset(&$$,0,sizeof($$));
2879 list_append($$.list, $2);
2881 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2885 list_append($$.list, $4);
2889 PARAM_LIST: PARAM_LIST ',' PARAM {
2892 list_append($$.list, $3);
2896 memset(&$$,0,sizeof($$));
2897 list_append($$.list, $1);
2900 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2902 $$ = rfx_calloc(sizeof(param_t));
2908 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2910 $$ = rfx_calloc(sizeof(param_t));
2912 $$->type = TYPE_ANY;
2920 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2921 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2924 endfunction(&$1,$3,$4,&$6,0,0);
2926 if(!state->method->info) syntaxerror("internal error");
2928 code_t*c = method_header(state->method);
2929 c = wrap_function(c, 0, $11);
2931 endfunction(&$1,$3,$4,&$6,$8,c);
2933 list_deep_free($6.list);
2937 MAYBE_IDENTIFIER: T_IDENTIFIER
2938 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2939 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2940 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2943 endfunction(0,0,$2,&$4,0,0);
2945 methodinfo_t*f = state->method->info;
2946 if(!f || !f->kind) syntaxerror("internal error");
2948 code_t*c = method_header(state->method);
2949 c = wrap_function(c, 0, $9);
2951 int index = state->method->var_index;
2952 endfunction(0,0,$2,&$4,$6,c);
2954 $$.c = abc_getlocal(0, index);
2955 $$.t = TYPE_FUNCTION(f);
2957 PASS12 list_deep_free($4.list);
2961 /* ------------- package + class ids --------------- */
2963 CLASS: X_IDENTIFIER {
2964 PASS1 NEW(unresolvedinfo_t,c);
2965 memset(c, 0, sizeof(*c));
2966 c->kind = INFOTYPE_UNRESOLVED;
2968 c->package = get_package_from_name($1);
2970 c->nsset = get_current_imports();
2971 /* make the compiler look for this class in the current directory,
2973 //as3_schedule_class_noerror(state->package, $1);
2975 $$ = (classinfo_t*)c;
2977 slotinfo_t*s = find_class($1);
2978 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2979 $$ = (classinfo_t*)s;
2982 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
2983 PASS1 NEW(unresolvedinfo_t,c);
2984 memset(c, 0, sizeof(*c));
2985 c->kind = INFOTYPE_UNRESOLVED;
2988 $$ = (classinfo_t*)c;
2990 slotinfo_t*s = registry_find($1, $3);
2991 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2993 $$ = (classinfo_t*)s;
2996 CLASS_SPEC: PACKAGEANDCLASS
2999 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
3000 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
3002 TYPE : CLASS_SPEC {PASS12 $$=$1;}
3003 | '*' {PASS12 $$=registry_getanytype();}
3004 | "void" {PASS12 $$=registry_getanytype();}
3006 | "String" {$$=registry_getstringclass();}
3007 | "int" {$$=registry_getintclass();}
3008 | "uint" {$$=registry_getuintclass();}
3009 | "Boolean" {$$=registry_getbooleanclass();}
3010 | "Number" {$$=registry_getnumberclass();}
3013 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
3014 MAYBETYPE: {PASS12 $$=0;}
3016 /* ----------function calls, delete, constructor calls ------ */
3018 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
3019 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
3021 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
3022 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
3023 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
3025 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
3029 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
3030 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
3031 $$.number= $1.number+1;
3032 $$.cc = code_append($1.cc, $2.c);
3036 NEW : "new" E XX MAYBE_PARAM_VALUES {
3038 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
3040 code_t*paramcode = $4.cc;
3041 if($$.c->opcode == OPCODE_GETPROPERTY) {
3042 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3043 $$.c = code_cutlast($$.c);
3044 $$.c = code_append($$.c, paramcode);
3045 $$.c = abc_constructprop2($$.c, name, $4.number);
3046 multiname_destroy(name);
3047 } else if($$.c->opcode == OPCODE_GETSLOT) {
3048 int slot = (int)(ptroff_t)$$.c->data[0];
3049 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3050 multiname_t*name = t->name;
3051 $$.c = code_cutlast($$.c);
3052 $$.c = code_append($$.c, paramcode);
3053 $$.c = abc_constructprop2($$.c, name, $4.number);
3055 $$.c = code_append($$.c, paramcode);
3056 $$.c = abc_construct($$.c, $4.number);
3060 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
3063 $$.c = abc_coerce_a($$.c);
3068 /* TODO: use abc_call (for calling local variables),
3069 abc_callstatic (for calling own methods)
3072 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3075 if($$.c->opcode == OPCODE_COERCE_A) {
3076 $$.c = code_cutlast($$.c);
3078 code_t*paramcode = $3.cc;
3081 if($$.c->opcode == OPCODE_GETPROPERTY) {
3082 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3083 $$.c = code_cutlast($$.c);
3084 $$.c = code_append($$.c, paramcode);
3085 $$.c = abc_callproperty2($$.c, name, $3.number);
3086 multiname_destroy(name);
3087 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3088 int slot = (int)(ptroff_t)$$.c->data[0];
3089 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3090 if(t->kind!=TRAIT_METHOD) {
3091 //ok: flash allows to assign closures to members.
3093 multiname_t*name = t->name;
3094 $$.c = code_cutlast($$.c);
3095 $$.c = code_append($$.c, paramcode);
3096 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3097 $$.c = abc_callproperty2($$.c, name, $3.number);
3098 } else if($$.c->opcode == OPCODE_GETSUPER) {
3099 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3100 $$.c = code_cutlast($$.c);
3101 $$.c = code_append($$.c, paramcode);
3102 $$.c = abc_callsuper2($$.c, name, $3.number);
3103 multiname_destroy(name);
3105 $$.c = abc_getglobalscope($$.c);
3106 $$.c = code_append($$.c, paramcode);
3107 $$.c = abc_call($$.c, $3.number);
3110 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
3111 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
3113 $$.c = abc_coerce_a($$.c);
3118 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3119 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3120 if(!state->method) syntaxerror("super() not allowed outside of a function");
3121 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3124 $$.c = abc_getlocal_0($$.c);
3126 $$.c = code_append($$.c, $3.cc);
3128 this is dependent on the control path, check this somewhere else
3129 if(state->method->has_super)
3130 syntaxerror("constructor may call super() only once");
3132 state->method->has_super = 1;
3134 $$.c = abc_constructsuper($$.c, $3.number);
3135 $$.c = abc_pushundefined($$.c);
3139 DELETE: "delete" E {
3141 if($$.c->opcode == OPCODE_COERCE_A) {
3142 $$.c = code_cutlast($$.c);
3144 multiname_t*name = 0;
3145 if($$.c->opcode == OPCODE_GETPROPERTY) {
3146 $$.c->opcode = OPCODE_DELETEPROPERTY;
3147 } else if($$.c->opcode == OPCODE_GETSLOT) {
3148 int slot = (int)(ptroff_t)$$.c->data[0];
3149 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3150 $$.c = code_cutlast($$.c);
3151 $$.c = abc_deleteproperty2($$.c, name);
3153 $$.c = abc_getlocal_0($$.c);
3154 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3155 $$.c = abc_deleteproperty2($$.c, &m);
3157 $$.t = TYPE_BOOLEAN;
3160 RETURN: "return" %prec prec_none {
3161 $$ = abc_returnvoid(0);
3163 RETURN: "return" EXPRESSION {
3165 $$ = abc_returnvalue($$);
3168 // ----------------------- expression types -------------------------------------
3170 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3171 EXPRESSION : E %prec below_minus {$$ = $1;}
3172 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3174 $$.c = cut_last_push($$.c);
3175 $$.c = code_append($$.c,$3.c);
3178 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3179 $$=cut_last_push($1.c);
3182 // ----------------------- expression evaluation -------------------------------------
3184 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3185 //V : CONSTANT {$$ = 0;}
3187 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
3188 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3189 //V : NEW {$$ = $1.c;}
3191 //V : DELETE {$$ = $1.c;}
3192 E : DELETE {$$ = $1;}
3198 namespace_t ns = {ACCESS_PACKAGE, ""};
3199 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3201 $$.c = abc_getlex2($$.c, &m);
3202 $$.c = abc_pushstring($$.c, $1.pattern);
3203 $$.c = abc_construct($$.c, 1);
3205 $$.c = abc_getlex2($$.c, &m);
3206 $$.c = abc_pushstring($$.c, $1.pattern);
3207 $$.c = abc_pushstring($$.c, $1.options);
3208 $$.c = abc_construct($$.c, 2);
3213 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3214 //MULTINAME(m, registry_getintclass());
3215 //$$.c = abc_coerce2($$.c, &m); // FIXME
3218 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3221 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3224 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3227 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3230 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3233 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3236 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3237 $$.t = TYPE_BOOLEAN;
3239 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3240 $$.t = TYPE_BOOLEAN;
3242 CONSTANT : "null" {$$.c = abc_pushnull(0);
3246 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3247 $$.t = TYPE_BOOLEAN;
3249 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3250 $$.t = TYPE_BOOLEAN;
3252 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3253 $$.t = TYPE_BOOLEAN;
3255 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3256 $$.t = TYPE_BOOLEAN;
3258 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3259 $$.t = TYPE_BOOLEAN;
3261 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3262 $$.t = TYPE_BOOLEAN;
3264 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3265 $$.t = TYPE_BOOLEAN;
3267 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3268 $$.t = TYPE_BOOLEAN;
3271 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3273 $$.c = converttype($$.c, $1.t, $$.t);
3274 $$.c = abc_dup($$.c);
3275 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3276 $$.c = cut_last_push($$.c);
3277 $$.c = code_append($$.c,$3.c);
3278 $$.c = converttype($$.c, $3.t, $$.t);
3279 code_t*label = $$.c = abc_label($$.c);
3280 jmp->branch = label;
3283 $$.t = join_types($1.t, $3.t, 'A');
3284 /*printf("%08x:\n",$1.t);
3285 code_dump($1.c, 0, 0, "", stdout);
3286 printf("%08x:\n",$3.t);
3287 code_dump($3.c, 0, 0, "", stdout);
3288 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3290 $$.c = converttype($$.c, $1.t, $$.t);
3291 $$.c = abc_dup($$.c);
3292 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3293 $$.c = cut_last_push($$.c);
3294 $$.c = code_append($$.c,$3.c);
3295 $$.c = converttype($$.c, $3.t, $$.t);
3296 code_t*label = $$.c = abc_label($$.c);
3297 jmp->branch = label;
3300 E : '!' E {$$.c=$2.c;
3301 $$.c = abc_not($$.c);
3302 $$.t = TYPE_BOOLEAN;
3305 E : '~' E {$$.c=$2.c;
3306 $$.c = abc_bitnot($$.c);
3310 E : E '&' E {$$.c = code_append($1.c,$3.c);
3311 $$.c = abc_bitand($$.c);
3315 E : E '^' E {$$.c = code_append($1.c,$3.c);
3316 $$.c = abc_bitxor($$.c);
3320 E : E '|' E {$$.c = code_append($1.c,$3.c);
3321 $$.c = abc_bitor($$.c);
3325 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3326 $$.c = abc_rshift($$.c);
3329 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3330 $$.c = abc_urshift($$.c);
3333 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3334 $$.c = abc_lshift($$.c);
3338 E : E '/' E {$$.c = code_append($1.c,$3.c);
3339 $$.c = abc_divide($$.c);
3342 E : E '%' E {$$.c = code_append($1.c,$3.c);
3343 $$.c = abc_modulo($$.c);
3346 E : E '+' E {$$.c = code_append($1.c,$3.c);
3347 if(BOTH_INT($1.t, $3.t)) {
3348 $$.c = abc_add_i($$.c);
3351 $$.c = abc_add($$.c);
3352 $$.t = join_types($1.t,$3.t,'+');
3355 E : E '-' E {$$.c = code_append($1.c,$3.c);
3356 if(BOTH_INT($1.t,$3.t)) {
3357 $$.c = abc_subtract_i($$.c);
3360 $$.c = abc_subtract($$.c);
3364 E : E '*' E {$$.c = code_append($1.c,$3.c);
3365 if(BOTH_INT($1.t,$3.t)) {
3366 $$.c = abc_multiply_i($$.c);
3369 $$.c = abc_multiply($$.c);
3374 E : E "in" E {$$.c = code_append($1.c,$3.c);
3375 $$.c = abc_in($$.c);
3376 $$.t = TYPE_BOOLEAN;
3379 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3380 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3381 MULTINAME(m, (classinfo_t*)($3.t->data));
3382 $$.c = abc_astype2($1.c, &m);
3385 $$.c = code_append($1.c, $3.c);
3386 $$.c = abc_astypelate($$.c);
3391 E : E "instanceof" E
3392 {$$.c = code_append($1.c, $3.c);
3393 $$.c = abc_instanceof($$.c);
3394 $$.t = TYPE_BOOLEAN;
3397 E : E "is" E {$$.c = code_append($1.c, $3.c);
3398 $$.c = abc_istypelate($$.c);
3399 $$.t = TYPE_BOOLEAN;
3402 E : "typeof" '(' E ')' {
3404 $$.c = abc_typeof($$.c);
3409 $$.c = cut_last_push($2.c);
3410 $$.c = abc_pushundefined($$.c);
3414 E : "void" { $$.c = abc_pushundefined(0);
3418 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3423 $$.c=abc_negate_i($$.c);
3426 $$.c=abc_negate($$.c);
3433 $$.c = code_append($$.c, $3.c);
3435 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3436 $$.c = abc_getproperty2($$.c, &m);
3437 $$.t = 0; // array elements have unknown type
3440 E : '[' MAYBE_EXPRESSION_LIST ']' {
3442 $$.c = code_append($$.c, $2.cc);
3443 $$.c = abc_newarray($$.c, $2.number);
3444 $$.t = registry_getarrayclass();
3447 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3448 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3450 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3452 $$.cc = code_append($$.cc, $1.c);
3453 $$.cc = code_append($$.cc, $3.c);
3456 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3458 $$.number = $1.number+2;
3459 $$.cc = code_append($$.cc, $3.c);
3460 $$.cc = code_append($$.cc, $5.c);
3465 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3467 $$.c = code_append($$.c, $2.cc);
3468 $$.c = abc_newobject($$.c, $2.number/2);
3469 $$.t = registry_getobjectclass();
3474 if(BOTH_INT($1.t,$3.t)) {
3475 c=abc_multiply_i(c);
3479 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3480 $$.c = toreadwrite($1.c, c, 0, 0);
3485 code_t*c = abc_modulo($3.c);
3486 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3487 $$.c = toreadwrite($1.c, c, 0, 0);
3491 code_t*c = abc_lshift($3.c);
3492 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3493 $$.c = toreadwrite($1.c, c, 0, 0);
3497 code_t*c = abc_rshift($3.c);
3498 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3499 $$.c = toreadwrite($1.c, c, 0, 0);
3503 code_t*c = abc_urshift($3.c);
3504 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3505 $$.c = toreadwrite($1.c, c, 0, 0);
3509 code_t*c = abc_divide($3.c);
3510 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3511 $$.c = toreadwrite($1.c, c, 0, 0);
3515 code_t*c = abc_bitor($3.c);
3516 c=converttype(c, TYPE_INT, $1.t);
3517 $$.c = toreadwrite($1.c, c, 0, 0);
3521 code_t*c = abc_bitxor($3.c);
3522 c=converttype(c, TYPE_INT, $1.t);
3523 $$.c = toreadwrite($1.c, c, 0, 0);
3529 if(TYPE_IS_INT($1.t)) {
3533 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3536 $$.c = toreadwrite($1.c, c, 0, 0);
3539 E : E "-=" E { code_t*c = $3.c;
3540 if(TYPE_IS_INT($1.t)) {
3541 c=abc_subtract_i(c);
3544 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3547 $$.c = toreadwrite($1.c, c, 0, 0);
3550 E : E '=' E { code_t*c = 0;
3551 c = code_append(c, $3.c);
3552 c = converttype(c, $3.t, $1.t);
3553 $$.c = toreadwrite($1.c, c, 1, 0);
3557 E : E '?' E ':' E %prec below_assignment {
3558 $$.t = join_types($3.t,$5.t,'?');
3560 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3561 $$.c = code_append($$.c, $3.c);
3562 $$.c = converttype($$.c, $3.t, $$.t);
3563 code_t*j2 = $$.c = abc_jump($$.c, 0);
3564 $$.c = j1->branch = abc_label($$.c);
3565 $$.c = code_append($$.c, $5.c);
3566 $$.c = converttype($$.c, $5.t, $$.t);
3567 $$.c = j2->branch = abc_label($$.c);
3570 E : E "++" { code_t*c = 0;
3571 classinfo_t*type = $1.t;
3572 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3573 int nr = getlocalnr($1.c);
3574 code_free($1.c);$1.c=0;
3575 if(TYPE_IS_INT($1.t)) {
3576 $$.c = abc_getlocal(0, nr);
3577 $$.c = abc_inclocal_i($$.c, nr);
3578 } else if(TYPE_IS_NUMBER($1.t)) {
3579 $$.c = abc_getlocal(0, nr);
3580 $$.c = abc_inclocal($$.c, nr);
3581 } else syntaxerror("internal error");
3583 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3584 c=abc_increment_i(c);
3590 c=converttype(c, type, $1.t);
3591 $$.c = toreadwrite($1.c, c, 0, 1);
3596 // TODO: use inclocal, like with ++
3597 E : E "--" { code_t*c = 0;
3598 classinfo_t*type = $1.t;
3599 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3600 c=abc_decrement_i(c);
3606 c=converttype(c, type, $1.t);
3607 $$.c = toreadwrite($1.c, c, 0, 1);
3611 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3612 classinfo_t*type = $2.t;
3613 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3614 c=abc_increment_i(c);
3620 c=converttype(c, type, $2.t);
3621 $$.c = toreadwrite($2.c, c, 0, 0);
3625 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3626 classinfo_t*type = $2.t;
3627 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3628 c=abc_decrement_i(c);
3634 c=converttype(c, type, $2.t);
3635 $$.c = toreadwrite($2.c, c, 0, 0);
3639 E : "super" '.' T_IDENTIFIER
3640 { if(!state->cls->info)
3641 syntaxerror("super keyword not allowed outside a class");
3642 classinfo_t*t = state->cls->info->superclass;
3643 if(!t) t = TYPE_OBJECT;
3645 memberinfo_t*f = findmember_nsset(t, $3, 1);
3647 MEMBER_MULTINAME(m, f, $3);
3649 $$.c = abc_getlocal_0($$.c);
3650 $$.c = abc_getsuper2($$.c, &m);
3651 $$.t = slotinfo_gettype((slotinfo_t*)f);
3654 E : '@' T_IDENTIFIER {
3656 $$.c = abc_pushundefined(0);
3658 as3_warning("ignored @ operator");
3661 E : E '.' '@' T_IDENTIFIER {
3662 // child attribute TODO
3663 $$.c = abc_pushundefined(0);
3665 as3_warning("ignored .@ operator");
3668 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3669 // namespace declaration TODO
3670 $$.c = abc_pushundefined(0);
3672 as3_warning("ignored :: operator");
3675 E : E ".." T_IDENTIFIER {
3677 $$.c = abc_pushundefined(0);
3679 as3_warning("ignored .. operator");
3682 E : E '.' '(' E ')' {
3684 $$.c = abc_pushundefined(0);
3686 as3_warning("ignored .() operator");
3689 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3693 E : E '.' T_IDENTIFIER {
3695 classinfo_t*t = $1.t;
3697 if(TYPE_IS_CLASS(t) && t->data) {
3702 if(t->subtype==INFOTYPE_UNRESOLVED) {
3703 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3705 memberinfo_t*f = findmember_nsset(t, $3, 1);
3707 if(f && !is_static != !(f->flags&FLAG_STATIC))
3709 if(f && f->slot && !noslot) {
3710 $$.c = abc_getslot($$.c, f->slot);
3712 MEMBER_MULTINAME(m, f, $3);
3713 $$.c = abc_getproperty2($$.c, &m);
3715 /* determine type */
3716 $$.t = slotinfo_gettype((slotinfo_t*)f);
3718 $$.c = abc_coerce_a($$.c);
3719 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3720 string_t*package = $1.c->data[0];
3721 char*package2 = concat3(package->str, ".", $3);
3723 slotinfo_t*a = registry_find(package->str, $3);
3726 } else if(dict_contains(state->import_toplevel_packages, package2) ||
3727 registry_ispackage(package2)) {
3729 $$.c->data[0] = string_new4(package2);
3732 syntaxerror("couldn't resolve %s", package2);
3735 /* when resolving a property on an unknown type, we do know the
3736 name of the property (and don't seem to need the package), but
3737 we need to make avm2 try out all access modes */
3738 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3739 $$.c = abc_getproperty2($$.c, &m);
3740 $$.c = abc_coerce_a($$.c);
3741 $$.t = registry_getanytype();
3745 VAR_READ : T_IDENTIFIER {
3747 /* Queue unresolved identifiers for checking against the parent
3748 function's variables.
3749 We consider everything which is not a local variable "unresolved".
3750 This encompasses class names, members of the surrounding class
3751 etc. which is *correct* because local variables of the parent function
3754 if(state->method->inner && !find_variable(state, $1)) {
3755 unknown_variable($1);
3758 /* let the compiler know that it might want to check the current directory/package
3759 for this identifier- maybe there's a file $1.as defining $1. */
3760 //as3_schedule_class_noerror(state->package, $1);
3769 /* look at variables */
3770 if((v = find_variable(state, $1))) {
3771 // $1 is a local variable
3772 $$.c = abc_getlocal($$.c, v->index);
3776 if((v = find_slot(state, $1))) {
3777 $$.c = abc_getscopeobject($$.c, 1);
3778 $$.c = abc_getslot($$.c, v->index);
3783 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3785 /* look at current class' members */
3786 if(!state->method->inner &&
3788 (f = findmember_nsset(state->cls->info, $1, 1)) &&
3789 (f->flags&FLAG_STATIC) >= i_am_static)
3791 // $1 is a function in this class
3792 int var_is_static = (f->flags&FLAG_STATIC);
3794 if(f->kind == INFOTYPE_METHOD) {
3795 $$.t = TYPE_FUNCTION(f);
3799 if(var_is_static && !i_am_static) {
3800 /* access to a static member from a non-static location.
3801 do this via findpropstrict:
3802 there doesn't seem to be any non-lookup way to access
3803 static properties of a class */
3804 state->method->late_binding = 1;
3806 namespace_t ns = {f->access, ""};
3807 multiname_t m = {QNAME, &ns, 0, $1};
3808 $$.c = abc_findpropstrict2($$.c, &m);
3809 $$.c = abc_getproperty2($$.c, &m);
3811 } else if(f->slot>0) {
3812 $$.c = abc_getlocal_0($$.c);
3813 $$.c = abc_getslot($$.c, f->slot);
3816 namespace_t ns = {f->access, ""};
3817 multiname_t m = {QNAME, &ns, 0, $1};
3818 $$.c = abc_getlocal_0($$.c);
3819 $$.c = abc_getproperty2($$.c, &m);
3824 /* look at actual classes, in the current package and imported */
3825 if((a = find_class($1))) {
3830 /* look through package prefixes */
3831 if(dict_contains(state->import_toplevel_packages, $1) ||
3832 registry_ispackage($1)) {
3833 $$.c = abc___pushpackage__($$.c, $1);
3838 /* unknown object, let the avm2 resolve it */
3840 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3841 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3842 state->method->late_binding = 1;
3844 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3847 $$.c = abc_findpropstrict2($$.c, &m);
3848 $$.c = abc_getproperty2($$.c, &m);
3852 // ----------------- namespaces -------------------------------------------------
3854 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3856 NEW(namespace_decl_t,n);
3861 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3863 NEW(namespace_decl_t,n);
3868 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3870 NEW(namespace_decl_t,n);
3875 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3877 trie_put(active_namespaces, $2->name, (void*)$2->url);
3879 namespace_t access = modifiers2access(&$1);
3880 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3881 var->type = TYPE_NAMESPACE;
3883 ns.access = ACCESS_NAMESPACE;
3885 var->value = constant_new_namespace(&ns);
3890 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3892 const char*url = $3->name;
3893 varinfo_t*s = (varinfo_t*)$3;
3894 if(!s || s->kind != INFOTYPE_SLOT)
3895 syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
3896 if(!s->value || !NS_TYPE(s->value->type))
3897 syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
3898 url = s->value->ns->name;
3900 trie_put(active_namespaces, $3->name, (void*)url);
3901 add_active_url(url);