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 */
31 #include "tokenizer.h"
47 enum yytokentype token;
49 classinfo_t*classinfo;
50 classinfo_list_t*classinfo_list;
52 slotinfo_list_t*slotinfo_list;
55 unsigned int number_uint;
59 //typedcode_list_t*value_list;
60 codeandnumber_t value_list;
66 for_start_t for_start;
67 abc_exception_t *exception;
70 namespace_decl_t* namespace_decl;
72 abc_exception_list_t *l;
78 %token<id> T_IDENTIFIER T_NAMESPACE
80 %token<regexp> T_REGEXP
82 %token<number_int> T_INT
83 %token<number_uint> T_UINT
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_NAN "NaN"
105 %token<token> KW_CONTINUE "continue"
106 %token<token> KW_CLASS "class"
107 %token<token> KW_CONST "const"
108 %token<token> KW_CATCH "catch"
109 %token<token> KW_CASE "case"
110 %token<token> KW_SET "set"
111 %token<token> KW_VOID "void"
112 %token<token> KW_THROW "throw"
113 %token<token> KW_STATIC "static"
114 %token<token> KW_WITH "with"
115 %token<token> KW_INSTANCEOF "instanceof"
116 %token<token> KW_IMPORT "import"
117 %token<token> KW_RETURN "return"
118 %token<token> KW_TYPEOF "typeof"
119 %token<token> KW_INTERFACE "interface"
120 %token<token> KW_NULL "null"
121 %token<token> KW_VAR "var"
122 %token<token> KW_DYNAMIC "dynamic"
123 %token<token> KW_OVERRIDE "override"
124 %token<token> KW_FINAL "final"
125 %token<token> KW_EACH "each"
126 %token<token> KW_GET "get"
127 %token<token> KW_TRY "try"
128 %token<token> KW_SUPER "super"
129 %token<token> KW_EXTENDS "extends"
130 %token<token> KW_FALSE "false"
131 %token<token> KW_TRUE "true"
132 %token<token> KW_BOOLEAN "Boolean"
133 %token<token> KW_UINT "uint"
134 %token<token> KW_INT "int"
135 %token<token> KW_NUMBER "Number"
136 %token<token> KW_STRING "String"
137 %token<token> KW_DEFAULT "default"
138 %token<token> KW_DELETE "delete"
139 %token<token> KW_IF "if"
140 %token<token> KW_ELSE "else"
141 %token<token> KW_BREAK "break"
142 %token<token> KW_IS "is"
143 %token<token> KW_IN "in"
144 %token<token> KW_AS "as"
146 %token<token> T_DICTSTART "{ (dictionary)"
147 %token<token> T_EQEQ "=="
148 %token<token> T_EQEQEQ "==="
149 %token<token> T_NE "!="
150 %token<token> T_NEE "!=="
151 %token<token> T_LE "<="
152 %token<token> T_GE ">="
153 %token<token> T_ORBY "|="
154 %token<token> T_DIVBY "/="
155 %token<token> T_MODBY "%="
156 %token<token> T_MULBY "*="
157 %token<token> T_PLUSBY "+="
158 %token<token> T_MINUSBY "-="
159 %token<token> T_XORBY "^="
160 %token<token> T_SHRBY ">>="
161 %token<token> T_SHLBY "<<="
162 %token<token> T_USHRBY ">>>="
163 %token<token> T_OROR "||"
164 %token<token> T_ANDAND "&&"
165 %token<token> T_COLONCOLON "::"
166 %token<token> T_MINUSMINUS "--"
167 %token<token> T_PLUSPLUS "++"
168 %token<token> T_DOTDOT ".."
169 %token<token> T_DOTDOTDOT "..."
170 %token<token> T_SHL "<<"
171 %token<token> T_USHR ">>>"
172 %token<token> T_SHR ">>"
174 %type <number_int> CONDITIONAL_COMPILATION
175 %type <for_start> FOR_START
176 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
177 %type <namespace_decl> NAMESPACE_ID
178 %type <token> VARCONST
180 %type <code> CODEPIECE CODE_STATEMENT
181 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
182 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
183 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
184 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
185 %type <exception> CATCH FINALLY
186 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
187 %type <code> CLASS_DECLARATION
188 %type <code> NAMESPACE_DECLARATION
189 %type <code> INTERFACE_DECLARATION
190 %type <code> VOIDEXPRESSION
191 %type <value> EXPRESSION NONCOMMAEXPRESSION
192 %type <value> MAYBEEXPRESSION
193 %type <value> E DELETE
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> CONSTANT MAYBECONSTANT
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_FLOAT KW_NAN
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
274 static void parsererror(const char*file, int line, const char*f)
276 syntaxerror("internal error in %s, %s:%d", f, file, line);
279 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
282 static char* concat2(const char* t1, const char* t2)
286 char*text = malloc(l1+l2+1);
287 memcpy(text , t1, l1);
288 memcpy(text+l1, t2, l2);
292 static char* concat3(const char* t1, const char* t2, const char* t3)
297 char*text = malloc(l1+l2+l3+1);
298 memcpy(text , t1, l1);
299 memcpy(text+l1, t2, l2);
300 memcpy(text+l1+l2, t3, l3);
305 typedef struct _import {
308 DECLARE_LIST(import);
310 DECLARE(methodstate);
311 DECLARE_LIST(methodstate);
313 typedef struct _classstate {
319 methodstate_t*static_init;
321 //code_t*static_init;
323 char has_constructor;
326 struct _methodstate {
336 dict_t*unresolved_variables;
339 char uses_parent_function;
345 int var_index; // for inner methods
346 int slot_index; // for inner methods
347 char is_a_slot; // for inner methods
352 abc_exception_list_t*exceptions;
354 methodstate_list_t*innerfunctions;
357 typedef struct _state {
362 import_list_t*wildcard_imports;
363 dict_t*import_toplevel_packages;
366 namespace_list_t*active_namespace_urls;
368 char has_own_imports;
369 char new_vars; // e.g. transition between two functions
372 methodstate_t*method;
381 typedef struct _global {
385 dict_t*file2token2info;
388 static global_t*global = 0;
389 static state_t* state = 0;
393 #define MULTINAME(m,x) \
397 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
399 #define MEMBER_MULTINAME(m,f,n) \
403 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
404 m##_ns.name = ((slotinfo_t*)(f))->package; \
409 m.namespace_set = 0; \
410 m.name = ((slotinfo_t*)(f))->name; \
412 m.type = MULTINAME; \
414 m.namespace_set = &nopackage_namespace_set; \
418 /* warning: list length of namespace set is undefined */
419 #define MULTINAME_LATE(m, access, package) \
420 namespace_t m##_ns = {access, package}; \
421 namespace_set_t m##_nsset; \
422 namespace_list_t m##_l;m##_l.next = 0; \
423 m##_nsset.namespaces = &m##_l; \
424 m##_nsset = m##_nsset; \
425 m##_l.namespace = &m##_ns; \
426 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
428 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
429 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
430 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
431 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
432 static namespace_list_t nl4 = {&ns4,0};
433 static namespace_list_t nl3 = {&ns3,&nl4};
434 static namespace_list_t nl2 = {&ns2,&nl3};
435 static namespace_list_t nl1 = {&ns1,&nl2};
436 static namespace_set_t nopackage_namespace_set = {&nl1};
438 static dict_t*definitions=0;
439 void as3_set_definition(const char*c)
442 definitions = dict_new();
443 if(!dict_contains(definitions,c))
444 dict_put(definitions,c,0);
447 static void new_state()
450 state_t*oldstate = state;
452 memcpy(s, state, sizeof(state_t)); //shallow copy
454 s->imports = dict_new();
456 if(!s->import_toplevel_packages) {
457 s->import_toplevel_packages = dict_new();
461 state->has_own_imports = 0;
462 state->vars = dict_new();
463 state->old = oldstate;
466 trie_remember(active_namespaces);
469 state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
472 static void state_destroy(state_t*state)
474 if(state->has_own_imports) {
475 list_free(state->wildcard_imports);
476 dict_destroy(state->imports);state->imports=0;
478 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
479 dict_destroy(state->imports);state->imports=0;
483 for(t=0;t<state->vars->hashsize;t++) {
484 dictentry_t*e =state->vars->slots[t];
486 free(e->data);e->data=0;
490 dict_destroy(state->vars);state->vars=0;
493 list_free(state->active_namespace_urls)
494 state->active_namespace_urls = 0;
499 static void old_state()
501 trie_rollback(active_namespaces);
503 if(!state || !state->old)
504 syntaxerror("invalid nesting");
505 state_t*leaving = state;
509 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
510 free(leaving->method);
513 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
518 state_destroy(leaving);
521 static code_t* method_header(methodstate_t*m);
522 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
523 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
526 static char* internal_filename_package = 0;
527 void initialize_file(char*filename)
530 syntaxerror("invalid call to initialize_file during parsing of another file");
533 active_namespaces = trie_new();
536 state->package = internal_filename_package = strdup(filename);
538 global->token2info = dict_lookup(global->file2token2info,
539 current_filename // use long version
541 if(!global->token2info) {
542 global->token2info = dict_new2(&ptr_type);
543 dict_put(global->file2token2info, current_filename, global->token2info);
547 state->method = rfx_calloc(sizeof(methodstate_t));
548 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
549 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
551 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
552 function_initvars(state->method, 0, 0, 1);
553 global->init = abc_initscript(global->file);
559 if(!state || state->level!=1) {
560 syntaxerror("unexpected end of file in pass %d", as3_pass);
564 code_t*header = method_header(state->method);
565 code_t*c = wrap_function(header, 0, global->init->method->body->code);
566 global->init->method->body->code = c;
567 free(state->method);state->method=0;
570 //free(state->package);state->package=0; // used in registry
571 state_destroy(state);state=0;
574 void initialize_parser()
576 global = rfx_calloc(sizeof(global_t));
577 global->file = abc_file_new();
578 global->file->flags &= ~ABCFILE_LAZY;
579 global->file2token2info = dict_new();
580 global->token2info = 0;
583 void* finish_parser()
585 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
586 global->token2info=0;
590 typedef struct _variable {
595 methodstate_t*is_inner_method;
598 static variable_t* find_variable(state_t*s, char*name)
602 v = dict_lookup(s->vars, name);
604 if(s->new_vars) break;
609 static variable_t* find_slot(state_t*s, const char*name)
611 if(s->method && s->method->slots)
612 return dict_lookup(s->method->slots, name);
616 static variable_t* find_variable_safe(state_t*s, char*name)
618 variable_t* v = find_variable(s, name);
620 syntaxerror("undefined variable: %s", name);
624 static char variable_exists(char*name)
626 return dict_contains(state->vars, name);
629 static code_t*defaultvalue(code_t*c, classinfo_t*type)
631 if(TYPE_IS_INT(type)) {
632 c = abc_pushbyte(c, 0);
633 } else if(TYPE_IS_UINT(type)) {
634 c = abc_pushuint(c, 0);
635 } else if(TYPE_IS_FLOAT(type)) {
637 } else if(TYPE_IS_BOOLEAN(type)) {
638 c = abc_pushfalse(c);
640 //c = abc_pushundefined(c);
641 syntaxerror("internal error: can't generate default value for * type");
645 c = abc_coerce2(c, &m);
650 static int alloc_local()
652 return state->method->variable_count++;
655 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
658 variable_t*v = find_slot(state, name);
664 v->index = alloc_local();
669 dict_put(state->vars, name, v);
673 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
675 return new_variable2(name, type, init, maybeslot)->index;
678 #define TEMPVARNAME "__as3_temp__"
679 static int gettempvar()
681 variable_t*v = find_variable(state, TEMPVARNAME);
684 return new_variable(TEMPVARNAME, 0, 0, 0);
687 static code_t* var_block(code_t*body)
693 for(t=0;t<state->vars->hashsize;t++) {
694 dictentry_t*e = state->vars->slots[t];
696 variable_t*v = (variable_t*)e->data;
697 if(v->type && v->init) {
698 c = defaultvalue(c, v->type);
699 c = abc_setlocal(c, v->index);
700 k = abc_kill(k, v->index);
710 if(x->opcode== OPCODE___BREAK__ ||
711 x->opcode== OPCODE___CONTINUE__) {
712 /* link kill code before break/continue */
713 code_t*e = code_dup(k);
714 code_t*s = code_start(e);
726 c = code_append(c, body);
727 c = code_append(c, k);
731 static void unknown_variable(char*name)
733 if(!state->method->unresolved_variables)
734 state->method->unresolved_variables = dict_new();
735 if(!dict_contains(state->method->unresolved_variables, name))
736 dict_put(state->method->unresolved_variables, name, 0);
739 static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
741 if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
742 c = abc_getlocal_0(c);
743 c = abc_pushscope(c);
746 /* FIXME: this alloc_local() causes variable indexes to be
747 different in pass2 than in pass1 */
748 if(!m->activation_var)
749 m->activation_var = alloc_local();
751 c = abc_newactivation(c);
753 c = abc_pushscope(c);
754 c = abc_setlocal(c, m->activation_var);
756 c = abc_getlocal(c, m->activation_var);
757 c = abc_pushscope(c);
763 static code_t* method_header(methodstate_t*m)
767 c = add_scope_code(c, m, 1);
769 methodstate_list_t*l = m->innerfunctions;
771 parserassert(l->methodstate->abc);
772 if(m->uses_slots && l->methodstate->is_a_slot) {
773 c = abc_getscopeobject(c, 1);
774 c = abc_newfunction(c, l->methodstate->abc);
776 c = abc_setlocal(c, l->methodstate->var_index);
777 c = abc_setslot(c, l->methodstate->slot_index);
779 c = abc_newfunction(c, l->methodstate->abc);
780 c = abc_setlocal(c, l->methodstate->var_index);
782 free(l->methodstate);l->methodstate=0;
786 c = code_append(c, m->header);
789 if(m->is_constructor && !m->has_super) {
790 // call default constructor
791 c = abc_getlocal_0(c);
792 c = abc_constructsuper(c, 0);
796 /* all parameters that are used by inner functions
797 need to be copied from local to slot */
798 parserassert(m->activation_var);
799 DICT_ITERATE_ITEMS(m->slots,char*,name,variable_t*,v) {
800 if(v->is_parameter) {
801 c = abc_getlocal(c, m->activation_var);
802 c = abc_getlocal(c, v->index);
803 c = abc_setslot(c, v->index);
807 list_free(m->innerfunctions);
808 m->innerfunctions = 0;
813 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
815 c = code_append(c, header);
816 c = code_append(c, var_block(body));
817 /* append return if necessary */
818 if(!c || (c->opcode != OPCODE_RETURNVOID &&
819 c->opcode != OPCODE_RETURNVALUE)) {
820 c = abc_returnvoid(c);
825 static void startpackage(char*name)
828 state->package = strdup(name);
830 static void endpackage()
832 //used e.g. in classinfo_register:
833 //free(state->package);state->package=0;
837 #define FLAG_PUBLIC 256
838 #define FLAG_PROTECTED 512
839 #define FLAG_PRIVATE 1024
840 #define FLAG_PACKAGEINTERNAL 2048
841 #define FLAG_NAMESPACE 4096
843 static namespace_t modifiers2access(modifiers_t*mod)
848 if(mod->flags&FLAG_NAMESPACE) {
849 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
850 syntaxerror("invalid combination of access levels and namespaces");
851 ns.access = ACCESS_NAMESPACE;
853 const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
855 /* shouldn't happen- the tokenizer only reports something as a namespace
856 if it was already registered */
857 trie_dump(active_namespaces);
858 syntaxerror("unknown namespace: %s", mod->ns);
861 } else if(mod->flags&FLAG_PUBLIC) {
862 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
863 syntaxerror("invalid combination of access levels");
864 ns.access = ACCESS_PACKAGE;
865 } else if(mod->flags&FLAG_PRIVATE) {
866 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
867 syntaxerror("invalid combination of access levels");
868 ns.access = ACCESS_PRIVATE;
869 } else if(mod->flags&FLAG_PROTECTED) {
870 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
871 syntaxerror("invalid combination of access levels");
872 ns.access = ACCESS_PROTECTED;
874 ns.access = ACCESS_PACKAGEINTERNAL;
878 static slotinfo_t* find_class(const char*name);
880 static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
882 return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
885 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
890 index = new_variable("this", 0, 0, 0);
891 else if(!m->is_global)
892 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
894 index = new_variable("globalscope", 0, 0, 0);
897 parserassert(!index);
901 /* as variables and slots share the same number, make sure
902 that those variable indices are reserved. It's up to the
903 optimizer to later shuffle the variables down to lower
905 m->variable_count = m->uses_slots;
910 for(p=params->list;p;p=p->next) {
911 variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1);
916 methodstate_list_t*l = m->innerfunctions;
918 methodstate_t*m = l->methodstate;
920 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
921 m->var_index = v->index;
922 m->slot_index = v->index;
923 v->is_inner_method = m;
929 m->scope_code = add_scope_code(m->scope_code, m, 0);
932 if(as3_pass==2 && m->slots) {
933 /* exchange unresolved identifiers with the actual objects */
934 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
935 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
936 classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
937 if(!type || type->kind != INFOTYPE_CLASS) {
938 syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name);
947 char*as3_globalclass=0;
948 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
951 syntaxerror("inner classes now allowed");
956 classinfo_list_t*mlist=0;
958 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
959 syntaxerror("invalid modifier(s)");
961 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
962 syntaxerror("public and internal not supported at the same time.");
964 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
965 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
966 // all classes extend object
967 extends = registry_getobjectclass();
970 /* create the class name, together with the proper attributes */
974 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
975 access = ACCESS_PRIVATE; package = internal_filename_package;
976 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
977 access = ACCESS_PACKAGEINTERNAL; package = state->package;
978 } else if(state->package!=internal_filename_package) {
979 access = ACCESS_PACKAGE; package = state->package;
981 syntaxerror("public classes only allowed inside a package");
985 state->cls = rfx_calloc(sizeof(classstate_t));
986 state->cls->init = rfx_calloc(sizeof(methodstate_t));
987 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
988 /* notice: we make no effort to initialize the top variable (local0) here,
989 even though it has special meaning. We just rely on the facat
990 that pass 1 won't do anything with variables */
992 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
994 /* set current method to constructor- all code within the class-level (except
995 static variable initializations) will be executed during construction time */
996 state->method = state->cls->init;
998 if(registry_find(package, classname)) {
999 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
1001 /* build info struct */
1002 int num_interfaces = (list_length(implements));
1003 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
1004 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
1007 classinfo_list_t*l = implements;
1008 for(l=implements;l;l=l->next) {
1009 state->cls->info->interfaces[pos++] = l->classinfo;
1014 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1016 state->method = state->cls->init;
1017 parserassert(state->cls && state->cls->info);
1019 function_initvars(state->cls->init, 0, 0, 1);
1020 function_initvars(state->cls->static_init, 0, 0, 0);
1022 if(extends && (extends->flags & FLAG_FINAL))
1023 syntaxerror("Can't extend final class '%s'", extends->name);
1026 while(state->cls->info->interfaces[pos]) {
1027 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1028 syntaxerror("'%s' is not an interface",
1029 state->cls->info->interfaces[pos]->name);
1033 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
1034 state->cls->info->superclass = extends;
1036 /* generate the abc code for this class */
1037 MULTINAME(classname2,state->cls->info);
1038 multiname_t*extends2 = sig2mname(extends);
1040 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1041 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1042 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1043 if(state->cls->info->flags&FLAG_INTERFACE) {
1044 abc_class_interface(state->cls->abc);
1047 abc_class_protectedNS(state->cls->abc, classname);
1049 for(mlist=implements;mlist;mlist=mlist->next) {
1050 MULTINAME(m, mlist->classinfo);
1051 abc_class_add_interface(state->cls->abc, &m);
1054 /* write the construction code for this class to the global init
1056 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1058 abc_method_body_t*m = global->init->method->body;
1059 __ getglobalscope(m);
1060 classinfo_t*s = extends;
1065 //TODO: take a look at the current scope stack, maybe
1066 // we can re-use something
1071 multiname_t*s2 = sig2mname(s);
1073 multiname_destroy(s2);
1075 __ pushscope(m); count++;
1076 m->code = m->code->prev->prev; // invert
1078 /* continue appending after last op end */
1079 while(m->code && m->code->next) m->code = m->code->next;
1081 /* TODO: if this is one of *our* classes, we can also
1082 do a getglobalscope/getslot <nr> (which references
1083 the init function's slots) */
1085 __ getlex2(m, extends2);
1087 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1088 stack is not the superclass */
1089 __ pushscope(m);count++;
1092 /* notice: we get a verify error #1107 if the top element on the scope
1093 stack is not the global object */
1095 __ pushscope(m);count++;
1097 __ newclass(m,state->cls->abc);
1101 __ setslot(m, slotindex);
1102 multiname_destroy(extends2);
1104 /* flash.display.MovieClip handling */
1106 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1107 if(state->package && state->package[0]) {
1108 as3_globalclass = concat3(state->package, ".", classname);
1110 as3_globalclass = strdup(classname);
1116 static void endclass()
1119 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1121 c = abc_getlocal_0(c);
1122 c = abc_constructsuper(c, 0);
1123 state->cls->init->header = code_append(state->cls->init->header, c);
1124 state->cls->has_constructor=1;
1126 if(state->cls->init) {
1127 if(state->cls->info->flags&FLAG_INTERFACE) {
1128 if(state->cls->init->header)
1129 syntaxerror("interface can not have class-level code");
1131 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1132 code_t*c = method_header(state->cls->init);
1133 m->body->code = wrap_function(c, 0, m->body->code);
1136 if(state->cls->static_init) {
1137 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1138 code_t*c = method_header(state->cls->static_init);
1139 m->body->code = wrap_function(c, 0, m->body->code);
1146 void check_code_for_break(code_t*c)
1149 if(c->opcode == OPCODE___BREAK__) {
1150 char*name = string_cstr(c->data[0]);
1151 syntaxerror("Unresolved \"break %s\"", name);
1153 if(c->opcode == OPCODE___CONTINUE__) {
1154 char*name = string_cstr(c->data[0]);
1155 syntaxerror("Unresolved \"continue %s\"", name);
1157 if(c->opcode == OPCODE___RETHROW__) {
1158 syntaxerror("Unresolved \"rethrow\"");
1160 if(c->opcode == OPCODE___FALLTHROUGH__) {
1161 syntaxerror("Unresolved \"fallthrough\"");
1163 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1164 char*name = string_cstr(c->data[0]);
1165 syntaxerror("Can't reference a package (%s) as such", name);
1171 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1173 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1174 if(TYPE_IS_NUMBER(t)) {
1175 xassert(c->type == CONSTANT_FLOAT
1176 || c->type == CONSTANT_INT
1177 || c->type == CONSTANT_UINT);
1178 } else if(TYPE_IS_UINT(t)) {
1179 xassert(c->type == CONSTANT_UINT ||
1180 (c->type == CONSTANT_INT && c->i>=0));
1181 } else if(TYPE_IS_INT(t)) {
1182 xassert(c->type == CONSTANT_INT);
1183 } else if(TYPE_IS_BOOLEAN(t)) {
1184 xassert(c->type == CONSTANT_TRUE
1185 || c->type == CONSTANT_FALSE);
1189 static void check_override(memberinfo_t*m, int flags)
1193 if(m->parent == state->cls->info)
1194 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1196 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1197 if(m->access==ACCESS_PRIVATE)
1199 if(m->flags & FLAG_FINAL)
1200 syntaxerror("can't override final member %s", m->name);
1202 /* allow this. it's no issue.
1203 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1204 syntaxerror("can't override static member %s", m->name);*/
1206 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1207 syntaxerror("can't override non-static member %s with static declaration", m->name);
1209 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1210 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1211 if(m->kind == INFOTYPE_METHOD)
1212 syntaxerror("can't override without explicit 'override' declaration");
1214 syntaxerror("can't override '%s'", m->name);
1219 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1221 methodinfo_t*minfo = 0;
1222 namespace_t ns = modifiers2access(mod);
1225 minfo = methodinfo_register_global(ns.access, state->package, name);
1226 minfo->return_type = 0; // save this for pass 2
1227 } else if(getset != KW_GET && getset != KW_SET) {
1229 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1231 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1233 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1234 minfo->return_type = 0; // save this for pass 2
1235 // getslot on a member slot only returns "undefined", so no need
1236 // to actually store these
1237 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1239 //class getter/setter
1240 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1242 if(getset == KW_GET) {
1244 } else if(params->list && params->list->param && !params->list->next) {
1245 type = params->list->param->type;
1247 syntaxerror("setter function needs to take exactly one argument");
1248 // not sure wether to look into superclasses here, too
1249 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1251 if(minfo->kind!=INFOTYPE_SLOT)
1252 syntaxerror("class already contains a method called '%s'", name);
1253 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1254 syntaxerror("class already contains a field called '%s'", name);
1255 if(minfo->subtype & gs)
1256 syntaxerror("getter/setter for '%s' already defined", name);
1257 /* make a setter or getter into a getset */
1258 minfo->subtype |= gs;
1261 FIXME: this check needs to be done in pass 2
1263 if((!minfo->return_type != !type) ||
1264 (minfo->return_type && type &&
1265 !strcmp(minfo->return_type->name, type->name))) {
1266 syntaxerror("different type in getter and setter: %s and %s",
1267 minfo->return_type?minfo->return_type->name:"*",
1268 type?type->name:"*");
1271 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1272 minfo->kind = INFOTYPE_SLOT; //hack
1273 minfo->subtype = gs;
1274 minfo->return_type = 0;
1276 /* can't assign a slot as getter and setter might have different slots */
1277 //minfo->slot = slot;
1279 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1280 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1281 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1286 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1288 //parserassert(state->method && state->method->info);
1290 methodstate_t*parent_method = state->method;
1293 return_type = 0; // not valid in pass 1
1297 state->new_vars = 1;
1300 state->method = rfx_calloc(sizeof(methodstate_t));
1301 state->method->inner = 1;
1302 state->method->variable_count = 0;
1303 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1305 NEW(methodinfo_t,minfo);
1306 minfo->kind = INFOTYPE_METHOD;
1307 minfo->access = ACCESS_PACKAGEINTERNAL;
1309 state->method->info = minfo;
1312 list_append(parent_method->innerfunctions, state->method);
1314 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1316 function_initvars(state->method, params, 0, 1);
1320 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1321 state->method->variable_count = 0;
1322 parserassert(state->method);
1324 state->method->info->return_type = return_type;
1325 function_initvars(state->method, params, 0, 1);
1329 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1330 params_t*params, classinfo_t*return_type)
1332 if(state->method && state->method->info) {
1333 syntaxerror("not able to start another method scope");
1336 state->new_vars = 1;
1339 state->method = rfx_calloc(sizeof(methodstate_t));
1340 state->method->has_super = 0;
1343 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1345 state->method->is_global = 1;
1346 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1348 if(state->method->is_constructor)
1349 name = "__as3_constructor__";
1351 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1353 function_initvars(state->method, params, mod->flags, 1);
1355 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1359 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1360 state->method->variable_count = 0;
1361 parserassert(state->method);
1364 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1365 check_override(m, mod->flags);
1369 state->cls->has_constructor |= state->method->is_constructor;
1372 state->method->info->return_type = return_type;
1373 function_initvars(state->method, params, mod->flags, 1);
1377 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1378 params_t*params, classinfo_t*return_type, code_t*body)
1381 // store inner methods in variables
1382 function_initvars(state->method, 0, 0, 0);
1384 methodstate_list_t*ml = state->method->innerfunctions;
1386 dict_t*xvars = dict_new();
1389 methodstate_t*m = ml->methodstate;
1390 parserassert(m->inner);
1391 if(m->unresolved_variables) {
1392 dict_t*d = m->unresolved_variables;
1394 for(t=0;t<d->hashsize;t++) {
1395 dictentry_t*l = d->slots[t];
1397 /* check parent method's variables */
1399 if((v=find_variable(state, l->key))) {
1400 m->uses_parent_function = 1;
1401 state->method->uses_slots = 1;
1402 dict_put(xvars, l->key, 0);
1409 dict_destroy(m->unresolved_variables);
1410 m->unresolved_variables = 0;
1415 if(state->method->uses_slots) {
1416 state->method->slots = dict_new();
1418 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1419 if(!name) syntaxerror("internal error");
1420 if(v->index && dict_contains(xvars, name)) {
1423 if(v->is_inner_method) {
1424 v->is_inner_method->is_a_slot = 1;
1427 dict_put(state->method->slots, name, v);
1430 state->method->uses_slots = i;
1431 dict_destroy(state->vars);state->vars = 0;
1438 /*if(state->method->uses_parent_function){
1439 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1444 multiname_t*type2 = sig2mname(return_type);
1446 if(state->method->inner) {
1447 f = state->method->abc;
1448 abc_method_init(f, global->file, type2, 1);
1449 } else if(state->method->is_constructor) {
1450 f = abc_class_getconstructor(state->cls->abc, type2);
1451 } else if(!state->method->is_global) {
1452 namespace_t mname_ns = modifiers2access(mod);
1453 multiname_t mname = {QNAME, &mname_ns, 0, name};
1455 if(mod->flags&FLAG_STATIC)
1456 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1458 f = abc_class_method(state->cls->abc, type2, &mname);
1459 slot = f->trait->slot_id;
1461 namespace_t mname_ns = {state->method->info->access, state->package};
1462 multiname_t mname = {QNAME, &mname_ns, 0, name};
1464 f = abc_method_new(global->file, type2, 1);
1465 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1466 //abc_code_t*c = global->init->method->body->code;
1468 //flash doesn't seem to allow us to access function slots
1469 //state->method->info->slot = slot;
1471 if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1472 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1473 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1474 if(params->varargs) f->flags |= METHOD_NEED_REST;
1478 for(p=params->list;p;p=p->next) {
1479 if(params->varargs && !p->next) {
1480 break; //varargs: omit last parameter in function signature
1482 multiname_t*m = sig2mname(p->param->type);
1483 list_append(f->parameters, m);
1484 if(p->param->value) {
1485 check_constant_against_type(p->param->type, p->param->value);
1486 opt=1;list_append(f->optional_parameters, p->param->value);
1488 syntaxerror("non-optional parameter not allowed after optional parameters");
1491 if(state->method->slots) {
1492 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1494 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1495 multiname_t*type = sig2mname(v->type);
1496 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1497 t->slot_id = v->index;
1502 check_code_for_break(body);
1504 /* Seems this works now.
1505 if(state->method->exceptions && state->method->uses_slots) {
1506 as3_warning("try/catch and activation not supported yet within the same method");
1510 f->body->code = body;
1511 f->body->exceptions = state->method->exceptions;
1512 } else { //interface
1514 syntaxerror("interface methods can't have a method body");
1524 void breakjumpsto(code_t*c, char*name, code_t*jump)
1527 if(c->opcode == OPCODE___BREAK__) {
1528 string_t*name2 = c->data[0];
1529 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1530 c->opcode = OPCODE_JUMP;
1537 void continuejumpsto(code_t*c, char*name, code_t*jump)
1540 if(c->opcode == OPCODE___CONTINUE__) {
1541 string_t*name2 = c->data[0];
1542 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1543 c->opcode = OPCODE_JUMP;
1551 /* TODO: move this to ast.c */
1552 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1553 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1554 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1555 static classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1557 if(!type1 || !type2)
1558 return registry_getanytype();
1559 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1560 return registry_getanytype();
1563 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1572 return registry_getanytype();
1574 static char is_getlocal(code_t*c)
1576 if(!c || c->prev || c->next)
1578 return(c->opcode == OPCODE_GETLOCAL
1579 || c->opcode == OPCODE_GETLOCAL_0
1580 || c->opcode == OPCODE_GETLOCAL_1
1581 || c->opcode == OPCODE_GETLOCAL_2
1582 || c->opcode == OPCODE_GETLOCAL_3);
1584 static int getlocalnr(code_t*c)
1586 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1587 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1588 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1589 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1590 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1591 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1594 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1599 return abc_coerce_a(c);
1603 // cast an "any" type to a specific type. subject to
1604 // runtime exceptions
1605 return abc_coerce2(c, &m);
1608 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1609 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1610 // allow conversion between number types
1611 if(TYPE_IS_UINT(to))
1612 return abc_convert_u(c);
1613 else if(TYPE_IS_INT(to))
1614 return abc_convert_i(c);
1615 else if(TYPE_IS_NUMBER(to))
1616 return abc_convert_d(c);
1617 return abc_coerce2(c, &m);
1620 if(TYPE_IS_BOOLEAN(to))
1621 return abc_convert_b(c);
1622 if(TYPE_IS_STRING(to))
1623 return abc_convert_s(c);
1624 if(TYPE_IS_OBJECT(to))
1625 return abc_convert_o(c);
1627 classinfo_t*supertype = from;
1629 if(supertype == to) {
1630 // target type is one of from's superclasses
1631 return abc_coerce2(c, &m);
1634 while(supertype->interfaces[t]) {
1635 if(supertype->interfaces[t]==to) {
1636 // target type is one of from's interfaces
1637 return abc_coerce2(c, &m);
1641 supertype = supertype->superclass;
1643 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1645 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1647 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1650 as3_error("can't convert type %s%s%s to %s%s%s",
1651 from->package, from->package[0]?".":"", from->name,
1652 to->package, to->package[0]?".":"", to->name);
1656 /* move to ast.c todo end */
1658 char is_pushundefined(code_t*c)
1660 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1663 static const char* get_package_from_name(const char*name)
1665 /* try explicit imports */
1666 dictentry_t* e = dict_get_slot(state->imports, name);
1668 if(!strcmp(e->key, name)) {
1669 slotinfo_t*c = (slotinfo_t*)e->data;
1670 if(c) return c->package;
1676 static namespace_list_t*get_current_imports()
1678 namespace_list_t*searchlist = 0;
1680 list_append(searchlist, namespace_new_package(state->package));
1682 import_list_t*l = state->wildcard_imports;
1684 namespace_t*ns = namespace_new_package(l->import->package);
1685 list_append(searchlist, ns);
1688 list_append(searchlist, namespace_new_package(""));
1689 list_append(searchlist, namespace_new_package(internal_filename_package));
1693 static slotinfo_t* find_class(const char*name)
1697 c = registry_find(state->package, name);
1700 /* try explicit imports */
1701 dictentry_t* e = dict_get_slot(state->imports, name);
1704 if(!strcmp(e->key, name)) {
1705 c = (slotinfo_t*)e->data;
1711 /* try package.* imports */
1712 import_list_t*l = state->wildcard_imports;
1714 //printf("does package %s contain a class %s?\n", l->import->package, name);
1715 c = registry_find(l->import->package, name);
1720 /* try global package */
1721 c = registry_find("", name);
1724 /* try local "filename" package */
1725 c = registry_find(internal_filename_package, name);
1730 typedcode_t push_class(slotinfo_t*a)
1735 if(a->access == ACCESS_PACKAGEINTERNAL &&
1736 strcmp(a->package, state->package) &&
1737 strcmp(a->package, internal_filename_package)
1739 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1740 infotypename(a), a->name, a->package, state->package);
1743 if(a->kind != INFOTYPE_CLASS) {
1745 x.c = abc_findpropstrict2(x.c, &m);
1746 x.c = abc_getproperty2(x.c, &m);
1747 if(a->kind == INFOTYPE_METHOD) {
1748 methodinfo_t*f = (methodinfo_t*)a;
1749 x.t = TYPE_FUNCTION(f);
1751 varinfo_t*v = (varinfo_t*)a;
1755 classinfo_t*c = (classinfo_t*)a;
1757 x.c = abc_getglobalscope(x.c);
1758 x.c = abc_getslot(x.c, c->slot);
1761 x.c = abc_getlex2(x.c, &m);
1763 x.t = TYPE_CLASS(c);
1769 code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore, char pushvalue)
1773 [prefix code] [read instruction]
1777 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1779 if(in && in->opcode == OPCODE_COERCE_A) {
1780 in = code_cutlast(in);
1783 syntaxerror("internal error");
1785 /* chop off read instruction */
1789 prefix = r->prev;r->prev = 0;
1795 char use_temp_var = readbefore;
1797 /* generate the write instruction, and maybe append a dup to the prefix code */
1798 code_t* write = abc_nop(0);
1799 if(r->opcode == OPCODE_GETPROPERTY) {
1800 write->opcode = OPCODE_SETPROPERTY;
1801 multiname_t*m = (multiname_t*)r->data[0];
1802 write->data[0] = multiname_clone(m);
1803 if(m->type == QNAME || m->type == MULTINAME) {
1805 prefix = abc_dup(prefix); // we need the object, too
1808 } else if(m->type == MULTINAMEL) {
1810 /* dupping two values on the stack requires 5 operations and one register-
1811 couldn't adobe just have given us a dup2? */
1812 int temp = gettempvar();
1813 prefix = abc_setlocal(prefix, temp);
1814 prefix = abc_dup(prefix);
1815 prefix = abc_getlocal(prefix, temp);
1816 prefix = abc_swap(prefix);
1817 prefix = abc_getlocal(prefix, temp);
1819 prefix = abc_kill(prefix, temp);
1823 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1825 } else if(r->opcode == OPCODE_GETSLOT) {
1826 write->opcode = OPCODE_SETSLOT;
1827 write->data[0] = r->data[0];
1829 prefix = abc_dup(prefix); // we need the object, too
1832 } else if(r->opcode == OPCODE_GETLOCAL) {
1833 write->opcode = OPCODE_SETLOCAL;
1834 write->data[0] = r->data[0];
1835 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1836 write->opcode = OPCODE_SETLOCAL_0;
1837 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1838 write->opcode = OPCODE_SETLOCAL_1;
1839 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1840 write->opcode = OPCODE_SETLOCAL_2;
1841 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1842 write->opcode = OPCODE_SETLOCAL_3;
1843 } else if(r->opcode == OPCODE_GETSUPER) {
1844 write->opcode = OPCODE_SETSUPER;
1845 multiname_t*m = (multiname_t*)r->data[0];
1846 write->data[0] = multiname_clone(m);
1849 syntaxerror("illegal lvalue: can't assign a value to this expression");
1856 /* with getproperty/getslot, we have to be extra careful not
1857 to execute the read code twice, as it might have side-effects
1858 (e.g. if the property is in fact a setter/getter combination)
1860 So read the value, modify it, and write it again,
1861 using prefix only once and making sure (by using a temporary
1862 register) that the return value is what we just wrote */
1863 temp = gettempvar();
1864 c = code_append(c, prefix);
1865 c = code_append(c, r);
1866 if(pushvalue && readbefore) {
1868 c = abc_setlocal(c, temp);
1870 c = code_append(c, middlepart);
1871 if(pushvalue && !readbefore) {
1873 c = abc_setlocal(c, temp);
1875 c = code_append(c, write);
1877 c = abc_getlocal(c, temp);
1878 c = abc_kill(c, temp);
1881 /* if we're allowed to execute the read code twice *and*
1882 the middlepart doesn't modify the code, things are easier.
1884 //c = code_append(c, prefix);
1885 parserassert(!prefix);
1890 c = code_append(c, r);
1891 c = code_append(c, middlepart);
1892 c = code_append(c, write);
1894 c = code_append(c, r2);
1898 /* even smaller version: overwrite the value without reading
1902 c = code_append(c, prefix);
1905 c = code_append(c, middlepart);
1906 c = code_append(c, write);
1908 c = code_append(c, r);
1912 temp = gettempvar();
1914 c = code_append(c, prefix);
1916 c = code_append(c, middlepart);
1919 c = abc_setlocal(c, temp);
1921 c = code_append(c, write);
1923 c = abc_getlocal(c, temp);
1924 c = abc_kill(c, temp);
1931 char is_break_or_jump(code_t*c)
1935 if(c->opcode == OPCODE_JUMP ||
1936 c->opcode == OPCODE___BREAK__ ||
1937 c->opcode == OPCODE___CONTINUE__ ||
1938 c->opcode == OPCODE_THROW ||
1939 c->opcode == OPCODE_RETURNVOID ||
1940 c->opcode == OPCODE_RETURNVALUE) {
1946 #define IS_FINALLY_TARGET(op) \
1947 ((op) == OPCODE___CONTINUE__ || \
1948 (op) == OPCODE___BREAK__ || \
1949 (op) == OPCODE_RETURNVOID || \
1950 (op) == OPCODE_RETURNVALUE || \
1951 (op) == OPCODE___RETHROW__)
1953 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1955 #define NEED_EXTRA_STACK_ARG
1956 code_t*finally_label = abc_nop(0);
1957 NEW(lookupswitch_t, l);
1963 code_t*prev = i->prev;
1964 if(IS_FINALLY_TARGET(i->opcode)) {
1967 if(i->opcode == OPCODE___RETHROW__ ||
1968 i->opcode == OPCODE_RETURNVALUE) {
1969 if(i->opcode == OPCODE___RETHROW__)
1970 i->opcode = OPCODE_THROW;
1972 p = abc_coerce_a(p);
1973 p = abc_setlocal(p, tempvar);
1975 p = abc_pushbyte(p, count++);
1976 p = abc_jump(p, finally_label);
1977 code_t*target = p = abc_label(p);
1978 #ifdef NEED_EXTRA_STACK_ARG
1982 p = abc_getlocal(p, tempvar);
1985 p->next = i;i->prev = p;
1986 list_append(l->targets, target);
1992 c = abc_pushbyte(c, -1);
1993 c = code_append(c, finally_label);
1994 c = code_append(c, finally);
1996 #ifdef NEED_EXTRA_STACK_ARG
1999 c = abc_lookupswitch(c, l);
2000 c = l->def = abc_label(c);
2001 #ifdef NEED_EXTRA_STACK_ARG
2008 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
2012 code_t*prev = i->prev;
2013 if(IS_FINALLY_TARGET(i->opcode)) {
2014 if(i->opcode == OPCODE___RETHROW__)
2015 i->opcode = OPCODE_THROW;
2016 code_t*end = code_dup(finally);
2017 code_t*start = code_start(end);
2018 if(prev) prev->next = start;
2025 return code_append(c, finally);
2028 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
2034 int num_insertion_points=0;
2036 if(IS_FINALLY_TARGET(i->opcode))
2037 num_insertion_points++;
2044 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
2049 int simple_version_cost = (1+num_insertion_points)*code_size;
2050 int lookup_version_cost = 4*num_insertion_points + 5;
2052 if(cantdup || simple_version_cost > lookup_version_cost) {
2053 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
2054 return insert_finally_lookup(c, finally, tempvar);
2056 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
2057 return insert_finally_simple(c, finally, tempvar);
2061 #define PASS1 }} if(as3_pass == 1) {{
2062 #define PASS1END }} if(as3_pass == 2) {{
2063 #define PASS2 }} if(as3_pass == 2) {{
2064 #define PASS12 }} if(as3_pass == 1 || as3_pass == 2) {{
2065 #define PASS12END }} if(as3_pass == 2) {{
2066 #define PASS_ALWAYS }} {{
2072 /* ------------ code blocks / statements ---------------- */
2074 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2076 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2077 PROGRAM_CODE_LIST: PROGRAM_CODE
2078 | PROGRAM_CODE_LIST PROGRAM_CODE
2080 PROGRAM_CODE: PACKAGE_DECLARATION
2081 | INTERFACE_DECLARATION
2083 | FUNCTION_DECLARATION
2086 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
2089 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2090 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2091 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2093 INPACKAGE_CODE: INTERFACE_DECLARATION
2095 | FUNCTION_DECLARATION
2098 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
2101 MAYBECODE: CODE {$$=$1;}
2102 MAYBECODE: {$$=code_new();}
2104 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2105 CODE: CODEPIECE {$$=$1;}
2107 // code which may appear outside of methods
2108 CODE_STATEMENT: IMPORT
2110 CODE_STATEMENT: FOR_IN
2111 CODE_STATEMENT: WHILE
2112 CODE_STATEMENT: DO_WHILE
2113 CODE_STATEMENT: SWITCH
2115 CODE_STATEMENT: WITH
2117 CODE_STATEMENT: VOIDEXPRESSION
2118 CODE_STATEMENT: USE_NAMESPACE
2119 CODE_STATEMENT: NAMESPACE_DECLARATION
2120 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2121 CODE_STATEMENT: '{' '}' {$$=0;}
2123 // code which may appear in methods
2124 CODEPIECE: ';' {$$=0;}
2125 CODEPIECE: CODE_STATEMENT
2126 CODEPIECE: VARIABLE_DECLARATION
2131 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {PASS_ALWAYS as3_pass=$1;}
2133 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2134 //CODEBLOCK : '{' '}' {$$=0;}
2135 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2136 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2138 /* ------------ package init code ------------------- */
2140 PACKAGE_INITCODE: CODE_STATEMENT {
2141 code_t**cc = &global->init->method->body->code;
2142 *cc = code_append(*cc, $1);
2145 /* ------------ conditional compilation ------------- */
2147 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER {
2150 char*key = concat3($1,"::",$3);
2151 if(!definitions || !dict_contains(definitions, key)) {
2157 /* ------------ variables --------------------------- */
2160 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
2166 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2167 | {$$.c=abc_pushundefined(0);
2171 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2172 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2174 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2175 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2177 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2180 if(variable_exists($1))
2181 syntaxerror("Variable %s already defined", $1);
2183 new_variable($1, 0, 1, 0);
2186 if(!is_subtype_of($3.t, $2)) {
2187 syntaxerror("Can't convert %s to %s", $3.t->name,
2193 if(state->method->uses_slots) {
2194 variable_t* v = find_slot(state, $1);
2196 // this variable is stored in a slot
2204 index = new_variable($1, $2, 1, 0);
2207 $$ = slot?abc_getscopeobject(0, 1):0;
2210 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2211 $$ = code_append($$, $3.c);
2212 $$ = converttype($$, $3.t, $2);
2215 $$ = defaultvalue($$, $2);
2218 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2219 $$ = code_append($$, $3.c);
2220 $$ = abc_coerce_a($$);
2222 // don't do anything
2230 $$ = abc_setslot($$, index);
2232 $$ = abc_setlocal($$, index);
2236 /* ------------ control flow ------------------------- */
2238 MAYBEELSE: %prec below_else {$$ = code_new();}
2239 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2240 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2242 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2245 $$ = code_append($$, $4.c);
2246 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2248 $$ = code_append($$, $6);
2250 myjmp = $$ = abc_jump($$, 0);
2252 myif->branch = $$ = abc_nop($$);
2254 $$ = code_append($$, $7);
2255 myjmp->branch = $$ = abc_nop($$);
2261 FOR_INIT : {$$=code_new();}
2262 FOR_INIT : VARIABLE_DECLARATION
2263 FOR_INIT : VOIDEXPRESSION
2265 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2266 // (I don't see any easy way to revolve this conflict otherwise, as we
2267 // can't touch VAR_READ without upsetting the precedence about "return")
2268 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2269 PASS1 $$=$2;new_variable($2,0,1,0);
2270 PASS2 $$=$2;new_variable($2,$3,1,0);
2272 FOR_IN_INIT : T_IDENTIFIER {
2277 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2278 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2280 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2281 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2283 $$ = code_append($$, $2);
2284 code_t*loopstart = $$ = abc_label($$);
2285 $$ = code_append($$, $4.c);
2286 code_t*myif = $$ = abc_iffalse($$, 0);
2287 $$ = code_append($$, $8);
2288 code_t*cont = $$ = abc_nop($$);
2289 $$ = code_append($$, $6);
2290 $$ = abc_jump($$, loopstart);
2291 code_t*out = $$ = abc_nop($$);
2292 breakjumpsto($$, $1.name, out);
2293 continuejumpsto($$, $1.name, cont);
2300 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2301 variable_t*var = find_variable(state, $2);
2303 syntaxerror("variable %s not known in this scope", $2);
2306 char*tmp1name = concat2($2, "__tmp1__");
2307 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2308 char*tmp2name = concat2($2, "__array__");
2309 int array = new_variable(tmp1name, 0, 0, 0);
2312 $$ = code_append($$, $4.c);
2313 $$ = abc_coerce_a($$);
2314 $$ = abc_setlocal($$, array);
2315 $$ = abc_pushbyte($$, 0);
2316 $$ = abc_setlocal($$, it);
2318 code_t*loopstart = $$ = abc_label($$);
2320 $$ = abc_hasnext2($$, array, it);
2321 code_t*myif = $$ = abc_iffalse($$, 0);
2322 $$ = abc_getlocal($$, array);
2323 $$ = abc_getlocal($$, it);
2325 $$ = abc_nextname($$);
2327 $$ = abc_nextvalue($$);
2328 $$ = converttype($$, 0, var->type);
2329 $$ = abc_setlocal($$, var->index);
2331 $$ = code_append($$, $6);
2332 $$ = abc_jump($$, loopstart);
2334 code_t*out = $$ = abc_nop($$);
2335 breakjumpsto($$, $1.name, out);
2336 continuejumpsto($$, $1.name, loopstart);
2348 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2352 code_t*myjmp = $$ = abc_jump($$, 0);
2353 code_t*loopstart = $$ = abc_label($$);
2354 $$ = code_append($$, $6);
2355 code_t*cont = $$ = abc_nop($$);
2356 myjmp->branch = cont;
2357 $$ = code_append($$, $4.c);
2358 $$ = abc_iftrue($$, loopstart);
2359 code_t*out = $$ = abc_nop($$);
2360 breakjumpsto($$, $1, out);
2361 continuejumpsto($$, $1, cont);
2367 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2369 code_t*loopstart = $$ = abc_label($$);
2370 $$ = code_append($$, $3);
2371 code_t*cont = $$ = abc_nop($$);
2372 $$ = code_append($$, $6.c);
2373 $$ = abc_iftrue($$, loopstart);
2374 code_t*out = $$ = abc_nop($$);
2375 breakjumpsto($$, $1, out);
2376 continuejumpsto($$, $1, cont);
2382 BREAK : "break" %prec prec_none {
2383 $$ = abc___break__(0, "");
2385 BREAK : "break" T_IDENTIFIER {
2386 $$ = abc___break__(0, $2);
2388 CONTINUE : "continue" %prec prec_none {
2389 $$ = abc___continue__(0, "");
2391 CONTINUE : "continue" T_IDENTIFIER {
2392 $$ = abc___continue__(0, $2);
2395 MAYBE_CASE_LIST : {$$=0;}
2396 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2397 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2398 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2399 CASE_LIST: CASE {$$=$1;}
2400 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2402 CASE: "case" E ':' MAYBECODE {
2403 $$ = abc_getlocal(0, state->switch_var);
2404 $$ = code_append($$, $2.c);
2405 code_t*j = $$ = abc_ifne($$, 0);
2406 $$ = code_append($$, $4);
2407 if($$->opcode != OPCODE___BREAK__) {
2408 $$ = abc___fallthrough__($$, "");
2410 code_t*e = $$ = abc_nop($$);
2413 DEFAULT: "default" ':' MAYBECODE {
2416 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2418 $$ = abc_setlocal($$, state->switch_var);
2419 $$ = code_append($$, $7);
2421 code_t*out = $$ = abc_kill($$, state->switch_var);
2422 breakjumpsto($$, $1, out);
2424 code_t*c = $$,*lastblock=0;
2426 if(c->opcode == OPCODE_IFNE) {
2427 if(!c->next) syntaxerror("internal error in fallthrough handling");
2429 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2431 c->opcode = OPCODE_JUMP;
2432 c->branch = lastblock;
2434 /* fall through end of switch */
2435 c->opcode = OPCODE_NOP;
2445 /* ------------ try / catch /finally ---------------- */
2447 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2448 state->exception_name=$3;
2449 PASS1 new_variable($3, 0, 0, 0);
2450 PASS2 new_variable($3, $4, 0, 0);
2453 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2454 multiname_t name = {QNAME, &name_ns, 0, $3};
2456 NEW(abc_exception_t, e)
2457 e->exc_type = sig2mname($4);
2458 e->var_name = multiname_clone(&name);
2462 int i = find_variable_safe(state, $3)->index;
2463 e->target = c = abc_nop(0);
2464 c = abc_setlocal(c, i);
2465 c = code_append(c, code_dup(state->method->scope_code));
2466 c = code_append(c, $8);
2472 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2477 NEW(abc_exception_t, e)
2478 e->exc_type = 0; //all exceptions
2479 e->var_name = 0; //no name
2482 e->to = code_append(e->to, $4);
2488 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2489 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2490 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2491 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2495 list_append($$.l,$2);
2496 $$.finally = $2->to;$2->to=0;
2499 CATCH_FINALLY_LIST: FINALLY {
2503 list_append($$.l,$1);
2504 $$.finally = $1->to;$1->to=0;
2508 TRY : "try" '{' {PASS12 new_state();
2509 state->method->has_exceptions=1;
2510 state->method->late_binding=1;//for invariant scope_code
2511 } MAYBECODE '}' CATCH_FINALLY_LIST {
2512 code_t*out = abc_nop(0);
2514 code_t*start = abc_nop(0);
2515 $$ = code_append(start, $4);
2516 if(!is_break_or_jump($4)) {
2517 $$ = abc_jump($$, out);
2519 code_t*end = $$ = abc_nop($$);
2523 tmp = new_variable("__finally__", 0, 0, 0);
2525 abc_exception_list_t*l = $6.l;
2528 abc_exception_t*e = l->abc_exception;
2530 $$ = code_append($$, e->target);
2531 $$ = abc_jump($$, out);
2533 parserassert((ptroff_t)$6.finally);
2535 e->target = $$ = abc_nop($$);
2536 $$ = code_append($$, code_dup(state->method->scope_code));
2537 $$ = abc___rethrow__($$);
2545 $$ = code_append($$, out);
2547 $$ = insert_finally($$, $6.finally, tmp);
2549 list_concat(state->method->exceptions, $6.l);
2555 /* ------------ throw ------------------------------- */
2557 THROW : "throw" EXPRESSION {
2561 THROW : "throw" %prec prec_none {
2562 if(!state->exception_name)
2563 syntaxerror("re-throw only possible within a catch block");
2564 variable_t*v = find_variable(state, state->exception_name);
2566 $$=abc_getlocal($$, v->index);
2570 /* ------------ with -------------------------------- */
2572 WITH_HEAD : "with" '(' EXPRESSION ')' {
2574 if(state->method->has_exceptions) {
2575 int v = alloc_local();
2576 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2577 state->method->scope_code = abc_pushwith(state->method->scope_code);
2582 WITH : WITH_HEAD CODEBLOCK {
2583 /* remove getlocal;pushwith from scope code again */
2584 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2587 if(state->method->has_exceptions) {
2589 $$ = abc_setlocal($$, $1.number);
2591 $$ = abc_pushwith($$);
2592 $$ = code_append($$, $2);
2593 $$ = abc_popscope($$);
2597 /* ------------ packages and imports ---------------- */
2599 X_IDENTIFIER: T_IDENTIFIER
2600 | "package" {PASS12 $$="package";}
2601 | T_NAMESPACE {PASS12 $$=$1;}
2603 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2604 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2606 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2607 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2608 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2609 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2612 static void state_has_imports()
2614 state->wildcard_imports = list_clone(state->wildcard_imports);
2615 state->imports = dict_clone(state->imports);
2616 state->has_own_imports = 1;
2618 static void import_toplevel(const char*package)
2620 char* s = strdup(package);
2622 dict_put(state->import_toplevel_packages, s, 0);
2623 char*x = strrchr(s, '.');
2631 IMPORT : "import" PACKAGEANDCLASS {
2633 slotinfo_t*s = registry_find($2->package, $2->name);
2634 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2635 as3_schedule_class($2->package, $2->name);
2639 syntaxerror("Couldn't import class\n");
2640 state_has_imports();
2641 dict_put(state->imports, c->name, c);
2642 import_toplevel(c->package);
2645 IMPORT : "import" PACKAGE '.' '*' {
2647 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2648 as3_schedule_package($2);
2653 state_has_imports();
2654 list_append(state->wildcard_imports, i);
2655 import_toplevel(i->package);
2659 /* ------------ classes and interfaces (header) -------------- */
2661 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2662 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2663 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2664 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2666 $$.flags=$1.flags|$2.flags;
2667 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2668 $$.ns=$1.ns?$1.ns:$2.ns;
2671 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2672 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2673 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2674 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2675 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2676 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2677 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2678 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2679 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2680 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2684 EXTENDS : {PASS12 $$=0;}
2685 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2687 EXTENDS_LIST : {PASS12 $$=list_new();}
2688 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2690 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2691 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2693 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2694 EXTENDS IMPLEMENTS_LIST
2695 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2697 '}' {PASS12 endclass();$$=0;}
2699 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2701 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2702 startclass(&$1,$3,0,$4);}
2703 MAYBE_INTERFACE_BODY
2704 '}' {PASS12 endclass();$$=0;}
2706 /* ------------ classes and interfaces (body) -------------- */
2709 MAYBE_CLASS_BODY : CLASS_BODY
2710 CLASS_BODY : CLASS_BODY_ITEM
2711 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2712 CLASS_BODY_ITEM : ';'
2713 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}' {PASS_ALWAYS as3_pass=$1;}
2714 CLASS_BODY_ITEM : SLOT_DECLARATION
2715 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2717 CLASS_BODY_ITEM : CODE_STATEMENT {
2718 code_t*c = state->cls->static_init->header;
2719 c = code_append(c, $1);
2720 state->cls->static_init->header = c;
2723 MAYBE_INTERFACE_BODY :
2724 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2725 INTERFACE_BODY : IDECLARATION
2726 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2728 IDECLARATION : "var" T_IDENTIFIER {
2729 syntaxerror("variable declarations not allowed in interfaces");
2731 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2733 $1.flags |= FLAG_PUBLIC;
2734 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2735 syntaxerror("invalid method modifiers: interface methods always need to be public");
2737 startfunction(&$1,$3,$4,&$6,$8);
2738 endfunction(&$1,$3,$4,&$6,$8, 0);
2739 list_deep_free($6.list);
2742 /* ------------ classes and interfaces (body, slots ) ------- */
2745 static int slotstate_varconst = 0;
2746 static modifiers_t*slotstate_flags = 0;
2747 static void setslotstate(modifiers_t* flags, int varconst)
2749 slotstate_varconst = varconst;
2750 slotstate_flags = flags;
2752 if(flags && flags->flags&FLAG_STATIC) {
2753 state->method = state->cls->static_init;
2755 state->method = state->cls->init;
2758 parserassert(state->method);
2763 VARCONST: "var" | "const"
2765 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {PASS12 setslotstate(&$1,$2);} SLOT_LIST {PASS12 $$=$4;setslotstate(0, 0);}
2767 SLOT_LIST: ONE_SLOT {PASS12 $$=0;}
2768 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {PASS12 $$=0;}
2770 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2773 int flags = slotstate_flags->flags;
2774 namespace_t ns = modifiers2access(slotstate_flags);
2778 varinfo_t* info = 0;
2780 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2782 check_override(i, flags);
2784 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2786 slotinfo_t*i = registry_find(state->package, $1);
2788 syntaxerror("package %s already contains '%s'", state->package, $1);
2790 if(ns.name && ns.name[0]) {
2791 syntaxerror("namespaces not allowed on package-level variables");
2793 info = varinfo_register_global(ns.access, state->package, $1);
2797 info->flags = flags;
2799 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, info);
2803 varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
2806 multiname_t mname = {QNAME, &ns, 0, $1};
2808 trait_list_t**traits;
2812 ns.name = state->package;
2813 traits = &global->init->traits;
2814 code = &global->init->method->body->code;
2815 } else if(flags&FLAG_STATIC) {
2817 traits = &state->cls->abc->static_traits;
2818 code = &state->cls->static_init->header;
2820 // instance variable
2821 traits = &state->cls->abc->traits;
2822 code = &state->cls->init->header;
2828 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2830 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2832 info->slot = t->slot_id;
2834 /* initalization code (if needed) */
2836 if($3.c && !is_pushundefined($3.c)) {
2837 c = abc_getlocal_0(c);
2838 c = code_append(c, $3.c);
2839 c = converttype(c, $3.t, $2);
2840 c = abc_setslot(c, t->slot_id);
2843 *code = code_append(*code, c);
2845 if(slotstate_varconst==KW_CONST) {
2846 t->kind= TRAIT_CONST;
2853 /* ------------ constants -------------------------------------- */
2855 MAYBECONSTANT: {$$=0;}
2856 MAYBECONSTANT: '=' CONSTANT {$$=$2;}
2858 //CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2859 CONSTANT : T_INT {$$ = constant_new_int($1);}
2861 $$ = constant_new_uint($1);
2863 CONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2864 CONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2865 CONSTANT : "true" {$$ = constant_new_true($1);}
2866 CONSTANT : "false" {$$ = constant_new_false($1);}
2867 CONSTANT : "null" {$$ = constant_new_null($1);}
2868 CONSTANT : "undefined" {$$ = constant_new_undefined($1);}
2869 CONSTANT : KW_NAN {$$ = constant_new_float(__builtin_nan(""));}
2872 CONSTANT : T_IDENTIFIER {
2873 if(!strcmp($1, "NaN")) {
2874 $$ = constant_new_float(__builtin_nan(""));
2876 as3_warning("Couldn't evaluate constant value of %s", $1);
2877 $$ = constant_new_null($1);
2881 /* ------------ classes and interfaces (body, functions) ------- */
2883 // non-vararg version
2886 memset(&$$,0,sizeof($$));
2888 MAYBE_PARAM_LIST: PARAM_LIST {
2894 MAYBE_PARAM_LIST: "..." PARAM {
2896 memset(&$$,0,sizeof($$));
2898 list_append($$.list, $2);
2900 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2904 list_append($$.list, $4);
2908 PARAM_LIST: PARAM_LIST ',' PARAM {
2911 list_append($$.list, $3);
2915 memset(&$$,0,sizeof($$));
2916 list_append($$.list, $1);
2919 PARAM: T_IDENTIFIER ':' TYPE MAYBECONSTANT {
2921 $$ = rfx_calloc(sizeof(param_t));
2927 PARAM: T_IDENTIFIER MAYBECONSTANT {
2929 $$ = rfx_calloc(sizeof(param_t));
2931 $$->type = TYPE_ANY;
2939 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2940 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2943 endfunction(&$1,$3,$4,&$6,0,0);
2945 if(!state->method->info) syntaxerror("internal error");
2947 code_t*c = method_header(state->method);
2948 c = wrap_function(c, 0, $11);
2950 endfunction(&$1,$3,$4,&$6,$8,c);
2952 list_deep_free($6.list);
2956 MAYBE_IDENTIFIER: T_IDENTIFIER
2957 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2958 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2959 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2962 endfunction(0,0,$2,&$4,0,0);
2964 methodinfo_t*f = state->method->info;
2965 if(!f || !f->kind) syntaxerror("internal error");
2967 code_t*c = method_header(state->method);
2968 c = wrap_function(c, 0, $9);
2970 int index = state->method->var_index;
2971 endfunction(0,0,$2,&$4,$6,c);
2973 $$.c = abc_getlocal(0, index);
2974 $$.t = TYPE_FUNCTION(f);
2976 PASS12 list_deep_free($4.list);
2980 /* ------------- package + class ids --------------- */
2982 CLASS: X_IDENTIFIER {
2983 PASS1 NEW(unresolvedinfo_t,c);
2984 memset(c, 0, sizeof(*c));
2985 c->kind = INFOTYPE_UNRESOLVED;
2987 c->package = get_package_from_name($1);
2989 c->nsset = get_current_imports();
2990 /* make the compiler look for this class in the current directory,
2992 as3_schedule_class_noerror(state->package, $1);
2994 $$ = (classinfo_t*)c;
2996 slotinfo_t*s = find_class($1);
2997 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2998 $$ = (classinfo_t*)s;
3001 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
3002 PASS1 NEW(unresolvedinfo_t,c);
3003 memset(c, 0, sizeof(*c));
3004 c->kind = INFOTYPE_UNRESOLVED;
3007 $$ = (classinfo_t*)c;
3009 slotinfo_t*s = registry_find($1, $3);
3010 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
3012 $$ = (classinfo_t*)s;
3015 CLASS_SPEC: PACKAGEANDCLASS
3018 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
3019 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
3021 TYPE : CLASS_SPEC {PASS12 $$=$1;}
3022 | '*' {PASS12 $$=registry_getanytype();}
3023 | "void" {PASS12 $$=registry_getanytype();}
3025 | "String" {$$=registry_getstringclass();}
3026 | "int" {$$=registry_getintclass();}
3027 | "uint" {$$=registry_getuintclass();}
3028 | "Boolean" {$$=registry_getbooleanclass();}
3029 | "Number" {$$=registry_getnumberclass();}
3032 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
3033 MAYBETYPE: {PASS12 $$=0;}
3035 /* ----------function calls, delete, constructor calls ------ */
3037 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
3038 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
3040 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
3041 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
3042 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
3044 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
3048 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
3049 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
3050 $$.number= $1.number+1;
3051 $$.cc = code_append($1.cc, $2.c);
3055 NEW : "new" E XX MAYBE_PARAM_VALUES {
3057 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
3059 code_t*paramcode = $4.cc;
3060 if($$.c->opcode == OPCODE_GETPROPERTY) {
3061 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3062 $$.c = code_cutlast($$.c);
3063 $$.c = code_append($$.c, paramcode);
3064 $$.c = abc_constructprop2($$.c, name, $4.number);
3065 multiname_destroy(name);
3066 } else if($$.c->opcode == OPCODE_GETSLOT) {
3067 int slot = (int)(ptroff_t)$$.c->data[0];
3068 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3069 multiname_t*name = t->name;
3070 $$.c = code_cutlast($$.c);
3071 $$.c = code_append($$.c, paramcode);
3072 $$.c = abc_constructprop2($$.c, name, $4.number);
3074 $$.c = code_append($$.c, paramcode);
3075 $$.c = abc_construct($$.c, $4.number);
3079 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
3082 $$.c = abc_coerce_a($$.c);
3087 /* TODO: use abc_call (for calling local variables),
3088 abc_callstatic (for calling own methods)
3091 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3094 if($$.c->opcode == OPCODE_COERCE_A) {
3095 $$.c = code_cutlast($$.c);
3097 code_t*paramcode = $3.cc;
3100 if($$.c->opcode == OPCODE_GETPROPERTY) {
3101 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3102 $$.c = code_cutlast($$.c);
3103 $$.c = code_append($$.c, paramcode);
3104 $$.c = abc_callproperty2($$.c, name, $3.number);
3105 multiname_destroy(name);
3106 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3107 int slot = (int)(ptroff_t)$$.c->data[0];
3108 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3109 if(t->kind!=TRAIT_METHOD) {
3110 //ok: flash allows to assign closures to members.
3112 multiname_t*name = t->name;
3113 $$.c = code_cutlast($$.c);
3114 $$.c = code_append($$.c, paramcode);
3115 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3116 $$.c = abc_callproperty2($$.c, name, $3.number);
3117 } else if($$.c->opcode == OPCODE_GETSUPER) {
3118 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3119 $$.c = code_cutlast($$.c);
3120 $$.c = code_append($$.c, paramcode);
3121 $$.c = abc_callsuper2($$.c, name, $3.number);
3122 multiname_destroy(name);
3124 $$.c = abc_getglobalscope($$.c);
3125 $$.c = code_append($$.c, paramcode);
3126 $$.c = abc_call($$.c, $3.number);
3129 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
3130 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
3132 $$.c = abc_coerce_a($$.c);
3137 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3138 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3139 if(!state->method) syntaxerror("super() not allowed outside of a function");
3140 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3143 $$.c = abc_getlocal_0($$.c);
3145 $$.c = code_append($$.c, $3.cc);
3147 this is dependent on the control path, check this somewhere else
3148 if(state->method->has_super)
3149 syntaxerror("constructor may call super() only once");
3151 state->method->has_super = 1;
3153 $$.c = abc_constructsuper($$.c, $3.number);
3154 $$.c = abc_pushundefined($$.c);
3158 DELETE: "delete" E {
3160 if($$.c->opcode == OPCODE_COERCE_A) {
3161 $$.c = code_cutlast($$.c);
3163 multiname_t*name = 0;
3164 if($$.c->opcode == OPCODE_GETPROPERTY) {
3165 $$.c->opcode = OPCODE_DELETEPROPERTY;
3166 } else if($$.c->opcode == OPCODE_GETSLOT) {
3167 int slot = (int)(ptroff_t)$$.c->data[0];
3168 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3169 $$.c = code_cutlast($$.c);
3170 $$.c = abc_deleteproperty2($$.c, name);
3172 $$.c = abc_getlocal_0($$.c);
3173 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3174 $$.c = abc_deleteproperty2($$.c, &m);
3176 $$.t = TYPE_BOOLEAN;
3179 RETURN: "return" %prec prec_none {
3180 $$ = abc_returnvoid(0);
3182 RETURN: "return" EXPRESSION {
3184 $$ = abc_returnvalue($$);
3187 // ----------------------- expression types -------------------------------------
3189 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3190 EXPRESSION : E %prec below_minus {$$ = $1;}
3191 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3193 $$.c = cut_last_push($$.c);
3194 $$.c = code_append($$.c,$3.c);
3197 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3198 $$=cut_last_push($1.c);
3201 // ----------------------- expression evaluation -------------------------------------
3203 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3204 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3206 E : DELETE {$$ = $1;}
3211 node_t*n = mkconstnode($1);
3217 namespace_t ns = {ACCESS_PACKAGE, ""};
3218 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3220 $$.c = abc_getlex2($$.c, &m);
3221 $$.c = abc_pushstring($$.c, $1.pattern);
3222 $$.c = abc_construct($$.c, 1);
3224 $$.c = abc_getlex2($$.c, &m);
3225 $$.c = abc_pushstring($$.c, $1.pattern);
3226 $$.c = abc_pushstring($$.c, $1.options);
3227 $$.c = abc_construct($$.c, 2);
3232 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3233 $$.t = TYPE_BOOLEAN;
3235 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3236 $$.t = TYPE_BOOLEAN;
3238 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3239 $$.t = TYPE_BOOLEAN;
3241 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3242 $$.t = TYPE_BOOLEAN;
3244 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3245 $$.t = TYPE_BOOLEAN;
3247 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3248 $$.t = TYPE_BOOLEAN;
3250 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3251 $$.t = TYPE_BOOLEAN;
3253 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3254 $$.t = TYPE_BOOLEAN;
3257 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3259 $$.c = converttype($$.c, $1.t, $$.t);
3260 $$.c = abc_dup($$.c);
3261 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3262 $$.c = cut_last_push($$.c);
3263 $$.c = code_append($$.c,$3.c);
3264 $$.c = converttype($$.c, $3.t, $$.t);
3265 code_t*label = $$.c = abc_label($$.c);
3266 jmp->branch = label;
3269 $$.t = join_types($1.t, $3.t, 'A');
3270 /*printf("%08x:\n",$1.t);
3271 code_dump($1.c, 0, 0, "", stdout);
3272 printf("%08x:\n",$3.t);
3273 code_dump($3.c, 0, 0, "", stdout);
3274 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3276 $$.c = converttype($$.c, $1.t, $$.t);
3277 $$.c = abc_dup($$.c);
3278 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3279 $$.c = cut_last_push($$.c);
3280 $$.c = code_append($$.c,$3.c);
3281 $$.c = converttype($$.c, $3.t, $$.t);
3282 code_t*label = $$.c = abc_label($$.c);
3283 jmp->branch = label;
3286 E : '!' E {$$.c=$2.c;
3287 $$.c = abc_not($$.c);
3288 $$.t = TYPE_BOOLEAN;
3291 E : '~' E {$$.c=$2.c;
3292 $$.c = abc_bitnot($$.c);
3296 E : E '&' E {$$.c = code_append($1.c,$3.c);
3297 $$.c = abc_bitand($$.c);
3301 E : E '^' E {$$.c = code_append($1.c,$3.c);
3302 $$.c = abc_bitxor($$.c);
3306 E : E '|' E {$$.c = code_append($1.c,$3.c);
3307 $$.c = abc_bitor($$.c);
3311 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3312 $$.c = abc_rshift($$.c);
3315 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3316 $$.c = abc_urshift($$.c);
3319 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3320 $$.c = abc_lshift($$.c);
3324 E : E '/' E {$$.c = code_append($1.c,$3.c);
3325 $$.c = abc_divide($$.c);
3328 E : E '%' E {$$.c = code_append($1.c,$3.c);
3329 $$.c = abc_modulo($$.c);
3332 E : E '+' E {$$.c = code_append($1.c,$3.c);
3333 if(BOTH_INT($1.t, $3.t)) {
3334 $$.c = abc_add_i($$.c);
3337 $$.c = abc_add($$.c);
3338 $$.t = join_types($1.t,$3.t,'+');
3341 E : E '-' E {$$.c = code_append($1.c,$3.c);
3342 if(BOTH_INT($1.t,$3.t)) {
3343 $$.c = abc_subtract_i($$.c);
3346 $$.c = abc_subtract($$.c);
3350 E : E '*' E {$$.c = code_append($1.c,$3.c);
3351 if(BOTH_INT($1.t,$3.t)) {
3352 $$.c = abc_multiply_i($$.c);
3355 $$.c = abc_multiply($$.c);
3360 E : E "in" E {$$.c = code_append($1.c,$3.c);
3361 $$.c = abc_in($$.c);
3362 $$.t = TYPE_BOOLEAN;
3365 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3366 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3367 MULTINAME(m, (classinfo_t*)($3.t->data));
3368 $$.c = abc_astype2($1.c, &m);
3371 $$.c = code_append($1.c, $3.c);
3372 $$.c = abc_astypelate($$.c);
3377 E : E "instanceof" E
3378 {$$.c = code_append($1.c, $3.c);
3379 $$.c = abc_instanceof($$.c);
3380 $$.t = TYPE_BOOLEAN;
3383 E : E "is" E {$$.c = code_append($1.c, $3.c);
3384 $$.c = abc_istypelate($$.c);
3385 $$.t = TYPE_BOOLEAN;
3388 E : "typeof" '(' E ')' {
3390 $$.c = abc_typeof($$.c);
3395 $$.c = cut_last_push($2.c);
3396 $$.c = abc_pushundefined($$.c);
3400 E : "void" { $$.c = abc_pushundefined(0);
3404 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3409 $$.c=abc_negate_i($$.c);
3412 $$.c=abc_negate($$.c);
3419 $$.c = code_append($$.c, $3.c);
3421 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3422 $$.c = abc_getproperty2($$.c, &m);
3423 $$.t = 0; // array elements have unknown type
3426 E : '[' MAYBE_EXPRESSION_LIST ']' {
3428 $$.c = code_append($$.c, $2.cc);
3429 $$.c = abc_newarray($$.c, $2.number);
3430 $$.t = registry_getarrayclass();
3433 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3434 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3436 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3438 $$.cc = code_append($$.cc, $1.c);
3439 $$.cc = code_append($$.cc, $3.c);
3442 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3444 $$.number = $1.number+2;
3445 $$.cc = code_append($$.cc, $3.c);
3446 $$.cc = code_append($$.cc, $5.c);
3451 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3453 $$.c = code_append($$.c, $2.cc);
3454 $$.c = abc_newobject($$.c, $2.number/2);
3455 $$.t = registry_getobjectclass();
3460 if(BOTH_INT($1.t,$3.t)) {
3461 c=abc_multiply_i(c);
3465 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3466 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3471 code_t*c = abc_modulo($3.c);
3472 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3473 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3477 code_t*c = abc_lshift($3.c);
3478 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3479 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3483 code_t*c = abc_rshift($3.c);
3484 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3485 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3489 code_t*c = abc_urshift($3.c);
3490 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3491 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3495 code_t*c = abc_divide($3.c);
3496 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3497 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3501 code_t*c = abc_bitor($3.c);
3502 c=converttype(c, TYPE_INT, $1.t);
3503 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3507 code_t*c = abc_bitxor($3.c);
3508 c=converttype(c, TYPE_INT, $1.t);
3509 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3515 if(TYPE_IS_INT($1.t)) {
3519 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3522 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3525 E : E "-=" E { code_t*c = $3.c;
3526 if(TYPE_IS_INT($1.t)) {
3527 c=abc_subtract_i(c);
3530 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3533 $$.c = toreadwrite($1.c, c, 0, 0, 1);
3536 E : E '=' E { code_t*c = 0;
3537 c = code_append(c, $3.c);
3538 c = converttype(c, $3.t, $1.t);
3539 $$.c = toreadwrite($1.c, c, 1, 0, 1);
3543 E : E '?' E ':' E %prec below_assignment {
3544 $$.t = join_types($3.t,$5.t,'?');
3546 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3547 $$.c = code_append($$.c, $3.c);
3548 $$.c = converttype($$.c, $3.t, $$.t);
3549 code_t*j2 = $$.c = abc_jump($$.c, 0);
3550 $$.c = j1->branch = abc_label($$.c);
3551 $$.c = code_append($$.c, $5.c);
3552 $$.c = converttype($$.c, $5.t, $$.t);
3553 $$.c = j2->branch = abc_label($$.c);
3556 E : E "++" { code_t*c = 0;
3557 classinfo_t*type = $1.t;
3558 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3559 int nr = getlocalnr($1.c);
3560 code_free($1.c);$1.c=0;
3561 if(TYPE_IS_INT($1.t)) {
3562 $$.c = abc_getlocal(0, nr);
3563 $$.c = abc_inclocal_i($$.c, nr);
3564 } else if(TYPE_IS_NUMBER($1.t)) {
3565 $$.c = abc_getlocal(0, nr);
3566 $$.c = abc_inclocal($$.c, nr);
3567 } else syntaxerror("internal error");
3569 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3570 c=abc_increment_i(c);
3576 c=converttype(c, type, $1.t);
3577 $$.c = toreadwrite($1.c, c, 0, 1, 1);
3582 // TODO: use inclocal, like with ++
3583 E : E "--" { code_t*c = 0;
3584 classinfo_t*type = $1.t;
3585 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3586 c=abc_decrement_i(c);
3592 c=converttype(c, type, $1.t);
3593 $$.c = toreadwrite($1.c, c, 0, 1, 1);
3597 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3598 classinfo_t*type = $2.t;
3599 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3600 c=abc_increment_i(c);
3606 c=converttype(c, type, $2.t);
3607 $$.c = toreadwrite($2.c, c, 0, 0, 1);
3611 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3612 classinfo_t*type = $2.t;
3613 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3614 c=abc_decrement_i(c);
3620 c=converttype(c, type, $2.t);
3621 $$.c = toreadwrite($2.c, c, 0, 0, 1);
3625 E : "super" '.' T_IDENTIFIER
3626 { if(!state->cls->info)
3627 syntaxerror("super keyword not allowed outside a class");
3628 classinfo_t*t = state->cls->info->superclass;
3629 if(!t) t = TYPE_OBJECT;
3631 memberinfo_t*f = findmember_nsset(t, $3, 1);
3633 MEMBER_MULTINAME(m, f, $3);
3635 $$.c = abc_getlocal_0($$.c);
3636 $$.c = abc_getsuper2($$.c, &m);
3637 $$.t = slotinfo_gettype((slotinfo_t*)f);
3640 E : '@' T_IDENTIFIER {
3642 $$.c = abc_pushundefined(0);
3644 as3_warning("ignored @ operator");
3647 E : E '.' '@' T_IDENTIFIER {
3648 // child attribute TODO
3649 $$.c = abc_pushundefined(0);
3651 as3_warning("ignored .@ operator");
3654 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3655 // namespace declaration TODO
3656 $$.c = abc_pushundefined(0);
3658 as3_warning("ignored :: operator");
3661 E : E ".." T_IDENTIFIER {
3663 $$.c = abc_pushundefined(0);
3665 as3_warning("ignored .. operator");
3668 E : E '.' '(' E ')' {
3670 $$.c = abc_pushundefined(0);
3672 as3_warning("ignored .() operator");
3675 //E : E "::" '[' E ']' {
3676 // // qualified expression TODO
3677 // $$.c = abc_pushundefined(0);
3679 // as3_warning("ignored ::[] operator");
3683 E : E '.' T_IDENTIFIER {
3685 classinfo_t*t = $1.t;
3687 if(TYPE_IS_CLASS(t) && t->data) {
3692 if(t->subtype==INFOTYPE_UNRESOLVED) {
3693 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3695 memberinfo_t*f = findmember_nsset(t, $3, 1);
3697 if(f && !is_static != !(f->flags&FLAG_STATIC))
3699 if(f && f->slot && !noslot) {
3700 $$.c = abc_getslot($$.c, f->slot);
3703 as3_warning("Access of undefined property '%s' in %s", $3, t->name);
3706 MEMBER_MULTINAME(m, f, $3);
3707 $$.c = abc_getproperty2($$.c, &m);
3709 /* determine type */
3710 $$.t = slotinfo_gettype((slotinfo_t*)f);
3712 $$.c = abc_coerce_a($$.c);
3713 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3714 string_t*package = $1.c->data[0];
3715 char*package2 = concat3(package->str, ".", $3);
3717 slotinfo_t*a = registry_find(package->str, $3);
3720 } else if(dict_contains(state->import_toplevel_packages, package2) ||
3721 registry_ispackage(package2)) {
3723 $$.c->data[0] = string_new4(package2);
3726 syntaxerror("couldn't resolve %s", package2);
3729 /* when resolving a property on an unknown type, we do know the
3730 name of the property (and don't seem to need the package), but
3731 we need to make avm2 try out all access modes */
3732 as3_warning("Resolving %s on unknown type", $3);
3733 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3734 $$.c = abc_getproperty2($$.c, &m);
3735 $$.c = abc_coerce_a($$.c);
3736 $$.t = registry_getanytype();
3740 VAR_READ : T_IDENTIFIER {
3742 /* Queue unresolved identifiers for checking against the parent
3743 function's variables.
3744 We consider everything which is not a local variable "unresolved".
3745 This encompasses class names, members of the surrounding class
3746 etc. which is *correct* because local variables of the parent function
3749 if(state->method->inner && !find_variable(state, $1)) {
3750 unknown_variable($1);
3753 /* let the compiler know that it might want to check the current directory/package
3754 for this identifier- maybe there's a file $1.as defining $1. */
3755 as3_schedule_class_noerror(state->package, $1);
3764 /* look at variables */
3765 if((v = find_variable(state, $1))) {
3766 // $1 is a local variable
3767 $$.c = abc_getlocal($$.c, v->index);
3771 if((v = find_slot(state, $1))) {
3772 $$.c = abc_getscopeobject($$.c, 1);
3773 $$.c = abc_getslot($$.c, v->index);
3778 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3780 /* look at current class' members */
3781 if(!state->method->inner &&
3783 (f = findmember_nsset(state->cls->info, $1, 1)) &&
3784 (f->flags&FLAG_STATIC) >= i_am_static)
3786 // $1 is a function in this class
3787 int var_is_static = (f->flags&FLAG_STATIC);
3789 if(f->kind == INFOTYPE_METHOD) {
3790 $$.t = TYPE_FUNCTION(f);
3794 if(var_is_static && !i_am_static) {
3795 /* access to a static member from a non-static location.
3796 do this via findpropstrict:
3797 there doesn't seem to be any non-lookup way to access
3798 static properties of a class */
3799 state->method->late_binding = 1;
3801 namespace_t ns = {f->access, f->package};
3802 multiname_t m = {QNAME, &ns, 0, $1};
3803 $$.c = abc_findpropstrict2($$.c, &m);
3804 $$.c = abc_getproperty2($$.c, &m);
3806 } else if(f->slot>0) {
3807 $$.c = abc_getlocal_0($$.c);
3808 $$.c = abc_getslot($$.c, f->slot);
3811 namespace_t ns = {f->access, f->package};
3812 multiname_t m = {QNAME, &ns, 0, $1};
3813 $$.c = abc_getlocal_0($$.c);
3814 $$.c = abc_getproperty2($$.c, &m);
3819 /* look at actual classes, in the current package and imported */
3820 if((a = find_class($1))) {
3825 /* look through package prefixes */
3826 if(dict_contains(state->import_toplevel_packages, $1) ||
3827 registry_ispackage($1)) {
3828 $$.c = abc___pushpackage__($$.c, $1);
3833 /* unknown object, let the avm2 resolve it */
3835 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3836 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3837 state->method->late_binding = 1;
3839 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3842 $$.c = abc_findpropstrict2($$.c, &m);
3843 $$.c = abc_getproperty2($$.c, &m);
3847 // ----------------- namespaces -------------------------------------------------
3849 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3851 NEW(namespace_decl_t,n);
3856 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3858 NEW(namespace_decl_t,n);
3863 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3865 NEW(namespace_decl_t,n);
3870 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3872 trie_put(active_namespaces, $2->name, (void*)$2->url);
3874 namespace_t access = modifiers2access(&$1);
3875 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3876 var->type = TYPE_NAMESPACE;
3878 ns.access = ACCESS_NAMESPACE;
3880 var->value = constant_new_namespace(&ns);
3886 void add_active_url(const char*url)
3890 list_append(state->active_namespace_urls, n);
3894 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3896 const char*url = $3->name;
3898 varinfo_t*s = (varinfo_t*)$3;
3899 if(s->kind == INFOTYPE_UNRESOLVED) {
3900 s = (varinfo_t*)registry_resolve((slotinfo_t*)s);
3902 syntaxerror("Couldn't resolve namespace %s", $3->name);
3905 if(!s || s->kind != INFOTYPE_SLOT)
3906 syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
3907 if(!s->value || !NS_TYPE(s->value->type))
3908 syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
3909 url = s->value->ns->name;
3911 trie_put(active_namespaces, $3->name, (void*)url);
3912 add_active_url(url);