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 <number_int> CONDITIONAL_COMPILATION
174 %type <for_start> FOR_START
175 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
176 %type <namespace_decl> NAMESPACE_ID
177 %type <token> VARCONST
179 %type <code> CODEPIECE CODE_STATEMENT
180 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
181 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
182 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
183 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
184 %type <exception> CATCH FINALLY
185 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
186 %type <code> CLASS_DECLARATION
187 %type <code> NAMESPACE_DECLARATION
188 %type <code> INTERFACE_DECLARATION
189 %type <code> VOIDEXPRESSION
190 %type <value> EXPRESSION NONCOMMAEXPRESSION
191 %type <value> MAYBEEXPRESSION
192 %type <value> E DELETE
193 %type <value> CONSTANT
194 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
195 %type <value> INNERFUNCTION
196 %type <code> USE_NAMESPACE
197 %type <code> FOR_INIT
199 %type <classinfo> MAYBETYPE
202 %type <params> PARAM_LIST
203 %type <params> MAYBE_PARAM_LIST
204 %type <flags> MAYBE_MODIFIERS
205 %type <flags> MODIFIER_LIST
206 %type <flags> MODIFIER
207 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
208 %type <classinfo_list> IMPLEMENTS_LIST
209 %type <classinfo> EXTENDS CLASS_SPEC
210 %type <classinfo_list> EXTENDS_LIST
212 %type <classinfo> CLASS PACKAGEANDCLASS
213 %type <classinfo_list> CLASS_SPEC_LIST
215 %type <classinfo> TYPE
216 //%type <token> VARIABLE
217 %type <value> VAR_READ
219 //%type <token> T_IDENTIFIER
220 %type <value> FUNCTIONCALL
221 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD
223 // precedence: from low to high
227 %left below_semicolon
230 %nonassoc below_assignment // for ?:, contrary to spec
231 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
238 %nonassoc "==" "!=" "===" "!=="
239 %nonassoc "is" "as" "in"
240 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
241 %left "<<" ">>" ">>>"
245 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
247 %nonassoc below_curly
251 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
254 %left above_identifier
258 // needed for "return" precedence:
259 %nonassoc T_STRING T_REGEXP
260 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
261 %nonassoc "false" "true" "null" "undefined" "super" "function"
268 static int a3_error(char*s)
270 syntaxerror("%s", s);
271 return 0; //make gcc happy
275 static char* concat2(const char* t1, const char* t2)
279 char*text = malloc(l1+l2+1);
280 memcpy(text , t1, l1);
281 memcpy(text+l1, t2, l2);
285 static char* concat3(const char* t1, const char* t2, const char* t3)
290 char*text = malloc(l1+l2+l3+1);
291 memcpy(text , t1, l1);
292 memcpy(text+l1, t2, l2);
293 memcpy(text+l1+l2, t3, l3);
298 typedef struct _import {
302 DECLARE_LIST(import);
304 DECLARE(methodstate);
305 DECLARE_LIST(methodstate);
307 typedef struct _classstate {
313 methodstate_t*static_init;
315 //code_t*static_init;
317 char has_constructor;
320 struct _methodstate {
330 dict_t*unresolved_variables;
333 char uses_parent_function;
339 int var_index; // for inner methods
340 int slot_index; // for inner methods
341 char is_a_slot; // for inner methods
346 abc_exception_list_t*exceptions;
348 methodstate_list_t*innerfunctions;
351 typedef struct _state {
356 import_list_t*wildcard_imports;
357 dict_t*import_toplevel_packages;
360 namespace_list_t*active_namespace_urls;
362 char has_own_imports;
363 char new_vars; // e.g. transition between two functions
366 methodstate_t*method;
375 typedef struct _global {
379 dict_t*file2token2info;
382 static global_t*global = 0;
383 static state_t* state = 0;
387 #define MULTINAME(m,x) \
391 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
393 #define MEMBER_MULTINAME(m,f,n) \
397 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
398 m##_ns.name = ((slotinfo_t*)(f))->package; \
403 m.namespace_set = 0; \
404 m.name = ((slotinfo_t*)(f))->name; \
406 m.type = MULTINAME; \
408 m.namespace_set = &nopackage_namespace_set; \
412 /* warning: list length of namespace set is undefined */
413 #define MULTINAME_LATE(m, access, package) \
414 namespace_t m##_ns = {access, package}; \
415 namespace_set_t m##_nsset; \
416 namespace_list_t m##_l;m##_l.next = 0; \
417 m##_nsset.namespaces = &m##_l; \
418 m##_nsset = m##_nsset; \
419 m##_l.namespace = &m##_ns; \
420 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
422 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
423 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
424 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
425 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
426 static namespace_list_t nl4 = {&ns4,0};
427 static namespace_list_t nl3 = {&ns3,&nl4};
428 static namespace_list_t nl2 = {&ns2,&nl3};
429 static namespace_list_t nl1 = {&ns1,&nl2};
430 static namespace_set_t nopackage_namespace_set = {&nl1};
432 dict_t*conditionals=0;
433 void as3_set_definition(const char*c)
436 conditionals = dict_new();
437 if(!dict_contains(conditionals,c))
438 dict_put(conditionals,c,0);
441 static void new_state()
444 state_t*oldstate = state;
446 memcpy(s, state, sizeof(state_t)); //shallow copy
448 s->imports = dict_new();
450 if(!s->import_toplevel_packages) {
451 s->import_toplevel_packages = dict_new();
455 state->has_own_imports = 0;
456 state->vars = dict_new();
457 state->old = oldstate;
460 trie_remember(active_namespaces);
463 state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
465 static void state_has_imports()
467 state->wildcard_imports = list_clone(state->wildcard_imports);
468 state->imports = dict_clone(state->imports);
469 state->has_own_imports = 1;
471 static void import_toplevel(const char*package)
473 char* s = strdup(package);
475 dict_put(state->import_toplevel_packages, s, 0);
476 char*x = strrchr(s, '.');
484 static void state_destroy(state_t*state)
486 if(state->has_own_imports) {
487 list_free(state->wildcard_imports);
488 dict_destroy(state->imports);state->imports=0;
490 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
491 dict_destroy(state->imports);state->imports=0;
495 for(t=0;t<state->vars->hashsize;t++) {
496 dictentry_t*e =state->vars->slots[t];
498 free(e->data);e->data=0;
502 dict_destroy(state->vars);state->vars=0;
505 list_free(state->active_namespace_urls)
506 state->active_namespace_urls = 0;
511 static void old_state()
513 trie_rollback(active_namespaces);
515 if(!state || !state->old)
516 syntaxerror("invalid nesting");
517 state_t*leaving = state;
521 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
522 free(leaving->method);
525 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
530 state_destroy(leaving);
533 static code_t* method_header(methodstate_t*m);
534 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
535 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
538 static char* internal_filename_package = 0;
539 void initialize_file(char*filename)
542 syntaxerror("invalid call to initialize_file during parsing of another file");
545 active_namespaces = trie_new();
548 state->package = internal_filename_package = strdup(filename);
550 global->token2info = dict_lookup(global->file2token2info,
551 current_filename // use long version
553 if(!global->token2info) {
554 global->token2info = dict_new2(&ptr_type);
555 dict_put(global->file2token2info, current_filename, global->token2info);
559 state->method = rfx_calloc(sizeof(methodstate_t));
560 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
561 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
563 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
564 function_initvars(state->method, 0, 0, 1);
565 global->init = abc_initscript(global->file);
571 if(!state || state->level!=1) {
572 syntaxerror("unexpected end of file in pass %d", as3_pass);
576 code_t*header = method_header(state->method);
577 code_t*c = wrap_function(header, 0, global->init->method->body->code);
578 global->init->method->body->code = c;
579 free(state->method);state->method=0;
582 //free(state->package);state->package=0; // used in registry
583 state_destroy(state);state=0;
586 void initialize_parser()
588 global = rfx_calloc(sizeof(global_t));
589 global->file = abc_file_new();
590 global->file->flags &= ~ABCFILE_LAZY;
591 global->file2token2info = dict_new();
592 global->token2info = 0;
595 void* finish_parser()
597 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
599 global->token2info=0;
605 static void xx_scopetest()
607 /* findpropstrict doesn't just return a scope object- it
608 also makes it "active" somehow. Push local_0 on the
609 scope stack and read it back with findpropstrict, it'll
610 contain properties like "trace". Trying to find the same
611 property on a "vanilla" local_0 yields only a "undefined" */
612 //c = abc_findpropstrict(c, "[package]::trace");
614 /*c = abc_getlocal_0(c);
615 c = abc_findpropstrict(c, "[package]::trace");
617 c = abc_setlocal_1(c);
619 c = abc_pushbyte(c, 0);
620 c = abc_setlocal_2(c);
622 code_t*xx = c = abc_label(c);
623 c = abc_findpropstrict(c, "[package]::trace");
624 c = abc_pushstring(c, "prop:");
625 c = abc_hasnext2(c, 1, 2);
627 c = abc_setlocal_3(c);
628 c = abc_callpropvoid(c, "[package]::trace", 2);
629 c = abc_getlocal_3(c);
631 c = abc_iftrue(c,xx);*/
634 typedef struct _variable {
639 methodstate_t*is_inner_method;
642 static variable_t* find_variable(state_t*s, char*name)
646 v = dict_lookup(s->vars, name);
648 if(s->new_vars) break;
653 static variable_t* find_slot(state_t*s, const char*name)
655 if(s->method && s->method->slots)
656 return dict_lookup(s->method->slots, name);
660 static variable_t* find_variable_safe(state_t*s, char*name)
662 variable_t* v = find_variable(s, name);
664 syntaxerror("undefined variable: %s", name);
667 static char variable_exists(char*name)
669 return dict_contains(state->vars, name);
671 code_t*defaultvalue(code_t*c, classinfo_t*type);
673 static int alloc_local()
675 return state->method->variable_count++;
678 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
681 variable_t*v = find_slot(state, name);
687 v->index = alloc_local();
692 dict_put(state->vars, name, v);
696 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
698 return new_variable2(name, type, init, maybeslot)->index;
701 #define TEMPVARNAME "__as3_temp__"
702 static int gettempvar()
704 variable_t*v = find_variable(state, TEMPVARNAME);
707 return new_variable(TEMPVARNAME, 0, 0, 0);
710 code_t* var_block(code_t*body)
716 for(t=0;t<state->vars->hashsize;t++) {
717 dictentry_t*e = state->vars->slots[t];
719 variable_t*v = (variable_t*)e->data;
720 if(v->type && v->init) {
721 c = defaultvalue(c, v->type);
722 c = abc_setlocal(c, v->index);
723 k = abc_kill(k, v->index);
733 if(x->opcode== OPCODE___BREAK__ ||
734 x->opcode== OPCODE___CONTINUE__) {
735 /* link kill code before break/continue */
736 code_t*e = code_dup(k);
737 code_t*s = code_start(e);
749 c = code_append(c, body);
750 c = code_append(c, k);
754 void unknown_variable(char*name)
756 if(!state->method->unresolved_variables)
757 state->method->unresolved_variables = dict_new();
758 if(!dict_contains(state->method->unresolved_variables, name))
759 dict_put(state->method->unresolved_variables, name, 0);
762 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
764 static void parsererror(const char*file, int line, const char*f)
766 syntaxerror("internal error in %s, %s:%d", f, file, line);
770 static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
772 if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
773 c = abc_getlocal_0(c);
774 c = abc_pushscope(c);
777 /* FIXME: this alloc_local() causes variable indexes to be
778 different in pass2 than in pass1 */
779 if(!m->activation_var)
780 m->activation_var = alloc_local();
782 c = abc_newactivation(c);
784 c = abc_pushscope(c);
785 c = abc_setlocal(c, m->activation_var);
787 c = abc_getlocal(c, m->activation_var);
788 c = abc_pushscope(c);
794 static code_t* method_header(methodstate_t*m)
798 c = add_scope_code(c, m, 1);
800 methodstate_list_t*l = m->innerfunctions;
802 parserassert(l->methodstate->abc);
803 if(m->uses_slots && l->methodstate->is_a_slot) {
804 c = abc_getscopeobject(c, 1);
805 c = abc_newfunction(c, l->methodstate->abc);
807 c = abc_setlocal(c, l->methodstate->var_index);
808 c = abc_setslot(c, l->methodstate->slot_index);
810 c = abc_newfunction(c, l->methodstate->abc);
811 c = abc_setlocal(c, l->methodstate->var_index);
813 free(l->methodstate);l->methodstate=0;
817 c = code_append(c, m->header);
820 if(m->is_constructor && !m->has_super) {
821 // call default constructor
822 c = abc_getlocal_0(c);
823 c = abc_constructsuper(c, 0);
827 /* all parameters that are used by inner functions
828 need to be copied from local to slot */
829 parserassert(m->activation_var);
830 DICT_ITERATE_ITEMS(m->slots,char*,name,variable_t*,v) {
831 if(v->is_parameter) {
832 c = abc_getlocal(c, m->activation_var);
833 c = abc_getlocal(c, v->index);
834 c = abc_setslot(c, v->index);
838 list_free(m->innerfunctions);
839 m->innerfunctions = 0;
844 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
846 c = code_append(c, header);
847 c = code_append(c, var_block(body));
848 /* append return if necessary */
849 if(!c || (c->opcode != OPCODE_RETURNVOID &&
850 c->opcode != OPCODE_RETURNVALUE)) {
851 c = abc_returnvoid(c);
857 static void startpackage(char*name)
860 /*printf("entering package \"%s\"\n", name);*/
861 state->package = strdup(name);
863 static void endpackage()
865 /*printf("leaving package \"%s\"\n", state->package);*/
867 //used e.g. in classinfo_register:
868 //free(state->package);state->package=0;
873 #define FLAG_PUBLIC 256
874 #define FLAG_PROTECTED 512
875 #define FLAG_PRIVATE 1024
876 #define FLAG_PACKAGEINTERNAL 2048
877 #define FLAG_NAMESPACE 4096
879 static namespace_t modifiers2access(modifiers_t*mod)
884 if(mod->flags&FLAG_NAMESPACE) {
885 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
886 syntaxerror("invalid combination of access levels and namespaces");
887 ns.access = ACCESS_NAMESPACE;
889 const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
891 /* shouldn't happen- the tokenizer only reports something as a namespace
892 if it was already registered */
893 trie_dump(active_namespaces);
894 syntaxerror("unknown namespace: %s", mod->ns);
897 } else if(mod->flags&FLAG_PUBLIC) {
898 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
899 syntaxerror("invalid combination of access levels");
900 ns.access = ACCESS_PACKAGE;
901 } else if(mod->flags&FLAG_PRIVATE) {
902 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
903 syntaxerror("invalid combination of access levels");
904 ns.access = ACCESS_PRIVATE;
905 } else if(mod->flags&FLAG_PROTECTED) {
906 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
907 syntaxerror("invalid combination of access levels");
908 ns.access = ACCESS_PROTECTED;
910 ns.access = ACCESS_PACKAGEINTERNAL;
914 static slotinfo_t* find_class(const char*name);
916 static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
918 return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
921 void add_active_url(const char*url)
925 list_append(state->active_namespace_urls, n);
928 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
933 index = new_variable("this", 0, 0, 0);
934 else if(!m->is_global)
935 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
937 index = new_variable("globalscope", 0, 0, 0);
940 parserassert(!index);
944 /* as variables and slots share the same number, make sure
945 that those variable indices are reserved. It's up to the
946 optimizer to later shuffle the variables down to lower
948 m->variable_count = m->uses_slots;
953 for(p=params->list;p;p=p->next) {
954 variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1);
959 methodstate_list_t*l = m->innerfunctions;
961 methodstate_t*m = l->methodstate;
963 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
964 m->var_index = v->index;
965 m->slot_index = v->index;
966 v->is_inner_method = m;
972 m->scope_code = add_scope_code(m->scope_code, m, 0);
975 if(as3_pass==2 && m->slots) {
976 /* exchange unresolved identifiers with the actual objects */
977 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
978 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
979 classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
980 if(!type || type->kind != INFOTYPE_CLASS) {
981 syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name);
990 char*as3_globalclass=0;
991 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
994 syntaxerror("inner classes now allowed");
999 classinfo_list_t*mlist=0;
1001 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
1002 syntaxerror("invalid modifier(s)");
1004 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
1005 syntaxerror("public and internal not supported at the same time.");
1007 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
1008 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
1009 // all classes extend object
1010 extends = registry_getobjectclass();
1013 /* create the class name, together with the proper attributes */
1017 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
1018 access = ACCESS_PRIVATE; package = internal_filename_package;
1019 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
1020 access = ACCESS_PACKAGEINTERNAL; package = state->package;
1021 } else if(state->package!=internal_filename_package) {
1022 access = ACCESS_PACKAGE; package = state->package;
1024 syntaxerror("public classes only allowed inside a package");
1028 state->cls = rfx_calloc(sizeof(classstate_t));
1029 state->cls->init = rfx_calloc(sizeof(methodstate_t));
1030 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
1031 /* notice: we make no effort to initialize the top variable (local0) here,
1032 even though it has special meaning. We just rely on the facat
1033 that pass 1 won't do anything with variables */
1035 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
1037 /* set current method to constructor- all code within the class-level (except
1038 static variable initializations) will be executed during construction time */
1039 state->method = state->cls->init;
1041 if(registry_find(package, classname)) {
1042 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
1044 /* build info struct */
1045 int num_interfaces = (list_length(implements));
1046 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
1047 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
1050 classinfo_list_t*l = implements;
1051 for(l=implements;l;l=l->next) {
1052 state->cls->info->interfaces[pos++] = l->classinfo;
1057 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1059 state->method = state->cls->init;
1060 parserassert(state->cls && state->cls->info);
1062 function_initvars(state->cls->init, 0, 0, 1);
1063 function_initvars(state->cls->static_init, 0, 0, 0);
1065 if(extends && (extends->flags & FLAG_FINAL))
1066 syntaxerror("Can't extend final class '%s'", extends->name);
1069 while(state->cls->info->interfaces[pos]) {
1070 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1071 syntaxerror("'%s' is not an interface",
1072 state->cls->info->interfaces[pos]->name);
1076 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
1077 state->cls->info->superclass = extends;
1079 /* generate the abc code for this class */
1080 MULTINAME(classname2,state->cls->info);
1081 multiname_t*extends2 = sig2mname(extends);
1083 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1084 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1085 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1086 if(state->cls->info->flags&FLAG_INTERFACE) {
1087 abc_class_interface(state->cls->abc);
1090 abc_class_protectedNS(state->cls->abc, classname);
1092 for(mlist=implements;mlist;mlist=mlist->next) {
1093 MULTINAME(m, mlist->classinfo);
1094 abc_class_add_interface(state->cls->abc, &m);
1097 /* write the construction code for this class to the global init
1099 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1101 abc_method_body_t*m = global->init->method->body;
1102 __ getglobalscope(m);
1103 classinfo_t*s = extends;
1108 //TODO: take a look at the current scope stack, maybe
1109 // we can re-use something
1114 multiname_t*s2 = sig2mname(s);
1116 multiname_destroy(s2);
1118 __ pushscope(m); count++;
1119 m->code = m->code->prev->prev; // invert
1121 /* continue appending after last op end */
1122 while(m->code && m->code->next) m->code = m->code->next;
1124 /* TODO: if this is one of *our* classes, we can also
1125 do a getglobalscope/getslot <nr> (which references
1126 the init function's slots) */
1128 __ getlex2(m, extends2);
1130 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1131 stack is not the superclass */
1132 __ pushscope(m);count++;
1135 /* notice: we get a verify error #1107 if the top element on the scope
1136 stack is not the global object */
1138 __ pushscope(m);count++;
1140 __ newclass(m,state->cls->abc);
1144 __ setslot(m, slotindex);
1145 multiname_destroy(extends2);
1147 /* flash.display.MovieClip handling */
1149 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1150 if(state->package && state->package[0]) {
1151 as3_globalclass = concat3(state->package, ".", classname);
1153 as3_globalclass = strdup(classname);
1159 static int slotstate_varconst = 0;
1160 static modifiers_t*slotstate_flags = 0;
1161 static void setslotstate(modifiers_t* flags, int varconst)
1163 slotstate_varconst = varconst;
1164 slotstate_flags = flags;
1166 if(flags && flags->flags&FLAG_STATIC) {
1167 state->method = state->cls->static_init;
1169 state->method = state->cls->init;
1172 parserassert(state->method);
1176 static void endclass()
1179 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1181 c = abc_getlocal_0(c);
1182 c = abc_constructsuper(c, 0);
1183 state->cls->init->header = code_append(state->cls->init->header, c);
1184 state->cls->has_constructor=1;
1186 if(state->cls->init) {
1187 if(state->cls->info->flags&FLAG_INTERFACE) {
1188 if(state->cls->init->header)
1189 syntaxerror("interface can not have class-level code");
1191 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1192 code_t*c = method_header(state->cls->init);
1193 m->body->code = wrap_function(c, 0, m->body->code);
1196 if(state->cls->static_init) {
1197 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1198 code_t*c = method_header(state->cls->static_init);
1199 m->body->code = wrap_function(c, 0, m->body->code);
1206 void check_code_for_break(code_t*c)
1209 if(c->opcode == OPCODE___BREAK__) {
1210 char*name = string_cstr(c->data[0]);
1211 syntaxerror("Unresolved \"break %s\"", name);
1213 if(c->opcode == OPCODE___CONTINUE__) {
1214 char*name = string_cstr(c->data[0]);
1215 syntaxerror("Unresolved \"continue %s\"", name);
1217 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1218 char*name = string_cstr(c->data[0]);
1219 syntaxerror("Can't reference a package (%s) as such", name);
1226 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1229 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1230 if(TYPE_IS_NUMBER(t)) {
1231 xassert(c->type == CONSTANT_FLOAT
1232 || c->type == CONSTANT_INT
1233 || c->type == CONSTANT_UINT);
1234 } else if(TYPE_IS_UINT(t)) {
1235 xassert(c->type == CONSTANT_UINT ||
1236 (c->type == CONSTANT_INT && c->i>=0));
1237 } else if(TYPE_IS_INT(t)) {
1238 xassert(c->type == CONSTANT_INT);
1239 } else if(TYPE_IS_BOOLEAN(t)) {
1240 xassert(c->type == CONSTANT_TRUE
1241 || c->type == CONSTANT_FALSE);
1245 static void check_override(memberinfo_t*m, int flags)
1249 if(m->parent == state->cls->info)
1250 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1252 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1253 if(m->access==ACCESS_PRIVATE)
1255 if(m->flags & FLAG_FINAL)
1256 syntaxerror("can't override final member %s", m->name);
1258 /* allow this. it's no issue.
1259 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1260 syntaxerror("can't override static member %s", m->name);*/
1262 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1263 syntaxerror("can't override non-static member %s with static declaration", m->name);
1265 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1266 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1267 if(m->kind == INFOTYPE_METHOD)
1268 syntaxerror("can't override without explicit 'override' declaration");
1270 syntaxerror("can't override '%s'", m->name);
1275 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1277 methodinfo_t*minfo = 0;
1278 namespace_t ns = modifiers2access(mod);
1281 minfo = methodinfo_register_global(ns.access, state->package, name);
1282 minfo->return_type = 0; // save this for pass 2
1283 } else if(getset != KW_GET && getset != KW_SET) {
1285 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1287 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1289 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1290 minfo->return_type = 0; // save this for pass 2
1291 // getslot on a member slot only returns "undefined", so no need
1292 // to actually store these
1293 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1295 //class getter/setter
1296 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1298 if(getset == KW_GET) {
1300 } else if(params->list && params->list->param && !params->list->next) {
1301 type = params->list->param->type;
1303 syntaxerror("setter function needs to take exactly one argument");
1304 // not sure wether to look into superclasses here, too
1305 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1307 if(minfo->kind!=INFOTYPE_SLOT)
1308 syntaxerror("class already contains a method called '%s'", name);
1309 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1310 syntaxerror("class already contains a field called '%s'", name);
1311 if(minfo->subtype & gs)
1312 syntaxerror("getter/setter for '%s' already defined", name);
1313 /* make a setter or getter into a getset */
1314 minfo->subtype |= gs;
1317 FIXME: this check needs to be done in pass 2
1319 if((!minfo->return_type != !type) ||
1320 (minfo->return_type && type &&
1321 !strcmp(minfo->return_type->name, type->name))) {
1322 syntaxerror("different type in getter and setter: %s and %s",
1323 minfo->return_type?minfo->return_type->name:"*",
1324 type?type->name:"*");
1327 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1328 minfo->kind = INFOTYPE_SLOT; //hack
1329 minfo->subtype = gs;
1330 minfo->return_type = 0;
1332 /* can't assign a slot as getter and setter might have different slots */
1333 //minfo->slot = slot;
1335 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1336 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1337 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1342 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1344 //parserassert(state->method && state->method->info);
1346 methodstate_t*parent_method = state->method;
1349 return_type = 0; // not valid in pass 1
1353 state->new_vars = 1;
1356 state->method = rfx_calloc(sizeof(methodstate_t));
1357 state->method->inner = 1;
1358 state->method->variable_count = 0;
1359 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1361 NEW(methodinfo_t,minfo);
1362 minfo->kind = INFOTYPE_METHOD;
1363 minfo->access = ACCESS_PACKAGEINTERNAL;
1365 state->method->info = minfo;
1368 list_append(parent_method->innerfunctions, state->method);
1370 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1372 function_initvars(state->method, params, 0, 1);
1376 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1377 state->method->variable_count = 0;
1378 parserassert(state->method);
1380 state->method->info->return_type = return_type;
1381 function_initvars(state->method, params, 0, 1);
1385 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1386 params_t*params, classinfo_t*return_type)
1388 if(state->method && state->method->info) {
1389 syntaxerror("not able to start another method scope");
1392 state->new_vars = 1;
1395 state->method = rfx_calloc(sizeof(methodstate_t));
1396 state->method->has_super = 0;
1399 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1401 state->method->is_global = 1;
1402 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1404 if(state->method->is_constructor)
1405 name = "__as3_constructor__";
1407 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1409 function_initvars(state->method, params, mod->flags, 1);
1411 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1415 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1416 state->method->variable_count = 0;
1417 parserassert(state->method);
1420 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1421 check_override(m, mod->flags);
1425 state->cls->has_constructor |= state->method->is_constructor;
1428 state->method->info->return_type = return_type;
1429 function_initvars(state->method, params, mod->flags, 1);
1433 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1434 params_t*params, classinfo_t*return_type, code_t*body)
1437 // store inner methods in variables
1438 function_initvars(state->method, 0, 0, 0);
1440 methodstate_list_t*ml = state->method->innerfunctions;
1442 dict_t*xvars = dict_new();
1445 methodstate_t*m = ml->methodstate;
1446 parserassert(m->inner);
1447 if(m->unresolved_variables) {
1448 dict_t*d = m->unresolved_variables;
1450 for(t=0;t<d->hashsize;t++) {
1451 dictentry_t*l = d->slots[t];
1453 /* check parent method's variables */
1455 if((v=find_variable(state, l->key))) {
1456 m->uses_parent_function = 1;
1457 state->method->uses_slots = 1;
1458 dict_put(xvars, l->key, 0);
1465 dict_destroy(m->unresolved_variables);
1466 m->unresolved_variables = 0;
1471 if(state->method->uses_slots) {
1472 state->method->slots = dict_new();
1474 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1475 if(!name) syntaxerror("internal error");
1476 if(v->index && dict_contains(xvars, name)) {
1479 if(v->is_inner_method) {
1480 v->is_inner_method->is_a_slot = 1;
1483 dict_put(state->method->slots, name, v);
1486 state->method->uses_slots = i;
1487 dict_destroy(state->vars);state->vars = 0;
1494 /*if(state->method->uses_parent_function){
1495 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1500 multiname_t*type2 = sig2mname(return_type);
1502 if(state->method->inner) {
1503 f = state->method->abc;
1504 abc_method_init(f, global->file, type2, 1);
1505 } else if(state->method->is_constructor) {
1506 f = abc_class_getconstructor(state->cls->abc, type2);
1507 } else if(!state->method->is_global) {
1508 namespace_t mname_ns = modifiers2access(mod);
1509 multiname_t mname = {QNAME, &mname_ns, 0, name};
1511 if(mod->flags&FLAG_STATIC)
1512 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1514 f = abc_class_method(state->cls->abc, type2, &mname);
1515 slot = f->trait->slot_id;
1517 namespace_t mname_ns = {state->method->info->access, state->package};
1518 multiname_t mname = {QNAME, &mname_ns, 0, name};
1520 f = abc_method_new(global->file, type2, 1);
1521 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1522 //abc_code_t*c = global->init->method->body->code;
1524 //flash doesn't seem to allow us to access function slots
1525 //state->method->info->slot = slot;
1527 if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1528 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1529 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1530 if(params->varargs) f->flags |= METHOD_NEED_REST;
1534 for(p=params->list;p;p=p->next) {
1535 if(params->varargs && !p->next) {
1536 break; //varargs: omit last parameter in function signature
1538 multiname_t*m = sig2mname(p->param->type);
1539 list_append(f->parameters, m);
1540 if(p->param->value) {
1541 check_constant_against_type(p->param->type, p->param->value);
1542 opt=1;list_append(f->optional_parameters, p->param->value);
1544 syntaxerror("non-optional parameter not allowed after optional parameters");
1547 if(state->method->slots) {
1548 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1550 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1551 multiname_t*type = sig2mname(v->type);
1552 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1553 t->slot_id = v->index;
1558 check_code_for_break(body);
1560 /* Seems this works now.
1561 if(state->method->exceptions && state->method->uses_slots) {
1562 as3_warning("try/catch and activation not supported yet within the same method");
1566 f->body->code = body;
1567 f->body->exceptions = state->method->exceptions;
1568 } else { //interface
1570 syntaxerror("interface methods can't have a method body");
1580 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1585 void breakjumpsto(code_t*c, char*name, code_t*jump)
1588 if(c->opcode == OPCODE___BREAK__) {
1589 string_t*name2 = c->data[0];
1590 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1591 c->opcode = OPCODE_JUMP;
1598 void continuejumpsto(code_t*c, char*name, code_t*jump)
1601 if(c->opcode == OPCODE___CONTINUE__) {
1602 string_t*name2 = c->data[0];
1603 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1604 c->opcode = OPCODE_JUMP;
1612 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1613 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1614 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1616 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1618 if(!type1 || !type2)
1619 return registry_getanytype();
1620 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1621 return registry_getanytype();
1624 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1633 return registry_getanytype();
1635 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1640 return abc_coerce_a(c);
1644 // cast an "any" type to a specific type. subject to
1645 // runtime exceptions
1646 return abc_coerce2(c, &m);
1649 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1650 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1651 // allow conversion between number types
1652 return abc_coerce2(c, &m);
1654 //printf("%s.%s\n", from.package, from.name);
1655 //printf("%s.%s\n", to.package, to.name);
1657 classinfo_t*supertype = from;
1659 if(supertype == to) {
1660 // target type is one of from's superclasses
1661 return abc_coerce2(c, &m);
1664 while(supertype->interfaces[t]) {
1665 if(supertype->interfaces[t]==to) {
1666 // target type is one of from's interfaces
1667 return abc_coerce2(c, &m);
1671 supertype = supertype->superclass;
1673 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1675 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1677 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1680 as3_error("can't convert type %s%s%s to %s%s%s",
1681 from->package, from->package?".":"", from->name,
1682 to->package, to->package?".":"", to->name);
1687 code_t*defaultvalue(code_t*c, classinfo_t*type)
1689 if(TYPE_IS_INT(type)) {
1690 c = abc_pushbyte(c, 0);
1691 } else if(TYPE_IS_UINT(type)) {
1692 c = abc_pushuint(c, 0);
1693 } else if(TYPE_IS_FLOAT(type)) {
1695 } else if(TYPE_IS_BOOLEAN(type)) {
1696 c = abc_pushfalse(c);
1698 //c = abc_pushundefined(c);
1700 c = abc_pushnull(c);
1702 c = abc_coerce2(c, &m);
1707 char is_pushundefined(code_t*c)
1709 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1712 static const char* get_package_from_name(const char*name)
1714 /* try explicit imports */
1715 dictentry_t* e = dict_get_slot(state->imports, name);
1717 if(!strcmp(e->key, name)) {
1718 slotinfo_t*c = (slotinfo_t*)e->data;
1719 if(c) return c->package;
1725 static namespace_list_t*get_current_imports()
1727 namespace_list_t*searchlist = 0;
1729 list_append(searchlist, namespace_new_package(state->package));
1731 import_list_t*l = state->wildcard_imports;
1733 namespace_t*ns = namespace_new_package(l->import->package);
1734 list_append(searchlist, ns);
1737 list_append(searchlist, namespace_new_package(""));
1738 list_append(searchlist, namespace_new_package(internal_filename_package));
1742 static slotinfo_t* find_class(const char*name)
1746 c = registry_find(state->package, name);
1749 /* try explicit imports */
1750 dictentry_t* e = dict_get_slot(state->imports, name);
1753 if(!strcmp(e->key, name)) {
1754 c = (slotinfo_t*)e->data;
1760 /* try package.* imports */
1761 import_list_t*l = state->wildcard_imports;
1763 //printf("does package %s contain a class %s?\n", l->import->package, name);
1764 c = registry_find(l->import->package, name);
1769 /* try global package */
1770 c = registry_find("", name);
1773 /* try local "filename" package */
1774 c = registry_find(internal_filename_package, name);
1779 typedcode_t push_class(slotinfo_t*a)
1784 if(a->access == ACCESS_PACKAGEINTERNAL &&
1785 strcmp(a->package, state->package) &&
1786 strcmp(a->package, internal_filename_package)
1788 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1789 infotypename(a), a->name, a->package, state->package);
1792 if(a->kind != INFOTYPE_CLASS) {
1794 x.c = abc_findpropstrict2(x.c, &m);
1795 x.c = abc_getproperty2(x.c, &m);
1796 if(a->kind == INFOTYPE_METHOD) {
1797 methodinfo_t*f = (methodinfo_t*)a;
1798 x.t = TYPE_FUNCTION(f);
1800 varinfo_t*v = (varinfo_t*)a;
1804 classinfo_t*c = (classinfo_t*)a;
1806 x.c = abc_getglobalscope(x.c);
1807 x.c = abc_getslot(x.c, c->slot);
1810 x.c = abc_getlex2(x.c, &m);
1812 x.t = TYPE_CLASS(c);
1817 static char is_getlocal(code_t*c)
1819 if(!c || c->prev || c->next)
1821 return(c->opcode == OPCODE_GETLOCAL
1822 || c->opcode == OPCODE_GETLOCAL_0
1823 || c->opcode == OPCODE_GETLOCAL_1
1824 || c->opcode == OPCODE_GETLOCAL_2
1825 || c->opcode == OPCODE_GETLOCAL_3);
1827 static int getlocalnr(code_t*c)
1829 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1830 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1831 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1832 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1833 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1834 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1838 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1842 [prefix code] [read instruction]
1846 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1848 if(in && in->opcode == OPCODE_COERCE_A) {
1849 in = code_cutlast(in);
1852 syntaxerror("internal error");
1854 /* chop off read instruction */
1858 prefix = r->prev;r->prev = 0;
1864 char use_temp_var = readbefore;
1866 /* generate the write instruction, and maybe append a dup to the prefix code */
1867 code_t* write = abc_nop(0);
1868 if(r->opcode == OPCODE_GETPROPERTY) {
1869 write->opcode = OPCODE_SETPROPERTY;
1870 multiname_t*m = (multiname_t*)r->data[0];
1871 write->data[0] = multiname_clone(m);
1872 if(m->type == QNAME || m->type == MULTINAME) {
1874 prefix = abc_dup(prefix); // we need the object, too
1877 } else if(m->type == MULTINAMEL) {
1879 /* dupping two values on the stack requires 5 operations and one register-
1880 couldn't adobe just have given us a dup2? */
1881 int temp = gettempvar();
1882 prefix = abc_setlocal(prefix, temp);
1883 prefix = abc_dup(prefix);
1884 prefix = abc_getlocal(prefix, temp);
1885 prefix = abc_swap(prefix);
1886 prefix = abc_getlocal(prefix, temp);
1888 prefix = abc_kill(prefix, temp);
1892 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1894 } else if(r->opcode == OPCODE_GETSLOT) {
1895 write->opcode = OPCODE_SETSLOT;
1896 write->data[0] = r->data[0];
1898 prefix = abc_dup(prefix); // we need the object, too
1901 } else if(r->opcode == OPCODE_GETLOCAL) {
1902 write->opcode = OPCODE_SETLOCAL;
1903 write->data[0] = r->data[0];
1904 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1905 write->opcode = OPCODE_SETLOCAL_0;
1906 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1907 write->opcode = OPCODE_SETLOCAL_1;
1908 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1909 write->opcode = OPCODE_SETLOCAL_2;
1910 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1911 write->opcode = OPCODE_SETLOCAL_3;
1912 } else if(r->opcode == OPCODE_GETSUPER) {
1913 write->opcode = OPCODE_SETSUPER;
1914 multiname_t*m = (multiname_t*)r->data[0];
1915 write->data[0] = multiname_clone(m);
1918 syntaxerror("illegal lvalue: can't assign a value to this expression");
1925 /* with getproperty/getslot, we have to be extra careful not
1926 to execute the read code twice, as it might have side-effects
1927 (e.g. if the property is in fact a setter/getter combination)
1929 So read the value, modify it, and write it again,
1930 using prefix only once and making sure (by using a temporary
1931 register) that the return value is what we just wrote */
1932 temp = gettempvar();
1933 c = code_append(c, prefix);
1934 c = code_append(c, r);
1937 c = abc_setlocal(c, temp);
1939 c = code_append(c, middlepart);
1942 c = abc_setlocal(c, temp);
1944 c = code_append(c, write);
1945 c = abc_getlocal(c, temp);
1946 c = abc_kill(c, temp);
1948 /* if we're allowed to execute the read code twice *and*
1949 the middlepart doesn't modify the code, things are easier.
1951 code_t* r2 = code_dup(r);
1952 //c = code_append(c, prefix);
1953 parserassert(!prefix);
1954 c = code_append(c, r);
1955 c = code_append(c, middlepart);
1956 c = code_append(c, write);
1957 c = code_append(c, r2);
1960 /* even smaller version: overwrite the value without reading
1964 c = code_append(c, prefix);
1967 c = code_append(c, middlepart);
1968 c = code_append(c, write);
1969 c = code_append(c, r);
1972 temp = gettempvar();
1974 c = code_append(c, prefix);
1976 c = code_append(c, middlepart);
1978 c = abc_setlocal(c, temp);
1979 c = code_append(c, write);
1980 c = abc_getlocal(c, temp);
1981 c = abc_kill(c, temp);
1987 char is_break_or_jump(code_t*c)
1991 if(c->opcode == OPCODE_JUMP ||
1992 c->opcode == OPCODE___BREAK__ ||
1993 c->opcode == OPCODE___CONTINUE__ ||
1994 c->opcode == OPCODE_THROW ||
1995 c->opcode == OPCODE_RETURNVOID ||
1996 c->opcode == OPCODE_RETURNVALUE) {
2002 #define IS_FINALLY_TARGET(op) \
2003 ((op) == OPCODE___CONTINUE__ || \
2004 (op) == OPCODE___BREAK__ || \
2005 (op) == OPCODE_RETURNVOID || \
2006 (op) == OPCODE_RETURNVALUE || \
2007 (op) == OPCODE___RETHROW__)
2009 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
2011 #define NEED_EXTRA_STACK_ARG
2012 code_t*finally_label = abc_nop(0);
2013 NEW(lookupswitch_t, l);
2019 code_t*prev = i->prev;
2020 if(IS_FINALLY_TARGET(i->opcode)) {
2023 if(i->opcode == OPCODE___RETHROW__ ||
2024 i->opcode == OPCODE_RETURNVALUE) {
2025 if(i->opcode == OPCODE___RETHROW__)
2026 i->opcode = OPCODE_THROW;
2028 p = abc_coerce_a(p);
2029 p = abc_setlocal(p, tempvar);
2031 p = abc_pushbyte(p, count++);
2032 p = abc_jump(p, finally_label);
2033 code_t*target = p = abc_label(p);
2034 #ifdef NEED_EXTRA_STACK_ARG
2038 p = abc_getlocal(p, tempvar);
2041 p->next = i;i->prev = p;
2042 list_append(l->targets, target);
2048 c = abc_pushbyte(c, -1);
2049 c = code_append(c, finally_label);
2050 c = code_append(c, finally);
2052 #ifdef NEED_EXTRA_STACK_ARG
2055 c = abc_lookupswitch(c, l);
2056 c = l->def = abc_label(c);
2057 #ifdef NEED_EXTRA_STACK_ARG
2064 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
2068 code_t*prev = i->prev;
2069 if(IS_FINALLY_TARGET(i->opcode)) {
2070 if(i->opcode == OPCODE___RETHROW__)
2071 i->opcode = OPCODE_THROW;
2072 code_t*end = code_dup(finally);
2073 code_t*start = code_start(end);
2074 if(prev) prev->next = start;
2081 return code_append(c, finally);
2084 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
2090 int num_insertion_points=0;
2092 if(IS_FINALLY_TARGET(i->opcode))
2093 num_insertion_points++;
2100 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
2105 int simple_version_cost = (1+num_insertion_points)*code_size;
2106 int lookup_version_cost = 4*num_insertion_points + 5;
2108 if(cantdup || simple_version_cost > lookup_version_cost) {
2109 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
2110 return insert_finally_lookup(c, finally, tempvar);
2112 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
2113 return insert_finally_simple(c, finally, tempvar);
2117 #define PASS1 }} if(as3_pass == 1) {{
2118 #define PASS1END }} if(as3_pass == 2) {{
2119 #define PASS2 }} if(as3_pass == 2) {{
2120 #define PASS12 }} if(as3_pass == 1 || as3_pass == 2) {{
2121 #define PASS12END }} if(as3_pass == 2) {{
2122 #define PASS_ALWAYS }} {{
2128 /* ------------ code blocks / statements ---------------- */
2130 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2132 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2133 PROGRAM_CODE_LIST: PROGRAM_CODE
2134 | PROGRAM_CODE_LIST PROGRAM_CODE
2136 PROGRAM_CODE: PACKAGE_DECLARATION
2137 | INTERFACE_DECLARATION
2139 | FUNCTION_DECLARATION
2142 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
2145 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2146 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2147 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2149 INPACKAGE_CODE: INTERFACE_DECLARATION
2151 | FUNCTION_DECLARATION
2154 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
2157 MAYBECODE: CODE {$$=$1;}
2158 MAYBECODE: {$$=code_new();}
2160 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2161 CODE: CODEPIECE {$$=$1;}
2163 // code which may appear outside of methods
2164 CODE_STATEMENT: IMPORT
2166 CODE_STATEMENT: FOR_IN
2167 CODE_STATEMENT: WHILE
2168 CODE_STATEMENT: DO_WHILE
2169 CODE_STATEMENT: SWITCH
2171 CODE_STATEMENT: WITH
2173 CODE_STATEMENT: VOIDEXPRESSION
2174 CODE_STATEMENT: USE_NAMESPACE
2175 CODE_STATEMENT: NAMESPACE_DECLARATION
2176 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2177 CODE_STATEMENT: '{' '}' {$$=0;}
2179 // code which may appear in methods
2180 CODEPIECE: ';' {$$=0;}
2181 CODEPIECE: CODE_STATEMENT
2182 CODEPIECE: VARIABLE_DECLARATION
2187 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {PASS_ALWAYS as3_pass=$1;}
2189 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2190 //CODEBLOCK : '{' '}' {$$=0;}
2191 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2192 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2194 /* ------------ package init code ------------------- */
2196 PACKAGE_INITCODE: CODE_STATEMENT {
2197 code_t**cc = &global->init->method->body->code;
2198 *cc = code_append(*cc, $1);
2201 /* ------------ conditional compilation ------------- */
2203 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER {
2206 char*key = concat3($1,"::",$3);
2207 if(!conditionals || !dict_contains(conditionals, key)) {
2213 /* ------------ variables --------------------------- */
2215 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2216 | {$$.c=abc_pushundefined(0);
2220 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2221 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2223 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2224 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2226 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2229 if(variable_exists($1))
2230 syntaxerror("Variable %s already defined", $1);
2232 new_variable($1, 0, 1, 0);
2235 if(!is_subtype_of($3.t, $2)) {
2236 syntaxerror("Can't convert %s to %s", $3.t->name,
2242 if(state->method->uses_slots) {
2243 variable_t* v = find_slot(state, $1);
2245 // this variable is stored in a slot
2253 index = new_variable($1, $2, 1, 0);
2256 $$ = slot?abc_getscopeobject(0, 1):0;
2259 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2260 $$ = code_append($$, $3.c);
2261 $$ = converttype($$, $3.t, $2);
2264 $$ = defaultvalue($$, $2);
2267 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2268 $$ = code_append($$, $3.c);
2269 $$ = abc_coerce_a($$);
2271 // don't do anything
2279 $$ = abc_setslot($$, index);
2281 $$ = abc_setlocal($$, index);
2285 /* ------------ control flow ------------------------- */
2287 MAYBEELSE: %prec below_else {$$ = code_new();}
2288 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2289 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2291 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2294 $$ = code_append($$, $4.c);
2295 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2297 $$ = code_append($$, $6);
2299 myjmp = $$ = abc_jump($$, 0);
2301 myif->branch = $$ = abc_nop($$);
2303 $$ = code_append($$, $7);
2304 myjmp->branch = $$ = abc_nop($$);
2310 FOR_INIT : {$$=code_new();}
2311 FOR_INIT : VARIABLE_DECLARATION
2312 FOR_INIT : VOIDEXPRESSION
2314 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2315 // (I don't see any easy way to revolve this conflict otherwise, as we
2316 // can't touch VAR_READ without upsetting the precedence about "return")
2317 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2318 PASS1 $$=$2;new_variable($2,0,1,0);
2319 PASS2 $$=$2;new_variable($2,$3,1,0);
2321 FOR_IN_INIT : T_IDENTIFIER {
2326 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2327 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2329 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2330 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2332 $$ = code_append($$, $2);
2333 code_t*loopstart = $$ = abc_label($$);
2334 $$ = code_append($$, $4.c);
2335 code_t*myif = $$ = abc_iffalse($$, 0);
2336 $$ = code_append($$, $8);
2337 code_t*cont = $$ = abc_nop($$);
2338 $$ = code_append($$, $6);
2339 $$ = abc_jump($$, loopstart);
2340 code_t*out = $$ = abc_nop($$);
2341 breakjumpsto($$, $1.name, out);
2342 continuejumpsto($$, $1.name, cont);
2349 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2350 variable_t*var = find_variable(state, $2);
2352 syntaxerror("variable %s not known in this scope", $2);
2355 char*tmp1name = concat2($2, "__tmp1__");
2356 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2357 char*tmp2name = concat2($2, "__array__");
2358 int array = new_variable(tmp1name, 0, 0, 0);
2361 $$ = code_append($$, $4.c);
2362 $$ = abc_coerce_a($$);
2363 $$ = abc_setlocal($$, array);
2364 $$ = abc_pushbyte($$, 0);
2365 $$ = abc_setlocal($$, it);
2367 code_t*loopstart = $$ = abc_label($$);
2369 $$ = abc_hasnext2($$, array, it);
2370 code_t*myif = $$ = abc_iffalse($$, 0);
2371 $$ = abc_getlocal($$, array);
2372 $$ = abc_getlocal($$, it);
2374 $$ = abc_nextname($$);
2376 $$ = abc_nextvalue($$);
2377 $$ = converttype($$, 0, var->type);
2378 $$ = abc_setlocal($$, var->index);
2380 $$ = code_append($$, $6);
2381 $$ = abc_jump($$, loopstart);
2383 code_t*out = $$ = abc_nop($$);
2384 breakjumpsto($$, $1.name, out);
2385 continuejumpsto($$, $1.name, loopstart);
2397 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2401 code_t*myjmp = $$ = abc_jump($$, 0);
2402 code_t*loopstart = $$ = abc_label($$);
2403 $$ = code_append($$, $6);
2404 code_t*cont = $$ = abc_nop($$);
2405 myjmp->branch = cont;
2406 $$ = code_append($$, $4.c);
2407 $$ = abc_iftrue($$, loopstart);
2408 code_t*out = $$ = abc_nop($$);
2409 breakjumpsto($$, $1, out);
2410 continuejumpsto($$, $1, cont);
2416 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2418 code_t*loopstart = $$ = abc_label($$);
2419 $$ = code_append($$, $3);
2420 code_t*cont = $$ = abc_nop($$);
2421 $$ = code_append($$, $6.c);
2422 $$ = abc_iftrue($$, loopstart);
2423 code_t*out = $$ = abc_nop($$);
2424 breakjumpsto($$, $1, out);
2425 continuejumpsto($$, $1, cont);
2431 BREAK : "break" %prec prec_none {
2432 $$ = abc___break__(0, "");
2434 BREAK : "break" T_IDENTIFIER {
2435 $$ = abc___break__(0, $2);
2437 CONTINUE : "continue" %prec prec_none {
2438 $$ = abc___continue__(0, "");
2440 CONTINUE : "continue" T_IDENTIFIER {
2441 $$ = abc___continue__(0, $2);
2444 MAYBE_CASE_LIST : {$$=0;}
2445 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2446 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2447 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2448 CASE_LIST: CASE {$$=$1;}
2449 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2451 CASE: "case" E ':' MAYBECODE {
2452 $$ = abc_getlocal(0, state->switch_var);
2453 $$ = code_append($$, $2.c);
2454 code_t*j = $$ = abc_ifne($$, 0);
2455 $$ = code_append($$, $4);
2456 if($$->opcode != OPCODE___BREAK__) {
2457 $$ = abc___fallthrough__($$, "");
2459 code_t*e = $$ = abc_nop($$);
2462 DEFAULT: "default" ':' MAYBECODE {
2465 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2467 $$ = abc_setlocal($$, state->switch_var);
2468 $$ = code_append($$, $7);
2470 code_t*out = $$ = abc_kill($$, state->switch_var);
2471 breakjumpsto($$, $1, out);
2473 code_t*c = $$,*lastblock=0;
2475 if(c->opcode == OPCODE_IFNE) {
2476 if(!c->next) syntaxerror("internal error in fallthrough handling");
2478 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2480 c->opcode = OPCODE_JUMP;
2481 c->branch = lastblock;
2483 /* fall through end of switch */
2484 c->opcode = OPCODE_NOP;
2494 /* ------------ try / catch /finally ---------------- */
2496 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2497 state->exception_name=$3;
2498 PASS1 new_variable($3, 0, 0, 0);
2499 PASS2 new_variable($3, $4, 0, 0);
2502 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2503 multiname_t name = {QNAME, &name_ns, 0, $3};
2505 NEW(abc_exception_t, e)
2506 e->exc_type = sig2mname($4);
2507 e->var_name = multiname_clone(&name);
2511 int i = find_variable_safe(state, $3)->index;
2512 e->target = c = abc_nop(0);
2513 c = abc_setlocal(c, i);
2514 c = code_append(c, code_dup(state->method->scope_code));
2515 c = code_append(c, $8);
2521 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2526 NEW(abc_exception_t, e)
2527 e->exc_type = 0; //all exceptions
2528 e->var_name = 0; //no name
2531 e->to = code_append(e->to, $4);
2537 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2538 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2539 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2540 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2544 list_append($$.l,$2);
2545 $$.finally = $2->to;$2->to=0;
2548 CATCH_FINALLY_LIST: FINALLY {
2552 list_append($$.l,$1);
2553 $$.finally = $1->to;$1->to=0;
2557 TRY : "try" '{' {PASS12 new_state();
2558 state->method->has_exceptions=1;
2559 state->method->late_binding=1;//for invariant scope_code
2560 } MAYBECODE '}' CATCH_FINALLY_LIST {
2561 code_t*out = abc_nop(0);
2563 code_t*start = abc_nop(0);
2564 $$ = code_append(start, $4);
2565 if(!is_break_or_jump($4)) {
2566 $$ = abc_jump($$, out);
2568 code_t*end = $$ = abc_nop($$);
2572 tmp = new_variable("__finally__", 0, 0, 0);
2574 abc_exception_list_t*l = $6.l;
2577 abc_exception_t*e = l->abc_exception;
2579 $$ = code_append($$, e->target);
2580 $$ = abc_jump($$, out);
2582 parserassert((ptroff_t)$6.finally);
2584 e->target = $$ = abc_nop($$);
2585 $$ = code_append($$, code_dup(state->method->scope_code));
2586 $$ = abc___rethrow__($$);
2594 $$ = code_append($$, out);
2596 $$ = insert_finally($$, $6.finally, tmp);
2598 list_concat(state->method->exceptions, $6.l);
2604 /* ------------ throw ------------------------------- */
2606 THROW : "throw" EXPRESSION {
2610 THROW : "throw" %prec prec_none {
2611 if(!state->exception_name)
2612 syntaxerror("re-throw only possible within a catch block");
2613 variable_t*v = find_variable(state, state->exception_name);
2615 $$=abc_getlocal($$, v->index);
2619 /* ------------ with -------------------------------- */
2621 WITH_HEAD : "with" '(' EXPRESSION ')' {
2623 if(state->method->has_exceptions) {
2624 int v = alloc_local();
2625 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2626 state->method->scope_code = abc_pushwith(state->method->scope_code);
2631 WITH : WITH_HEAD CODEBLOCK {
2632 /* remove getlocal;pushwith from scope code again */
2633 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2636 if(state->method->has_exceptions) {
2638 $$ = abc_setlocal($$, $1.number);
2640 $$ = abc_pushwith($$);
2641 $$ = code_append($$, $2);
2642 $$ = abc_popscope($$);
2646 /* ------------ packages and imports ---------------- */
2648 X_IDENTIFIER: T_IDENTIFIER
2649 | "package" {PASS12 $$="package";}
2650 | T_NAMESPACE {PASS12 $$=$1;}
2652 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2653 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2655 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2656 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2657 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2658 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2660 IMPORT : "import" PACKAGEANDCLASS {
2662 slotinfo_t*s = registry_find($2->package, $2->name);
2663 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2664 as3_schedule_class($2->package, $2->name);
2668 syntaxerror("Couldn't import class\n");
2669 state_has_imports();
2670 dict_put(state->imports, c->name, c);
2671 import_toplevel(c->package);
2674 IMPORT : "import" PACKAGE '.' '*' {
2676 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2677 as3_schedule_package($2);
2682 state_has_imports();
2683 list_append(state->wildcard_imports, i);
2684 import_toplevel(i->package);
2688 /* ------------ classes and interfaces (header) -------------- */
2690 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2691 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2692 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2693 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2695 $$.flags=$1.flags|$2.flags;
2696 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2697 $$.ns=$1.ns?$1.ns:$2.ns;
2700 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2701 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2702 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2703 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2704 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2705 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2706 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2707 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2708 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2709 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2713 EXTENDS : {PASS12 $$=0;}
2714 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2716 EXTENDS_LIST : {PASS12 $$=list_new();}
2717 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2719 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2720 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2722 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2723 EXTENDS IMPLEMENTS_LIST
2724 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2726 '}' {PASS12 endclass();$$=0;}
2728 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2730 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2731 startclass(&$1,$3,0,$4);}
2732 MAYBE_INTERFACE_BODY
2733 '}' {PASS12 endclass();$$=0;}
2735 /* ------------ classes and interfaces (body) -------------- */
2738 MAYBE_CLASS_BODY : CLASS_BODY
2739 CLASS_BODY : CLASS_BODY_ITEM
2740 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2741 CLASS_BODY_ITEM : ';'
2742 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}' {PASS_ALWAYS as3_pass=$1;}
2743 CLASS_BODY_ITEM : SLOT_DECLARATION
2744 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2746 CLASS_BODY_ITEM : CODE_STATEMENT {
2747 code_t*c = state->cls->static_init->header;
2748 c = code_append(c, $1);
2749 state->cls->static_init->header = c;
2752 MAYBE_INTERFACE_BODY :
2753 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2754 INTERFACE_BODY : IDECLARATION
2755 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2757 IDECLARATION : "var" T_IDENTIFIER {
2758 syntaxerror("variable declarations not allowed in interfaces");
2760 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2762 $1.flags |= FLAG_PUBLIC;
2763 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2764 syntaxerror("invalid method modifiers: interface methods always need to be public");
2766 startfunction(&$1,$3,$4,&$6,$8);
2767 endfunction(&$1,$3,$4,&$6,$8, 0);
2768 list_deep_free($6.list);
2771 /* ------------ classes and interfaces (body, slots ) ------- */
2773 VARCONST: "var" | "const"
2775 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {PASS12 setslotstate(&$1,$2);} SLOT_LIST {PASS12 $$=$4;setslotstate(0, 0);}
2777 SLOT_LIST: ONE_SLOT {PASS12 $$=0;}
2778 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {PASS12 $$=0;}
2780 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2783 int flags = slotstate_flags->flags;
2784 namespace_t ns = modifiers2access(slotstate_flags);
2788 varinfo_t* info = 0;
2790 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2792 check_override(i, flags);
2794 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2796 slotinfo_t*i = registry_find(state->package, $1);
2798 syntaxerror("package %s already contains '%s'", state->package, $1);
2800 if(ns.name && ns.name[0]) {
2801 syntaxerror("namespaces not allowed on package-level variables");
2803 info = varinfo_register_global(ns.access, state->package, $1);
2807 info->flags = flags;
2809 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, info);
2813 varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
2816 multiname_t mname = {QNAME, &ns, 0, $1};
2818 trait_list_t**traits;
2822 ns.name = state->package;
2823 traits = &global->init->traits;
2824 code = &global->init->method->body->code;
2825 } else if(flags&FLAG_STATIC) {
2827 traits = &state->cls->abc->static_traits;
2828 code = &state->cls->static_init->header;
2830 // instance variable
2831 traits = &state->cls->abc->traits;
2832 code = &state->cls->init->header;
2838 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2840 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2842 info->slot = t->slot_id;
2844 /* initalization code (if needed) */
2846 if($3.c && !is_pushundefined($3.c)) {
2847 c = abc_getlocal_0(c);
2848 c = code_append(c, $3.c);
2849 c = converttype(c, $3.t, $2);
2850 c = abc_setslot(c, t->slot_id);
2853 *code = code_append(*code, c);
2855 if(slotstate_varconst==KW_CONST) {
2856 t->kind= TRAIT_CONST;
2863 /* ------------ constants -------------------------------------- */
2865 MAYBESTATICCONSTANT: {$$=0;}
2866 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2868 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2869 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2870 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2871 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2872 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2873 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2874 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2875 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2876 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2877 STATICCONSTANT : T_IDENTIFIER {
2878 if(!strcmp($1, "NaN")) {
2879 $$ = constant_new_float(__builtin_nan(""));
2881 as3_warning("Couldn't evaluate constant value of %s", $1);
2882 $$ = constant_new_null($1);
2886 /* ------------ classes and interfaces (body, functions) ------- */
2888 // non-vararg version
2891 memset(&$$,0,sizeof($$));
2893 MAYBE_PARAM_LIST: PARAM_LIST {
2899 MAYBE_PARAM_LIST: "..." PARAM {
2901 memset(&$$,0,sizeof($$));
2903 list_append($$.list, $2);
2905 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2909 list_append($$.list, $4);
2913 PARAM_LIST: PARAM_LIST ',' PARAM {
2916 list_append($$.list, $3);
2920 memset(&$$,0,sizeof($$));
2921 list_append($$.list, $1);
2924 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2926 $$ = rfx_calloc(sizeof(param_t));
2932 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2934 $$ = rfx_calloc(sizeof(param_t));
2936 $$->type = TYPE_ANY;
2944 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2945 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2948 endfunction(&$1,$3,$4,&$6,0,0);
2950 if(!state->method->info) syntaxerror("internal error");
2952 code_t*c = method_header(state->method);
2953 c = wrap_function(c, 0, $11);
2955 endfunction(&$1,$3,$4,&$6,$8,c);
2957 list_deep_free($6.list);
2961 MAYBE_IDENTIFIER: T_IDENTIFIER
2962 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2963 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2964 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2967 endfunction(0,0,$2,&$4,0,0);
2969 methodinfo_t*f = state->method->info;
2970 if(!f || !f->kind) syntaxerror("internal error");
2972 code_t*c = method_header(state->method);
2973 c = wrap_function(c, 0, $9);
2975 int index = state->method->var_index;
2976 endfunction(0,0,$2,&$4,$6,c);
2978 $$.c = abc_getlocal(0, index);
2979 $$.t = TYPE_FUNCTION(f);
2981 PASS12 list_deep_free($4.list);
2985 /* ------------- package + class ids --------------- */
2987 CLASS: X_IDENTIFIER {
2988 PASS1 NEW(unresolvedinfo_t,c);
2989 memset(c, 0, sizeof(*c));
2990 c->kind = INFOTYPE_UNRESOLVED;
2992 c->package = get_package_from_name($1);
2994 c->nsset = get_current_imports();
2995 /* make the compiler look for this class in the current directory,
2997 as3_schedule_class_noerror(state->package, $1);
2999 $$ = (classinfo_t*)c;
3001 slotinfo_t*s = find_class($1);
3002 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
3003 $$ = (classinfo_t*)s;
3006 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
3007 PASS1 NEW(unresolvedinfo_t,c);
3008 memset(c, 0, sizeof(*c));
3009 c->kind = INFOTYPE_UNRESOLVED;
3012 $$ = (classinfo_t*)c;
3014 slotinfo_t*s = registry_find($1, $3);
3015 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
3017 $$ = (classinfo_t*)s;
3020 CLASS_SPEC: PACKAGEANDCLASS
3023 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
3024 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
3026 TYPE : CLASS_SPEC {PASS12 $$=$1;}
3027 | '*' {PASS12 $$=registry_getanytype();}
3028 | "void" {PASS12 $$=registry_getanytype();}
3030 | "String" {$$=registry_getstringclass();}
3031 | "int" {$$=registry_getintclass();}
3032 | "uint" {$$=registry_getuintclass();}
3033 | "Boolean" {$$=registry_getbooleanclass();}
3034 | "Number" {$$=registry_getnumberclass();}
3037 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
3038 MAYBETYPE: {PASS12 $$=0;}
3040 /* ----------function calls, delete, constructor calls ------ */
3042 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
3043 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
3045 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
3046 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
3047 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
3049 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
3053 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
3054 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
3055 $$.number= $1.number+1;
3056 $$.cc = code_append($1.cc, $2.c);
3060 NEW : "new" E XX MAYBE_PARAM_VALUES {
3062 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
3064 code_t*paramcode = $4.cc;
3065 if($$.c->opcode == OPCODE_GETPROPERTY) {
3066 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3067 $$.c = code_cutlast($$.c);
3068 $$.c = code_append($$.c, paramcode);
3069 $$.c = abc_constructprop2($$.c, name, $4.number);
3070 multiname_destroy(name);
3071 } else if($$.c->opcode == OPCODE_GETSLOT) {
3072 int slot = (int)(ptroff_t)$$.c->data[0];
3073 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3074 multiname_t*name = t->name;
3075 $$.c = code_cutlast($$.c);
3076 $$.c = code_append($$.c, paramcode);
3077 $$.c = abc_constructprop2($$.c, name, $4.number);
3079 $$.c = code_append($$.c, paramcode);
3080 $$.c = abc_construct($$.c, $4.number);
3084 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
3087 $$.c = abc_coerce_a($$.c);
3092 /* TODO: use abc_call (for calling local variables),
3093 abc_callstatic (for calling own methods)
3096 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3099 if($$.c->opcode == OPCODE_COERCE_A) {
3100 $$.c = code_cutlast($$.c);
3102 code_t*paramcode = $3.cc;
3105 if($$.c->opcode == OPCODE_GETPROPERTY) {
3106 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3107 $$.c = code_cutlast($$.c);
3108 $$.c = code_append($$.c, paramcode);
3109 $$.c = abc_callproperty2($$.c, name, $3.number);
3110 multiname_destroy(name);
3111 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3112 int slot = (int)(ptroff_t)$$.c->data[0];
3113 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3114 if(t->kind!=TRAIT_METHOD) {
3115 //ok: flash allows to assign closures to members.
3117 multiname_t*name = t->name;
3118 $$.c = code_cutlast($$.c);
3119 $$.c = code_append($$.c, paramcode);
3120 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3121 $$.c = abc_callproperty2($$.c, name, $3.number);
3122 } else if($$.c->opcode == OPCODE_GETSUPER) {
3123 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3124 $$.c = code_cutlast($$.c);
3125 $$.c = code_append($$.c, paramcode);
3126 $$.c = abc_callsuper2($$.c, name, $3.number);
3127 multiname_destroy(name);
3129 $$.c = abc_getglobalscope($$.c);
3130 $$.c = code_append($$.c, paramcode);
3131 $$.c = abc_call($$.c, $3.number);
3134 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
3135 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
3137 $$.c = abc_coerce_a($$.c);
3142 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3143 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3144 if(!state->method) syntaxerror("super() not allowed outside of a function");
3145 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3148 $$.c = abc_getlocal_0($$.c);
3150 $$.c = code_append($$.c, $3.cc);
3152 this is dependent on the control path, check this somewhere else
3153 if(state->method->has_super)
3154 syntaxerror("constructor may call super() only once");
3156 state->method->has_super = 1;
3158 $$.c = abc_constructsuper($$.c, $3.number);
3159 $$.c = abc_pushundefined($$.c);
3163 DELETE: "delete" E {
3165 if($$.c->opcode == OPCODE_COERCE_A) {
3166 $$.c = code_cutlast($$.c);
3168 multiname_t*name = 0;
3169 if($$.c->opcode == OPCODE_GETPROPERTY) {
3170 $$.c->opcode = OPCODE_DELETEPROPERTY;
3171 } else if($$.c->opcode == OPCODE_GETSLOT) {
3172 int slot = (int)(ptroff_t)$$.c->data[0];
3173 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3174 $$.c = code_cutlast($$.c);
3175 $$.c = abc_deleteproperty2($$.c, name);
3177 $$.c = abc_getlocal_0($$.c);
3178 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3179 $$.c = abc_deleteproperty2($$.c, &m);
3181 $$.t = TYPE_BOOLEAN;
3184 RETURN: "return" %prec prec_none {
3185 $$ = abc_returnvoid(0);
3187 RETURN: "return" EXPRESSION {
3189 $$ = abc_returnvalue($$);
3192 // ----------------------- expression types -------------------------------------
3194 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3195 EXPRESSION : E %prec below_minus {$$ = $1;}
3196 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3198 $$.c = cut_last_push($$.c);
3199 $$.c = code_append($$.c,$3.c);
3202 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3203 $$=cut_last_push($1.c);
3206 // ----------------------- expression evaluation -------------------------------------
3208 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3210 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3212 E : DELETE {$$ = $1;}
3218 namespace_t ns = {ACCESS_PACKAGE, ""};
3219 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3221 $$.c = abc_getlex2($$.c, &m);
3222 $$.c = abc_pushstring($$.c, $1.pattern);
3223 $$.c = abc_construct($$.c, 1);
3225 $$.c = abc_getlex2($$.c, &m);
3226 $$.c = abc_pushstring($$.c, $1.pattern);
3227 $$.c = abc_pushstring($$.c, $1.options);
3228 $$.c = abc_construct($$.c, 2);
3233 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3234 //MULTINAME(m, registry_getintclass());
3235 //$$.c = abc_coerce2($$.c, &m); // FIXME
3238 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3241 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3244 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3247 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3250 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3253 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3256 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3257 $$.t = TYPE_BOOLEAN;
3259 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3260 $$.t = TYPE_BOOLEAN;
3262 CONSTANT : "null" {$$.c = abc_pushnull(0);
3266 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3267 $$.t = TYPE_BOOLEAN;
3269 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3270 $$.t = TYPE_BOOLEAN;
3272 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3273 $$.t = TYPE_BOOLEAN;
3275 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3276 $$.t = TYPE_BOOLEAN;
3278 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3279 $$.t = TYPE_BOOLEAN;
3281 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3282 $$.t = TYPE_BOOLEAN;
3284 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3285 $$.t = TYPE_BOOLEAN;
3287 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3288 $$.t = TYPE_BOOLEAN;
3291 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3293 $$.c = converttype($$.c, $1.t, $$.t);
3294 $$.c = abc_dup($$.c);
3295 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3296 $$.c = cut_last_push($$.c);
3297 $$.c = code_append($$.c,$3.c);
3298 $$.c = converttype($$.c, $3.t, $$.t);
3299 code_t*label = $$.c = abc_label($$.c);
3300 jmp->branch = label;
3303 $$.t = join_types($1.t, $3.t, 'A');
3304 /*printf("%08x:\n",$1.t);
3305 code_dump($1.c, 0, 0, "", stdout);
3306 printf("%08x:\n",$3.t);
3307 code_dump($3.c, 0, 0, "", stdout);
3308 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3310 $$.c = converttype($$.c, $1.t, $$.t);
3311 $$.c = abc_dup($$.c);
3312 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3313 $$.c = cut_last_push($$.c);
3314 $$.c = code_append($$.c,$3.c);
3315 $$.c = converttype($$.c, $3.t, $$.t);
3316 code_t*label = $$.c = abc_label($$.c);
3317 jmp->branch = label;
3320 E : '!' E {$$.c=$2.c;
3321 $$.c = abc_not($$.c);
3322 $$.t = TYPE_BOOLEAN;
3325 E : '~' E {$$.c=$2.c;
3326 $$.c = abc_bitnot($$.c);
3330 E : E '&' E {$$.c = code_append($1.c,$3.c);
3331 $$.c = abc_bitand($$.c);
3335 E : E '^' E {$$.c = code_append($1.c,$3.c);
3336 $$.c = abc_bitxor($$.c);
3340 E : E '|' E {$$.c = code_append($1.c,$3.c);
3341 $$.c = abc_bitor($$.c);
3345 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3346 $$.c = abc_rshift($$.c);
3349 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3350 $$.c = abc_urshift($$.c);
3353 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3354 $$.c = abc_lshift($$.c);
3358 E : E '/' E {$$.c = code_append($1.c,$3.c);
3359 $$.c = abc_divide($$.c);
3362 E : E '%' E {$$.c = code_append($1.c,$3.c);
3363 $$.c = abc_modulo($$.c);
3366 E : E '+' E {$$.c = code_append($1.c,$3.c);
3367 if(BOTH_INT($1.t, $3.t)) {
3368 $$.c = abc_add_i($$.c);
3371 $$.c = abc_add($$.c);
3372 $$.t = join_types($1.t,$3.t,'+');
3375 E : E '-' E {$$.c = code_append($1.c,$3.c);
3376 if(BOTH_INT($1.t,$3.t)) {
3377 $$.c = abc_subtract_i($$.c);
3380 $$.c = abc_subtract($$.c);
3384 E : E '*' E {$$.c = code_append($1.c,$3.c);
3385 if(BOTH_INT($1.t,$3.t)) {
3386 $$.c = abc_multiply_i($$.c);
3389 $$.c = abc_multiply($$.c);
3394 E : E "in" E {$$.c = code_append($1.c,$3.c);
3395 $$.c = abc_in($$.c);
3396 $$.t = TYPE_BOOLEAN;
3399 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3400 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3401 MULTINAME(m, (classinfo_t*)($3.t->data));
3402 $$.c = abc_astype2($1.c, &m);
3405 $$.c = code_append($1.c, $3.c);
3406 $$.c = abc_astypelate($$.c);
3411 E : E "instanceof" E
3412 {$$.c = code_append($1.c, $3.c);
3413 $$.c = abc_instanceof($$.c);
3414 $$.t = TYPE_BOOLEAN;
3417 E : E "is" E {$$.c = code_append($1.c, $3.c);
3418 $$.c = abc_istypelate($$.c);
3419 $$.t = TYPE_BOOLEAN;
3422 E : "typeof" '(' E ')' {
3424 $$.c = abc_typeof($$.c);
3429 $$.c = cut_last_push($2.c);
3430 $$.c = abc_pushundefined($$.c);
3434 E : "void" { $$.c = abc_pushundefined(0);
3438 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3443 $$.c=abc_negate_i($$.c);
3446 $$.c=abc_negate($$.c);
3453 $$.c = code_append($$.c, $3.c);
3455 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3456 $$.c = abc_getproperty2($$.c, &m);
3457 $$.t = 0; // array elements have unknown type
3460 E : '[' MAYBE_EXPRESSION_LIST ']' {
3462 $$.c = code_append($$.c, $2.cc);
3463 $$.c = abc_newarray($$.c, $2.number);
3464 $$.t = registry_getarrayclass();
3467 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3468 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3470 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3472 $$.cc = code_append($$.cc, $1.c);
3473 $$.cc = code_append($$.cc, $3.c);
3476 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3478 $$.number = $1.number+2;
3479 $$.cc = code_append($$.cc, $3.c);
3480 $$.cc = code_append($$.cc, $5.c);
3485 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3487 $$.c = code_append($$.c, $2.cc);
3488 $$.c = abc_newobject($$.c, $2.number/2);
3489 $$.t = registry_getobjectclass();
3494 if(BOTH_INT($1.t,$3.t)) {
3495 c=abc_multiply_i(c);
3499 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3500 $$.c = toreadwrite($1.c, c, 0, 0);
3505 code_t*c = abc_modulo($3.c);
3506 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3507 $$.c = toreadwrite($1.c, c, 0, 0);
3511 code_t*c = abc_lshift($3.c);
3512 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3513 $$.c = toreadwrite($1.c, c, 0, 0);
3517 code_t*c = abc_rshift($3.c);
3518 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3519 $$.c = toreadwrite($1.c, c, 0, 0);
3523 code_t*c = abc_urshift($3.c);
3524 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3525 $$.c = toreadwrite($1.c, c, 0, 0);
3529 code_t*c = abc_divide($3.c);
3530 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3531 $$.c = toreadwrite($1.c, c, 0, 0);
3535 code_t*c = abc_bitor($3.c);
3536 c=converttype(c, TYPE_INT, $1.t);
3537 $$.c = toreadwrite($1.c, c, 0, 0);
3541 code_t*c = abc_bitxor($3.c);
3542 c=converttype(c, TYPE_INT, $1.t);
3543 $$.c = toreadwrite($1.c, c, 0, 0);
3549 if(TYPE_IS_INT($1.t)) {
3553 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3556 $$.c = toreadwrite($1.c, c, 0, 0);
3559 E : E "-=" E { code_t*c = $3.c;
3560 if(TYPE_IS_INT($1.t)) {
3561 c=abc_subtract_i(c);
3564 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3567 $$.c = toreadwrite($1.c, c, 0, 0);
3570 E : E '=' E { code_t*c = 0;
3571 c = code_append(c, $3.c);
3572 c = converttype(c, $3.t, $1.t);
3573 $$.c = toreadwrite($1.c, c, 1, 0);
3577 E : E '?' E ':' E %prec below_assignment {
3578 $$.t = join_types($3.t,$5.t,'?');
3580 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3581 $$.c = code_append($$.c, $3.c);
3582 $$.c = converttype($$.c, $3.t, $$.t);
3583 code_t*j2 = $$.c = abc_jump($$.c, 0);
3584 $$.c = j1->branch = abc_label($$.c);
3585 $$.c = code_append($$.c, $5.c);
3586 $$.c = converttype($$.c, $5.t, $$.t);
3587 $$.c = j2->branch = abc_label($$.c);
3590 E : E "++" { code_t*c = 0;
3591 classinfo_t*type = $1.t;
3592 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3593 int nr = getlocalnr($1.c);
3594 code_free($1.c);$1.c=0;
3595 if(TYPE_IS_INT($1.t)) {
3596 $$.c = abc_getlocal(0, nr);
3597 $$.c = abc_inclocal_i($$.c, nr);
3598 } else if(TYPE_IS_NUMBER($1.t)) {
3599 $$.c = abc_getlocal(0, nr);
3600 $$.c = abc_inclocal($$.c, nr);
3601 } else syntaxerror("internal error");
3603 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3604 c=abc_increment_i(c);
3610 c=converttype(c, type, $1.t);
3611 $$.c = toreadwrite($1.c, c, 0, 1);
3616 // TODO: use inclocal, like with ++
3617 E : E "--" { code_t*c = 0;
3618 classinfo_t*type = $1.t;
3619 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3620 c=abc_decrement_i(c);
3626 c=converttype(c, type, $1.t);
3627 $$.c = toreadwrite($1.c, c, 0, 1);
3631 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3632 classinfo_t*type = $2.t;
3633 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3634 c=abc_increment_i(c);
3640 c=converttype(c, type, $2.t);
3641 $$.c = toreadwrite($2.c, c, 0, 0);
3645 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3646 classinfo_t*type = $2.t;
3647 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3648 c=abc_decrement_i(c);
3654 c=converttype(c, type, $2.t);
3655 $$.c = toreadwrite($2.c, c, 0, 0);
3659 E : "super" '.' T_IDENTIFIER
3660 { if(!state->cls->info)
3661 syntaxerror("super keyword not allowed outside a class");
3662 classinfo_t*t = state->cls->info->superclass;
3663 if(!t) t = TYPE_OBJECT;
3665 memberinfo_t*f = findmember_nsset(t, $3, 1);
3667 MEMBER_MULTINAME(m, f, $3);
3669 $$.c = abc_getlocal_0($$.c);
3670 $$.c = abc_getsuper2($$.c, &m);
3671 $$.t = slotinfo_gettype((slotinfo_t*)f);
3674 E : '@' T_IDENTIFIER {
3676 $$.c = abc_pushundefined(0);
3678 as3_warning("ignored @ operator");
3681 E : E '.' '@' T_IDENTIFIER {
3682 // child attribute TODO
3683 $$.c = abc_pushundefined(0);
3685 as3_warning("ignored .@ operator");
3688 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3689 // namespace declaration TODO
3690 $$.c = abc_pushundefined(0);
3692 as3_warning("ignored :: operator");
3695 E : E ".." T_IDENTIFIER {
3697 $$.c = abc_pushundefined(0);
3699 as3_warning("ignored .. operator");
3702 E : E '.' '(' E ')' {
3704 $$.c = abc_pushundefined(0);
3706 as3_warning("ignored .() operator");
3709 //E : E "::" '[' E ']' {
3710 // // qualified expression TODO
3711 // $$.c = abc_pushundefined(0);
3713 // as3_warning("ignored ::[] operator");
3717 E : E '.' T_IDENTIFIER {
3719 classinfo_t*t = $1.t;
3721 if(TYPE_IS_CLASS(t) && t->data) {
3726 if(t->subtype==INFOTYPE_UNRESOLVED) {
3727 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3729 memberinfo_t*f = findmember_nsset(t, $3, 1);
3731 if(f && !is_static != !(f->flags&FLAG_STATIC))
3733 if(f && f->slot && !noslot) {
3734 $$.c = abc_getslot($$.c, f->slot);
3737 as3_warning("Access of undefined property '%s' in %s", $3, t->name);
3740 MEMBER_MULTINAME(m, f, $3);
3741 $$.c = abc_getproperty2($$.c, &m);
3743 /* determine type */
3744 $$.t = slotinfo_gettype((slotinfo_t*)f);
3746 $$.c = abc_coerce_a($$.c);
3747 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3748 string_t*package = $1.c->data[0];
3749 char*package2 = concat3(package->str, ".", $3);
3751 slotinfo_t*a = registry_find(package->str, $3);
3754 } else if(dict_contains(state->import_toplevel_packages, package2) ||
3755 registry_ispackage(package2)) {
3757 $$.c->data[0] = string_new4(package2);
3760 syntaxerror("couldn't resolve %s", package2);
3763 /* when resolving a property on an unknown type, we do know the
3764 name of the property (and don't seem to need the package), but
3765 we need to make avm2 try out all access modes */
3766 as3_warning("Resolving %s on unknown type", $3);
3767 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3768 $$.c = abc_getproperty2($$.c, &m);
3769 $$.c = abc_coerce_a($$.c);
3770 $$.t = registry_getanytype();
3774 VAR_READ : T_IDENTIFIER {
3776 /* Queue unresolved identifiers for checking against the parent
3777 function's variables.
3778 We consider everything which is not a local variable "unresolved".
3779 This encompasses class names, members of the surrounding class
3780 etc. which is *correct* because local variables of the parent function
3783 if(state->method->inner && !find_variable(state, $1)) {
3784 unknown_variable($1);
3787 /* let the compiler know that it might want to check the current directory/package
3788 for this identifier- maybe there's a file $1.as defining $1. */
3789 as3_schedule_class_noerror(state->package, $1);
3798 /* look at variables */
3799 if((v = find_variable(state, $1))) {
3800 // $1 is a local variable
3801 $$.c = abc_getlocal($$.c, v->index);
3805 if((v = find_slot(state, $1))) {
3806 $$.c = abc_getscopeobject($$.c, 1);
3807 $$.c = abc_getslot($$.c, v->index);
3812 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3814 /* look at current class' members */
3815 if(!state->method->inner &&
3817 (f = findmember_nsset(state->cls->info, $1, 1)) &&
3818 (f->flags&FLAG_STATIC) >= i_am_static)
3820 // $1 is a function in this class
3821 int var_is_static = (f->flags&FLAG_STATIC);
3823 if(f->kind == INFOTYPE_METHOD) {
3824 $$.t = TYPE_FUNCTION(f);
3828 if(var_is_static && !i_am_static) {
3829 /* access to a static member from a non-static location.
3830 do this via findpropstrict:
3831 there doesn't seem to be any non-lookup way to access
3832 static properties of a class */
3833 state->method->late_binding = 1;
3835 namespace_t ns = {f->access, f->package};
3836 multiname_t m = {QNAME, &ns, 0, $1};
3837 $$.c = abc_findpropstrict2($$.c, &m);
3838 $$.c = abc_getproperty2($$.c, &m);
3840 } else if(f->slot>0) {
3841 $$.c = abc_getlocal_0($$.c);
3842 $$.c = abc_getslot($$.c, f->slot);
3845 namespace_t ns = {f->access, f->package};
3846 multiname_t m = {QNAME, &ns, 0, $1};
3847 $$.c = abc_getlocal_0($$.c);
3848 $$.c = abc_getproperty2($$.c, &m);
3853 /* look at actual classes, in the current package and imported */
3854 if((a = find_class($1))) {
3859 /* look through package prefixes */
3860 if(dict_contains(state->import_toplevel_packages, $1) ||
3861 registry_ispackage($1)) {
3862 $$.c = abc___pushpackage__($$.c, $1);
3867 /* unknown object, let the avm2 resolve it */
3869 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3870 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3871 state->method->late_binding = 1;
3873 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3876 $$.c = abc_findpropstrict2($$.c, &m);
3877 $$.c = abc_getproperty2($$.c, &m);
3881 // ----------------- namespaces -------------------------------------------------
3883 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3885 NEW(namespace_decl_t,n);
3890 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3892 NEW(namespace_decl_t,n);
3897 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3899 NEW(namespace_decl_t,n);
3904 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3906 trie_put(active_namespaces, $2->name, (void*)$2->url);
3908 namespace_t access = modifiers2access(&$1);
3909 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3910 var->type = TYPE_NAMESPACE;
3912 ns.access = ACCESS_NAMESPACE;
3914 var->value = constant_new_namespace(&ns);
3919 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3921 const char*url = $3->name;
3923 varinfo_t*s = (varinfo_t*)$3;
3924 if(s->kind == INFOTYPE_UNRESOLVED) {
3925 s = (varinfo_t*)registry_resolve((slotinfo_t*)s);
3927 syntaxerror("Couldn't resolve namespace %s", $3->name);
3930 if(!s || s->kind != INFOTYPE_SLOT)
3931 syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
3932 if(!s->value || !NS_TYPE(s->value->type))
3933 syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
3934 url = s->value->ns->name;
3936 trie_put(active_namespaces, $3->name, (void*)url);
3937 add_active_url(url);