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"
48 enum yytokentype token;
50 classinfo_t*classinfo;
51 classinfo_list_t*classinfo_list;
53 slotinfo_list_t*slotinfo_list;
56 unsigned int number_uint;
60 //typedcode_list_t*value_list;
61 codeandnumber_t value_list;
67 for_start_t for_start;
68 abc_exception_t *exception;
71 namespace_decl_t* namespace_decl;
74 abc_exception_list_t *l;
80 %token<id> T_IDENTIFIER T_NAMESPACE
82 %token<regexp> T_REGEXP
84 %token<number_int> T_INT
85 %token<number_uint> T_UINT
86 %token<number_float> T_FLOAT
88 %token<id> T_FOR "for"
89 %token<id> T_WHILE "while"
91 %token<id> T_SWITCH "switch"
93 %token<token> KW_IMPLEMENTS "implements"
94 %token<token> KW_NAMESPACE "namespace"
95 %token<token> KW_PACKAGE "package"
96 %token<token> KW_PROTECTED "protected"
97 %token<token> KW_ARGUMENTS "arguments"
98 %token<token> KW_PUBLIC "public"
99 %token<token> KW_PRIVATE "private"
100 %token<token> KW_USE "use"
101 %token<token> KW_INTERNAL "internal"
102 %token<token> KW_NEW "new"
103 %token<token> KW_NATIVE "native"
104 %token<token> KW_FUNCTION "function"
105 %token<token> KW_FINALLY "finally"
106 %token<token> KW_UNDEFINED "undefined"
107 %token<token> KW_NAN "NaN"
108 %token<token> KW_CONTINUE "continue"
109 %token<token> KW_CLASS "class"
110 %token<token> KW_CONST "const"
111 %token<token> KW_CATCH "catch"
112 %token<token> KW_CASE "case"
113 %token<token> KW_SET "set"
114 %token<token> KW_VOID "void"
115 %token<token> KW_THROW "throw"
116 %token<token> KW_STATIC "static"
117 %token<token> KW_WITH "with"
118 %token<token> KW_INSTANCEOF "instanceof"
119 %token<token> KW_IMPORT "import"
120 %token<token> KW_RETURN "return"
121 %token<token> KW_TYPEOF "typeof"
122 %token<token> KW_INTERFACE "interface"
123 %token<token> KW_NULL "null"
124 %token<token> KW_VAR "var"
125 %token<token> KW_DYNAMIC "dynamic"
126 %token<token> KW_OVERRIDE "override"
127 %token<token> KW_FINAL "final"
128 %token<token> KW_EACH "each"
129 %token<token> KW_GET "get"
130 %token<token> KW_TRY "try"
131 %token<token> KW_SUPER "super"
132 %token<token> KW_EXTENDS "extends"
133 %token<token> KW_FALSE "false"
134 %token<token> KW_TRUE "true"
135 %token<token> KW_BOOLEAN "Boolean"
136 %token<token> KW_UINT "uint"
137 %token<token> KW_INT "int"
138 %token<token> KW_NUMBER "Number"
139 %token<token> KW_STRING "String"
140 %token<token> KW_DEFAULT "default"
141 %token<token> KW_DEFAULT_XML "default xml"
142 %token<token> KW_DELETE "delete"
143 %token<token> KW_IF "if"
144 %token<token> KW_ELSE "else"
145 %token<token> KW_BREAK "break"
146 %token<token> KW_IS "is"
147 %token<token> KW_IN "in"
148 %token<token> KW_AS "as"
150 %token<token> T_DICTSTART "{ (dictionary)"
151 %token<token> T_EQEQ "=="
152 %token<token> T_EQEQEQ "==="
153 %token<token> T_NE "!="
154 %token<token> T_NEE "!=="
155 %token<token> T_LE "<="
156 %token<token> T_GE ">="
157 %token<token> T_ORBY "|="
158 %token<token> T_DIVBY "/="
159 %token<token> T_MODBY "%="
160 %token<token> T_MULBY "*="
161 %token<token> T_ANDBY "&="
162 %token<token> T_PLUSBY "+="
163 %token<token> T_MINUSBY "-="
164 %token<token> T_XORBY "^="
165 %token<token> T_SHRBY ">>="
166 %token<token> T_SHLBY "<<="
167 %token<token> T_USHRBY ">>>="
168 %token<token> T_OROR "||"
169 %token<token> T_ANDAND "&&"
170 %token<token> T_COLONCOLON "::"
171 %token<token> T_MINUSMINUS "--"
172 %token<token> T_PLUSPLUS "++"
173 %token<token> T_DOTDOT ".."
174 %token<token> T_DOTDOTDOT "..."
175 %token<token> T_SHL "<<"
176 %token<token> T_USHR ">>>"
177 %token<token> T_SHR ">>"
179 %type <number_int> CONDITIONAL_COMPILATION
180 %type <for_start> FOR_START
181 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER ID_OR_NS SUBNODE
182 %type <namespace_decl> NAMESPACE_ID
183 %type <token> VARCONST
185 %type <code> CODEPIECE CODE_STATEMENT
186 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
187 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
188 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
189 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
190 %type <exception> CATCH FINALLY
191 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
192 %type <code> CLASS_DECLARATION
193 %type <code> NAMESPACE_DECLARATION
194 %type <code> INTERFACE_DECLARATION
195 %type <code> VOIDEXPRESSION
196 %type <value> EXPRESSION NONCOMMAEXPRESSION
197 %type <node> MAYBEEXPRESSION
199 %type <node> E COMMA_EXPRESSION
200 %type <node> VAR_READ
201 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
202 %type <value> INNERFUNCTION
203 %type <code> USE_NAMESPACE DEFAULT_NAMESPACE
204 %type <code> FOR_INIT
206 %type <classinfo> MAYBETYPE
209 %type <params> PARAM_LIST
210 %type <params> MAYBE_PARAM_LIST
211 %type <flags> MAYBE_MODIFIERS
212 %type <flags> MODIFIER_LIST
213 %type <flags> MODIFIER
214 %type <constant> CONSTANT MAYBECONSTANT
215 %type <classinfo_list> IMPLEMENTS_LIST
216 %type <classinfo> EXTENDS CLASS_SPEC
217 %type <classinfo_list> EXTENDS_LIST
218 %type <classinfo> CLASS PACKAGEANDCLASS
219 %type <classinfo_list> CLASS_SPEC_LIST
220 %type <id> XML XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT XML_ID_OR_EXPR XMLEXPR1 XMLEXPR2
221 %type <classinfo> TYPE
222 //%type <token> VARIABLE
225 //%type <token> T_IDENTIFIER
226 %type <value> FUNCTIONCALL
227 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES
228 %type <value_list> MAYBE_DICT_EXPRPAIR_LIST DICT_EXPRPAIR_LIST WITH_HEAD
231 // precedence: from low to high
235 %left below_semicolon
238 %nonassoc below_assignment // for ?:, contrary to spec
239 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
246 %nonassoc "==" "!=" "===" "!=="
247 %nonassoc "is" "as" "in"
249 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
250 %left "<<" ">>" ">>>"
254 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
256 %nonassoc below_curly
260 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
262 %left T_IDENTIFIER "arguments"
263 %left above_identifier
267 // needed for "return" precedence:
268 %nonassoc T_STRING T_REGEXP
269 %nonassoc T_INT T_UINT T_FLOAT KW_NAN
271 %nonassoc "false" "true" "null" "undefined" "super" "function"
278 static int a3_error(char*s)
280 syntaxerror("%s", s);
281 return 0; //make gcc happy
284 static void parsererror(const char*file, int line, const char*f)
286 syntaxerror("internal error in %s, %s:%d", f, file, line);
289 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
292 static char* concat2(const char* t1, const char* t2)
296 char*text = malloc(l1+l2+1);
297 memcpy(text , t1, l1);
298 memcpy(text+l1, t2, l2);
302 static char* concat3(const char* t1, const char* t2, const char* t3)
307 char*text = malloc(l1+l2+l3+1);
308 memcpy(text , t1, l1);
309 memcpy(text+l1, t2, l2);
310 memcpy(text+l1+l2, t3, l3);
315 typedef struct _import {
318 DECLARE_LIST(import);
320 DECLARE(methodstate);
321 DECLARE_LIST(methodstate);
323 typedef struct _classstate {
329 methodstate_t*static_init;
331 //code_t*static_init;
332 parsedclass_t*dependencies;
334 char has_constructor;
337 struct _methodstate {
348 dict_t*unresolved_variables;
351 char uses_parent_function;
359 int var_index; // for inner methods
360 int slot_index; // for inner methods
361 char is_a_slot; // for inner methods
366 abc_exception_list_t*exceptions;
368 methodstate_list_t*innerfunctions;
371 typedef struct _state {
376 import_list_t*wildcard_imports;
377 dict_t*import_toplevel_packages;
380 namespace_list_t*active_namespace_urls;
382 char has_own_imports;
383 char new_vars; // e.g. transition between two functions
384 char xmlfilter; // are we inside a xmlobj..() filter?
387 methodstate_t*method;
394 dict_t*allvars; // also contains variables from sublevels
397 typedef struct _global {
400 parsedclass_list_t*classes;
401 abc_script_t*classinit;
403 abc_script_t*init; //package-level code
406 dict_t*file2token2info;
409 static global_t*global = 0;
410 static state_t* state = 0;
414 /* protected handling here is a big hack: we just assume the protectedns
415 is package:class. the correct approach would be to add the proper
416 namespace to all protected members in the registry, even though that
417 would slow down searching */
418 #define MEMBER_MULTINAME(m,f,n) \
422 m##_ns.access = ((slotinfo_t*)(f))->access; \
423 if(m##_ns.access == ACCESS_NAMESPACE) \
424 m##_ns.name = ((slotinfo_t*)(f))->package; \
425 else if(m##_ns.access == ACCESS_PROTECTED && (f)->parent) \
426 m##_ns.name = concat3((f)->parent->package,":",(f)->parent->name); \
431 m.namespace_set = 0; \
432 m.name = ((slotinfo_t*)(f))->name; \
434 m.type = MULTINAME; \
436 m.namespace_set = &nopackage_namespace_set; \
440 /* warning: list length of namespace set is undefined */
441 #define MULTINAME_LATE(m, access, package) \
442 namespace_t m##_ns = {access, package}; \
443 namespace_set_t m##_nsset; \
444 namespace_list_t m##_l;m##_l.next = 0; \
445 m##_nsset.namespaces = &m##_l; \
446 m##_nsset = m##_nsset; \
447 m##_l.namespace = &m##_ns; \
448 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
450 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
451 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
452 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
453 static namespace_t stdns = {ACCESS_PACKAGE, ""};
454 static namespace_list_t nl4 = {&stdns,0};
455 static namespace_list_t nl3 = {&ns3,&nl4};
456 static namespace_list_t nl2 = {&ns2,&nl3};
457 static namespace_list_t nl1 = {&ns1,&nl2};
458 static namespace_set_t nopackage_namespace_set = {&nl1};
460 static dict_t*definitions=0;
461 void as3_set_define(const char*c)
464 definitions = dict_new();
465 if(!dict_contains(definitions,c))
466 dict_put(definitions,c,0);
469 static void new_state()
472 state_t*oldstate = state;
474 memcpy(s, state, sizeof(state_t)); //shallow copy
476 s->imports = dict_new();
478 if(!s->import_toplevel_packages) {
479 s->import_toplevel_packages = dict_new();
483 state->has_own_imports = 0;
484 state->vars = dict_new();
485 state->old = oldstate;
488 trie_remember(active_namespaces);
491 state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
494 static void state_destroy(state_t*state)
496 if(state->has_own_imports) {
497 list_free(state->wildcard_imports);
498 dict_destroy(state->imports);state->imports=0;
500 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
501 dict_destroy(state->imports);state->imports=0;
504 dict_destroy(state->vars);state->vars=0;
506 if(state->new_vars && state->allvars) {
507 parserassert(!state->old || state->old->allvars != state->allvars);
508 DICT_ITERATE_DATA(state->allvars, void*, data) {
511 dict_destroy(state->allvars);
514 list_free(state->active_namespace_urls)
515 state->active_namespace_urls = 0;
520 static void old_state()
522 trie_rollback(active_namespaces);
524 if(!state || !state->old)
525 syntaxerror("invalid nesting");
526 state_t*leaving = state;
530 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
531 free(leaving->method);
534 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
539 state_destroy(leaving);
542 static code_t* method_header(methodstate_t*m);
543 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
544 static void function_initvars(methodstate_t*m, char has_params, params_t*params, int flags, char var0);
547 static char* internal_filename_package = 0;
548 void initialize_file(char*filename)
551 syntaxerror("invalid call to initialize_file during parsing of another file");
554 active_namespaces = trie_new();
557 state->package = internal_filename_package = strdup(filename);
558 state->allvars = dict_new();
560 global->token2info = dict_lookup(global->file2token2info,
561 current_filename // use long version
563 if(!global->token2info) {
564 global->token2info = dict_new2(&ptr_type);
565 dict_put(global->file2token2info, current_filename, global->token2info);
569 state->method = rfx_calloc(sizeof(methodstate_t));
570 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
571 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
573 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
574 state->method->variable_count = 0;
576 syntaxerror("internal error: skewed tokencount");
577 function_initvars(state->method, 0, 0, 0, 1);
584 if(!state || state->level!=1) {
585 syntaxerror("unexpected end of file in pass %d", as3_pass);
589 dict_del(global->file2token2info, current_filename);
590 code_t*header = method_header(state->method);
591 //if(global->init->method->body->code || global->init->traits) {
593 code_t*c = wrap_function(header, 0, global->init->method->body->code);
594 global->init->method->body->code = abc_returnvoid(c);
595 free(state->method);state->method=0;
599 //free(state->package);state->package=0; // used in registry
600 state_destroy(state);state=0;
603 void initialize_parser()
605 global = rfx_calloc(sizeof(global_t));
606 global->file = abc_file_new();
607 global->file->flags &= ~ABCFILE_LAZY;
608 global->file2token2info = dict_new();
609 global->token2info = 0;
610 global->classinit = abc_initscript(global->file);
613 void* finish_parser()
615 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
616 global->token2info=0;
618 initcode_add_classlist(global->classinit, global->classes);
623 typedef struct _variable {
628 methodstate_t*is_inner_method;
631 static variable_t* find_variable(state_t*s, char*name)
636 v = dict_lookup(s->vars, name);
638 if(s->new_vars) break;
641 return dict_lookup(top->allvars, name);
643 static variable_t* find_slot(state_t*s, const char*name)
645 if(s->method && s->method->slots)
646 return dict_lookup(s->method->slots, name);
650 static variable_t* find_variable_safe(state_t*s, char*name)
652 variable_t* v = find_variable(s, name);
654 syntaxerror("undefined variable: %s", name);
658 static char variable_exists(char*name)
660 return dict_contains(state->vars, name);
663 static code_t*defaultvalue(code_t*c, classinfo_t*type)
665 if(TYPE_IS_INT(type)) {
666 c = abc_pushbyte(c, 0);
667 } else if(TYPE_IS_UINT(type)) {
668 c = abc_pushuint(c, 0);
669 } else if(TYPE_IS_FLOAT(type)) {
671 } else if(TYPE_IS_BOOLEAN(type)) {
672 c = abc_pushfalse(c);
673 } else if(TYPE_IS_STRING(type)) {
677 //c = abc_pushundefined(c);
678 syntaxerror("internal error: can't generate default value for * type");
682 c = abc_coerce2(c, &m);
687 static int alloc_local()
689 return state->method->variable_count++;
692 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
695 variable_t*v = find_slot(state, name);
703 v->index = alloc_local();
708 dict_put(state->vars, name, v);
709 dict_put(state->allvars, name, v);
714 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
716 return new_variable2(name, type, init, maybeslot)->index;
719 #define TEMPVARNAME "__as3_temp__"
722 variable_t*v = find_variable(state, TEMPVARNAME);
727 i = new_variable(TEMPVARNAME, 0, 0, 0);
732 static code_t* var_block(code_t*body)
738 DICT_ITERATE_DATA(state->vars, variable_t*, v) {
739 if(v->type && v->init) {
740 c = defaultvalue(c, v->type);
741 c = abc_setlocal(c, v->index);
742 k = abc_kill(k, v->index);
750 if(x->opcode== OPCODE___BREAK__ ||
751 x->opcode== OPCODE___CONTINUE__) {
752 /* link kill code before break/continue */
753 code_t*e = code_dup(k);
754 code_t*s = code_start(e);
766 c = code_append(c, body);
767 c = code_append(c, k);
771 static void unknown_variable(char*name)
773 if(!state->method->unresolved_variables)
774 state->method->unresolved_variables = dict_new();
775 if(!dict_contains(state->method->unresolved_variables, name))
776 dict_put(state->method->unresolved_variables, name, 0);
779 static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
781 if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
782 c = abc_getlocal_0(c);
783 c = abc_pushscope(c);
786 /* FIXME: this alloc_local() causes variable indexes to be
787 different in pass2 than in pass1 */
788 if(!m->activation_var) {
789 m->activation_var = alloc_local();
792 c = abc_newactivation(c);
794 c = abc_pushscope(c);
795 c = abc_setlocal(c, m->activation_var);
797 c = abc_getlocal(c, m->activation_var);
798 c = abc_pushscope(c);
804 static code_t* method_header(methodstate_t*m)
808 c = add_scope_code(c, m, 1);
810 methodstate_list_t*l = m->innerfunctions;
812 parserassert(l->methodstate->abc);
813 if(m->uses_slots && l->methodstate->is_a_slot) {
814 c = abc_getscopeobject(c, 1);
815 c = abc_newfunction(c, l->methodstate->abc);
817 c = abc_setlocal(c, l->methodstate->var_index);
818 c = abc_setslot(c, l->methodstate->slot_index);
820 c = abc_newfunction(c, l->methodstate->abc);
821 c = abc_setlocal(c, l->methodstate->var_index);
823 free(l->methodstate);l->methodstate=0;
827 c = code_append(c, m->header);
830 if(m->is_constructor && !m->has_super) {
831 // call default constructor
832 c = abc_getlocal_0(c);
833 c = abc_constructsuper(c, 0);
837 /* all parameters that are used by inner functions
838 need to be copied from local to slot */
839 parserassert(m->activation_var);
840 DICT_ITERATE_ITEMS(m->slots,char*,name,variable_t*,v) {
841 if(v->is_parameter) {
842 c = abc_getlocal(c, m->activation_var);
843 c = abc_getlocal(c, v->index);
844 c = abc_setslot(c, v->index);
848 list_free(m->innerfunctions);
849 m->innerfunctions = 0;
854 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
856 c = code_append(c, header);
857 c = code_append(c, var_block(body));
858 /* append return if necessary */
859 if(!c || (c->opcode != OPCODE_RETURNVOID &&
860 c->opcode != OPCODE_RETURNVALUE)) {
861 c = abc_returnvoid(c);
866 static void startpackage(char*name)
869 state->package = strdup(name);
871 static void endpackage()
873 //used e.g. in classinfo_register:
874 //free(state->package);state->package=0;
878 #define FLAG_PUBLIC 256
879 #define FLAG_PROTECTED 512
880 #define FLAG_PRIVATE 1024
881 #define FLAG_PACKAGEINTERNAL 2048
882 #define FLAG_NAMESPACE 4096
884 static namespace_t modifiers2access(modifiers_t*mod)
889 if(mod->flags&FLAG_NAMESPACE) {
890 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
891 syntaxerror("invalid combination of access levels and namespaces");
892 ns.access = ACCESS_NAMESPACE;
894 const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
896 /* shouldn't happen- the tokenizer only reports something as a namespace
897 if it was already registered */
898 trie_dump(active_namespaces);
899 syntaxerror("unknown namespace: %s", mod->ns);
902 } else if(mod->flags&FLAG_PUBLIC) {
903 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
904 syntaxerror("invalid combination of access levels");
905 ns.access = ACCESS_PACKAGE;
906 } else if(mod->flags&FLAG_PRIVATE) {
907 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
908 syntaxerror("invalid combination of access levels");
909 ns.access = ACCESS_PRIVATE;
910 } else if(mod->flags&FLAG_PROTECTED) {
911 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
912 syntaxerror("invalid combination of access levels");
913 ns.access = ACCESS_PROTECTED;
915 ns.access = ACCESS_PACKAGEINTERNAL;
919 static slotinfo_t* find_class(const char*name);
921 static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
923 return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
926 static void innerfunctions2vars(methodstate_t*m)
928 methodstate_list_t*l = m->innerfunctions;
930 methodstate_t*m = l->methodstate;
932 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 0);
933 m->var_index = v->index;
935 m->slot_index = m->is_a_slot;
936 v->is_inner_method = m;
941 static void function_initvars(methodstate_t*m, char has_params, params_t*params, int flags, char var0)
946 index = new_variable("this", 0, 0, 0);
947 else if(!m->is_global)
948 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
950 index = new_variable("globalscope", 0, 0, 0);
951 parserassert(!index);
956 for(p=params->list;p;p=p->next) {
957 variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1);
960 if(as3_pass==2 && m->need_arguments) {
961 /* arguments can never be used by an innerfunction (the inner functions
962 have their own arguments var), so it's ok to not initialize this until
963 pass 2. (We don't know whether we need it before, anyway) */
964 variable_t*v = new_variable2("arguments", TYPE_ARRAY, 0, 0);
965 m->need_arguments = v->index;
969 innerfunctions2vars(m);
972 m->scope_code = add_scope_code(m->scope_code, m, 0);
974 /* exchange unresolved identifiers with the actual objects */
975 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
976 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
977 classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
978 if(!type || type->kind != INFOTYPE_CLASS) {
979 syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name);
989 char*as3_globalclass=0;
990 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
993 syntaxerror("inner classes now allowed");
998 classinfo_list_t*mlist=0;
1000 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
1001 syntaxerror("invalid modifier(s)");
1003 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
1004 syntaxerror("public and internal not supported at the same time.");
1006 if((mod->flags&(FLAG_PROTECTED|FLAG_STATIC)) == (FLAG_PROTECTED|FLAG_STATIC))
1007 syntaxerror("protected and static not supported at the same time.");
1009 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
1010 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
1011 // all classes extend object
1012 extends = registry_getobjectclass();
1015 /* create the class name, together with the proper attributes */
1019 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
1020 access = ACCESS_PRIVATE; package = internal_filename_package;
1021 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
1022 access = ACCESS_PACKAGEINTERNAL; package = state->package;
1023 } else if(state->package!=internal_filename_package) {
1024 access = ACCESS_PACKAGE; package = state->package;
1026 syntaxerror("public classes only allowed inside a package");
1030 state->cls = rfx_calloc(sizeof(classstate_t));
1031 state->cls->init = rfx_calloc(sizeof(methodstate_t));
1032 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
1033 state->cls->static_init->is_static=FLAG_STATIC;
1034 state->cls->static_init->variable_count=1;
1035 /* notice: we make no effort to initialize the top variable (local0) here,
1036 even though it has special meaning. We just rely on the fact
1037 that pass 1 won't do anything with variables */
1039 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
1041 /* set current method to constructor- all code within the class-level (except
1042 static variable initializations) will be executed during construction time */
1043 state->method = state->cls->init;
1045 if(registry_find(package, classname)) {
1046 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
1048 /* build info struct */
1049 int num_interfaces = (list_length(implements));
1050 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
1051 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
1052 state->cls->info->superclass = extends;
1055 classinfo_list_t*l = implements;
1056 for(l=implements;l;l=l->next) {
1057 state->cls->info->interfaces[pos++] = l->classinfo;
1062 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1064 state->method = state->cls->init;
1065 parserassert(state->cls && state->cls->info);
1067 function_initvars(state->cls->init, 0, 0, 0, 1);
1068 function_initvars(state->cls->static_init, 0, 0, 0, 0);
1070 if(extends && (extends->flags & FLAG_FINAL))
1071 syntaxerror("Can't extend final class '%s'", extends->name);
1074 while(state->cls->info->interfaces[pos]) {
1075 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1076 syntaxerror("'%s' is not an interface",
1077 state->cls->info->interfaces[pos]->name);
1081 /* generate the abc code for this class */
1082 MULTINAME(classname2,state->cls->info);
1083 multiname_t*extends2 = sig2mname(extends);
1085 /* don't add the class to the class index just yet- that will be done later
1087 state->cls->abc = abc_class_new(0, &classname2, extends2);
1088 state->cls->abc->file = global->file;
1090 multiname_destroy(extends2);
1091 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1092 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1093 if(state->cls->info->flags&FLAG_INTERFACE) {
1094 abc_class_interface(state->cls->abc);
1097 for(mlist=implements;mlist;mlist=mlist->next) {
1098 MULTINAME(m, mlist->classinfo);
1099 abc_class_add_interface(state->cls->abc, &m);
1102 state->cls->dependencies = parsedclass_new(state->cls->info, state->cls->abc);
1103 list_append(global->classes, state->cls->dependencies);
1105 /* 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);
1142 trait_list_t*trait = state->cls->abc->traits;
1143 /* switch all protected members to the protected ns of this class */
1145 trait_t*t = trait->trait;
1146 if(t->name->ns->access == ACCESS_PROTECTED) {
1147 if(!state->cls->abc->protectedNS) {
1148 char*n = concat3(state->cls->info->package, ":", state->cls->info->name);
1149 state->cls->abc->protectedNS = namespace_new_protected(n);
1150 state->cls->abc->flags |= CLASS_PROTECTED_NS;
1152 t->name->ns->name = strdup(state->cls->abc->protectedNS->name);
1154 trait = trait->next;
1161 void check_code_for_break(code_t*c)
1164 if(c->opcode == OPCODE___BREAK__) {
1165 char*name = string_cstr(c->data[0]);
1166 syntaxerror("Unresolved \"break %s\"", name);
1168 if(c->opcode == OPCODE___CONTINUE__) {
1169 char*name = string_cstr(c->data[0]);
1170 syntaxerror("Unresolved \"continue %s\"", name);
1172 if(c->opcode == OPCODE___RETHROW__) {
1173 syntaxerror("Unresolved \"rethrow\"");
1175 if(c->opcode == OPCODE___FALLTHROUGH__) {
1176 syntaxerror("Unresolved \"fallthrough\"");
1178 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1179 char*name = string_cstr(c->data[0]);
1180 syntaxerror("Can't reference a package (%s) as such", name);
1186 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1188 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1189 if(TYPE_IS_NUMBER(t)) {
1190 xassert(c->type == CONSTANT_FLOAT
1191 || c->type == CONSTANT_INT
1192 || c->type == CONSTANT_UINT);
1193 } else if(TYPE_IS_UINT(t)) {
1194 xassert(c->type == CONSTANT_UINT ||
1195 (c->type == CONSTANT_INT && c->i>=0));
1196 } else if(TYPE_IS_INT(t)) {
1197 xassert(c->type == CONSTANT_INT);
1198 } else if(TYPE_IS_BOOLEAN(t)) {
1199 xassert(c->type == CONSTANT_TRUE
1200 || c->type == CONSTANT_FALSE);
1204 static void check_override(memberinfo_t*m, int flags)
1208 if(m->parent == state->cls->info)
1209 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1211 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1212 if(m->access==ACCESS_PRIVATE)
1214 if(m->flags & FLAG_FINAL)
1215 syntaxerror("can't override final member %s", m->name);
1217 /* allow this. it's no issue.
1218 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1219 syntaxerror("can't override static member %s", m->name);*/
1221 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1222 syntaxerror("can't override non-static member %s with static declaration", m->name);
1224 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1225 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1226 if(m->kind == INFOTYPE_METHOD)
1227 syntaxerror("can't override without explicit 'override' declaration");
1229 syntaxerror("can't override '%s'", m->name);
1234 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1236 methodinfo_t*minfo = 0;
1237 namespace_t ns = modifiers2access(mod);
1240 minfo = methodinfo_register_global(ns.access, state->package, name);
1241 minfo->return_type = return_type;
1242 } else if(getset != KW_GET && getset != KW_SET) {
1244 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1246 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1248 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1249 minfo->return_type = return_type;
1250 // getslot on a member slot only returns "undefined", so no need
1251 // to actually store these
1252 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1254 //class getter/setter
1255 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1257 if(getset == KW_GET) {
1259 } else if(params->list && params->list->param && !params->list->next) {
1260 type = params->list->param->type;
1262 syntaxerror("setter function needs to take exactly one argument");
1263 // not sure wether to look into superclasses here, too
1264 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1266 if(minfo->kind!=INFOTYPE_VAR)
1267 syntaxerror("class already contains a method called '%s'", name);
1268 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1269 syntaxerror("class already contains a field called '%s'", name);
1270 if(minfo->subtype & gs)
1271 syntaxerror("getter/setter for '%s' already defined", name);
1272 /* make a setter or getter into a getset */
1273 minfo->subtype |= gs;
1276 FIXME: this check needs to be done in pass 2
1278 if((!minfo->return_type != !type) ||
1279 (minfo->return_type && type &&
1280 !strcmp(minfo->return_type->name, type->name))) {
1281 syntaxerror("different type in getter and setter: %s and %s",
1282 minfo->return_type?minfo->return_type->name:"*",
1283 type?type->name:"*");
1286 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1287 minfo->kind = INFOTYPE_VAR; //hack
1288 minfo->subtype = gs;
1289 minfo->return_type = type;
1292 /* can't assign a slot as getter and setter might have different slots */
1293 //minfo->slot = slot;
1295 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1296 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1297 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1302 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1304 //parserassert(state->method && state->method->info);
1306 methodstate_t*parent_method = state->method;
1309 return_type = 0; // not valid in pass 1
1313 state->new_vars = 1;
1314 state->allvars = dict_new();
1317 state->method = rfx_calloc(sizeof(methodstate_t));
1318 state->method->inner = 1;
1319 state->method->is_static = parent_method->is_static;
1320 state->method->variable_count = 0;
1321 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1323 NEW(methodinfo_t,minfo);
1324 minfo->kind = INFOTYPE_METHOD;
1325 minfo->access = ACCESS_PACKAGEINTERNAL;
1327 state->method->info = minfo;
1330 list_append(parent_method->innerfunctions, state->method);
1332 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1334 function_initvars(state->method, 1, params, 0, 1);
1338 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1339 state->method->variable_count = 0;
1340 parserassert(state->method);
1342 state->method->info->return_type = return_type;
1343 function_initvars(state->method, 1, params, 0, 1);
1347 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1348 params_t*params, classinfo_t*return_type)
1350 if(state->method && state->method->info) {
1351 syntaxerror("not able to start another method scope");
1354 state->new_vars = 1;
1355 state->allvars = dict_new();
1358 state->method = rfx_calloc(sizeof(methodstate_t));
1359 state->method->has_super = 0;
1360 state->method->is_static = mod->flags&FLAG_STATIC;
1363 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1365 state->method->is_global = 1;
1366 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1368 if(state->method->is_constructor)
1369 name = "__as3_constructor__";
1371 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1373 function_initvars(state->method, 1, params, mod->flags, 1);
1375 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1379 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1380 state->method->variable_count = 0;
1381 parserassert(state->method);
1384 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1385 check_override(m, mod->flags);
1389 state->cls->has_constructor |= state->method->is_constructor;
1392 function_initvars(state->method, 1, params, mod->flags, 1);
1396 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1397 params_t*params, classinfo_t*return_type, code_t*body)
1400 innerfunctions2vars(state->method);
1402 methodstate_list_t*ml = state->method->innerfunctions;
1404 dict_t*xvars = dict_new();
1407 methodstate_t*m = ml->methodstate;
1408 parserassert(m->inner);
1409 if(m->unresolved_variables) {
1410 dict_t*d = m->unresolved_variables;
1412 DICT_ITERATE_KEY(d, char*, id) {
1413 /* check parent method's variables */
1415 if((v=find_variable(state, id))) {
1416 m->uses_parent_function = 1;
1417 state->method->uses_slots = 1;
1418 dict_put(xvars, id, 0);
1421 dict_destroy(m->unresolved_variables);m->unresolved_variables = 0;
1426 if(state->method->uses_slots) {
1427 state->method->slots = dict_new();
1429 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1430 if(!name) syntaxerror("internal error");
1431 if(v->index && dict_contains(xvars, name)) {
1434 if(v->is_inner_method) {
1435 v->is_inner_method->is_a_slot = i;
1438 dict_put(state->method->slots, name, v);
1441 state->method->uses_slots = i;
1442 dict_destroy(state->vars);state->vars = 0;
1443 parserassert(state->new_vars);
1444 dict_destroy(state->allvars);state->allvars = 0;
1451 /*if(state->method->uses_parent_function){
1452 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1457 multiname_t*type2 = sig2mname(return_type);
1459 if(state->method->inner) {
1460 f = state->method->abc;
1461 abc_method_init(f, global->file, type2, 1);
1462 } else if(state->method->is_constructor) {
1463 f = abc_class_getconstructor(state->cls->abc, type2);
1464 } else if(!state->method->is_global) {
1465 namespace_t ns = modifiers2access(mod);
1466 multiname_t mname = {QNAME, &ns, 0, name};
1467 if(mod->flags&FLAG_STATIC)
1468 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1470 f = abc_class_method(state->cls->abc, type2, &mname);
1471 slot = f->trait->slot_id;
1473 namespace_t mname_ns = {state->method->info->access, state->package};
1474 multiname_t mname = {QNAME, &mname_ns, 0, name};
1476 f = abc_method_new(global->file, type2, 1);
1477 if(!global->init) global->init = abc_initscript(global->file);
1478 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1479 //abc_code_t*c = global->init->method->body->code;
1481 //flash doesn't seem to allow us to access function slots
1482 //state->method->info->slot = slot;
1484 if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1485 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1486 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1487 if(params->varargs) f->flags |= METHOD_NEED_REST;
1488 if(state->method->need_arguments) f->flags |= METHOD_NEED_ARGUMENTS;
1492 for(p=params->list;p;p=p->next) {
1493 if(params->varargs && !p->next) {
1494 break; //varargs: omit last parameter in function signature
1496 multiname_t*m = sig2mname(p->param->type);
1497 list_append(f->parameters, m);
1498 if(p->param->value) {
1499 check_constant_against_type(p->param->type, p->param->value);
1500 opt=1;list_append(f->optional_parameters, p->param->value);
1502 syntaxerror("function %s: non-optional parameter not allowed after optional parameters", name);
1505 if(state->method->slots) {
1506 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1508 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1509 multiname_t*type = sig2mname(v->type);
1510 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1511 t->slot_id = v->index;
1516 check_code_for_break(body);
1518 /* Seems this works now.
1519 if(state->method->exceptions && state->method->uses_slots) {
1520 as3_warning("try/catch and activation not supported yet within the same method");
1524 f->body->code = body;
1525 f->body->exceptions = state->method->exceptions;
1526 } else { //interface
1528 syntaxerror("interface methods can't have a method body");
1538 void breakjumpsto(code_t*c, char*name, code_t*jump)
1541 if(c->opcode == OPCODE___BREAK__) {
1542 string_t*name2 = c->data[0];
1543 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1544 c->opcode = OPCODE_JUMP;
1551 void continuejumpsto(code_t*c, char*name, code_t*jump)
1554 if(c->opcode == OPCODE___CONTINUE__) {
1555 string_t*name2 = c->data[0];
1556 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1557 c->opcode = OPCODE_JUMP;
1565 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1570 return abc_coerce_a(c);
1574 // cast an "any" type to a specific type. subject to
1575 // runtime exceptions
1576 return abc_coerce2(c, &m);
1579 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1580 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1581 // allow conversion between number types
1582 if(TYPE_IS_UINT(to))
1583 return abc_convert_u(c);
1584 else if(TYPE_IS_INT(to))
1585 return abc_convert_i(c);
1586 else if(TYPE_IS_NUMBER(to))
1587 return abc_convert_d(c);
1588 return abc_coerce2(c, &m);
1591 if(TYPE_IS_XMLLIST(to) && TYPE_IS_XML(from))
1594 if(TYPE_IS_BOOLEAN(to))
1595 return abc_convert_b(c);
1596 if(TYPE_IS_STRING(to))
1597 return abc_convert_s(c);
1598 if(TYPE_IS_OBJECT(to))
1599 return abc_coerce2(c, &m);
1600 if(TYPE_IS_OBJECT(from) && TYPE_IS_XMLLIST(to))
1601 return abc_coerce2(c, &m);
1602 if(TYPE_IS_OBJECT(from) && TYPE_IS_ARRAY(to))
1603 return abc_coerce2(c, &m);
1605 classinfo_t*supertype = from;
1607 if(supertype == to) {
1608 /* target type is one of from's superclasses.
1609 (not sure we need this coerce - as far as the verifier
1610 is concerned, object==object (i think) */
1611 return abc_coerce2(c, &m);
1614 while(supertype->interfaces[t]) {
1615 if(supertype->interfaces[t]==to) {
1616 // target type is one of from's interfaces
1617 return abc_coerce2(c, &m);
1621 supertype = supertype->superclass;
1623 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1625 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1627 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1630 as3_error("can't convert type %s%s%s to %s%s%s",
1631 from->package, from->package[0]?".":"", from->name,
1632 to->package, to->package[0]?".":"", to->name);
1636 code_t* coerce_to_type(code_t*c, classinfo_t*t)
1639 return abc_coerce_a(c);
1640 } else if(TYPE_IS_STRING(t)) {
1641 return abc_coerce_s(c);
1644 return abc_coerce2(c, &m);
1648 char is_pushundefined(code_t*c)
1650 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1653 static const char* get_package_from_name(const char*name)
1655 /* try explicit imports */
1656 dictentry_t* e = dict_get_slot(state->imports, name);
1658 if(!strcmp(e->key, name)) {
1659 slotinfo_t*c = (slotinfo_t*)e->data;
1660 if(c) return c->package;
1666 static namespace_list_t*get_current_imports()
1668 namespace_list_t*searchlist = 0;
1670 list_append(searchlist, namespace_new_package(state->package));
1672 import_list_t*l = state->wildcard_imports;
1674 namespace_t*ns = namespace_new_package(l->import->package);
1675 list_append(searchlist, ns);
1678 list_append(searchlist, namespace_new_package(""));
1679 list_append(searchlist, namespace_new_package(internal_filename_package));
1683 static slotinfo_t* find_class(const char*name)
1687 c = registry_find(state->package, name);
1690 /* try explicit imports */
1691 dictentry_t* e = dict_get_slot(state->imports, name);
1694 if(!strcmp(e->key, name)) {
1695 c = (slotinfo_t*)e->data;
1701 /* try package.* imports */
1702 import_list_t*l = state->wildcard_imports;
1704 //printf("does package %s contain a class %s?\n", l->import->package, name);
1705 c = registry_find(l->import->package, name);
1710 /* try global package */
1711 c = registry_find("", name);
1714 /* try local "filename" package */
1715 c = registry_find(internal_filename_package, name);
1720 typedcode_t push_class(slotinfo_t*a)
1725 if(a->access == ACCESS_PACKAGEINTERNAL &&
1726 strcmp(a->package, state->package) &&
1727 strcmp(a->package, internal_filename_package)
1729 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1730 infotypename(a), a->name, a->package, state->package);
1734 if(a->kind != INFOTYPE_CLASS) {
1736 x.c = abc_findpropstrict2(x.c, &m);
1737 x.c = abc_getproperty2(x.c, &m);
1738 if(a->kind == INFOTYPE_METHOD) {
1739 methodinfo_t*f = (methodinfo_t*)a;
1740 x.t = TYPE_FUNCTION(f);
1742 varinfo_t*v = (varinfo_t*)a;
1747 if(state->cls && state->method == state->cls->static_init) {
1748 /* we're in the static initializer.
1749 record the fact that we're using this class here */
1750 parsedclass_add_dependency(state->cls->dependencies, (classinfo_t*)a);
1752 classinfo_t*c = (classinfo_t*)a;
1754 if(0) { //Error #1026: Slot 1 exceeds slotCount=0 of global
1755 x.c = abc_getglobalscope(x.c);
1756 x.c = abc_getslot(x.c, c->slot);
1759 x.c = abc_getlex2(x.c, &m);
1761 x.t = TYPE_CLASS(c);
1767 char is_break_or_jump(code_t*c)
1771 if(c->opcode == OPCODE_JUMP ||
1772 c->opcode == OPCODE___BREAK__ ||
1773 c->opcode == OPCODE___CONTINUE__ ||
1774 c->opcode == OPCODE_THROW ||
1775 c->opcode == OPCODE_RETURNVOID ||
1776 c->opcode == OPCODE_RETURNVALUE) {
1782 #define IS_FINALLY_TARGET(op) \
1783 ((op) == OPCODE___CONTINUE__ || \
1784 (op) == OPCODE___BREAK__ || \
1785 (op) == OPCODE_RETURNVOID || \
1786 (op) == OPCODE_RETURNVALUE || \
1787 (op) == OPCODE___RETHROW__)
1789 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1791 #define NEED_EXTRA_STACK_ARG
1792 code_t*finally_label = abc_nop(0);
1793 NEW(lookupswitch_t, l);
1799 code_t*prev = i->prev;
1800 if(IS_FINALLY_TARGET(i->opcode)) {
1803 if(i->opcode == OPCODE___RETHROW__ ||
1804 i->opcode == OPCODE_RETURNVALUE) {
1805 if(i->opcode == OPCODE___RETHROW__)
1806 i->opcode = OPCODE_THROW;
1808 p = abc_coerce_a(p);
1809 p = abc_setlocal(p, tempvar);
1811 p = abc_pushbyte(p, count++);
1812 p = abc_jump(p, finally_label);
1813 code_t*target = p = abc_label(p);
1814 #ifdef NEED_EXTRA_STACK_ARG
1818 p = abc_getlocal(p, tempvar);
1821 p->next = i;i->prev = p;
1822 list_append(l->targets, target);
1828 c = abc_pushbyte(c, -1);
1829 c = code_append(c, finally_label);
1830 c = code_append(c, finally);
1832 #ifdef NEED_EXTRA_STACK_ARG
1835 c = abc_lookupswitch(c, l);
1836 c = l->def = abc_label(c);
1837 #ifdef NEED_EXTRA_STACK_ARG
1844 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1848 code_t*prev = i->prev;
1849 if(IS_FINALLY_TARGET(i->opcode)) {
1850 if(i->opcode == OPCODE___RETHROW__)
1851 i->opcode = OPCODE_THROW;
1852 code_t*end = code_dup(finally);
1853 code_t*start = code_start(end);
1854 if(prev) prev->next = start;
1861 return code_append(c, finally);
1864 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1870 int num_insertion_points=0;
1872 if(IS_FINALLY_TARGET(i->opcode))
1873 num_insertion_points++;
1880 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1885 int simple_version_cost = (1+num_insertion_points)*code_size;
1886 int lookup_version_cost = 4*num_insertion_points + 5;
1888 if(cantdup || simple_version_cost > lookup_version_cost) {
1889 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
1890 return insert_finally_lookup(c, finally, tempvar);
1892 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
1893 return insert_finally_simple(c, finally, tempvar);
1897 #define PASS1 }} if(as3_pass == 1) {{
1898 #define PASS1END }} if(as3_pass == 2) {{
1899 #define PASS2 }} if(as3_pass == 2) {{
1900 #define PASS12 }} if(as3_pass == 1 || as3_pass == 2) {{
1901 #define PASS12END }} if(as3_pass == 2) {{
1902 #define PASS_ALWAYS }} {{
1908 /* ------------ code blocks / statements ---------------- */
1910 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1912 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1913 PROGRAM_CODE_LIST: PROGRAM_CODE
1914 | PROGRAM_CODE_LIST PROGRAM_CODE
1916 PROGRAM_CODE: PACKAGE_DECLARATION
1917 | INTERFACE_DECLARATION
1919 | FUNCTION_DECLARATION
1922 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
1925 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1926 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1927 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1929 INPACKAGE_CODE: INTERFACE_DECLARATION
1931 | FUNCTION_DECLARATION
1934 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
1937 MAYBECODE: CODE {$$=$1;}
1938 MAYBECODE: {$$=code_new();}
1940 CODE: CODE CODEPIECE {
1941 $$=code_append($1,$2);
1943 CODE: CODEPIECE {$$=$1;}
1945 // code which may appear outside of methods
1946 CODE_STATEMENT: DEFAULT_NAMESPACE
1947 CODE_STATEMENT: IMPORT
1949 CODE_STATEMENT: FOR_IN
1950 CODE_STATEMENT: WHILE
1951 CODE_STATEMENT: DO_WHILE
1952 CODE_STATEMENT: SWITCH
1954 CODE_STATEMENT: WITH
1956 CODE_STATEMENT: VOIDEXPRESSION
1957 CODE_STATEMENT: USE_NAMESPACE
1958 CODE_STATEMENT: NAMESPACE_DECLARATION
1959 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1960 CODE_STATEMENT: '{' '}' {$$=0;}
1962 // code which may appear in methods (includes the above)
1963 CODEPIECE: ';' {$$=0;}
1964 CODEPIECE: CODE_STATEMENT
1965 CODEPIECE: VARIABLE_DECLARATION
1970 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {
1980 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1981 //CODEBLOCK : '{' '}' {$$=0;}
1982 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1983 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1985 /* ------------ package init code ------------------- */
1987 PACKAGE_INITCODE: CODE_STATEMENT {
1990 global->init = abc_initscript(global->file);
1991 code_t**cc = &global->init->method->body->code;
1992 *cc = code_append(*cc, $1);
1996 /* ------------ conditional compilation ------------- */
1998 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER {
2001 char*key = concat3($1,"::",$3);
2002 if(!definitions || !dict_contains(definitions, key)) {
2008 /* ------------ variables --------------------------- */
2011 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
2017 MAYBEEXPRESSION : '=' E {$$=$2;}
2018 | {$$=mkdummynode();}
2020 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2021 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2023 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2024 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2026 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2029 if(variable_exists($1))
2030 syntaxerror("Variable %s already defined", $1);
2032 new_variable($1, 0, 1, 0);
2037 if(state->method->uses_slots) {
2038 variable_t* v = find_slot(state, $1);
2040 // this variable is stored in a slot
2048 index = new_variable($1, $2, 1, 0);
2051 $$ = slot?abc_getscopeobject(0, 1):0;
2053 typedcode_t v = node_read($3);
2054 if(!is_subtype_of(v.t, $2)) {
2055 syntaxerror("Can't convert %s to %s", v.t->name, $2->name);
2058 if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
2059 $$ = code_append($$, v.c);
2060 $$ = converttype($$, v.t, $2);
2063 $$ = defaultvalue($$, $2);
2066 if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
2067 $$ = code_append($$, v.c);
2068 $$ = abc_coerce_a($$);
2070 // don't do anything
2078 $$ = abc_setslot($$, index);
2080 $$ = abc_setlocal($$, index);
2084 /* ------------ control flow ------------------------- */
2086 MAYBEELSE: %prec below_else {$$ = code_new();}
2087 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2088 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2090 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2093 $$ = code_append($$, $4.c);
2094 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2096 $$ = code_append($$, $6);
2098 myjmp = $$ = abc_jump($$, 0);
2100 myif->branch = $$ = abc_nop($$);
2102 $$ = code_append($$, $7);
2103 myjmp->branch = $$ = abc_nop($$);
2109 FOR_INIT : {$$=code_new();}
2110 FOR_INIT : VARIABLE_DECLARATION
2111 FOR_INIT : VOIDEXPRESSION
2113 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2114 // (I don't see any easy way to revolve this conflict otherwise, as we
2115 // can't touch VAR_READ without upsetting the precedence about "return")
2116 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2117 PASS1 $$=$2;new_variable($2,0,1,0);
2118 PASS2 $$=$2;new_variable($2,$3,1,0);
2120 FOR_IN_INIT : T_IDENTIFIER {
2125 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2126 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2128 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2129 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2131 $$ = code_append($$, $2);
2132 code_t*loopstart = $$ = abc_label($$);
2133 $$ = code_append($$, $4.c);
2134 code_t*myif = $$ = abc_iffalse($$, 0);
2135 $$ = code_append($$, $8);
2136 code_t*cont = $$ = abc_nop($$);
2137 $$ = code_append($$, $6);
2138 $$ = abc_jump($$, loopstart);
2139 code_t*out = $$ = abc_nop($$);
2140 breakjumpsto($$, $1.name, out);
2141 continuejumpsto($$, $1.name, cont);
2148 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2149 variable_t*var = find_variable(state, $2);
2151 syntaxerror("variable %s not known in this scope", $2);
2154 char*tmp1name = concat2($2, "__tmp1__");
2155 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2156 char*tmp2name = concat2($2, "__array__");
2157 int array = new_variable(tmp1name, 0, 0, 0);
2160 $$ = code_append($$, $4.c);
2161 $$ = abc_coerce_a($$);
2162 $$ = abc_setlocal($$, array);
2163 $$ = abc_pushbyte($$, 0);
2164 $$ = abc_setlocal($$, it);
2166 code_t*loopstart = $$ = abc_label($$);
2168 $$ = abc_hasnext2($$, array, it);
2169 code_t*myif = $$ = abc_iffalse($$, 0);
2170 $$ = abc_getlocal($$, array);
2171 $$ = abc_getlocal($$, it);
2173 $$ = abc_nextname($$);
2175 $$ = abc_nextvalue($$);
2176 $$ = converttype($$, 0, var->type);
2177 $$ = abc_setlocal($$, var->index);
2179 $$ = code_append($$, $6);
2180 $$ = abc_jump($$, loopstart);
2182 code_t*out = $$ = abc_nop($$);
2183 breakjumpsto($$, $1.name, out);
2184 continuejumpsto($$, $1.name, loopstart);
2196 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2200 code_t*myjmp = $$ = abc_jump($$, 0);
2201 code_t*loopstart = $$ = abc_label($$);
2202 $$ = code_append($$, $6);
2203 code_t*cont = $$ = abc_nop($$);
2204 myjmp->branch = cont;
2205 $$ = code_append($$, $4.c);
2206 $$ = abc_iftrue($$, loopstart);
2207 code_t*out = $$ = abc_nop($$);
2208 breakjumpsto($$, $1, out);
2209 continuejumpsto($$, $1, cont);
2215 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2217 code_t*loopstart = $$ = abc_label($$);
2218 $$ = code_append($$, $3);
2219 code_t*cont = $$ = abc_nop($$);
2220 $$ = code_append($$, $6.c);
2221 $$ = abc_iftrue($$, loopstart);
2222 code_t*out = $$ = abc_nop($$);
2223 breakjumpsto($$, $1, out);
2224 continuejumpsto($$, $1, cont);
2230 BREAK : "break" %prec prec_none {
2231 $$ = abc___break__(0, "");
2233 BREAK : "break" T_IDENTIFIER {
2234 $$ = abc___break__(0, $2);
2236 CONTINUE : "continue" %prec prec_none {
2237 $$ = abc___continue__(0, "");
2239 CONTINUE : "continue" T_IDENTIFIER {
2240 $$ = abc___continue__(0, $2);
2243 MAYBE_CASE_LIST : {$$=0;}
2244 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2245 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2246 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2247 CASE_LIST: CASE {$$=$1;}
2248 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2250 CASE: "case" E ':' MAYBECODE {
2251 $$ = abc_getlocal(0, state->switch_var);
2252 $$ = code_append($$, node_read($2).c);
2253 code_t*j = $$ = abc_ifne($$, 0);
2254 $$ = code_append($$, $4);
2255 if($$->opcode != OPCODE___BREAK__) {
2256 $$ = abc___fallthrough__($$, "");
2258 code_t*e = $$ = abc_nop($$);
2261 DEFAULT: "default" ':' MAYBECODE {
2264 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2265 $$ = node_read($4).c;
2266 $$ = abc_setlocal($$, state->switch_var);
2267 $$ = code_append($$, $7);
2269 code_t*out = $$ = abc_kill($$, state->switch_var);
2270 breakjumpsto($$, $1, out);
2272 code_t*c = $$,*lastblock=0;
2274 if(c->opcode == OPCODE_IFNE) {
2275 if(!c->next) syntaxerror("internal error in fallthrough handling");
2277 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2279 c->opcode = OPCODE_JUMP;
2280 c->branch = lastblock;
2282 /* fall through end of switch */
2283 c->opcode = OPCODE_NOP;
2293 /* ------------ try / catch /finally ---------------- */
2295 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2296 state->exception_name=$3;
2297 PASS1 new_variable($3, 0, 0, 0);
2298 PASS2 new_variable($3, $4, 0, 0);
2301 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2302 multiname_t name = {QNAME, &name_ns, 0, $3};
2304 NEW(abc_exception_t, e)
2305 e->exc_type = sig2mname($4);
2306 e->var_name = multiname_clone(&name);
2310 int i = find_variable_safe(state, $3)->index;
2311 e->target = c = abc_nop(0);
2312 c = abc_setlocal(c, i);
2313 c = code_append(c, code_dup(state->method->scope_code));
2314 c = code_append(c, $8);
2320 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2325 NEW(abc_exception_t, e)
2326 e->exc_type = 0; //all exceptions
2327 e->var_name = 0; //no name
2330 e->to = code_append(e->to, $4);
2336 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2337 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2338 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2339 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2343 list_append($$.l,$2);
2344 $$.finally = $2->to;$2->to=0;
2347 CATCH_FINALLY_LIST: FINALLY {
2351 list_append($$.l,$1);
2352 $$.finally = $1->to;$1->to=0;
2356 TRY : "try" '{' {PASS12 new_state();
2357 state->method->has_exceptions=1;
2358 state->method->late_binding=1;//for invariant scope_code
2359 } MAYBECODE '}' CATCH_FINALLY_LIST {
2360 code_t*out = abc_nop(0);
2362 code_t*start = abc_nop(0);
2363 $$ = code_append(start, $4);
2364 if(!is_break_or_jump($4)) {
2365 $$ = abc_jump($$, out);
2367 code_t*end = $$ = abc_nop($$);
2371 tmp = new_variable("__finally__", 0, 0, 0);
2373 abc_exception_list_t*l = $6.l;
2376 abc_exception_t*e = l->abc_exception;
2378 $$ = code_append($$, e->target);
2379 $$ = abc_jump($$, out);
2381 parserassert((ptroff_t)$6.finally);
2383 e->target = $$ = abc_nop($$);
2384 $$ = code_append($$, code_dup(state->method->scope_code));
2385 $$ = abc___rethrow__($$);
2393 $$ = code_append($$, out);
2395 $$ = insert_finally($$, $6.finally, tmp);
2397 list_concat(state->method->exceptions, $6.l);
2403 /* ------------ throw ------------------------------- */
2405 THROW : "throw" EXPRESSION {
2409 THROW : "throw" %prec prec_none {
2410 if(!state->exception_name)
2411 syntaxerror("re-throw only possible within a catch block");
2412 variable_t*v = find_variable(state, state->exception_name);
2414 $$=abc_getlocal($$, v->index);
2418 /* ------------ with -------------------------------- */
2420 WITH_HEAD : "with" '(' EXPRESSION ')' {
2422 if(state->method->has_exceptions) {
2423 int v = alloc_local();
2424 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2425 state->method->scope_code = abc_pushwith(state->method->scope_code);
2430 WITH : WITH_HEAD CODEBLOCK {
2431 /* remove getlocal;pushwith from scope code again */
2432 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2435 if(state->method->has_exceptions) {
2437 $$ = abc_setlocal($$, $1.number);
2439 $$ = abc_pushwith($$);
2440 $$ = code_append($$, $2);
2441 $$ = abc_popscope($$);
2445 /* ------------ packages and imports ---------------- */
2447 X_IDENTIFIER: T_IDENTIFIER
2448 | "package" {PASS12 $$="package";}
2449 | "namespace" {PASS12 $$="namespace";}
2450 | "NaN" {PASS12 $$="NaN";}
2451 | T_NAMESPACE {PASS12 $$=$1;}
2453 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2454 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2456 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2457 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2458 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2459 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2462 static void state_has_imports()
2464 state->wildcard_imports = list_clone(state->wildcard_imports);
2465 state->imports = dict_clone(state->imports);
2466 state->has_own_imports = 1;
2468 static void import_toplevel(const char*package)
2470 char* s = strdup(package);
2472 dict_put(state->import_toplevel_packages, s, 0);
2473 char*x = strrchr(s, '.');
2482 IMPORT : "import" T_IDENTIFIER {
2484 slotinfo_t*s = registry_find(state->package, $2);
2485 if(!s && as3_pass==1) {as3_schedule_class(state->package, $2);}
2486 state_has_imports();
2487 dict_put(state->imports, state->package, $2);
2490 IMPORT : "import" PACKAGEANDCLASS {
2492 slotinfo_t*s = registry_find($2->package, $2->name);
2493 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2494 as3_schedule_class($2->package, $2->name);
2496 state_has_imports();
2497 dict_put(state->imports, $2->name, $2);
2498 import_toplevel($2->package);
2501 IMPORT : "import" PACKAGE '.' '*' {
2503 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2504 as3_schedule_package($2);
2509 state_has_imports();
2510 list_append(state->wildcard_imports, i);
2511 import_toplevel(i->package);
2515 /* ------------ classes and interfaces (header) -------------- */
2517 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2518 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2519 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2520 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2522 $$.flags=$1.flags|$2.flags;
2523 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2524 $$.ns=$1.ns?$1.ns:$2.ns;
2527 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2528 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2529 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2530 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2531 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2532 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2533 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2534 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2535 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2536 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2540 EXTENDS : {PASS12 $$=0;}
2541 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2543 EXTENDS_LIST : {PASS12 $$=list_new();}
2544 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2546 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2547 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2549 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2550 EXTENDS IMPLEMENTS_LIST
2551 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2553 '}' {PASS12 endclass();$$=0;}
2555 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2557 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2558 startclass(&$1,$3,0,$4);}
2559 MAYBE_INTERFACE_BODY
2560 '}' {PASS12 endclass();$$=0;}
2562 /* ------------ classes and interfaces (body) -------------- */
2565 MAYBE_CLASS_BODY : CLASS_BODY
2566 CLASS_BODY : CLASS_BODY_ITEM
2567 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2568 CLASS_BODY_ITEM : ';'
2569 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}' {PASS_ALWAYS as3_pass=$1;}
2570 CLASS_BODY_ITEM : SLOT_DECLARATION
2571 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2573 CLASS_BODY_ITEM : CODE_STATEMENT {
2574 code_t*c = state->cls->static_init->header;
2575 c = code_append(c, $1);
2576 state->cls->static_init->header = c;
2579 MAYBE_INTERFACE_BODY :
2580 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2581 INTERFACE_BODY : IDECLARATION
2582 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2584 IDECLARATION : "var" T_IDENTIFIER {
2585 syntaxerror("variable declarations not allowed in interfaces");
2587 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2589 $1.flags |= FLAG_PUBLIC;
2590 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2591 syntaxerror("invalid method modifiers: interface methods always need to be public");
2593 startfunction(&$1,$3,$4,&$6,$8);
2594 endfunction(&$1,$3,$4,&$6,$8, 0);
2595 list_deep_free($6.list);
2598 /* ------------ classes and interfaces (body, slots ) ------- */
2601 static int slotstate_varconst = 0;
2602 static modifiers_t*slotstate_flags = 0;
2603 static void setslotstate(modifiers_t* flags, int varconst)
2605 slotstate_varconst = varconst;
2606 slotstate_flags = flags;
2609 if(flags->flags&FLAG_STATIC) {
2610 state->method = state->cls->static_init;
2612 state->method = state->cls->init;
2615 // reset to "default" state (all in class code is static by default) */
2616 state->method = state->cls->static_init;
2619 parserassert(state->method);
2622 static trait_t* add_abc_slot(modifiers_t* modifiers, const char*name, multiname_t*m, code_t***c)
2624 int flags = modifiers->flags;
2625 namespace_t ns = modifiers2access(modifiers);
2628 multiname_t mname = {QNAME, &ns, 0, name};
2630 trait_list_t**traits;
2634 if(!global->init) global->init = abc_initscript(global->file);
2635 ns.name = state->package;
2636 traits = &global->init->traits;
2637 code = &global->init->method->body->code;
2638 } else if(flags&FLAG_STATIC) {
2640 traits = &state->cls->abc->static_traits;
2641 code = &state->cls->static_init->header;
2643 // instance variable
2644 traits = &state->cls->abc->traits;
2645 code = &state->cls->init->header;
2647 if(ns.access == ACCESS_PROTECTED) {
2648 ns.name = concat3(state->cls->info->package,":",state->cls->info->name);
2654 *m = *multiname_clone(&mname);
2656 return trait_new_member(traits, 0, multiname_clone(&mname), 0);
2660 VARCONST: "var" | "const"
2662 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {PASS12 setslotstate(&$1,$2);} SLOT_LIST {PASS12 $$=$4;setslotstate(0, 0);}
2664 SLOT_LIST: ONE_SLOT {PASS12 $$=0;}
2665 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {PASS12 $$=0;}
2667 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2670 int flags = slotstate_flags->flags;
2671 namespace_t ns = modifiers2access(slotstate_flags);
2675 varinfo_t* info = 0;
2677 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2679 check_override(i, flags);
2681 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2683 slotinfo_t*i = registry_find(state->package, $1);
2685 syntaxerror("package %s already contains '%s'", state->package, $1);
2687 if(ns.name && ns.name[0]) {
2688 syntaxerror("namespaces not allowed on package-level variables");
2690 info = varinfo_register_global(ns.access, state->package, $1);
2694 info->flags = flags;
2696 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, info);
2700 varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
2704 trait_t*t = add_abc_slot(slotstate_flags, $1, &mname, &code);
2708 t->type_name = multiname_clone(&m);
2710 info->slot = t->slot_id;
2712 /* workaround for "VerifyError: Error #1053: Illegal override of ::test2 in C1"
2713 FIXME: is there a way to use slots and still don't have conflicting overrides?
2715 info->slot = t->slot_id = 0;
2717 constant_t cval = $3->type->eval($3);
2718 if(cval.type!=CONSTANT_UNKNOWN) {
2719 /* compile time constant */
2720 t->value = malloc(sizeof(constant_t));
2721 memcpy(t->value, &cval, sizeof(constant_t));
2722 info->value = constant_clone(t->value);
2724 typedcode_t v = node_read($3);
2725 /* initalization code (if needed) */
2727 if(v.c && !is_pushundefined(v.c)) {
2728 c = abc_getlocal_0(c);
2729 c = code_append(c, v.c);
2730 c = converttype(c, v.t, $2);
2732 c = abc_initproperty2(c, &mname);
2734 c = abc_setslot(c, t->slot_id);
2737 *code = code_append(*code, c);
2740 if(slotstate_varconst==KW_CONST) {
2741 t->kind= TRAIT_CONST;
2742 info->flags |= FLAG_CONST;
2749 /* ------------ constants -------------------------------------- */
2751 MAYBECONSTANT: {$$=0;}
2752 MAYBECONSTANT: '=' E {
2753 $$ = malloc(sizeof(constant_t));
2754 *$$ = node_eval($2);
2755 if($$->type == CONSTANT_UNKNOWN) {
2756 syntaxerror("can't evaluate default parameter value (needs to be a compile-time constant)");
2760 //CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2761 CONSTANT : T_INT {$$ = constant_new_int($1);}
2763 $$ = constant_new_uint($1);
2765 CONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2766 CONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2767 CONSTANT : "true" {$$ = constant_new_true($1);}
2768 CONSTANT : "false" {$$ = constant_new_false($1);}
2769 CONSTANT : "null" {$$ = constant_new_null($1);}
2770 CONSTANT : "undefined" {$$ = constant_new_undefined($1);}
2771 CONSTANT : KW_NAN {$$ = constant_new_float(__builtin_nan(""));}
2773 /*CONSTANT : T_NAMESPACE {
2775 $$ = constant_new_namespace(namespace_new_namespace($1.url));
2778 /* ---------------------------xml ------------------------------ */
2781 static int xml_level = 0;
2786 OPEN : '<' {PASS_ALWAYS if(!xml_level++) tokenizer_begin_xml();}
2787 CLOSE : '>' {PASS_ALWAYS tokenizer_begin_xmltext();}
2788 CLOSE2 : {PASS_ALWAYS if(!--xml_level) tokenizer_end_xml(); else tokenizer_begin_xmltext();}
2790 XMLEXPR1 : '{' E {PASS_ALWAYS tokenizer_begin_xmltext();} '}' {
2792 as3_warning("xml string substitution not yet supported");
2794 XMLEXPR2 : '{' E {PASS_ALWAYS tokenizer_begin_xml();} '}' {
2796 as3_warning("xml string substitution not yet supported");
2799 XMLTEXT : XMLTEXT XMLEXPR1 {
2800 $$ = concat2($1, "{...}");
2802 XMLTEXT : XMLTEXT T_STRING {$$=concat2($1, string_cstr(&$2));}
2803 XMLTEXT : XMLTEXT '>' {$$=concat2($1, ">");}
2805 XML2 : XMLNODE XMLTEXT {$$=concat2($1,$2);}
2806 XML2 : XML2 XMLNODE XMLTEXT {$$=concat3($1,$2,$3);free($1);free($2);free($3);}
2808 XML_ID_OR_EXPR: T_IDENTIFIER {$$=$1;}
2809 XML_ID_OR_EXPR: XMLEXPR2 {$$=$1;}
2811 XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
2812 $$ = allocprintf("<%s%s>%s</%s>", $2, $3, $5, $8);
2813 free($2);free($3);free($5);free($8);
2815 XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' {
2816 $$ = allocprintf("<%s%s/>", $2, $3);
2818 XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT XML2 '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
2819 $$ = allocprintf("<%s%s>%s%s</%s>", $2, $3, $5, $6, $9);
2820 free($2);free($3);free($5);free($6);free($9);
2823 MAYBE_XMLATTRIBUTES: {$$=strdup("");}
2824 MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {$$=concat2(" ",$1);}
2825 XMLATTRIBUTES: XMLATTRIBUTE {$$=$1;}
2826 XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE {$$=concat3($1," ",$2);free($1);free($2);}
2828 XMLATTRIBUTE: XMLEXPR2 {
2829 $$ = strdup("{...}");
2831 XMLATTRIBUTE: XMLEXPR2 '=' T_STRING {
2832 char* str = string_cstr(&$3);
2833 $$ = concat2("{...}=",str);
2835 XMLATTRIBUTE: XMLEXPR2 '=' XMLEXPR2 {
2836 $$ = strdup("{...}={...}");
2838 XMLATTRIBUTE: T_IDENTIFIER '=' XMLEXPR2 {
2839 $$ = concat2($1,"={...}");
2841 XMLATTRIBUTE: T_IDENTIFIER '=' T_STRING {
2842 char* str = string_cstr(&$3);
2843 $$=allocprintf("%s=%s", $1,str);
2845 free($1);free((char*)$3.str);
2848 /* ------------ classes and interfaces (body, functions) ------- */
2850 // non-vararg version
2853 memset(&$$,0,sizeof($$));
2855 MAYBE_PARAM_LIST: PARAM_LIST {
2861 MAYBE_PARAM_LIST: "..." PARAM {
2863 memset(&$$,0,sizeof($$));
2865 list_append($$.list, $2);
2867 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2871 list_append($$.list, $4);
2875 PARAM_LIST: PARAM_LIST ',' PARAM {
2878 list_append($$.list, $3);
2882 memset(&$$,0,sizeof($$));
2883 list_append($$.list, $1);
2886 PARAM: T_IDENTIFIER ':' TYPE MAYBECONSTANT {
2888 $$ = rfx_calloc(sizeof(param_t));
2894 PARAM: T_IDENTIFIER MAYBECONSTANT {
2896 $$ = rfx_calloc(sizeof(param_t));
2898 $$->type = TYPE_ANY;
2906 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2907 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2910 endfunction(&$1,$3,$4,&$6,0,0);
2912 if(!state->method->info) syntaxerror("internal error");
2914 code_t*c = method_header(state->method);
2915 c = wrap_function(c, 0, $11);
2917 endfunction(&$1,$3,$4,&$6,$8,c);
2919 list_deep_free($6.list);
2923 MAYBE_IDENTIFIER: T_IDENTIFIER
2924 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2925 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2926 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2929 endfunction(0,0,$2,&$4,0,0);
2931 methodinfo_t*f = state->method->info;
2932 if(!f || !f->kind) syntaxerror("internal error");
2934 code_t*c = method_header(state->method);
2935 c = wrap_function(c, 0, $9);
2937 int index = state->method->var_index;
2938 endfunction(0,0,$2,&$4,$6,c);
2940 $$.c = abc_getlocal(0, index);
2941 $$.t = TYPE_FUNCTION(f);
2943 PASS12 list_deep_free($4.list);
2947 /* ------------- package + class ids --------------- */
2949 CLASS: X_IDENTIFIER {
2950 PASS1 NEW(unresolvedinfo_t,c);
2951 memset(c, 0, sizeof(*c));
2952 c->kind = INFOTYPE_UNRESOLVED;
2954 c->package = get_package_from_name($1);
2956 c->nsset = get_current_imports();
2957 /* make the compiler look for this class in the current directory,
2959 as3_schedule_class_noerror(state->package, $1);
2961 $$ = (classinfo_t*)c;
2963 slotinfo_t*s = find_class($1);
2964 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2965 $$ = (classinfo_t*)s;
2968 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
2969 PASS1 NEW(unresolvedinfo_t,c);
2970 memset(c, 0, sizeof(*c));
2971 c->kind = INFOTYPE_UNRESOLVED;
2974 $$ = (classinfo_t*)c;
2976 slotinfo_t*s = registry_find($1, $3);
2977 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2979 $$ = (classinfo_t*)s;
2982 CLASS_SPEC: PACKAGEANDCLASS
2985 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2986 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2988 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2989 | '*' {PASS12 $$=TYPE_ANY;}
2990 | "void" {PASS12 $$=TYPE_VOID;}
2992 | "String" {$$=registry_getstringclass();}
2993 | "int" {$$=registry_getintclass();}
2994 | "uint" {$$=registry_getuintclass();}
2995 | "Boolean" {$$=registry_getbooleanclass();}
2996 | "Number" {$$=registry_getnumberclass();}
2999 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
3000 MAYBETYPE: {PASS12 $$=0;}
3002 /* ----------function calls, delete, constructor calls ------ */
3004 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
3005 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
3007 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
3008 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
3009 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
3011 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
3015 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
3016 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
3017 $$.number= $1.number+1;
3018 $$.cc = code_append($1.cc, $2.c);
3022 NEW : "new" E XX MAYBE_PARAM_VALUES {
3023 typedcode_t v = node_read($2);
3025 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
3027 code_t*paramcode = $4.cc;
3028 if($$.c->opcode == OPCODE_GETPROPERTY) {
3029 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3030 $$.c = code_cutlast($$.c);
3031 $$.c = code_append($$.c, paramcode);
3032 $$.c = abc_constructprop2($$.c, name, $4.number);
3033 multiname_destroy(name);
3034 } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
3036 classinfo_t*c = v.t->data;
3038 $$.c = abc_findpropstrict2(0, &m);
3039 $$.c = code_append($$.c, paramcode);
3040 $$.c = abc_constructprop2($$.c, &m, $4.number);
3041 /*} else if($$.c->opcode == OPCODE_GETSLOT) {
3042 int slot = (int)(ptroff_t)$$.c->data[0];
3043 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3044 multiname_t*name = t->name;
3045 $$.c = code_cutlast($$.c);
3046 $$.c = code_append($$.c, paramcode);
3047 $$.c = abc_constructprop2($$.c, name, $4.number);*/
3049 $$.c = code_append($$.c, paramcode);
3050 $$.c = abc_construct($$.c, $4.number);
3054 if(TYPE_IS_CLASS(v.t) && v.t->data) {
3057 $$.c = abc_coerce_a($$.c);
3062 /* TODO: use abc_call (for calling local variables),
3063 abc_callstatic (for calling own methods)
3066 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3068 typedcode_t v = node_read($1);
3070 if($$.c->opcode == OPCODE_COERCE_A) {
3071 $$.c = code_cutlast($$.c);
3073 code_t*paramcode = $3.cc;
3076 if($$.c->opcode == OPCODE_GETPROPERTY) {
3077 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3078 $$.c = code_cutlast($$.c);
3079 $$.c = code_append($$.c, paramcode);
3080 $$.c = abc_callproperty2($$.c, name, $3.number);
3081 multiname_destroy(name);
3082 /* } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3083 int slot = (int)(ptroff_t)$$.c->data[0];
3084 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3085 if(t->kind!=TRAIT_METHOD) {
3086 //ok: flash allows to assign closures to members.
3088 multiname_t*name = t->name;
3089 $$.c = code_cutlast($$.c);
3090 $$.c = code_append($$.c, paramcode);
3091 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3092 $$.c = abc_callproperty2($$.c, name, $3.number);*/
3093 } else if($$.c->opcode == OPCODE_GETSUPER) {
3094 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3095 $$.c = code_cutlast($$.c);
3096 $$.c = code_append($$.c, paramcode);
3097 $$.c = abc_callsuper2($$.c, name, $3.number);
3098 multiname_destroy(name);
3100 $$.c = abc_getglobalscope($$.c);
3101 $$.c = code_append($$.c, paramcode);
3102 $$.c = abc_call($$.c, $3.number);
3105 if(TYPE_IS_FUNCTION(v.t) && v.t->data) {
3106 $$.t = ((methodinfo_t*)(v.t->data))->return_type;
3107 } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
3108 // calling a class is like a typecast
3109 $$.t = (classinfo_t*)v.t->data;
3112 $$.c = abc_coerce_a($$.c);
3116 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3117 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3118 if(!state->method) syntaxerror("super() not allowed outside of a function");
3119 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3122 $$.c = abc_getlocal_0($$.c);
3124 $$.c = code_append($$.c, $3.cc);
3126 this is dependent on the control path, check this somewhere else
3127 if(state->method->has_super)
3128 syntaxerror("constructor may call super() only once");
3130 state->method->has_super = 1;
3132 $$.c = abc_constructsuper($$.c, $3.number);
3133 $$.c = abc_pushundefined($$.c);
3137 DELETE: "delete" E {
3138 typedcode_t v = node_read($2);
3140 if($$.c->opcode == OPCODE_COERCE_A) {
3141 $$.c = code_cutlast($$.c);
3143 multiname_t*name = 0;
3144 if($$.c->opcode == OPCODE_GETPROPERTY) {
3145 $$.c->opcode = OPCODE_DELETEPROPERTY;
3146 } else if($$.c->opcode == OPCODE_GETSLOT) {
3147 int slot = (int)(ptroff_t)$$.c->data[0];
3148 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3149 $$.c = code_cutlast($$.c);
3150 $$.c = abc_deleteproperty2($$.c, name);
3152 $$.c = abc_getlocal_0($$.c);
3153 MULTINAME_LATE(m, v.t?v.t->access:ACCESS_PACKAGE, "");
3154 $$.c = abc_deleteproperty2($$.c, &m);
3156 $$.t = TYPE_BOOLEAN;
3159 RETURN: "return" %prec prec_none {
3160 $$ = abc_returnvoid(0);
3162 RETURN: "return" EXPRESSION {
3164 $$ = abc_returnvalue($$);
3167 // ----------------------- expression types -------------------------------------
3169 NONCOMMAEXPRESSION : E %prec below_lt {
3172 EXPRESSION : COMMA_EXPRESSION {
3175 COMMA_EXPRESSION : E %prec below_lt {
3176 $$ = mkmultinode(&node_comma, $1);
3178 COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_lt {
3179 $$ = multinode_extend($1, $3);
3181 VOIDEXPRESSION : E %prec below_minus {
3184 VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_lt {
3186 $$ = code_append($$, node_exec($3));
3189 MAYBE_DICT_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3190 MAYBE_DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST {$$=$1;}
3192 DICTLH: T_IDENTIFIER {$$=abc_pushstring(0,$1);}
3193 DICTLH: T_STRING {$$=abc_pushstring2(0,&$1);}
3194 DICTLH: T_INT {syntaxerror("dictionary keys must be strings");}
3195 DICTLH: T_UINT {syntaxerror("dictionary keys must be strings");}
3196 DICTLH: T_FLOAT {syntaxerror("dictionary keys must be strings");}
3198 DICT_EXPRPAIR_LIST : DICTLH ':' NONCOMMAEXPRESSION {
3200 $$.cc = code_append($$.cc, $1);
3201 $$.cc = code_append($$.cc, $3.c);
3204 DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST ',' DICTLH ':' NONCOMMAEXPRESSION {
3206 $$.number = $1.number+2;
3207 $$.cc = code_append($$.cc, $3);
3208 $$.cc = code_append($$.cc, $5.c);
3211 // ----------------------- expression evaluation -------------------------------------
3213 E : INNERFUNCTION %prec prec_none {$$ = mkcodenode($1);}
3214 E : MEMBER %prec '.' {$$ = mkcodenode($1);}
3215 E : NEW {$$ = mkcodenode($1);}
3216 E : DELETE {$$ = mkcodenode($1);}
3217 E : FUNCTIONCALL {$$ = mkcodenode($1);}
3218 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3221 $$ = mkconstnode($1);
3227 multiname_t m = {QNAME, &stdns, 0, "XML"};
3228 v.c = abc_getlex2(v.c, &m);
3229 v.c = abc_pushstring(v.c, $1);
3230 v.c = abc_construct(v.c, 1);
3239 multiname_t m = {QNAME, &stdns, 0, "RegExp"};
3241 v.c = abc_getlex2(v.c, &m);
3242 v.c = abc_pushstring(v.c, $1.pattern);
3243 v.c = abc_construct(v.c, 1);
3245 v.c = abc_getlex2(v.c, &m);
3246 v.c = abc_pushstring(v.c, $1.pattern);
3247 v.c = abc_pushstring(v.c, $1.options);
3248 v.c = abc_construct(v.c, 2);
3256 state->method->need_arguments = 1;
3259 v.c = abc_getlocal(0, state->method->need_arguments);
3265 E : '[' MAYBE_EXPRESSION_LIST ']' {
3268 v.c = code_append(v.c, $2.cc);
3269 v.c = abc_newarray(v.c, $2.number);
3270 v.t = registry_getarrayclass();
3275 E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' {
3278 v.c = code_append(v.c, $2.cc);
3279 v.c = abc_newobject(v.c, $2.number/2);
3280 v.t = registry_getobjectclass();
3284 E : E '<' E {$$ = mknode2(&node_lt,$1,$3);}
3285 E : E '>' E {$$ = mknode2(&node_gt,$1,$3);}
3286 E : E "<=" E {$$ = mknode2(&node_le,$1,$3);}
3287 E : E ">=" E {$$ = mknode2(&node_ge,$1,$3);}
3288 E : E "==" E {$$ = mknode2(&node_eqeq,$1,$3);}
3289 E : E "===" E {$$ = mknode2(&node_eqeqeq,$1,$3);}
3290 E : E "!==" E {$$ = mknode2(&node_noteqeq,$1,$3);}
3291 E : E "!=" E {$$ = mknode2(&node_noteq,$1,$3);}
3292 E : E "||" E {$$ = mknode2(&node_oror,$1,$3);}
3293 E : E "&&" E {$$ = mknode2(&node_andand,$1,$3);}
3294 E : '!' E {$$ = mknode1(&node_not, $2);}
3295 E : '~' E {$$ = mknode1(&node_bitnot, $2);}
3296 E : E '&' E {$$ = mknode2(&node_bitand, $1, $3);}
3297 E : E '^' E {$$ = mknode2(&node_bitxor, $1, $3);}
3298 E : E '|' E {$$ = mknode2(&node_bitor, $1, $3);}
3299 E : E ">>" E {$$ = mknode2(&node_shr, $1, $3);}
3300 E : E ">>>" E {$$ = mknode2(&node_ushr, $1, $3);}
3301 E : E "<<" E {$$ = mknode2(&node_shl, $1, $3);}
3302 E : E '/' E {$$ = mknode2(&node_div, $1, $3);}
3303 E : E '%' E {$$ = mknode2(&node_mod, $1, $3);}
3304 E : E '+' E {$$ = mknode2(&node_plus, $1, $3);}
3305 E : E '-' E {$$ = mknode2(&node_minus, $1, $3);}
3306 E : E '*' E {$$ = mknode2(&node_multiply, $1, $3);}
3307 E : E "in" E {$$ = mknode2(&node_in, $1, $3);}
3308 E : E "as" E {$$ = mknode2(&node_as, $1, $3);}
3309 E : E "instanceof" E {$$ = mknode2(&node_instanceof, $1, $3);}
3310 E : E "is" E {$$ = mknode2(&node_is, $1, $3);}
3311 E : "typeof" E {$$ = mknode1(&node_typeof, $2);}
3312 E : "void" E {$$ = mknode1(&node_void, $2);}
3313 E : "void" { $$ = mkconstnode(constant_new_undefined());}
3314 E : '(' COMMA_EXPRESSION ')' { $$=$2;}
3315 E : '-' E {$$ = mknode1(&node_neg, $2);}
3316 E : E '[' E ']' {$$ = mknode2(&node_arraylookup, $1,$3);}
3317 E : E "*=" E {$$ = mknode2(&node_muleq, $1, $3);}
3318 E : E "%=" E {$$ = mknode2(&node_modeq, $1, $3);}
3319 E : E "<<=" E {$$ = mknode2(&node_shleq, $1, $3);}
3320 E : E ">>=" E {$$ = mknode2(&node_shreq, $1, $3);}
3321 E : E ">>>=" E {$$ = mknode2(&node_ushreq, $1, $3);}
3322 E : E "/=" E { $$ = mknode2(&node_diveq, $1, $3);}
3323 E : E "|=" E { $$ = mknode2(&node_bitoreq, $1, $3);}
3324 E : E "^=" E { $$ = mknode2(&node_bitxoreq, $1, $3);}
3325 E : E "&=" E { $$ = mknode2(&node_bitandeq, $1, $3);}
3326 E : E "+=" E { $$ = mknode2(&node_pluseq, $1, $3);}
3327 E : E "-=" E { $$ = mknode2(&node_minuseq, $1, $3);}
3328 E : E '=' E { $$ = mknode2(&node_assign, $1, $3);}
3329 E : E '?' E ':' E %prec below_assignment { $$ = mknode3(&node_tenary, $1, $3, $5);}
3331 E : E "++" { $$ = mknode1(&node_rplusplus, $1);}
3332 E : E "--" { $$ = mknode1(&node_rminusminus, $1);}
3333 E : "++" %prec plusplus_prefix E {$$ = mknode1(&node_lplusplus, $2); }
3334 E : "--" %prec minusminus_prefix E {$$ = mknode1(&node_lminusminus, $2); }
3336 E : "super" '.' T_IDENTIFIER
3337 { if(!state->cls->info)
3338 syntaxerror("super keyword not allowed outside a class");
3339 classinfo_t*t = state->cls->info->superclass;
3340 if(!t) t = TYPE_OBJECT;
3341 memberinfo_t*f = findmember_nsset(t, $3, 1);
3342 MEMBER_MULTINAME(m, f, $3);
3345 v.c = abc_getlocal_0(v.c);
3346 v.c = abc_getsuper2(v.c, &m);
3347 v.t = slotinfo_gettype((slotinfo_t*)f);
3351 E : '@' T_IDENTIFIER {
3353 multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $2};
3354 v.c = abc_getlex2(0, &m);
3359 E : E '.' '(' {PASS12 new_state();state->xmlfilter=1;} E ')' {
3362 typedcode_t v = node_read($1);
3363 typedcode_t w = node_read($5);
3365 int index = alloc_local();
3366 int result = alloc_local();
3367 int tmp = alloc_local();
3368 int xml = alloc_local();
3370 c = code_append(c, v.c);
3371 c = abc_checkfilter(c);
3372 c = abc_coerce_a(c); //hasnext2 converts to *
3373 c = abc_setlocal(c, xml);
3374 multiname_t m = {QNAME, &stdns, 0, "XMLList"};
3375 c = abc_getlex2(c, &m);
3376 c = abc_construct(c, 0);
3377 c = abc_setlocal(c, result);
3378 c = abc_pushbyte(c, 0);
3379 c = abc_setlocal(c, index);
3380 code_t*jmp = c = abc_jump(c, 0);
3381 code_t*loop = c = abc_label(c);
3382 c = abc_getlocal(c, xml);
3383 c = abc_getlocal(c, index);
3384 c = abc_nextvalue(c);
3386 c = abc_setlocal(c, tmp);
3387 c = abc_pushwith(c);
3388 c = code_append(c, w.c);
3389 c = abc_popscope(c);
3390 code_t*b = c = abc_iffalse(c, 0);
3391 c = abc_getlocal(c, result);
3392 c = abc_getlocal(c, index);
3393 c = abc_getlocal(c, tmp);
3394 multiname_t m2 = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
3395 c = abc_setproperty2(c, &m2);
3396 c = b->branch = jmp->branch = abc_nop(c);
3397 c = abc_kill(c, tmp);
3398 c = abc_hasnext2(c, xml, index);
3399 c = abc_iftrue(c, loop);
3400 c = abc_getlocal(c, result);
3401 c = abc_kill(c, xml);
3402 c = abc_kill(c, result);
3403 c = abc_kill(c, index);
3413 ID_OR_NS : T_IDENTIFIER {$$=$1;}
3414 ID_OR_NS : '*' {$$="*";}
3415 ID_OR_NS : T_NAMESPACE {$$=(char*)$1;}
3416 SUBNODE: X_IDENTIFIER
3420 MAYBE_NS: T_IDENTIFIER "::" {$$=$1;}
3421 | T_NAMESPACE "::" {$$=(char*)$1;}
3422 | '*' "::" {$$="*";}
3425 E : E '.' ID_OR_NS "::" SUBNODE {
3426 typedcode_t v = node_read($1);
3427 typedcode_t w = node_read(resolve_identifier($3));
3428 v.c = code_append(v.c, w.c);
3429 if(!TYPE_IS_NAMESPACE(w.t)) {
3430 as3_softwarning("%s might not be a namespace", $3);
3432 v.c = converttype(v.c, w.t, TYPE_NAMESPACE);
3433 multiname_t m = {RTQNAME, 0, 0, $5};
3434 v.c = abc_getproperty2(v.c, &m);
3435 if(TYPE_IS_XML(v.t)) {
3438 v.c = abc_coerce_a(v.c);
3443 E : E ".." SUBNODE {
3444 typedcode_t v = node_read($1);
3445 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3446 v.c = abc_getdescendants2(v.c, &m);
3450 E : E '.' '[' E ']' {
3451 typedcode_t v = node_read($1);
3452 typedcode_t w = node_read($4);
3453 multiname_t m = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
3454 v.c = code_append(v.c, w.c);
3455 v.c = converttype(w.c, w.t, TYPE_STRING);
3456 v.c = abc_getproperty2(v.c, &m);
3461 E : E '.' '@' SUBNODE {
3462 typedcode_t v = node_read($1);
3463 multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
3464 v.c = abc_getproperty2(v.c, &m);
3468 E : E ".." '@' SUBNODE {
3469 typedcode_t v = node_read($1);
3470 multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
3471 v.c = abc_getdescendants2(v.c, &m);
3475 E : E '.' '@' '[' E ']' {
3476 typedcode_t v = node_read($1);
3477 typedcode_t w = node_read($5);
3478 multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
3479 v.c = code_append(v.c, w.c);
3480 v.c = converttype(w.c, w.t, TYPE_STRING);
3481 v.c = abc_getproperty2(v.c, &m);
3485 E : E ".." '@' '[' E ']' {
3486 typedcode_t v = node_read($1);
3487 typedcode_t w = node_read($5);
3488 multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
3489 v.c = code_append(v.c, w.c);
3490 v.c = converttype(w.c, w.t, TYPE_STRING);
3491 v.c = abc_getdescendants2(v.c, &m);
3496 MEMBER : E '.' SUBNODE {
3497 typedcode_t v1 = node_read($1);
3499 classinfo_t*t = v1.t;
3501 if(TYPE_IS_CLASS(t) && t->data) {
3505 if(TYPE_IS_XML(t)) {
3506 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3507 $$.c = abc_getproperty2($$.c, &m);
3508 $$.c = abc_coerce_a($$.c);
3509 $$.t = TYPE_XMLLIST;
3511 if(t->subtype==INFOTYPE_UNRESOLVED) {
3512 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3514 memberinfo_t*f = findmember_nsset(t, $3, 1);
3516 if(f && !is_static != !(f->flags&FLAG_STATIC))
3518 if(f && f->slot && !noslot) {
3519 $$.c = abc_getslot($$.c, f->slot);
3522 as3_softwarning("Access of undefined property '%s' in %s", $3, t->name);
3524 MEMBER_MULTINAME(m, f, $3);
3525 $$.c = abc_getproperty2($$.c, &m);
3527 /* determine type */
3528 $$.t = slotinfo_gettype((slotinfo_t*)f);
3530 $$.c = abc_coerce_a($$.c);
3532 } else if(v1.c && v1.c->opcode == OPCODE___PUSHPACKAGE__) {
3533 string_t*package = v1.c->data[0];
3534 char*package2 = concat3(package->str, ".", $3);
3536 slotinfo_t*a = registry_find(package->str, $3);
3539 } else if(dict_contains(state->import_toplevel_packages, package2) ||
3540 registry_ispackage(package2)) {
3542 $$.c->data[0] = string_new4(package2);
3545 syntaxerror("couldn't resolve %s", package2);
3548 /* when resolving a property on an unknown type, we do know the
3549 name of the property (and don't seem to need the package), but
3550 we need to make avm2 try out all access modes */
3551 as3_softwarning("Resolving %s on unknown type", $3);
3552 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3553 $$.c = abc_getproperty2($$.c, &m);
3554 $$.c = abc_coerce_a($$.c);
3560 node_t* resolve_identifier(char*name)
3570 /* look at variables */
3571 if((v = find_variable(state, name))) {
3572 // name is a local variable
3573 o.c = abc_getlocal(o.c, v->index);
3575 return mkcodenode(o);
3577 if((v = find_slot(state, name))) {
3578 o.c = abc_getscopeobject(o.c, 1);
3579 o.c = abc_getslot(o.c, v->index);
3581 return mkcodenode(o);
3584 int i_am_static = state->method->is_static;
3586 /* look at current class' members */
3587 if(!state->method->inner &&
3588 !state->xmlfilter &&
3590 (f = findmember_nsset(state->cls->info, name, 1)))
3592 // name is a member or attribute in this class
3593 int var_is_static = (f->flags&FLAG_STATIC);
3595 if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
3596 /* if the variable is a constant (and we know what is evaluates to), we
3597 can just use the value itself */
3598 varinfo_t*v = (varinfo_t*)f;
3600 return mkconstnode(v->value);
3604 if(var_is_static >= i_am_static) {
3605 if(f->kind == INFOTYPE_METHOD) {
3606 o.t = TYPE_FUNCTION(f);
3611 if(var_is_static && !i_am_static) {
3612 /* access to a static member from a non-static location.
3613 do this via findpropstrict:
3614 there doesn't seem to be any non-lookup way to access
3615 static properties of a class */
3616 state->method->late_binding = 1;
3618 namespace_t ns = {f->access, f->package};
3619 multiname_t m = {QNAME, &ns, 0, name};
3620 o.c = abc_findpropstrict2(o.c, &m);
3621 o.c = abc_getproperty2(o.c, &m);
3622 return mkcodenode(o);
3623 } else if(f->slot>0) {
3624 o.c = abc_getlocal_0(o.c);
3625 o.c = abc_getslot(o.c, f->slot);
3626 return mkcodenode(o);
3628 MEMBER_MULTINAME(m, f, name);
3629 o.c = abc_getlocal_0(o.c);
3630 o.c = abc_getproperty2(o.c, &m);
3631 return mkcodenode(o);
3636 /* look at actual classes, in the current package and imported */
3637 if(!state->xmlfilter && (a = find_class(name))) {
3638 if(state->cls && state->cls->info == (classinfo_t*)a && i_am_static) {
3639 o.c = abc_getlocal_0(0);
3640 o.t = TYPE_CLASS((classinfo_t*)a);
3644 return mkcodenode(o);
3647 /* look through package prefixes */
3648 if(!state->xmlfilter &&
3649 (dict_contains(state->import_toplevel_packages, name) ||
3650 registry_ispackage(name))) {
3651 o.c = abc___pushpackage__(o.c, name);
3653 return mkcodenode(o); //?
3656 /* unknown object, let the avm2 resolve it */
3658 if(!state->method->inner && !state->xmlfilter) {
3659 /* we really should make inner functions aware of the class context */
3660 as3_warning("Couldn't resolve '%s', doing late binding", name);
3662 state->method->late_binding = 1;
3664 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, name};
3667 o.c = abc_findpropstrict2(o.c, &m);
3668 o.c = abc_getproperty2(o.c, &m);
3669 return mkcodenode(o);
3674 VAR_READ : T_IDENTIFIER {
3676 /* Queue unresolved identifiers for checking against the parent
3677 function's variables.
3678 We consider everything which is not a local variable "unresolved".
3679 This encompasses class names, members of the surrounding class
3680 etc. which is *correct* because local variables of the parent function
3684 if(!find_variable(state, $1)) {
3685 if(state->method->inner) {
3686 unknown_variable($1);
3688 /* let the compiler know that it might want to check the current directory/package
3689 for this identifier- maybe there's a file $1.as defining $1. */
3690 as3_schedule_class_noerror(state->package, $1);
3696 $$ = resolve_identifier($1);
3699 // ----------------- namespaces -------------------------------------------------
3702 void add_active_url(const char*url)
3706 list_append(state->active_namespace_urls, n);
3710 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3712 NEW(namespace_decl_t,n);
3717 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3719 NEW(namespace_decl_t,n);
3724 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3726 NEW(namespace_decl_t,n);
3731 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3733 trie_put(active_namespaces, $2->name, (void*)$2->url);
3735 namespace_t access = modifiers2access(&$1);
3736 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3737 var->type = TYPE_NAMESPACE;
3739 ns.access = ACCESS_NAMESPACE;
3741 var->value = constant_new_namespace(&ns);
3744 MULTINAME(m, TYPE_NAMESPACE);
3745 trait_t*t = add_abc_slot(&$1, $2->name, 0, 0);
3746 t->value = var->value;
3747 t->type_name = multiname_clone(&m);
3753 DEFAULT_NAMESPACE : "default xml" "namespace" '=' E
3755 as3_warning("default xml namespaces not supported yet");
3759 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3761 const char*url = $3->name;
3763 varinfo_t*s = (varinfo_t*)$3;
3764 if(s->kind == INFOTYPE_UNRESOLVED) {
3765 s = (varinfo_t*)registry_resolve((slotinfo_t*)s);
3767 syntaxerror("Couldn't resolve namespace %s", $3->name);
3770 if(!s || s->kind != INFOTYPE_VAR)
3771 syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
3772 if(!s->value || !NS_TYPE(s->value->type))
3773 syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
3774 url = s->value->ns->name;
3776 trie_put(active_namespaces, $3->name, (void*)url);
3777 add_active_url(url);