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
270 %nonassoc "false" "true" "null" "undefined" "super" "function"
277 static int a3_error(char*s)
279 syntaxerror("%s", s);
280 return 0; //make gcc happy
283 static void parsererror(const char*file, int line, const char*f)
285 syntaxerror("internal error in %s, %s:%d", f, file, line);
288 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
291 static char* concat2(const char* t1, const char* t2)
295 char*text = malloc(l1+l2+1);
296 memcpy(text , t1, l1);
297 memcpy(text+l1, t2, l2);
301 static char* concat3(const char* t1, const char* t2, const char* t3)
306 char*text = malloc(l1+l2+l3+1);
307 memcpy(text , t1, l1);
308 memcpy(text+l1, t2, l2);
309 memcpy(text+l1+l2, t3, l3);
314 typedef struct _import {
317 DECLARE_LIST(import);
319 DECLARE(methodstate);
320 DECLARE_LIST(methodstate);
322 typedef struct _classstate {
328 methodstate_t*static_init;
330 //code_t*static_init;
331 parsedclass_t*dependencies;
333 char has_constructor;
336 struct _methodstate {
347 dict_t*unresolved_variables;
350 char uses_parent_function;
358 int var_index; // for inner methods
359 int slot_index; // for inner methods
360 char is_a_slot; // for inner methods
365 abc_exception_list_t*exceptions;
367 methodstate_list_t*innerfunctions;
370 typedef struct _state {
375 import_list_t*wildcard_imports;
376 dict_t*import_toplevel_packages;
379 namespace_list_t*active_namespace_urls;
381 char has_own_imports;
382 char new_vars; // e.g. transition between two functions
383 char xmlfilter; // are we inside a xmlobj..() filter?
386 methodstate_t*method;
393 dict_t*allvars; // also contains variables from sublevels
396 typedef struct _global {
399 parsedclass_list_t*classes;
400 abc_script_t*classinit;
402 abc_script_t*init; //package-level code
405 dict_t*file2token2info;
408 static global_t*global = 0;
409 static state_t* state = 0;
413 /* protected handling here is a big hack: we just assume the protectedns
414 is package:class. the correct approach would be to add the proper
415 namespace to all protected members in the registry, even though that
416 would slow down searching */
417 #define MEMBER_MULTINAME(m,f,n) \
421 m##_ns.access = ((slotinfo_t*)(f))->access; \
422 if(m##_ns.access == ACCESS_NAMESPACE) \
423 m##_ns.name = ((slotinfo_t*)(f))->package; \
424 else if(m##_ns.access == ACCESS_PROTECTED && (f)->parent) \
425 m##_ns.name = concat3((f)->parent->package,":",(f)->parent->name); \
430 m.namespace_set = 0; \
431 m.name = ((slotinfo_t*)(f))->name; \
433 m.type = MULTINAME; \
435 m.namespace_set = &nopackage_namespace_set; \
439 /* warning: list length of namespace set is undefined */
440 #define MULTINAME_LATE(m, access, package) \
441 namespace_t m##_ns = {access, package}; \
442 namespace_set_t m##_nsset; \
443 namespace_list_t m##_l;m##_l.next = 0; \
444 m##_nsset.namespaces = &m##_l; \
445 m##_nsset = m##_nsset; \
446 m##_l.namespace = &m##_ns; \
447 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
449 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
450 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
451 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
452 static namespace_t stdns = {ACCESS_PACKAGE, ""};
453 static namespace_list_t nl4 = {&stdns,0};
454 static namespace_list_t nl3 = {&ns3,&nl4};
455 static namespace_list_t nl2 = {&ns2,&nl3};
456 static namespace_list_t nl1 = {&ns1,&nl2};
457 static namespace_set_t nopackage_namespace_set = {&nl1};
459 static dict_t*definitions=0;
460 void as3_set_define(const char*c)
463 definitions = dict_new();
464 if(!dict_contains(definitions,c))
465 dict_put(definitions,c,0);
468 static void new_state()
471 state_t*oldstate = state;
473 memcpy(s, state, sizeof(state_t)); //shallow copy
475 s->imports = dict_new();
477 if(!s->import_toplevel_packages) {
478 s->import_toplevel_packages = dict_new();
482 state->has_own_imports = 0;
483 state->vars = dict_new();
484 state->old = oldstate;
487 trie_remember(active_namespaces);
490 state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
493 static void state_destroy(state_t*state)
495 if(state->has_own_imports) {
496 list_free(state->wildcard_imports);
497 dict_destroy(state->imports);state->imports=0;
499 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
500 dict_destroy(state->imports);state->imports=0;
503 dict_destroy(state->vars);state->vars=0;
505 if(state->new_vars && state->allvars) {
506 parserassert(!state->old || state->old->allvars != state->allvars);
507 DICT_ITERATE_DATA(state->allvars, void*, data) {
510 dict_destroy(state->allvars);
513 list_free(state->active_namespace_urls)
514 state->active_namespace_urls = 0;
519 static void old_state()
521 trie_rollback(active_namespaces);
523 if(!state || !state->old)
524 syntaxerror("invalid nesting");
525 state_t*leaving = state;
529 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
530 free(leaving->method);
533 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
538 state_destroy(leaving);
541 static code_t* method_header(methodstate_t*m);
542 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
543 static void function_initvars(methodstate_t*m, char has_params, params_t*params, int flags, char var0);
546 static char* internal_filename_package = 0;
547 void initialize_file(char*filename)
550 syntaxerror("invalid call to initialize_file during parsing of another file");
553 active_namespaces = trie_new();
556 state->package = internal_filename_package = strdup(filename);
557 state->allvars = dict_new();
559 global->token2info = dict_lookup(global->file2token2info,
560 current_filename // use long version
562 if(!global->token2info) {
563 global->token2info = dict_new2(&ptr_type);
564 dict_put(global->file2token2info, current_filename, global->token2info);
568 state->method = rfx_calloc(sizeof(methodstate_t));
569 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
570 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
572 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
574 syntaxerror("internal error: skewed tokencount");
575 function_initvars(state->method, 0, 0, 0, 1);
576 global->init = abc_initscript(global->file);
582 if(!state || state->level!=1) {
583 syntaxerror("unexpected end of file in pass %d", as3_pass);
587 dict_del(global->file2token2info, current_filename);
588 code_t*header = method_header(state->method);
589 code_t*c = wrap_function(header, 0, global->init->method->body->code);
590 global->init->method->body->code = abc_returnvoid(c);
591 free(state->method);state->method=0;
594 //free(state->package);state->package=0; // used in registry
595 state_destroy(state);state=0;
598 void initialize_parser()
600 global = rfx_calloc(sizeof(global_t));
601 global->file = abc_file_new();
602 global->file->flags &= ~ABCFILE_LAZY;
603 global->file2token2info = dict_new();
604 global->token2info = 0;
605 global->classinit = abc_initscript(global->file);
608 void* finish_parser()
610 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
611 global->token2info=0;
613 initcode_add_classlist(global->classinit, global->classes);
618 typedef struct _variable {
623 methodstate_t*is_inner_method;
626 static variable_t* find_variable(state_t*s, char*name)
631 v = dict_lookup(s->vars, name);
633 if(s->new_vars) break;
636 return dict_lookup(top->allvars, name);
638 static variable_t* find_slot(state_t*s, const char*name)
640 if(s->method && s->method->slots)
641 return dict_lookup(s->method->slots, name);
645 static variable_t* find_variable_safe(state_t*s, char*name)
647 variable_t* v = find_variable(s, name);
649 syntaxerror("undefined variable: %s", name);
653 static char variable_exists(char*name)
655 return dict_contains(state->vars, name);
658 static code_t*defaultvalue(code_t*c, classinfo_t*type)
660 if(TYPE_IS_INT(type)) {
661 c = abc_pushbyte(c, 0);
662 } else if(TYPE_IS_UINT(type)) {
663 c = abc_pushuint(c, 0);
664 } else if(TYPE_IS_FLOAT(type)) {
666 } else if(TYPE_IS_BOOLEAN(type)) {
667 c = abc_pushfalse(c);
669 //c = abc_pushundefined(c);
670 syntaxerror("internal error: can't generate default value for * type");
674 c = abc_coerce2(c, &m);
679 static int alloc_local()
681 return state->method->variable_count++;
684 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
687 variable_t*v = find_slot(state, name);
695 v->index = alloc_local();
700 dict_put(state->vars, name, v);
701 dict_put(state->allvars, name, v);
706 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
708 return new_variable2(name, type, init, maybeslot)->index;
711 #define TEMPVARNAME "__as3_temp__"
714 variable_t*v = find_variable(state, TEMPVARNAME);
719 i = new_variable(TEMPVARNAME, 0, 0, 0);
724 static code_t* var_block(code_t*body)
730 for(t=0;t<state->vars->hashsize;t++) {
731 DICT_ITERATE_DATA(state->vars, variable_t*, v) {
732 if(v->type && v->init) {
733 c = defaultvalue(c, v->type);
734 c = abc_setlocal(c, v->index);
735 k = abc_kill(k, v->index);
744 if(x->opcode== OPCODE___BREAK__ ||
745 x->opcode== OPCODE___CONTINUE__) {
746 /* link kill code before break/continue */
747 code_t*e = code_dup(k);
748 code_t*s = code_start(e);
760 c = code_append(c, body);
761 c = code_append(c, k);
765 static void unknown_variable(char*name)
767 if(!state->method->unresolved_variables)
768 state->method->unresolved_variables = dict_new();
769 if(!dict_contains(state->method->unresolved_variables, name))
770 dict_put(state->method->unresolved_variables, name, 0);
773 static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
775 if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
776 c = abc_getlocal_0(c);
777 c = abc_pushscope(c);
780 /* FIXME: this alloc_local() causes variable indexes to be
781 different in pass2 than in pass1 */
782 if(!m->activation_var) {
783 m->activation_var = alloc_local();
786 c = abc_newactivation(c);
788 c = abc_pushscope(c);
789 c = abc_setlocal(c, m->activation_var);
791 c = abc_getlocal(c, m->activation_var);
792 c = abc_pushscope(c);
798 static code_t* method_header(methodstate_t*m)
802 c = add_scope_code(c, m, 1);
804 methodstate_list_t*l = m->innerfunctions;
806 parserassert(l->methodstate->abc);
807 if(m->uses_slots && l->methodstate->is_a_slot) {
808 c = abc_getscopeobject(c, 1);
809 c = abc_newfunction(c, l->methodstate->abc);
811 c = abc_setlocal(c, l->methodstate->var_index);
812 c = abc_setslot(c, l->methodstate->slot_index);
814 c = abc_newfunction(c, l->methodstate->abc);
815 c = abc_setlocal(c, l->methodstate->var_index);
817 free(l->methodstate);l->methodstate=0;
821 c = code_append(c, m->header);
824 if(m->is_constructor && !m->has_super) {
825 // call default constructor
826 c = abc_getlocal_0(c);
827 c = abc_constructsuper(c, 0);
831 /* all parameters that are used by inner functions
832 need to be copied from local to slot */
833 parserassert(m->activation_var);
834 DICT_ITERATE_ITEMS(m->slots,char*,name,variable_t*,v) {
835 if(v->is_parameter) {
836 c = abc_getlocal(c, m->activation_var);
837 c = abc_getlocal(c, v->index);
838 c = abc_setslot(c, v->index);
842 list_free(m->innerfunctions);
843 m->innerfunctions = 0;
848 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
850 c = code_append(c, header);
851 c = code_append(c, var_block(body));
852 /* append return if necessary */
853 if(!c || (c->opcode != OPCODE_RETURNVOID &&
854 c->opcode != OPCODE_RETURNVALUE)) {
855 c = abc_returnvoid(c);
860 static void startpackage(char*name)
863 state->package = strdup(name);
865 static void endpackage()
867 //used e.g. in classinfo_register:
868 //free(state->package);state->package=0;
872 #define FLAG_PUBLIC 256
873 #define FLAG_PROTECTED 512
874 #define FLAG_PRIVATE 1024
875 #define FLAG_PACKAGEINTERNAL 2048
876 #define FLAG_NAMESPACE 4096
878 static namespace_t modifiers2access(modifiers_t*mod)
883 if(mod->flags&FLAG_NAMESPACE) {
884 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
885 syntaxerror("invalid combination of access levels and namespaces");
886 ns.access = ACCESS_NAMESPACE;
888 const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
890 /* shouldn't happen- the tokenizer only reports something as a namespace
891 if it was already registered */
892 trie_dump(active_namespaces);
893 syntaxerror("unknown namespace: %s", mod->ns);
896 } else if(mod->flags&FLAG_PUBLIC) {
897 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
898 syntaxerror("invalid combination of access levels");
899 ns.access = ACCESS_PACKAGE;
900 } else if(mod->flags&FLAG_PRIVATE) {
901 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
902 syntaxerror("invalid combination of access levels");
903 ns.access = ACCESS_PRIVATE;
904 } else if(mod->flags&FLAG_PROTECTED) {
905 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
906 syntaxerror("invalid combination of access levels");
907 ns.access = ACCESS_PROTECTED;
909 ns.access = ACCESS_PACKAGEINTERNAL;
913 static slotinfo_t* find_class(const char*name);
915 static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
917 return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
920 static void innerfunctions2vars(methodstate_t*m)
922 methodstate_list_t*l = m->innerfunctions;
924 methodstate_t*m = l->methodstate;
926 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 0);
927 m->var_index = v->index;
929 m->slot_index = m->is_a_slot;
930 v->is_inner_method = m;
935 static void function_initvars(methodstate_t*m, char has_params, params_t*params, int flags, char var0)
940 index = new_variable("this", 0, 0, 0);
941 else if(!m->is_global)
942 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
944 index = new_variable("globalscope", 0, 0, 0);
945 parserassert(!index);
950 for(p=params->list;p;p=p->next) {
951 variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1);
954 if(as3_pass==2 && m->need_arguments) {
955 /* arguments can never be used by an innerfunction (the inner functions
956 have their own arguments var), so it's ok to not initialize this until
957 pass 2. (We don't know whether we need it before, anyway) */
958 variable_t*v = new_variable2("arguments", TYPE_ARRAY, 0, 0);
959 m->need_arguments = v->index;
963 innerfunctions2vars(m);
966 m->scope_code = add_scope_code(m->scope_code, m, 0);
968 /* exchange unresolved identifiers with the actual objects */
969 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
970 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
971 classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
972 if(!type || type->kind != INFOTYPE_CLASS) {
973 syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name);
983 char*as3_globalclass=0;
984 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
987 syntaxerror("inner classes now allowed");
992 classinfo_list_t*mlist=0;
994 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
995 syntaxerror("invalid modifier(s)");
997 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
998 syntaxerror("public and internal not supported at the same time.");
1000 if((mod->flags&(FLAG_PROTECTED|FLAG_STATIC)) == (FLAG_PROTECTED|FLAG_STATIC))
1001 syntaxerror("protected and static not supported at the same time.");
1003 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
1004 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
1005 // all classes extend object
1006 extends = registry_getobjectclass();
1009 /* create the class name, together with the proper attributes */
1013 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
1014 access = ACCESS_PRIVATE; package = internal_filename_package;
1015 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
1016 access = ACCESS_PACKAGEINTERNAL; package = state->package;
1017 } else if(state->package!=internal_filename_package) {
1018 access = ACCESS_PACKAGE; package = state->package;
1020 syntaxerror("public classes only allowed inside a package");
1024 state->cls = rfx_calloc(sizeof(classstate_t));
1025 state->cls->init = rfx_calloc(sizeof(methodstate_t));
1026 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
1027 state->cls->static_init->is_static=FLAG_STATIC;
1028 state->cls->static_init->variable_count=1;
1029 /* notice: we make no effort to initialize the top variable (local0) here,
1030 even though it has special meaning. We just rely on the fact
1031 that pass 1 won't do anything with variables */
1033 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
1035 /* set current method to constructor- all code within the class-level (except
1036 static variable initializations) will be executed during construction time */
1037 state->method = state->cls->init;
1039 if(registry_find(package, classname)) {
1040 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
1042 /* build info struct */
1043 int num_interfaces = (list_length(implements));
1044 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
1045 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
1046 state->cls->info->superclass = extends;
1049 classinfo_list_t*l = implements;
1050 for(l=implements;l;l=l->next) {
1051 state->cls->info->interfaces[pos++] = l->classinfo;
1056 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1058 state->method = state->cls->init;
1059 parserassert(state->cls && state->cls->info);
1061 function_initvars(state->cls->init, 0, 0, 0, 1);
1062 function_initvars(state->cls->static_init, 0, 0, 0, 0);
1064 if(extends && (extends->flags & FLAG_FINAL))
1065 syntaxerror("Can't extend final class '%s'", extends->name);
1068 while(state->cls->info->interfaces[pos]) {
1069 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1070 syntaxerror("'%s' is not an interface",
1071 state->cls->info->interfaces[pos]->name);
1075 /* generate the abc code for this class */
1076 MULTINAME(classname2,state->cls->info);
1077 multiname_t*extends2 = sig2mname(extends);
1079 /* don't add the class to the class index just yet- that will be done later
1081 state->cls->abc = abc_class_new(0, &classname2, extends2);
1082 state->cls->abc->file = global->file;
1084 multiname_destroy(extends2);
1085 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1086 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1087 if(state->cls->info->flags&FLAG_INTERFACE) {
1088 abc_class_interface(state->cls->abc);
1091 for(mlist=implements;mlist;mlist=mlist->next) {
1092 MULTINAME(m, mlist->classinfo);
1093 abc_class_add_interface(state->cls->abc, &m);
1096 state->cls->dependencies = parsedclass_new(state->cls->info, state->cls->abc);
1097 list_append(global->classes, state->cls->dependencies);
1099 /* flash.display.MovieClip handling */
1100 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1101 if(state->package && state->package[0]) {
1102 as3_globalclass = concat3(state->package, ".", classname);
1104 as3_globalclass = strdup(classname);
1110 static void endclass()
1113 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1115 c = abc_getlocal_0(c);
1116 c = abc_constructsuper(c, 0);
1117 state->cls->init->header = code_append(state->cls->init->header, c);
1118 state->cls->has_constructor=1;
1120 if(state->cls->init) {
1121 if(state->cls->info->flags&FLAG_INTERFACE) {
1122 if(state->cls->init->header)
1123 syntaxerror("interface can not have class-level code");
1125 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1126 code_t*c = method_header(state->cls->init);
1127 m->body->code = wrap_function(c, 0, m->body->code);
1130 if(state->cls->static_init) {
1131 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1132 code_t*c = method_header(state->cls->static_init);
1133 m->body->code = wrap_function(c, 0, m->body->code);
1136 trait_list_t*trait = state->cls->abc->traits;
1137 /* switch all protected members to the protected ns of this class */
1139 trait_t*t = trait->trait;
1140 if(t->name->ns->access == ACCESS_PROTECTED) {
1141 if(!state->cls->abc->protectedNS) {
1142 char*n = concat3(state->cls->info->package, ":", state->cls->info->name);
1143 state->cls->abc->protectedNS = namespace_new_protected(n);
1144 state->cls->abc->flags |= CLASS_PROTECTED_NS;
1146 t->name->ns->name = strdup(state->cls->abc->protectedNS->name);
1148 trait = trait->next;
1155 void check_code_for_break(code_t*c)
1158 if(c->opcode == OPCODE___BREAK__) {
1159 char*name = string_cstr(c->data[0]);
1160 syntaxerror("Unresolved \"break %s\"", name);
1162 if(c->opcode == OPCODE___CONTINUE__) {
1163 char*name = string_cstr(c->data[0]);
1164 syntaxerror("Unresolved \"continue %s\"", name);
1166 if(c->opcode == OPCODE___RETHROW__) {
1167 syntaxerror("Unresolved \"rethrow\"");
1169 if(c->opcode == OPCODE___FALLTHROUGH__) {
1170 syntaxerror("Unresolved \"fallthrough\"");
1172 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1173 char*name = string_cstr(c->data[0]);
1174 syntaxerror("Can't reference a package (%s) as such", name);
1180 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1182 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1183 if(TYPE_IS_NUMBER(t)) {
1184 xassert(c->type == CONSTANT_FLOAT
1185 || c->type == CONSTANT_INT
1186 || c->type == CONSTANT_UINT);
1187 } else if(TYPE_IS_UINT(t)) {
1188 xassert(c->type == CONSTANT_UINT ||
1189 (c->type == CONSTANT_INT && c->i>=0));
1190 } else if(TYPE_IS_INT(t)) {
1191 xassert(c->type == CONSTANT_INT);
1192 } else if(TYPE_IS_BOOLEAN(t)) {
1193 xassert(c->type == CONSTANT_TRUE
1194 || c->type == CONSTANT_FALSE);
1198 static void check_override(memberinfo_t*m, int flags)
1202 if(m->parent == state->cls->info)
1203 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1205 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1206 if(m->access==ACCESS_PRIVATE)
1208 if(m->flags & FLAG_FINAL)
1209 syntaxerror("can't override final member %s", m->name);
1211 /* allow this. it's no issue.
1212 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1213 syntaxerror("can't override static member %s", m->name);*/
1215 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1216 syntaxerror("can't override non-static member %s with static declaration", m->name);
1218 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1219 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1220 if(m->kind == INFOTYPE_METHOD)
1221 syntaxerror("can't override without explicit 'override' declaration");
1223 syntaxerror("can't override '%s'", m->name);
1228 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1230 methodinfo_t*minfo = 0;
1231 namespace_t ns = modifiers2access(mod);
1234 minfo = methodinfo_register_global(ns.access, state->package, name);
1235 minfo->return_type = return_type;
1236 } else if(getset != KW_GET && getset != KW_SET) {
1238 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1240 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1242 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1243 minfo->return_type = return_type;
1244 // getslot on a member slot only returns "undefined", so no need
1245 // to actually store these
1246 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1248 //class getter/setter
1249 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1251 if(getset == KW_GET) {
1253 } else if(params->list && params->list->param && !params->list->next) {
1254 type = params->list->param->type;
1256 syntaxerror("setter function needs to take exactly one argument");
1257 // not sure wether to look into superclasses here, too
1258 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1260 if(minfo->kind!=INFOTYPE_VAR)
1261 syntaxerror("class already contains a method called '%s'", name);
1262 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1263 syntaxerror("class already contains a field called '%s'", name);
1264 if(minfo->subtype & gs)
1265 syntaxerror("getter/setter for '%s' already defined", name);
1266 /* make a setter or getter into a getset */
1267 minfo->subtype |= gs;
1270 FIXME: this check needs to be done in pass 2
1272 if((!minfo->return_type != !type) ||
1273 (minfo->return_type && type &&
1274 !strcmp(minfo->return_type->name, type->name))) {
1275 syntaxerror("different type in getter and setter: %s and %s",
1276 minfo->return_type?minfo->return_type->name:"*",
1277 type?type->name:"*");
1280 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1281 minfo->kind = INFOTYPE_VAR; //hack
1282 minfo->subtype = gs;
1283 minfo->return_type = type;
1286 /* can't assign a slot as getter and setter might have different slots */
1287 //minfo->slot = slot;
1289 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1290 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1291 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1296 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1298 //parserassert(state->method && state->method->info);
1300 methodstate_t*parent_method = state->method;
1303 return_type = 0; // not valid in pass 1
1307 state->new_vars = 1;
1308 state->allvars = dict_new();
1311 state->method = rfx_calloc(sizeof(methodstate_t));
1312 state->method->inner = 1;
1313 state->method->is_static = parent_method->is_static;
1314 state->method->variable_count = 0;
1315 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1317 NEW(methodinfo_t,minfo);
1318 minfo->kind = INFOTYPE_METHOD;
1319 minfo->access = ACCESS_PACKAGEINTERNAL;
1321 state->method->info = minfo;
1324 list_append(parent_method->innerfunctions, state->method);
1326 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1328 function_initvars(state->method, 1, params, 0, 1);
1332 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1333 state->method->variable_count = 0;
1334 parserassert(state->method);
1336 state->method->info->return_type = return_type;
1337 function_initvars(state->method, 1, params, 0, 1);
1341 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1342 params_t*params, classinfo_t*return_type)
1344 if(state->method && state->method->info) {
1345 syntaxerror("not able to start another method scope");
1348 state->new_vars = 1;
1349 state->allvars = dict_new();
1352 state->method = rfx_calloc(sizeof(methodstate_t));
1353 state->method->has_super = 0;
1354 state->method->is_static = mod->flags&FLAG_STATIC;
1357 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1359 state->method->is_global = 1;
1360 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1362 if(state->method->is_constructor)
1363 name = "__as3_constructor__";
1365 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1367 function_initvars(state->method, 1, params, mod->flags, 1);
1369 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1373 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1374 state->method->variable_count = 0;
1375 parserassert(state->method);
1378 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1379 check_override(m, mod->flags);
1383 state->cls->has_constructor |= state->method->is_constructor;
1386 function_initvars(state->method, 1, params, mod->flags, 1);
1390 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1391 params_t*params, classinfo_t*return_type, code_t*body)
1394 innerfunctions2vars(state->method);
1396 methodstate_list_t*ml = state->method->innerfunctions;
1398 dict_t*xvars = dict_new();
1401 methodstate_t*m = ml->methodstate;
1402 parserassert(m->inner);
1403 if(m->unresolved_variables) {
1404 dict_t*d = m->unresolved_variables;
1406 DICT_ITERATE_KEY(d, char*, id) {
1407 /* check parent method's variables */
1409 if((v=find_variable(state, id))) {
1410 m->uses_parent_function = 1;
1411 state->method->uses_slots = 1;
1412 dict_put(xvars, id, 0);
1415 dict_destroy(m->unresolved_variables);m->unresolved_variables = 0;
1420 if(state->method->uses_slots) {
1421 state->method->slots = dict_new();
1423 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1424 if(!name) syntaxerror("internal error");
1425 if(v->index && dict_contains(xvars, name)) {
1428 if(v->is_inner_method) {
1429 v->is_inner_method->is_a_slot = i;
1432 dict_put(state->method->slots, name, v);
1435 state->method->uses_slots = i;
1436 dict_destroy(state->vars);state->vars = 0;
1437 parserassert(state->new_vars);
1438 dict_destroy(state->allvars);state->allvars = 0;
1445 /*if(state->method->uses_parent_function){
1446 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1451 multiname_t*type2 = sig2mname(return_type);
1453 if(state->method->inner) {
1454 f = state->method->abc;
1455 abc_method_init(f, global->file, type2, 1);
1456 } else if(state->method->is_constructor) {
1457 f = abc_class_getconstructor(state->cls->abc, type2);
1458 } else if(!state->method->is_global) {
1459 namespace_t ns = modifiers2access(mod);
1460 multiname_t mname = {QNAME, &ns, 0, name};
1461 if(mod->flags&FLAG_STATIC)
1462 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1464 f = abc_class_method(state->cls->abc, type2, &mname);
1465 slot = f->trait->slot_id;
1467 namespace_t mname_ns = {state->method->info->access, state->package};
1468 multiname_t mname = {QNAME, &mname_ns, 0, name};
1470 f = abc_method_new(global->file, type2, 1);
1471 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1472 //abc_code_t*c = global->init->method->body->code;
1474 //flash doesn't seem to allow us to access function slots
1475 //state->method->info->slot = slot;
1477 if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1478 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1479 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1480 if(params->varargs) f->flags |= METHOD_NEED_REST;
1481 if(state->method->need_arguments) f->flags |= METHOD_NEED_ARGUMENTS;
1485 for(p=params->list;p;p=p->next) {
1486 if(params->varargs && !p->next) {
1487 break; //varargs: omit last parameter in function signature
1489 multiname_t*m = sig2mname(p->param->type);
1490 list_append(f->parameters, m);
1491 if(p->param->value) {
1492 check_constant_against_type(p->param->type, p->param->value);
1493 opt=1;list_append(f->optional_parameters, p->param->value);
1495 syntaxerror("function %s: non-optional parameter not allowed after optional parameters", name);
1498 if(state->method->slots) {
1499 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1501 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1502 multiname_t*type = sig2mname(v->type);
1503 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1504 t->slot_id = v->index;
1509 check_code_for_break(body);
1511 /* Seems this works now.
1512 if(state->method->exceptions && state->method->uses_slots) {
1513 as3_warning("try/catch and activation not supported yet within the same method");
1517 f->body->code = body;
1518 f->body->exceptions = state->method->exceptions;
1519 } else { //interface
1521 syntaxerror("interface methods can't have a method body");
1531 void breakjumpsto(code_t*c, char*name, code_t*jump)
1534 if(c->opcode == OPCODE___BREAK__) {
1535 string_t*name2 = c->data[0];
1536 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1537 c->opcode = OPCODE_JUMP;
1544 void continuejumpsto(code_t*c, char*name, code_t*jump)
1547 if(c->opcode == OPCODE___CONTINUE__) {
1548 string_t*name2 = c->data[0];
1549 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1550 c->opcode = OPCODE_JUMP;
1558 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1563 return abc_coerce_a(c);
1567 // cast an "any" type to a specific type. subject to
1568 // runtime exceptions
1569 return abc_coerce2(c, &m);
1572 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1573 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1574 // allow conversion between number types
1575 if(TYPE_IS_UINT(to))
1576 return abc_convert_u(c);
1577 else if(TYPE_IS_INT(to))
1578 return abc_convert_i(c);
1579 else if(TYPE_IS_NUMBER(to))
1580 return abc_convert_d(c);
1581 return abc_coerce2(c, &m);
1584 if(TYPE_IS_XMLLIST(to) && TYPE_IS_XML(from))
1587 if(TYPE_IS_BOOLEAN(to))
1588 return abc_convert_b(c);
1589 if(TYPE_IS_STRING(to))
1590 return abc_convert_s(c);
1591 if(TYPE_IS_OBJECT(to))
1592 return abc_convert_o(c);
1594 classinfo_t*supertype = from;
1596 if(supertype == to) {
1597 // target type is one of from's superclasses
1598 return abc_coerce2(c, &m);
1601 while(supertype->interfaces[t]) {
1602 if(supertype->interfaces[t]==to) {
1603 // target type is one of from's interfaces
1604 return abc_coerce2(c, &m);
1608 supertype = supertype->superclass;
1610 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1612 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1614 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1617 as3_error("can't convert type %s%s%s to %s%s%s",
1618 from->package, from->package[0]?".":"", from->name,
1619 to->package, to->package[0]?".":"", to->name);
1623 /* move to ast.c todo end */
1625 char is_pushundefined(code_t*c)
1627 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1630 static const char* get_package_from_name(const char*name)
1632 /* try explicit imports */
1633 dictentry_t* e = dict_get_slot(state->imports, name);
1635 if(!strcmp(e->key, name)) {
1636 slotinfo_t*c = (slotinfo_t*)e->data;
1637 if(c) return c->package;
1643 static namespace_list_t*get_current_imports()
1645 namespace_list_t*searchlist = 0;
1647 list_append(searchlist, namespace_new_package(state->package));
1649 import_list_t*l = state->wildcard_imports;
1651 namespace_t*ns = namespace_new_package(l->import->package);
1652 list_append(searchlist, ns);
1655 list_append(searchlist, namespace_new_package(""));
1656 list_append(searchlist, namespace_new_package(internal_filename_package));
1660 static slotinfo_t* find_class(const char*name)
1664 c = registry_find(state->package, name);
1667 /* try explicit imports */
1668 dictentry_t* e = dict_get_slot(state->imports, name);
1671 if(!strcmp(e->key, name)) {
1672 c = (slotinfo_t*)e->data;
1678 /* try package.* imports */
1679 import_list_t*l = state->wildcard_imports;
1681 //printf("does package %s contain a class %s?\n", l->import->package, name);
1682 c = registry_find(l->import->package, name);
1687 /* try global package */
1688 c = registry_find("", name);
1691 /* try local "filename" package */
1692 c = registry_find(internal_filename_package, name);
1697 typedcode_t push_class(slotinfo_t*a)
1702 if(a->access == ACCESS_PACKAGEINTERNAL &&
1703 strcmp(a->package, state->package) &&
1704 strcmp(a->package, internal_filename_package)
1706 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1707 infotypename(a), a->name, a->package, state->package);
1711 if(a->kind != INFOTYPE_CLASS) {
1713 x.c = abc_findpropstrict2(x.c, &m);
1714 x.c = abc_getproperty2(x.c, &m);
1715 if(a->kind == INFOTYPE_METHOD) {
1716 methodinfo_t*f = (methodinfo_t*)a;
1717 x.t = TYPE_FUNCTION(f);
1719 varinfo_t*v = (varinfo_t*)a;
1724 if(state->cls && state->method == state->cls->static_init) {
1725 /* we're in the static initializer.
1726 record the fact that we're using this class here */
1727 parsedclass_add_dependency(state->cls->dependencies, (classinfo_t*)a);
1729 classinfo_t*c = (classinfo_t*)a;
1731 if(0) { //Error #1026: Slot 1 exceeds slotCount=0 of global
1732 x.c = abc_getglobalscope(x.c);
1733 x.c = abc_getslot(x.c, c->slot);
1736 x.c = abc_getlex2(x.c, &m);
1738 x.t = TYPE_CLASS(c);
1744 char is_break_or_jump(code_t*c)
1748 if(c->opcode == OPCODE_JUMP ||
1749 c->opcode == OPCODE___BREAK__ ||
1750 c->opcode == OPCODE___CONTINUE__ ||
1751 c->opcode == OPCODE_THROW ||
1752 c->opcode == OPCODE_RETURNVOID ||
1753 c->opcode == OPCODE_RETURNVALUE) {
1759 #define IS_FINALLY_TARGET(op) \
1760 ((op) == OPCODE___CONTINUE__ || \
1761 (op) == OPCODE___BREAK__ || \
1762 (op) == OPCODE_RETURNVOID || \
1763 (op) == OPCODE_RETURNVALUE || \
1764 (op) == OPCODE___RETHROW__)
1766 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1768 #define NEED_EXTRA_STACK_ARG
1769 code_t*finally_label = abc_nop(0);
1770 NEW(lookupswitch_t, l);
1776 code_t*prev = i->prev;
1777 if(IS_FINALLY_TARGET(i->opcode)) {
1780 if(i->opcode == OPCODE___RETHROW__ ||
1781 i->opcode == OPCODE_RETURNVALUE) {
1782 if(i->opcode == OPCODE___RETHROW__)
1783 i->opcode = OPCODE_THROW;
1785 p = abc_coerce_a(p);
1786 p = abc_setlocal(p, tempvar);
1788 p = abc_pushbyte(p, count++);
1789 p = abc_jump(p, finally_label);
1790 code_t*target = p = abc_label(p);
1791 #ifdef NEED_EXTRA_STACK_ARG
1795 p = abc_getlocal(p, tempvar);
1798 p->next = i;i->prev = p;
1799 list_append(l->targets, target);
1805 c = abc_pushbyte(c, -1);
1806 c = code_append(c, finally_label);
1807 c = code_append(c, finally);
1809 #ifdef NEED_EXTRA_STACK_ARG
1812 c = abc_lookupswitch(c, l);
1813 c = l->def = abc_label(c);
1814 #ifdef NEED_EXTRA_STACK_ARG
1821 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1825 code_t*prev = i->prev;
1826 if(IS_FINALLY_TARGET(i->opcode)) {
1827 if(i->opcode == OPCODE___RETHROW__)
1828 i->opcode = OPCODE_THROW;
1829 code_t*end = code_dup(finally);
1830 code_t*start = code_start(end);
1831 if(prev) prev->next = start;
1838 return code_append(c, finally);
1841 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1847 int num_insertion_points=0;
1849 if(IS_FINALLY_TARGET(i->opcode))
1850 num_insertion_points++;
1857 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1862 int simple_version_cost = (1+num_insertion_points)*code_size;
1863 int lookup_version_cost = 4*num_insertion_points + 5;
1865 if(cantdup || simple_version_cost > lookup_version_cost) {
1866 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
1867 return insert_finally_lookup(c, finally, tempvar);
1869 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
1870 return insert_finally_simple(c, finally, tempvar);
1874 #define PASS1 }} if(as3_pass == 1) {{
1875 #define PASS1END }} if(as3_pass == 2) {{
1876 #define PASS2 }} if(as3_pass == 2) {{
1877 #define PASS12 }} if(as3_pass == 1 || as3_pass == 2) {{
1878 #define PASS12END }} if(as3_pass == 2) {{
1879 #define PASS_ALWAYS }} {{
1885 /* ------------ code blocks / statements ---------------- */
1887 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1889 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1890 PROGRAM_CODE_LIST: PROGRAM_CODE
1891 | PROGRAM_CODE_LIST PROGRAM_CODE
1893 PROGRAM_CODE: PACKAGE_DECLARATION
1894 | INTERFACE_DECLARATION
1896 | FUNCTION_DECLARATION
1899 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
1902 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1903 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1904 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1906 INPACKAGE_CODE: INTERFACE_DECLARATION
1908 | FUNCTION_DECLARATION
1911 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
1914 MAYBECODE: CODE {$$=$1;}
1915 MAYBECODE: {$$=code_new();}
1917 CODE: CODE CODEPIECE {
1918 $$=code_append($1,$2);
1920 CODE: CODEPIECE {$$=$1;}
1922 // code which may appear outside of methods
1923 CODE_STATEMENT: DEFAULT_NAMESPACE
1924 CODE_STATEMENT: IMPORT
1926 CODE_STATEMENT: FOR_IN
1927 CODE_STATEMENT: WHILE
1928 CODE_STATEMENT: DO_WHILE
1929 CODE_STATEMENT: SWITCH
1931 CODE_STATEMENT: WITH
1933 CODE_STATEMENT: VOIDEXPRESSION
1934 CODE_STATEMENT: USE_NAMESPACE
1935 CODE_STATEMENT: NAMESPACE_DECLARATION
1936 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1937 CODE_STATEMENT: '{' '}' {$$=0;}
1939 // code which may appear in methods (includes the above)
1940 CODEPIECE: ';' {$$=0;}
1941 CODEPIECE: CODE_STATEMENT
1942 CODEPIECE: VARIABLE_DECLARATION
1947 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {
1957 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1958 //CODEBLOCK : '{' '}' {$$=0;}
1959 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1960 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1962 /* ------------ package init code ------------------- */
1964 PACKAGE_INITCODE: CODE_STATEMENT {
1965 code_t**cc = &global->init->method->body->code;
1966 *cc = code_append(*cc, $1);
1969 /* ------------ conditional compilation ------------- */
1971 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER {
1974 char*key = concat3($1,"::",$3);
1975 if(!definitions || !dict_contains(definitions, key)) {
1981 /* ------------ variables --------------------------- */
1984 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1990 MAYBEEXPRESSION : '=' E {$$=$2;}
1991 | {$$=mkdummynode();}
1993 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1994 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1996 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1997 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1999 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2002 if(variable_exists($1))
2003 syntaxerror("Variable %s already defined", $1);
2005 new_variable($1, 0, 1, 0);
2010 if(state->method->uses_slots) {
2011 variable_t* v = find_slot(state, $1);
2013 // this variable is stored in a slot
2021 index = new_variable($1, $2, 1, 0);
2024 $$ = slot?abc_getscopeobject(0, 1):0;
2026 typedcode_t v = node_read($3);
2027 if(!is_subtype_of(v.t, $2)) {
2028 syntaxerror("Can't convert %s to %s", v.t->name, $2->name);
2031 if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
2032 $$ = code_append($$, v.c);
2033 $$ = converttype($$, v.t, $2);
2036 $$ = defaultvalue($$, $2);
2039 if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
2040 $$ = code_append($$, v.c);
2041 $$ = abc_coerce_a($$);
2043 // don't do anything
2051 $$ = abc_setslot($$, index);
2053 $$ = abc_setlocal($$, index);
2057 /* ------------ control flow ------------------------- */
2059 MAYBEELSE: %prec below_else {$$ = code_new();}
2060 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2061 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2063 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2066 $$ = code_append($$, $4.c);
2067 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2069 $$ = code_append($$, $6);
2071 myjmp = $$ = abc_jump($$, 0);
2073 myif->branch = $$ = abc_nop($$);
2075 $$ = code_append($$, $7);
2076 myjmp->branch = $$ = abc_nop($$);
2082 FOR_INIT : {$$=code_new();}
2083 FOR_INIT : VARIABLE_DECLARATION
2084 FOR_INIT : VOIDEXPRESSION
2086 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2087 // (I don't see any easy way to revolve this conflict otherwise, as we
2088 // can't touch VAR_READ without upsetting the precedence about "return")
2089 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2090 PASS1 $$=$2;new_variable($2,0,1,0);
2091 PASS2 $$=$2;new_variable($2,$3,1,0);
2093 FOR_IN_INIT : T_IDENTIFIER {
2098 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2099 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2101 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2102 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2104 $$ = code_append($$, $2);
2105 code_t*loopstart = $$ = abc_label($$);
2106 $$ = code_append($$, $4.c);
2107 code_t*myif = $$ = abc_iffalse($$, 0);
2108 $$ = code_append($$, $8);
2109 code_t*cont = $$ = abc_nop($$);
2110 $$ = code_append($$, $6);
2111 $$ = abc_jump($$, loopstart);
2112 code_t*out = $$ = abc_nop($$);
2113 breakjumpsto($$, $1.name, out);
2114 continuejumpsto($$, $1.name, cont);
2121 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2122 variable_t*var = find_variable(state, $2);
2124 syntaxerror("variable %s not known in this scope", $2);
2127 char*tmp1name = concat2($2, "__tmp1__");
2128 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2129 char*tmp2name = concat2($2, "__array__");
2130 int array = new_variable(tmp1name, 0, 0, 0);
2133 $$ = code_append($$, $4.c);
2134 $$ = abc_coerce_a($$);
2135 $$ = abc_setlocal($$, array);
2136 $$ = abc_pushbyte($$, 0);
2137 $$ = abc_setlocal($$, it);
2139 code_t*loopstart = $$ = abc_label($$);
2141 $$ = abc_hasnext2($$, array, it);
2142 code_t*myif = $$ = abc_iffalse($$, 0);
2143 $$ = abc_getlocal($$, array);
2144 $$ = abc_getlocal($$, it);
2146 $$ = abc_nextname($$);
2148 $$ = abc_nextvalue($$);
2149 $$ = converttype($$, 0, var->type);
2150 $$ = abc_setlocal($$, var->index);
2152 $$ = code_append($$, $6);
2153 $$ = abc_jump($$, loopstart);
2155 code_t*out = $$ = abc_nop($$);
2156 breakjumpsto($$, $1.name, out);
2157 continuejumpsto($$, $1.name, loopstart);
2169 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2173 code_t*myjmp = $$ = abc_jump($$, 0);
2174 code_t*loopstart = $$ = abc_label($$);
2175 $$ = code_append($$, $6);
2176 code_t*cont = $$ = abc_nop($$);
2177 myjmp->branch = cont;
2178 $$ = code_append($$, $4.c);
2179 $$ = abc_iftrue($$, loopstart);
2180 code_t*out = $$ = abc_nop($$);
2181 breakjumpsto($$, $1, out);
2182 continuejumpsto($$, $1, cont);
2188 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2190 code_t*loopstart = $$ = abc_label($$);
2191 $$ = code_append($$, $3);
2192 code_t*cont = $$ = abc_nop($$);
2193 $$ = code_append($$, $6.c);
2194 $$ = abc_iftrue($$, loopstart);
2195 code_t*out = $$ = abc_nop($$);
2196 breakjumpsto($$, $1, out);
2197 continuejumpsto($$, $1, cont);
2203 BREAK : "break" %prec prec_none {
2204 $$ = abc___break__(0, "");
2206 BREAK : "break" T_IDENTIFIER {
2207 $$ = abc___break__(0, $2);
2209 CONTINUE : "continue" %prec prec_none {
2210 $$ = abc___continue__(0, "");
2212 CONTINUE : "continue" T_IDENTIFIER {
2213 $$ = abc___continue__(0, $2);
2216 MAYBE_CASE_LIST : {$$=0;}
2217 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2218 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2219 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2220 CASE_LIST: CASE {$$=$1;}
2221 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2223 CASE: "case" E ':' MAYBECODE {
2224 $$ = abc_getlocal(0, state->switch_var);
2225 $$ = code_append($$, node_read($2).c);
2226 code_t*j = $$ = abc_ifne($$, 0);
2227 $$ = code_append($$, $4);
2228 if($$->opcode != OPCODE___BREAK__) {
2229 $$ = abc___fallthrough__($$, "");
2231 code_t*e = $$ = abc_nop($$);
2234 DEFAULT: "default" ':' MAYBECODE {
2237 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2238 $$ = node_read($4).c;
2239 $$ = abc_setlocal($$, state->switch_var);
2240 $$ = code_append($$, $7);
2242 code_t*out = $$ = abc_kill($$, state->switch_var);
2243 breakjumpsto($$, $1, out);
2245 code_t*c = $$,*lastblock=0;
2247 if(c->opcode == OPCODE_IFNE) {
2248 if(!c->next) syntaxerror("internal error in fallthrough handling");
2250 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2252 c->opcode = OPCODE_JUMP;
2253 c->branch = lastblock;
2255 /* fall through end of switch */
2256 c->opcode = OPCODE_NOP;
2266 /* ------------ try / catch /finally ---------------- */
2268 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2269 state->exception_name=$3;
2270 PASS1 new_variable($3, 0, 0, 0);
2271 PASS2 new_variable($3, $4, 0, 0);
2274 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2275 multiname_t name = {QNAME, &name_ns, 0, $3};
2277 NEW(abc_exception_t, e)
2278 e->exc_type = sig2mname($4);
2279 e->var_name = multiname_clone(&name);
2283 int i = find_variable_safe(state, $3)->index;
2284 e->target = c = abc_nop(0);
2285 c = abc_setlocal(c, i);
2286 c = code_append(c, code_dup(state->method->scope_code));
2287 c = code_append(c, $8);
2293 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2298 NEW(abc_exception_t, e)
2299 e->exc_type = 0; //all exceptions
2300 e->var_name = 0; //no name
2303 e->to = code_append(e->to, $4);
2309 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2310 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2311 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2312 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2316 list_append($$.l,$2);
2317 $$.finally = $2->to;$2->to=0;
2320 CATCH_FINALLY_LIST: FINALLY {
2324 list_append($$.l,$1);
2325 $$.finally = $1->to;$1->to=0;
2329 TRY : "try" '{' {PASS12 new_state();
2330 state->method->has_exceptions=1;
2331 state->method->late_binding=1;//for invariant scope_code
2332 } MAYBECODE '}' CATCH_FINALLY_LIST {
2333 code_t*out = abc_nop(0);
2335 code_t*start = abc_nop(0);
2336 $$ = code_append(start, $4);
2337 if(!is_break_or_jump($4)) {
2338 $$ = abc_jump($$, out);
2340 code_t*end = $$ = abc_nop($$);
2344 tmp = new_variable("__finally__", 0, 0, 0);
2346 abc_exception_list_t*l = $6.l;
2349 abc_exception_t*e = l->abc_exception;
2351 $$ = code_append($$, e->target);
2352 $$ = abc_jump($$, out);
2354 parserassert((ptroff_t)$6.finally);
2356 e->target = $$ = abc_nop($$);
2357 $$ = code_append($$, code_dup(state->method->scope_code));
2358 $$ = abc___rethrow__($$);
2366 $$ = code_append($$, out);
2368 $$ = insert_finally($$, $6.finally, tmp);
2370 list_concat(state->method->exceptions, $6.l);
2376 /* ------------ throw ------------------------------- */
2378 THROW : "throw" EXPRESSION {
2382 THROW : "throw" %prec prec_none {
2383 if(!state->exception_name)
2384 syntaxerror("re-throw only possible within a catch block");
2385 variable_t*v = find_variable(state, state->exception_name);
2387 $$=abc_getlocal($$, v->index);
2391 /* ------------ with -------------------------------- */
2393 WITH_HEAD : "with" '(' EXPRESSION ')' {
2395 if(state->method->has_exceptions) {
2396 int v = alloc_local();
2397 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2398 state->method->scope_code = abc_pushwith(state->method->scope_code);
2403 WITH : WITH_HEAD CODEBLOCK {
2404 /* remove getlocal;pushwith from scope code again */
2405 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2408 if(state->method->has_exceptions) {
2410 $$ = abc_setlocal($$, $1.number);
2412 $$ = abc_pushwith($$);
2413 $$ = code_append($$, $2);
2414 $$ = abc_popscope($$);
2418 /* ------------ packages and imports ---------------- */
2420 X_IDENTIFIER: T_IDENTIFIER
2421 | "package" {PASS12 $$="package";}
2422 | "namespace" {PASS12 $$="namespace";}
2423 | T_NAMESPACE {PASS12 $$=$1;}
2425 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2426 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2428 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2429 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2430 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2431 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2434 static void state_has_imports()
2436 state->wildcard_imports = list_clone(state->wildcard_imports);
2437 state->imports = dict_clone(state->imports);
2438 state->has_own_imports = 1;
2440 static void import_toplevel(const char*package)
2442 char* s = strdup(package);
2444 dict_put(state->import_toplevel_packages, s, 0);
2445 char*x = strrchr(s, '.');
2453 IMPORT : "import" PACKAGEANDCLASS {
2455 slotinfo_t*s = registry_find($2->package, $2->name);
2456 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2457 as3_schedule_class($2->package, $2->name);
2461 syntaxerror("Couldn't import class\n");
2462 state_has_imports();
2463 dict_put(state->imports, c->name, c);
2464 import_toplevel(c->package);
2467 IMPORT : "import" PACKAGE '.' '*' {
2469 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2470 as3_schedule_package($2);
2475 state_has_imports();
2476 list_append(state->wildcard_imports, i);
2477 import_toplevel(i->package);
2481 /* ------------ classes and interfaces (header) -------------- */
2483 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2484 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2485 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2486 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2488 $$.flags=$1.flags|$2.flags;
2489 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2490 $$.ns=$1.ns?$1.ns:$2.ns;
2493 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2494 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2495 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2496 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2497 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2498 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2499 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2500 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2501 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2502 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2506 EXTENDS : {PASS12 $$=0;}
2507 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2509 EXTENDS_LIST : {PASS12 $$=list_new();}
2510 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2512 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2513 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2515 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2516 EXTENDS IMPLEMENTS_LIST
2517 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2519 '}' {PASS12 endclass();$$=0;}
2521 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2523 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2524 startclass(&$1,$3,0,$4);}
2525 MAYBE_INTERFACE_BODY
2526 '}' {PASS12 endclass();$$=0;}
2528 /* ------------ classes and interfaces (body) -------------- */
2531 MAYBE_CLASS_BODY : CLASS_BODY
2532 CLASS_BODY : CLASS_BODY_ITEM
2533 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2534 CLASS_BODY_ITEM : ';'
2535 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}' {PASS_ALWAYS as3_pass=$1;}
2536 CLASS_BODY_ITEM : SLOT_DECLARATION
2537 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2539 CLASS_BODY_ITEM : CODE_STATEMENT {
2540 code_t*c = state->cls->static_init->header;
2541 c = code_append(c, $1);
2542 state->cls->static_init->header = c;
2545 MAYBE_INTERFACE_BODY :
2546 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2547 INTERFACE_BODY : IDECLARATION
2548 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2550 IDECLARATION : "var" T_IDENTIFIER {
2551 syntaxerror("variable declarations not allowed in interfaces");
2553 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2555 $1.flags |= FLAG_PUBLIC;
2556 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2557 syntaxerror("invalid method modifiers: interface methods always need to be public");
2559 startfunction(&$1,$3,$4,&$6,$8);
2560 endfunction(&$1,$3,$4,&$6,$8, 0);
2561 list_deep_free($6.list);
2564 /* ------------ classes and interfaces (body, slots ) ------- */
2567 static int slotstate_varconst = 0;
2568 static modifiers_t*slotstate_flags = 0;
2569 static void setslotstate(modifiers_t* flags, int varconst)
2571 slotstate_varconst = varconst;
2572 slotstate_flags = flags;
2575 if(flags->flags&FLAG_STATIC) {
2576 state->method = state->cls->static_init;
2578 state->method = state->cls->init;
2581 // reset to "default" state (all in class code is static by default) */
2582 state->method = state->cls->static_init;
2585 parserassert(state->method);
2588 static trait_t* add_abc_slot(modifiers_t* modifiers, const char*name, multiname_t*m, code_t***c)
2590 int flags = modifiers->flags;
2591 namespace_t ns = modifiers2access(modifiers);
2594 multiname_t mname = {QNAME, &ns, 0, name};
2596 trait_list_t**traits;
2600 ns.name = state->package;
2601 traits = &global->init->traits;
2602 code = &global->init->method->body->code;
2603 } else if(flags&FLAG_STATIC) {
2605 traits = &state->cls->abc->static_traits;
2606 code = &state->cls->static_init->header;
2608 // instance variable
2609 traits = &state->cls->abc->traits;
2610 code = &state->cls->init->header;
2615 *m = *multiname_clone(&mname);
2617 return trait_new_member(traits, 0, multiname_clone(&mname), 0);
2621 VARCONST: "var" | "const"
2623 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {PASS12 setslotstate(&$1,$2);} SLOT_LIST {PASS12 $$=$4;setslotstate(0, 0);}
2625 SLOT_LIST: ONE_SLOT {PASS12 $$=0;}
2626 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {PASS12 $$=0;}
2628 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2631 int flags = slotstate_flags->flags;
2632 namespace_t ns = modifiers2access(slotstate_flags);
2636 varinfo_t* info = 0;
2638 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2640 check_override(i, flags);
2642 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2644 slotinfo_t*i = registry_find(state->package, $1);
2646 syntaxerror("package %s already contains '%s'", state->package, $1);
2648 if(ns.name && ns.name[0]) {
2649 syntaxerror("namespaces not allowed on package-level variables");
2651 info = varinfo_register_global(ns.access, state->package, $1);
2655 info->flags = flags;
2657 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, info);
2661 varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
2665 trait_t*t = add_abc_slot(slotstate_flags, $1, &mname, &code);
2669 t->type_name = multiname_clone(&m);
2671 info->slot = t->slot_id;
2673 /* workaround for "VerifyError: Error #1053: Illegal override of ::test2 in C1"
2674 FIXME: is there a way to use slots and still don't have conflicting overrides?
2676 info->slot = t->slot_id = 0;
2678 constant_t cval = $3->type->eval($3);
2679 if(cval.type!=CONSTANT_UNKNOWN) {
2680 /* compile time constant */
2681 t->value = malloc(sizeof(constant_t));
2682 memcpy(t->value, &cval, sizeof(constant_t));
2683 info->value = constant_clone(t->value);
2685 typedcode_t v = node_read($3);
2686 /* initalization code (if needed) */
2688 if(v.c && !is_pushundefined(v.c)) {
2689 c = abc_getlocal_0(c);
2690 c = code_append(c, v.c);
2691 c = converttype(c, v.t, $2);
2693 c = abc_initproperty2(c, &mname);
2695 c = abc_setslot(c, t->slot_id);
2698 *code = code_append(*code, c);
2701 if(slotstate_varconst==KW_CONST) {
2702 t->kind= TRAIT_CONST;
2703 info->flags |= FLAG_CONST;
2710 /* ------------ constants -------------------------------------- */
2712 MAYBECONSTANT: {$$=0;}
2713 MAYBECONSTANT: '=' E {
2714 $$ = malloc(sizeof(constant_t));
2715 *$$ = node_eval($2);
2716 if($$->type == CONSTANT_UNKNOWN) {
2717 syntaxerror("can't evaluate default parameter value (needs to be a compile-time constant)");
2721 //CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2722 CONSTANT : T_INT {$$ = constant_new_int($1);}
2724 $$ = constant_new_uint($1);
2726 CONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2727 CONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2728 CONSTANT : "true" {$$ = constant_new_true($1);}
2729 CONSTANT : "false" {$$ = constant_new_false($1);}
2730 CONSTANT : "null" {$$ = constant_new_null($1);}
2731 CONSTANT : "undefined" {$$ = constant_new_undefined($1);}
2732 CONSTANT : KW_NAN {$$ = constant_new_float(__builtin_nan(""));}
2735 CONSTANT : T_IDENTIFIER {
2736 if(!strcmp($1, "NaN")) {
2737 $$ = constant_new_float(__builtin_nan(""));
2739 as3_warning("Couldn't evaluate constant value of %s", $1);
2740 $$ = constant_new_null($1);
2744 /* ---------------------------xml ------------------------------ */
2747 static int xml_level = 0;
2752 OPEN : '<' {PASS_ALWAYS if(!xml_level++) tokenizer_begin_xml();}
2753 CLOSE : '>' {PASS_ALWAYS tokenizer_begin_xmltext();}
2754 CLOSE2 : {PASS_ALWAYS if(!--xml_level) tokenizer_end_xml(); else tokenizer_begin_xmltext();}
2756 XMLEXPR1 : '{' E {PASS_ALWAYS tokenizer_begin_xmltext();} '}' {
2758 as3_warning("xml string substitution not yet supported");
2760 XMLEXPR2 : '{' E {PASS_ALWAYS tokenizer_begin_xml();} '}' {
2762 as3_warning("xml string substitution not yet supported");
2765 XMLTEXT : XMLTEXT XMLEXPR1 {
2766 $$ = concat2($1, "{...}");
2768 XMLTEXT : XMLTEXT T_STRING {$$=concat2($1, string_cstr(&$2));}
2769 XMLTEXT : XMLTEXT '>' {$$=concat2($1, ">");}
2771 XML2 : XMLNODE XMLTEXT {$$=concat2($1,$2);}
2772 XML2 : XML2 XMLNODE XMLTEXT {$$=concat3($1,$2,$3);free($1);free($2);free($3);}
2774 XML_ID_OR_EXPR: T_IDENTIFIER {$$=$1;}
2775 XML_ID_OR_EXPR: XMLEXPR2 {$$=$1;}
2777 XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
2778 $$ = allocprintf("<%s%s>%s</%s>", $2, $3, $5, $8);
2779 free($2);free($3);free($5);free($8);
2781 XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' {
2782 $$ = allocprintf("<%s%s/>", $2, $3);
2784 XMLNODE : OPEN XML_ID_OR_EXPR MAYBE_XMLATTRIBUTES CLOSE XMLTEXT XML2 '<' '/' XML_ID_OR_EXPR CLOSE2 '>' {
2785 $$ = allocprintf("<%s%s>%s%s</%s>", $2, $3, $5, $6, $9);
2786 free($2);free($3);free($5);free($6);free($9);
2789 MAYBE_XMLATTRIBUTES: {$$=strdup("");}
2790 MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {$$=concat2(" ",$1);}
2791 XMLATTRIBUTES: XMLATTRIBUTE {$$=$1;}
2792 XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE {$$=concat3($1," ",$2);free($1);free($2);}
2794 XMLATTRIBUTE: XMLEXPR2 {
2795 $$ = strdup("{...}");
2797 XMLATTRIBUTE: XMLEXPR2 '=' T_STRING {
2798 char* str = string_cstr(&$3);
2799 $$ = concat2("{...}=",str);
2801 XMLATTRIBUTE: XMLEXPR2 '=' XMLEXPR2 {
2802 $$ = strdup("{...}={...}");
2804 XMLATTRIBUTE: T_IDENTIFIER '=' XMLEXPR2 {
2805 $$ = concat2($1,"={...}");
2807 XMLATTRIBUTE: T_IDENTIFIER '=' T_STRING {
2808 char* str = string_cstr(&$3);
2809 $$=allocprintf("%s=%s", $1,str);
2811 free($1);free((char*)$3.str);
2814 /* ------------ classes and interfaces (body, functions) ------- */
2816 // non-vararg version
2819 memset(&$$,0,sizeof($$));
2821 MAYBE_PARAM_LIST: PARAM_LIST {
2827 MAYBE_PARAM_LIST: "..." PARAM {
2829 memset(&$$,0,sizeof($$));
2831 list_append($$.list, $2);
2833 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2837 list_append($$.list, $4);
2841 PARAM_LIST: PARAM_LIST ',' PARAM {
2844 list_append($$.list, $3);
2848 memset(&$$,0,sizeof($$));
2849 list_append($$.list, $1);
2852 PARAM: T_IDENTIFIER ':' TYPE MAYBECONSTANT {
2854 $$ = rfx_calloc(sizeof(param_t));
2860 PARAM: T_IDENTIFIER MAYBECONSTANT {
2862 $$ = rfx_calloc(sizeof(param_t));
2864 $$->type = TYPE_ANY;
2872 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2873 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2876 endfunction(&$1,$3,$4,&$6,0,0);
2878 if(!state->method->info) syntaxerror("internal error");
2880 code_t*c = method_header(state->method);
2881 c = wrap_function(c, 0, $11);
2883 endfunction(&$1,$3,$4,&$6,$8,c);
2885 list_deep_free($6.list);
2889 MAYBE_IDENTIFIER: T_IDENTIFIER
2890 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2891 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2892 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2895 endfunction(0,0,$2,&$4,0,0);
2897 methodinfo_t*f = state->method->info;
2898 if(!f || !f->kind) syntaxerror("internal error");
2900 code_t*c = method_header(state->method);
2901 c = wrap_function(c, 0, $9);
2903 int index = state->method->var_index;
2904 endfunction(0,0,$2,&$4,$6,c);
2906 $$.c = abc_getlocal(0, index);
2907 $$.t = TYPE_FUNCTION(f);
2909 PASS12 list_deep_free($4.list);
2913 /* ------------- package + class ids --------------- */
2915 CLASS: X_IDENTIFIER {
2916 PASS1 NEW(unresolvedinfo_t,c);
2917 memset(c, 0, sizeof(*c));
2918 c->kind = INFOTYPE_UNRESOLVED;
2920 c->package = get_package_from_name($1);
2922 c->nsset = get_current_imports();
2923 /* make the compiler look for this class in the current directory,
2925 as3_schedule_class_noerror(state->package, $1);
2927 $$ = (classinfo_t*)c;
2929 slotinfo_t*s = find_class($1);
2930 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2931 $$ = (classinfo_t*)s;
2934 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
2935 PASS1 NEW(unresolvedinfo_t,c);
2936 memset(c, 0, sizeof(*c));
2937 c->kind = INFOTYPE_UNRESOLVED;
2940 $$ = (classinfo_t*)c;
2942 slotinfo_t*s = registry_find($1, $3);
2943 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2945 $$ = (classinfo_t*)s;
2948 CLASS_SPEC: PACKAGEANDCLASS
2951 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2952 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2954 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2955 | '*' {PASS12 $$=TYPE_ANY;}
2956 | "void" {PASS12 $$=TYPE_VOID;}
2958 | "String" {$$=registry_getstringclass();}
2959 | "int" {$$=registry_getintclass();}
2960 | "uint" {$$=registry_getuintclass();}
2961 | "Boolean" {$$=registry_getbooleanclass();}
2962 | "Number" {$$=registry_getnumberclass();}
2965 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2966 MAYBETYPE: {PASS12 $$=0;}
2968 /* ----------function calls, delete, constructor calls ------ */
2970 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
2971 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2973 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
2974 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2975 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2977 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
2981 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2982 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2983 $$.number= $1.number+1;
2984 $$.cc = code_append($1.cc, $2.c);
2988 NEW : "new" E XX MAYBE_PARAM_VALUES {
2989 typedcode_t v = node_read($2);
2991 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2993 code_t*paramcode = $4.cc;
2994 if($$.c->opcode == OPCODE_GETPROPERTY) {
2995 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2996 $$.c = code_cutlast($$.c);
2997 $$.c = code_append($$.c, paramcode);
2998 $$.c = abc_constructprop2($$.c, name, $4.number);
2999 multiname_destroy(name);
3000 } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
3002 classinfo_t*c = v.t->data;
3004 $$.c = abc_findpropstrict2(0, &m);
3005 $$.c = code_append($$.c, paramcode);
3006 $$.c = abc_constructprop2($$.c, &m, $4.number);
3007 /*} else if($$.c->opcode == OPCODE_GETSLOT) {
3008 int slot = (int)(ptroff_t)$$.c->data[0];
3009 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3010 multiname_t*name = t->name;
3011 $$.c = code_cutlast($$.c);
3012 $$.c = code_append($$.c, paramcode);
3013 $$.c = abc_constructprop2($$.c, name, $4.number);*/
3015 $$.c = code_append($$.c, paramcode);
3016 $$.c = abc_construct($$.c, $4.number);
3020 if(TYPE_IS_CLASS(v.t) && v.t->data) {
3023 $$.c = abc_coerce_a($$.c);
3028 /* TODO: use abc_call (for calling local variables),
3029 abc_callstatic (for calling own methods)
3032 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3034 typedcode_t v = node_read($1);
3036 if($$.c->opcode == OPCODE_COERCE_A) {
3037 $$.c = code_cutlast($$.c);
3039 code_t*paramcode = $3.cc;
3042 if($$.c->opcode == OPCODE_GETPROPERTY) {
3043 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3044 $$.c = code_cutlast($$.c);
3045 $$.c = code_append($$.c, paramcode);
3046 $$.c = abc_callproperty2($$.c, name, $3.number);
3047 multiname_destroy(name);
3048 /* } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3049 int slot = (int)(ptroff_t)$$.c->data[0];
3050 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3051 if(t->kind!=TRAIT_METHOD) {
3052 //ok: flash allows to assign closures to members.
3054 multiname_t*name = t->name;
3055 $$.c = code_cutlast($$.c);
3056 $$.c = code_append($$.c, paramcode);
3057 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3058 $$.c = abc_callproperty2($$.c, name, $3.number);*/
3059 } else if($$.c->opcode == OPCODE_GETSUPER) {
3060 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3061 $$.c = code_cutlast($$.c);
3062 $$.c = code_append($$.c, paramcode);
3063 $$.c = abc_callsuper2($$.c, name, $3.number);
3064 multiname_destroy(name);
3066 $$.c = abc_getglobalscope($$.c);
3067 $$.c = code_append($$.c, paramcode);
3068 $$.c = abc_call($$.c, $3.number);
3071 if(TYPE_IS_FUNCTION(v.t) && v.t->data) {
3072 $$.t = ((methodinfo_t*)(v.t->data))->return_type;
3073 } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
3074 // calling a class is like a typecast
3075 $$.t = (classinfo_t*)v.t->data;
3077 $$.c = abc_coerce_a($$.c);
3082 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3083 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3084 if(!state->method) syntaxerror("super() not allowed outside of a function");
3085 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3088 $$.c = abc_getlocal_0($$.c);
3090 $$.c = code_append($$.c, $3.cc);
3092 this is dependent on the control path, check this somewhere else
3093 if(state->method->has_super)
3094 syntaxerror("constructor may call super() only once");
3096 state->method->has_super = 1;
3098 $$.c = abc_constructsuper($$.c, $3.number);
3099 $$.c = abc_pushundefined($$.c);
3103 DELETE: "delete" E {
3104 typedcode_t v = node_read($2);
3106 if($$.c->opcode == OPCODE_COERCE_A) {
3107 $$.c = code_cutlast($$.c);
3109 multiname_t*name = 0;
3110 if($$.c->opcode == OPCODE_GETPROPERTY) {
3111 $$.c->opcode = OPCODE_DELETEPROPERTY;
3112 } else if($$.c->opcode == OPCODE_GETSLOT) {
3113 int slot = (int)(ptroff_t)$$.c->data[0];
3114 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3115 $$.c = code_cutlast($$.c);
3116 $$.c = abc_deleteproperty2($$.c, name);
3118 $$.c = abc_getlocal_0($$.c);
3119 MULTINAME_LATE(m, v.t?v.t->access:ACCESS_PACKAGE, "");
3120 $$.c = abc_deleteproperty2($$.c, &m);
3122 $$.t = TYPE_BOOLEAN;
3125 RETURN: "return" %prec prec_none {
3126 $$ = abc_returnvoid(0);
3128 RETURN: "return" EXPRESSION {
3130 $$ = abc_returnvalue($$);
3133 // ----------------------- expression types -------------------------------------
3135 NONCOMMAEXPRESSION : E %prec below_lt {
3138 EXPRESSION : COMMA_EXPRESSION {
3141 COMMA_EXPRESSION : E %prec below_lt {
3142 $$ = mkmultinode(&node_comma, $1);
3144 COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_lt {
3145 $$ = multinode_extend($1, $3);
3147 VOIDEXPRESSION : E %prec below_minus {
3150 VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_lt {
3152 $$ = code_append($$, node_exec($3));
3155 MAYBE_DICT_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3156 MAYBE_DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST {$$=$1;}
3158 DICTLH: T_IDENTIFIER {$$=abc_pushstring(0,$1);}
3159 DICTLH: T_STRING {$$=abc_pushstring2(0,&$1);}
3160 DICTLH: T_INT {syntaxerror("dictionary keys must be strings");}
3161 DICTLH: T_UINT {syntaxerror("dictionary keys must be strings");}
3162 DICTLH: T_FLOAT {syntaxerror("dictionary keys must be strings");}
3164 DICT_EXPRPAIR_LIST : DICTLH ':' NONCOMMAEXPRESSION {
3166 $$.cc = code_append($$.cc, $1);
3167 $$.cc = code_append($$.cc, $3.c);
3170 DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST ',' DICTLH ':' NONCOMMAEXPRESSION {
3172 $$.number = $1.number+2;
3173 $$.cc = code_append($$.cc, $3);
3174 $$.cc = code_append($$.cc, $5.c);
3177 // ----------------------- expression evaluation -------------------------------------
3179 E : INNERFUNCTION %prec prec_none {$$ = mkcodenode($1);}
3180 E : MEMBER %prec '.' {$$ = mkcodenode($1);}
3181 E : NEW {$$ = mkcodenode($1);}
3182 E : DELETE {$$ = mkcodenode($1);}
3183 E : FUNCTIONCALL {$$ = mkcodenode($1);}
3184 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3187 $$ = mkconstnode($1);
3193 multiname_t m = {QNAME, &stdns, 0, "XML"};
3194 v.c = abc_getlex2(v.c, &m);
3195 v.c = abc_pushstring(v.c, $1);
3196 v.c = abc_construct(v.c, 1);
3205 multiname_t m = {QNAME, &stdns, 0, "RegExp"};
3207 v.c = abc_getlex2(v.c, &m);
3208 v.c = abc_pushstring(v.c, $1.pattern);
3209 v.c = abc_construct(v.c, 1);
3211 v.c = abc_getlex2(v.c, &m);
3212 v.c = abc_pushstring(v.c, $1.pattern);
3213 v.c = abc_pushstring(v.c, $1.options);
3214 v.c = abc_construct(v.c, 2);
3222 state->method->need_arguments = 1;
3225 v.c = abc_getlocal(0, state->method->need_arguments);
3231 E : '[' MAYBE_EXPRESSION_LIST ']' {
3234 v.c = code_append(v.c, $2.cc);
3235 v.c = abc_newarray(v.c, $2.number);
3236 v.t = registry_getarrayclass();
3241 E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' {
3244 v.c = code_append(v.c, $2.cc);
3245 v.c = abc_newobject(v.c, $2.number/2);
3246 v.t = registry_getobjectclass();
3250 E : E '<' E {$$ = mknode2(&node_lt,$1,$3);}
3251 E : E '>' E {$$ = mknode2(&node_gt,$1,$3);}
3252 E : E "<=" E {$$ = mknode2(&node_le,$1,$3);}
3253 E : E ">=" E {$$ = mknode2(&node_ge,$1,$3);}
3254 E : E "==" E {$$ = mknode2(&node_eqeq,$1,$3);}
3255 E : E "===" E {$$ = mknode2(&node_eqeqeq,$1,$3);}
3256 E : E "!==" E {$$ = mknode2(&node_noteqeq,$1,$3);}
3257 E : E "!=" E {$$ = mknode2(&node_noteq,$1,$3);}
3258 E : E "||" E {$$ = mknode2(&node_oror,$1,$3);}
3259 E : E "&&" E {$$ = mknode2(&node_andand,$1,$3);}
3260 E : '!' E {$$ = mknode1(&node_not, $2);}
3261 E : '~' E {$$ = mknode1(&node_bitnot, $2);}
3262 E : E '&' E {$$ = mknode2(&node_bitand, $1, $3);}
3263 E : E '^' E {$$ = mknode2(&node_bitxor, $1, $3);}
3264 E : E '|' E {$$ = mknode2(&node_bitor, $1, $3);}
3265 E : E ">>" E {$$ = mknode2(&node_shr, $1, $3);}
3266 E : E ">>>" E {$$ = mknode2(&node_ushr, $1, $3);}
3267 E : E "<<" E {$$ = mknode2(&node_shl, $1, $3);}
3268 E : E '/' E {$$ = mknode2(&node_div, $1, $3);}
3269 E : E '%' E {$$ = mknode2(&node_mod, $1, $3);}
3270 E : E '+' E {$$ = mknode2(&node_plus, $1, $3);}
3271 E : E '-' E {$$ = mknode2(&node_minus, $1, $3);}
3272 E : E '*' E {$$ = mknode2(&node_multiply, $1, $3);}
3273 E : E "in" E {$$ = mknode2(&node_in, $1, $3);}
3274 E : E "as" E {$$ = mknode2(&node_as, $1, $3);}
3275 E : E "instanceof" E {$$ = mknode2(&node_instanceof, $1, $3);}
3276 E : E "is" E {$$ = mknode2(&node_is, $1, $3);}
3277 E : "typeof" '(' E ')' {$$ = mknode1(&node_typeof, $3);}
3278 E : "void" E {$$ = mknode1(&node_void, $2);}
3279 E : "void" { $$ = mkconstnode(constant_new_undefined());}
3280 E : '(' COMMA_EXPRESSION ')' { $$=$2;}
3281 E : '-' E {$$ = mknode1(&node_neg, $2);}
3282 E : E '[' E ']' {$$ = mknode2(&node_arraylookup, $1,$3);}
3283 E : E "*=" E {$$ = mknode2(&node_muleq, $1, $3);}
3284 E : E "%=" E {$$ = mknode2(&node_modeq, $1, $3);}
3285 E : E "<<=" E {$$ = mknode2(&node_shleq, $1, $3);}
3286 E : E ">>=" E {$$ = mknode2(&node_shreq, $1, $3);}
3287 E : E ">>>=" E {$$ = mknode2(&node_ushreq, $1, $3);}
3288 E : E "/=" E { $$ = mknode2(&node_diveq, $1, $3);}
3289 E : E "|=" E { $$ = mknode2(&node_bitoreq, $1, $3);}
3290 E : E "^=" E { $$ = mknode2(&node_bitxoreq, $1, $3);}
3291 E : E "&=" E { $$ = mknode2(&node_bitandeq, $1, $3);}
3292 E : E "+=" E { $$ = mknode2(&node_pluseq, $1, $3);}
3293 E : E "-=" E { $$ = mknode2(&node_minuseq, $1, $3);}
3294 E : E '=' E { $$ = mknode2(&node_assign, $1, $3);}
3295 E : E '?' E ':' E %prec below_assignment { $$ = mknode3(&node_tenary, $1, $3, $5);}
3297 E : E "++" { $$ = mknode1(&node_rplusplus, $1);}
3298 E : E "--" { $$ = mknode1(&node_rminusminus, $1);}
3299 E : "++" %prec plusplus_prefix E {$$ = mknode1(&node_lplusplus, $2); }
3300 E : "--" %prec minusminus_prefix E {$$ = mknode1(&node_lminusminus, $2); }
3302 E : "super" '.' T_IDENTIFIER
3303 { if(!state->cls->info)
3304 syntaxerror("super keyword not allowed outside a class");
3305 classinfo_t*t = state->cls->info->superclass;
3306 if(!t) t = TYPE_OBJECT;
3307 memberinfo_t*f = findmember_nsset(t, $3, 1);
3308 MEMBER_MULTINAME(m, f, $3);
3311 v.c = abc_getlocal_0(v.c);
3312 v.c = abc_getsuper2(v.c, &m);
3313 v.t = slotinfo_gettype((slotinfo_t*)f);
3317 E : '@' T_IDENTIFIER {
3319 multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $2};
3320 v.c = abc_getlex2(0, &m);
3325 E : E '.' '(' {PASS12 new_state();state->xmlfilter=1;} E ')' {
3328 typedcode_t v = node_read($1);
3329 typedcode_t w = node_read($5);
3331 int index = alloc_local();
3332 int result = alloc_local();
3333 int tmp = alloc_local();
3334 int xml = alloc_local();
3336 c = code_append(c, v.c);
3337 c = abc_checkfilter(c);
3338 c = abc_coerce_a(c); //hasnext2 converts to *
3339 c = abc_setlocal(c, xml);
3340 multiname_t m = {QNAME, &stdns, 0, "XMLList"};
3341 c = abc_getlex2(c, &m);
3342 c = abc_construct(c, 0);
3343 c = abc_setlocal(c, result);
3344 c = abc_pushbyte(c, 0);
3345 c = abc_setlocal(c, index);
3346 code_t*jmp = c = abc_jump(c, 0);
3347 code_t*loop = c = abc_label(c);
3348 c = abc_getlocal(c, xml);
3349 c = abc_getlocal(c, index);
3350 c = abc_nextvalue(c);
3352 c = abc_setlocal(c, tmp);
3353 c = abc_pushwith(c);
3354 c = code_append(c, w.c);
3355 c = abc_popscope(c);
3356 code_t*b = c = abc_iffalse(c, 0);
3357 c = abc_getlocal(c, result);
3358 c = abc_getlocal(c, index);
3359 c = abc_getlocal(c, tmp);
3360 multiname_t m2 = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
3361 c = abc_setproperty2(c, &m2);
3362 c = b->branch = jmp->branch = abc_nop(c);
3363 c = abc_kill(c, tmp);
3364 c = abc_hasnext2(c, xml, index);
3365 c = abc_iftrue(c, loop);
3366 c = abc_getlocal(c, result);
3367 c = abc_kill(c, xml);
3368 c = abc_kill(c, result);
3369 c = abc_kill(c, index);
3379 ID_OR_NS : T_IDENTIFIER {$$=$1;}
3380 ID_OR_NS : '*' {$$="*";}
3381 ID_OR_NS : T_NAMESPACE {$$=(char*)$1;}
3382 SUBNODE: X_IDENTIFIER
3386 MAYBE_NS: T_IDENTIFIER "::" {$$=$1;}
3387 | T_NAMESPACE "::" {$$=(char*)$1;}
3388 | '*' "::" {$$="*";}
3391 E : E '.' ID_OR_NS "::" SUBNODE {
3392 typedcode_t v = node_read($1);
3393 typedcode_t w = node_read(resolve_identifier($3));
3394 v.c = code_append(v.c, w.c);
3395 if(!TYPE_IS_NAMESPACE(w.t)) {
3396 as3_softwarning("%s might not be a namespace", $3);
3398 v.c = converttype(v.c, w.t, TYPE_NAMESPACE);
3399 multiname_t m = {RTQNAME, 0, 0, $5};
3400 v.c = abc_getproperty2(v.c, &m);
3401 if(TYPE_IS_XML(v.t)) {
3404 v.c = abc_coerce_a(v.c);
3409 E : E ".." SUBNODE {
3410 typedcode_t v = node_read($1);
3411 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3412 v.c = abc_getdescendants2(v.c, &m);
3416 E : E '.' '[' E ']' {
3417 typedcode_t v = node_read($1);
3418 typedcode_t w = node_read($4);
3419 multiname_t m = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
3420 v.c = code_append(v.c, w.c);
3421 v.c = converttype(w.c, w.t, TYPE_STRING);
3422 v.c = abc_getproperty2(v.c, &m);
3427 E : E '.' '@' SUBNODE {
3428 typedcode_t v = node_read($1);
3429 multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
3430 v.c = abc_getproperty2(v.c, &m);
3434 E : E ".." '@' SUBNODE {
3435 typedcode_t v = node_read($1);
3436 multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
3437 v.c = abc_getdescendants2(v.c, &m);
3441 E : E '.' '@' '[' E ']' {
3442 typedcode_t v = node_read($1);
3443 typedcode_t w = node_read($5);
3444 multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
3445 v.c = code_append(v.c, w.c);
3446 v.c = converttype(w.c, w.t, TYPE_STRING);
3447 v.c = abc_getproperty2(v.c, &m);
3451 E : E ".." '@' '[' E ']' {
3452 typedcode_t v = node_read($1);
3453 typedcode_t w = node_read($5);
3454 multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
3455 v.c = code_append(v.c, w.c);
3456 v.c = converttype(w.c, w.t, TYPE_STRING);
3457 v.c = abc_getdescendants2(v.c, &m);
3462 MEMBER : E '.' SUBNODE {
3463 typedcode_t v1 = node_read($1);
3465 classinfo_t*t = v1.t;
3467 if(TYPE_IS_CLASS(t) && t->data) {
3471 if(TYPE_IS_XML(t)) {
3472 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3473 $$.c = abc_getproperty2($$.c, &m);
3474 $$.c = abc_coerce_a($$.c);
3475 $$.t = TYPE_XMLLIST;
3477 if(t->subtype==INFOTYPE_UNRESOLVED) {
3478 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3480 memberinfo_t*f = findmember_nsset(t, $3, 1);
3482 if(f && !is_static != !(f->flags&FLAG_STATIC))
3484 if(f && f->slot && !noslot) {
3485 $$.c = abc_getslot($$.c, f->slot);
3488 as3_softwarning("Access of undefined property '%s' in %s", $3, t->name);
3490 MEMBER_MULTINAME(m, f, $3);
3491 $$.c = abc_getproperty2($$.c, &m);
3493 /* determine type */
3494 $$.t = slotinfo_gettype((slotinfo_t*)f);
3496 $$.c = abc_coerce_a($$.c);
3498 } else if(v1.c && v1.c->opcode == OPCODE___PUSHPACKAGE__) {
3499 string_t*package = v1.c->data[0];
3500 char*package2 = concat3(package->str, ".", $3);
3502 slotinfo_t*a = registry_find(package->str, $3);
3505 } else if(dict_contains(state->import_toplevel_packages, package2) ||
3506 registry_ispackage(package2)) {
3508 $$.c->data[0] = string_new4(package2);
3511 syntaxerror("couldn't resolve %s", package2);
3514 /* when resolving a property on an unknown type, we do know the
3515 name of the property (and don't seem to need the package), but
3516 we need to make avm2 try out all access modes */
3517 as3_softwarning("Resolving %s on unknown type", $3);
3518 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3519 $$.c = abc_getproperty2($$.c, &m);
3520 $$.c = abc_coerce_a($$.c);
3526 node_t* resolve_identifier(char*name)
3536 /* look at variables */
3537 if((v = find_variable(state, name))) {
3538 // name is a local variable
3539 o.c = abc_getlocal(o.c, v->index);
3541 return mkcodenode(o);
3543 if((v = find_slot(state, name))) {
3544 o.c = abc_getscopeobject(o.c, 1);
3545 o.c = abc_getslot(o.c, v->index);
3547 return mkcodenode(o);
3550 int i_am_static = state->method->is_static;
3552 /* look at current class' members */
3553 if(!state->method->inner &&
3554 !state->xmlfilter &&
3556 (f = findmember_nsset(state->cls->info, name, 1)))
3558 // name is a member or attribute in this class
3559 int var_is_static = (f->flags&FLAG_STATIC);
3561 if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
3562 /* if the variable is a constant (and we know what is evaluates to), we
3563 can just use the value itself */
3564 varinfo_t*v = (varinfo_t*)f;
3566 return mkconstnode(v->value);
3570 if(var_is_static >= i_am_static) {
3571 if(f->kind == INFOTYPE_METHOD) {
3572 o.t = TYPE_FUNCTION(f);
3577 if(var_is_static && !i_am_static) {
3578 /* access to a static member from a non-static location.
3579 do this via findpropstrict:
3580 there doesn't seem to be any non-lookup way to access
3581 static properties of a class */
3582 state->method->late_binding = 1;
3584 namespace_t ns = {f->access, f->package};
3585 multiname_t m = {QNAME, &ns, 0, name};
3586 o.c = abc_findpropstrict2(o.c, &m);
3587 o.c = abc_getproperty2(o.c, &m);
3588 return mkcodenode(o);
3589 } else if(f->slot>0) {
3590 o.c = abc_getlocal_0(o.c);
3591 o.c = abc_getslot(o.c, f->slot);
3592 return mkcodenode(o);
3594 MEMBER_MULTINAME(m, f, name);
3595 o.c = abc_getlocal_0(o.c);
3596 o.c = abc_getproperty2(o.c, &m);
3597 return mkcodenode(o);
3602 /* look at actual classes, in the current package and imported */
3603 if(!state->xmlfilter && (a = find_class(name))) {
3604 if(state->cls && state->cls->info == (classinfo_t*)a && i_am_static) {
3605 o.c = abc_getlocal_0(0);
3606 o.t = TYPE_CLASS((classinfo_t*)a);
3610 return mkcodenode(o);
3613 /* look through package prefixes */
3614 if(!state->xmlfilter &&
3615 (dict_contains(state->import_toplevel_packages, name) ||
3616 registry_ispackage(name))) {
3617 o.c = abc___pushpackage__(o.c, name);
3619 return mkcodenode(o); //?
3622 /* unknown object, let the avm2 resolve it */
3624 if(!state->method->inner && !state->xmlfilter) {
3625 /* we really should make inner functions aware of the class context */
3626 as3_warning("Couldn't resolve '%s', doing late binding", name);
3628 state->method->late_binding = 1;
3630 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, name};
3633 o.c = abc_findpropstrict2(o.c, &m);
3634 o.c = abc_getproperty2(o.c, &m);
3635 return mkcodenode(o);
3640 VAR_READ : T_IDENTIFIER {
3642 /* Queue unresolved identifiers for checking against the parent
3643 function's variables.
3644 We consider everything which is not a local variable "unresolved".
3645 This encompasses class names, members of the surrounding class
3646 etc. which is *correct* because local variables of the parent function
3650 if(!find_variable(state, $1)) {
3651 if(state->method->inner) {
3652 unknown_variable($1);
3654 /* let the compiler know that it might want to check the current directory/package
3655 for this identifier- maybe there's a file $1.as defining $1. */
3656 as3_schedule_class_noerror(state->package, $1);
3662 $$ = resolve_identifier($1);
3665 // ----------------- namespaces -------------------------------------------------
3668 void add_active_url(const char*url)
3672 list_append(state->active_namespace_urls, n);
3676 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3678 NEW(namespace_decl_t,n);
3683 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3685 NEW(namespace_decl_t,n);
3690 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3692 NEW(namespace_decl_t,n);
3697 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3699 trie_put(active_namespaces, $2->name, (void*)$2->url);
3701 namespace_t access = modifiers2access(&$1);
3702 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3703 var->type = TYPE_NAMESPACE;
3705 ns.access = ACCESS_NAMESPACE;
3707 var->value = constant_new_namespace(&ns);
3710 MULTINAME(m, TYPE_NAMESPACE);
3711 trait_t*t = add_abc_slot(&$1, $2->name, 0, 0);
3712 t->value = var->value;
3713 t->type_name = multiname_clone(&m);
3719 DEFAULT_NAMESPACE : "default xml" "namespace" '=' E
3721 as3_warning("default xml namespaces not supported yet");
3724 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3726 const char*url = $3->name;
3728 varinfo_t*s = (varinfo_t*)$3;
3729 if(s->kind == INFOTYPE_UNRESOLVED) {
3730 s = (varinfo_t*)registry_resolve((slotinfo_t*)s);
3732 syntaxerror("Couldn't resolve namespace %s", $3->name);
3735 if(!s || s->kind != INFOTYPE_VAR)
3736 syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
3737 if(!s->value || !NS_TYPE(s->value->type))
3738 syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
3739 url = s->value->ns->name;
3741 trie_put(active_namespaces, $3->name, (void*)url);
3742 add_active_url(url);