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_PUBLIC "public"
98 %token<token> KW_PRIVATE "private"
99 %token<token> KW_USE "use"
100 %token<token> KW_INTERNAL "internal"
101 %token<token> KW_NEW "new"
102 %token<token> KW_NATIVE "native"
103 %token<token> KW_FUNCTION "function"
104 %token<token> KW_FINALLY "finally"
105 %token<token> KW_UNDEFINED "undefined"
106 %token<token> KW_NAN "NaN"
107 %token<token> KW_CONTINUE "continue"
108 %token<token> KW_CLASS "class"
109 %token<token> KW_CONST "const"
110 %token<token> KW_CATCH "catch"
111 %token<token> KW_CASE "case"
112 %token<token> KW_SET "set"
113 %token<token> KW_VOID "void"
114 %token<token> KW_THROW "throw"
115 %token<token> KW_STATIC "static"
116 %token<token> KW_WITH "with"
117 %token<token> KW_INSTANCEOF "instanceof"
118 %token<token> KW_IMPORT "import"
119 %token<token> KW_RETURN "return"
120 %token<token> KW_TYPEOF "typeof"
121 %token<token> KW_INTERFACE "interface"
122 %token<token> KW_NULL "null"
123 %token<token> KW_VAR "var"
124 %token<token> KW_DYNAMIC "dynamic"
125 %token<token> KW_OVERRIDE "override"
126 %token<token> KW_FINAL "final"
127 %token<token> KW_EACH "each"
128 %token<token> KW_GET "get"
129 %token<token> KW_TRY "try"
130 %token<token> KW_SUPER "super"
131 %token<token> KW_EXTENDS "extends"
132 %token<token> KW_FALSE "false"
133 %token<token> KW_TRUE "true"
134 %token<token> KW_BOOLEAN "Boolean"
135 %token<token> KW_UINT "uint"
136 %token<token> KW_INT "int"
137 %token<token> KW_NUMBER "Number"
138 %token<token> KW_STRING "String"
139 %token<token> KW_DEFAULT "default"
140 %token<token> KW_DELETE "delete"
141 %token<token> KW_IF "if"
142 %token<token> KW_ELSE "else"
143 %token<token> KW_BREAK "break"
144 %token<token> KW_IS "is"
145 %token<token> KW_IN "in"
146 %token<token> KW_AS "as"
148 %token<token> T_DICTSTART "{ (dictionary)"
149 %token<token> T_EQEQ "=="
150 %token<token> T_EQEQEQ "==="
151 %token<token> T_NE "!="
152 %token<token> T_NEE "!=="
153 %token<token> T_LE "<="
154 %token<token> T_GE ">="
155 %token<token> T_ORBY "|="
156 %token<token> T_DIVBY "/="
157 %token<token> T_MODBY "%="
158 %token<token> T_MULBY "*="
159 %token<token> T_ANDBY "&="
160 %token<token> T_PLUSBY "+="
161 %token<token> T_MINUSBY "-="
162 %token<token> T_XORBY "^="
163 %token<token> T_SHRBY ">>="
164 %token<token> T_SHLBY "<<="
165 %token<token> T_USHRBY ">>>="
166 %token<token> T_OROR "||"
167 %token<token> T_ANDAND "&&"
168 %token<token> T_COLONCOLON "::"
169 %token<token> T_MINUSMINUS "--"
170 %token<token> T_PLUSPLUS "++"
171 %token<token> T_DOTDOT ".."
172 %token<token> T_DOTDOTDOT "..."
173 %token<token> T_SHL "<<"
174 %token<token> T_USHR ">>>"
175 %token<token> T_SHR ">>"
177 %type <number_int> CONDITIONAL_COMPILATION
178 %type <for_start> FOR_START
179 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
180 %type <namespace_decl> NAMESPACE_ID
181 %type <token> VARCONST
183 %type <code> CODEPIECE CODE_STATEMENT
184 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
185 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
186 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
187 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
188 %type <exception> CATCH FINALLY
189 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
190 %type <code> CLASS_DECLARATION
191 %type <code> NAMESPACE_DECLARATION
192 %type <code> INTERFACE_DECLARATION
193 %type <code> VOIDEXPRESSION
194 %type <value> EXPRESSION NONCOMMAEXPRESSION
195 %type <node> MAYBEEXPRESSION
197 %type <node> E COMMA_EXPRESSION
198 %type <node> VAR_READ
199 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
200 %type <value> INNERFUNCTION
201 %type <code> USE_NAMESPACE
202 %type <code> FOR_INIT
204 %type <classinfo> MAYBETYPE
207 %type <params> PARAM_LIST
208 %type <params> MAYBE_PARAM_LIST
209 %type <flags> MAYBE_MODIFIERS
210 %type <flags> MODIFIER_LIST
211 %type <flags> MODIFIER
212 %type <constant> CONSTANT MAYBECONSTANT
213 %type <classinfo_list> IMPLEMENTS_LIST
214 %type <classinfo> EXTENDS CLASS_SPEC
215 %type <classinfo_list> EXTENDS_LIST
217 %type <classinfo> CLASS PACKAGEANDCLASS
218 %type <classinfo_list> CLASS_SPEC_LIST
220 %type <classinfo> TYPE
221 //%type <token> VARIABLE
224 //%type <token> T_IDENTIFIER
225 %type <value> FUNCTIONCALL
226 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES
227 %type <value_list> MAYBE_DICT_EXPRPAIR_LIST DICT_EXPRPAIR_LIST WITH_HEAD
230 // precedence: from low to high
234 %left below_semicolon
237 %nonassoc below_assignment // for ?:, contrary to spec
238 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
245 %nonassoc "==" "!=" "===" "!=="
246 %nonassoc "is" "as" "in"
247 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
248 %left "<<" ">>" ">>>"
252 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
254 %nonassoc below_curly
258 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
261 %left above_identifier
265 // needed for "return" precedence:
266 %nonassoc T_STRING T_REGEXP
267 %nonassoc T_INT T_UINT T_FLOAT KW_NAN
268 %nonassoc "false" "true" "null" "undefined" "super" "function"
275 static int a3_error(char*s)
277 syntaxerror("%s", s);
278 return 0; //make gcc happy
281 static void parsererror(const char*file, int line, const char*f)
283 syntaxerror("internal error in %s, %s:%d", f, file, line);
286 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
289 static char* concat2(const char* t1, const char* t2)
293 char*text = malloc(l1+l2+1);
294 memcpy(text , t1, l1);
295 memcpy(text+l1, t2, l2);
299 static char* concat3(const char* t1, const char* t2, const char* t3)
304 char*text = malloc(l1+l2+l3+1);
305 memcpy(text , t1, l1);
306 memcpy(text+l1, t2, l2);
307 memcpy(text+l1+l2, t3, l3);
312 typedef struct _import {
315 DECLARE_LIST(import);
317 DECLARE(methodstate);
318 DECLARE_LIST(methodstate);
320 typedef struct _classstate {
326 methodstate_t*static_init;
328 //code_t*static_init;
330 char has_constructor;
333 struct _methodstate {
343 dict_t*unresolved_variables;
346 char uses_parent_function;
352 int var_index; // for inner methods
353 int slot_index; // for inner methods
354 char is_a_slot; // for inner methods
359 abc_exception_list_t*exceptions;
361 methodstate_list_t*innerfunctions;
364 typedef struct _state {
369 import_list_t*wildcard_imports;
370 dict_t*import_toplevel_packages;
373 namespace_list_t*active_namespace_urls;
375 char has_own_imports;
376 char new_vars; // e.g. transition between two functions
379 methodstate_t*method;
386 dict_t*allvars; // also contains variables from sublevels
389 typedef struct _global {
392 parsedclass_list_t*classes;
393 abc_script_t*classinit;
395 abc_script_t*init; //package-level code
398 dict_t*file2token2info;
401 static global_t*global = 0;
402 static state_t* state = 0;
406 #define MEMBER_MULTINAME(m,f,n) \
410 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
411 m##_ns.name = ((slotinfo_t*)(f))->package; \
416 m.namespace_set = 0; \
417 m.name = ((slotinfo_t*)(f))->name; \
419 m.type = MULTINAME; \
421 m.namespace_set = &nopackage_namespace_set; \
425 /* warning: list length of namespace set is undefined */
426 #define MULTINAME_LATE(m, access, package) \
427 namespace_t m##_ns = {access, package}; \
428 namespace_set_t m##_nsset; \
429 namespace_list_t m##_l;m##_l.next = 0; \
430 m##_nsset.namespaces = &m##_l; \
431 m##_nsset = m##_nsset; \
432 m##_l.namespace = &m##_ns; \
433 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
435 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
436 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
437 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
438 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
439 static namespace_list_t nl4 = {&ns4,0};
440 static namespace_list_t nl3 = {&ns3,&nl4};
441 static namespace_list_t nl2 = {&ns2,&nl3};
442 static namespace_list_t nl1 = {&ns1,&nl2};
443 static namespace_set_t nopackage_namespace_set = {&nl1};
445 static dict_t*definitions=0;
446 void as3_set_define(const char*c)
449 definitions = dict_new();
450 if(!dict_contains(definitions,c))
451 dict_put(definitions,c,0);
454 static void new_state()
457 state_t*oldstate = state;
459 memcpy(s, state, sizeof(state_t)); //shallow copy
461 s->imports = dict_new();
463 if(!s->import_toplevel_packages) {
464 s->import_toplevel_packages = dict_new();
468 state->has_own_imports = 0;
469 state->vars = dict_new();
470 state->old = oldstate;
473 trie_remember(active_namespaces);
476 state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
479 static void state_destroy(state_t*state)
481 if(state->has_own_imports) {
482 list_free(state->wildcard_imports);
483 dict_destroy(state->imports);state->imports=0;
485 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
486 dict_destroy(state->imports);state->imports=0;
489 dict_destroy(state->vars);state->vars=0;
491 if(state->new_vars && state->allvars) {
492 parserassert(!state->old || state->old->allvars != state->allvars);
493 DICT_ITERATE_DATA(state->allvars, void*, data) {
496 dict_destroy(state->allvars);
499 list_free(state->active_namespace_urls)
500 state->active_namespace_urls = 0;
505 static void old_state()
507 trie_rollback(active_namespaces);
509 if(!state || !state->old)
510 syntaxerror("invalid nesting");
511 state_t*leaving = state;
515 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
516 free(leaving->method);
519 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
524 state_destroy(leaving);
527 static code_t* method_header(methodstate_t*m);
528 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
529 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
532 static char* internal_filename_package = 0;
533 void initialize_file(char*filename)
536 syntaxerror("invalid call to initialize_file during parsing of another file");
539 active_namespaces = trie_new();
542 state->package = internal_filename_package = strdup(filename);
543 state->allvars = dict_new();
545 global->token2info = dict_lookup(global->file2token2info,
546 current_filename // use long version
548 if(!global->token2info) {
549 global->token2info = dict_new2(&ptr_type);
550 dict_put(global->file2token2info, current_filename, global->token2info);
554 state->method = rfx_calloc(sizeof(methodstate_t));
555 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
556 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
558 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
560 syntaxerror("internal error: skewed tokencount");
561 function_initvars(state->method, 0, 0, 1);
562 global->classinit = abc_initscript(global->file);
563 global->init = abc_initscript(global->file);
569 if(!state || state->level!=1) {
570 syntaxerror("unexpected end of file in pass %d", as3_pass);
574 dict_del(global->file2token2info, current_filename);
576 code_t*header = method_header(state->method);
577 code_t*c = wrap_function(header, 0, global->init->method->body->code);
578 global->init->method->body->code = c;
579 free(state->method);state->method=0;
582 //free(state->package);state->package=0; // used in registry
583 state_destroy(state);state=0;
586 void initialize_parser()
588 global = rfx_calloc(sizeof(global_t));
589 global->file = abc_file_new();
590 global->file->flags &= ~ABCFILE_LAZY;
591 global->file2token2info = dict_new();
592 global->token2info = 0;
595 void* finish_parser()
597 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
598 global->token2info=0;
600 initcode_add_classlist(global->classinit, global->classes);
605 typedef struct _variable {
610 methodstate_t*is_inner_method;
613 static variable_t* find_variable(state_t*s, char*name)
618 v = dict_lookup(s->vars, name);
620 if(s->new_vars) break;
623 return dict_lookup(top->allvars, name);
625 static variable_t* find_slot(state_t*s, const char*name)
627 if(s->method && s->method->slots)
628 return dict_lookup(s->method->slots, name);
632 static variable_t* find_variable_safe(state_t*s, char*name)
634 variable_t* v = find_variable(s, name);
636 syntaxerror("undefined variable: %s", name);
640 static char variable_exists(char*name)
642 return dict_contains(state->vars, name);
645 static code_t*defaultvalue(code_t*c, classinfo_t*type)
647 if(TYPE_IS_INT(type)) {
648 c = abc_pushbyte(c, 0);
649 } else if(TYPE_IS_UINT(type)) {
650 c = abc_pushuint(c, 0);
651 } else if(TYPE_IS_FLOAT(type)) {
653 } else if(TYPE_IS_BOOLEAN(type)) {
654 c = abc_pushfalse(c);
656 //c = abc_pushundefined(c);
657 syntaxerror("internal error: can't generate default value for * type");
661 c = abc_coerce2(c, &m);
666 static int alloc_local()
668 return state->method->variable_count++;
671 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
674 variable_t*v = find_slot(state, name);
680 v->index = alloc_local();
685 dict_put(state->vars, name, v);
686 dict_put(state->allvars, name, v);
691 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
693 return new_variable2(name, type, init, maybeslot)->index;
696 #define TEMPVARNAME "__as3_temp__"
699 variable_t*v = find_variable(state, TEMPVARNAME);
704 i = new_variable(TEMPVARNAME, 0, 0, 0);
709 static code_t* var_block(code_t*body)
715 for(t=0;t<state->vars->hashsize;t++) {
716 dictentry_t*e = state->vars->slots[t];
718 variable_t*v = (variable_t*)e->data;
719 if(v->type && v->init) {
720 c = defaultvalue(c, v->type);
721 c = abc_setlocal(c, v->index);
722 k = abc_kill(k, v->index);
732 if(x->opcode== OPCODE___BREAK__ ||
733 x->opcode== OPCODE___CONTINUE__) {
734 /* link kill code before break/continue */
735 code_t*e = code_dup(k);
736 code_t*s = code_start(e);
748 c = code_append(c, body);
749 c = code_append(c, k);
753 static void unknown_variable(char*name)
755 if(!state->method->unresolved_variables)
756 state->method->unresolved_variables = dict_new();
757 if(!dict_contains(state->method->unresolved_variables, name))
758 dict_put(state->method->unresolved_variables, name, 0);
761 static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
763 if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
764 c = abc_getlocal_0(c);
765 c = abc_pushscope(c);
768 /* FIXME: this alloc_local() causes variable indexes to be
769 different in pass2 than in pass1 */
770 if(!m->activation_var)
771 m->activation_var = alloc_local();
773 c = abc_newactivation(c);
775 c = abc_pushscope(c);
776 c = abc_setlocal(c, m->activation_var);
778 c = abc_getlocal(c, m->activation_var);
779 c = abc_pushscope(c);
785 static code_t* method_header(methodstate_t*m)
789 c = add_scope_code(c, m, 1);
791 methodstate_list_t*l = m->innerfunctions;
793 parserassert(l->methodstate->abc);
794 if(m->uses_slots && l->methodstate->is_a_slot) {
795 c = abc_getscopeobject(c, 1);
796 c = abc_newfunction(c, l->methodstate->abc);
798 c = abc_setlocal(c, l->methodstate->var_index);
799 c = abc_setslot(c, l->methodstate->slot_index);
801 c = abc_newfunction(c, l->methodstate->abc);
802 c = abc_setlocal(c, l->methodstate->var_index);
804 free(l->methodstate);l->methodstate=0;
808 c = code_append(c, m->header);
811 if(m->is_constructor && !m->has_super) {
812 // call default constructor
813 c = abc_getlocal_0(c);
814 c = abc_constructsuper(c, 0);
818 /* all parameters that are used by inner functions
819 need to be copied from local to slot */
820 parserassert(m->activation_var);
821 DICT_ITERATE_ITEMS(m->slots,char*,name,variable_t*,v) {
822 if(v->is_parameter) {
823 c = abc_getlocal(c, m->activation_var);
824 c = abc_getlocal(c, v->index);
825 c = abc_setslot(c, v->index);
829 list_free(m->innerfunctions);
830 m->innerfunctions = 0;
835 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
837 c = code_append(c, header);
838 c = code_append(c, var_block(body));
839 /* append return if necessary */
840 if(!c || (c->opcode != OPCODE_RETURNVOID &&
841 c->opcode != OPCODE_RETURNVALUE)) {
842 c = abc_returnvoid(c);
847 static void startpackage(char*name)
850 state->package = strdup(name);
852 static void endpackage()
854 //used e.g. in classinfo_register:
855 //free(state->package);state->package=0;
859 #define FLAG_PUBLIC 256
860 #define FLAG_PROTECTED 512
861 #define FLAG_PRIVATE 1024
862 #define FLAG_PACKAGEINTERNAL 2048
863 #define FLAG_NAMESPACE 4096
865 static namespace_t modifiers2access(modifiers_t*mod)
870 if(mod->flags&FLAG_NAMESPACE) {
871 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
872 syntaxerror("invalid combination of access levels and namespaces");
873 ns.access = ACCESS_NAMESPACE;
875 const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
877 /* shouldn't happen- the tokenizer only reports something as a namespace
878 if it was already registered */
879 trie_dump(active_namespaces);
880 syntaxerror("unknown namespace: %s", mod->ns);
883 } else if(mod->flags&FLAG_PUBLIC) {
884 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
885 syntaxerror("invalid combination of access levels");
886 ns.access = ACCESS_PACKAGE;
887 } else if(mod->flags&FLAG_PRIVATE) {
888 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
889 syntaxerror("invalid combination of access levels");
890 ns.access = ACCESS_PRIVATE;
891 } else if(mod->flags&FLAG_PROTECTED) {
892 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
893 syntaxerror("invalid combination of access levels");
894 ns.access = ACCESS_PROTECTED;
896 ns.access = ACCESS_PACKAGEINTERNAL;
900 static slotinfo_t* find_class(const char*name);
902 static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
904 return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
907 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
912 index = new_variable("this", 0, 0, 0);
913 else if(!m->is_global)
914 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
916 index = new_variable("globalscope", 0, 0, 0);
917 parserassert(!index);
921 /* as variables and slots share the same number, make sure
922 that those variable indices are reserved. It's up to the
923 optimizer to later shuffle the variables down to lower
925 m->variable_count = m->uses_slots;
930 for(p=params->list;p;p=p->next) {
931 variable_t*v = new_variable2(p->param->name, p->param->type, 0, 1);
936 methodstate_list_t*l = m->innerfunctions;
938 methodstate_t*m = l->methodstate;
940 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
941 m->var_index = v->index;
942 m->slot_index = v->index;
943 v->is_inner_method = m;
949 m->scope_code = add_scope_code(m->scope_code, m, 0);
952 if(as3_pass==2 && m->slots) {
953 /* exchange unresolved identifiers with the actual objects */
954 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
955 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
956 classinfo_t*type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
957 if(!type || type->kind != INFOTYPE_CLASS) {
958 syntaxerror("Couldn't find class %s::%s (%s)", v->type->package, v->type->name, name);
967 char*as3_globalclass=0;
968 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
971 syntaxerror("inner classes now allowed");
976 classinfo_list_t*mlist=0;
978 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
979 syntaxerror("invalid modifier(s)");
981 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
982 syntaxerror("public and internal not supported at the same time.");
984 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
985 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
986 // all classes extend object
987 extends = registry_getobjectclass();
990 /* create the class name, together with the proper attributes */
994 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
995 access = ACCESS_PRIVATE; package = internal_filename_package;
996 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
997 access = ACCESS_PACKAGEINTERNAL; package = state->package;
998 } else if(state->package!=internal_filename_package) {
999 access = ACCESS_PACKAGE; package = state->package;
1001 syntaxerror("public classes only allowed inside a package");
1005 state->cls = rfx_calloc(sizeof(classstate_t));
1006 state->cls->init = rfx_calloc(sizeof(methodstate_t));
1007 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
1008 state->cls->static_init->variable_count=1;
1009 /* notice: we make no effort to initialize the top variable (local0) here,
1010 even though it has special meaning. We just rely on the facat
1011 that pass 1 won't do anything with variables */
1013 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
1015 /* set current method to constructor- all code within the class-level (except
1016 static variable initializations) will be executed during construction time */
1017 state->method = state->cls->init;
1019 if(registry_find(package, classname)) {
1020 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
1022 /* build info struct */
1023 int num_interfaces = (list_length(implements));
1024 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
1025 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
1026 state->cls->info->superclass = extends;
1029 classinfo_list_t*l = implements;
1030 for(l=implements;l;l=l->next) {
1031 state->cls->info->interfaces[pos++] = l->classinfo;
1036 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1038 state->method = state->cls->init;
1039 parserassert(state->cls && state->cls->info);
1041 function_initvars(state->cls->init, 0, 0, 1);
1042 function_initvars(state->cls->static_init, 0, 0, 0);
1044 if(extends && (extends->flags & FLAG_FINAL))
1045 syntaxerror("Can't extend final class '%s'", extends->name);
1048 while(state->cls->info->interfaces[pos]) {
1049 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1050 syntaxerror("'%s' is not an interface",
1051 state->cls->info->interfaces[pos]->name);
1055 /* generate the abc code for this class */
1056 MULTINAME(classname2,state->cls->info);
1057 multiname_t*extends2 = sig2mname(extends);
1058 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1059 multiname_destroy(extends2);
1061 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1062 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1063 if(state->cls->info->flags&FLAG_INTERFACE) {
1064 abc_class_interface(state->cls->abc);
1067 abc_class_protectedNS(state->cls->abc, classname);
1069 for(mlist=implements;mlist;mlist=mlist->next) {
1070 MULTINAME(m, mlist->classinfo);
1071 abc_class_add_interface(state->cls->abc, &m);
1074 NEW(parsedclass_t,p);
1075 p->cls = state->cls->info;
1076 p->abc = state->cls->abc;
1077 list_append(global->classes, p);
1079 /* flash.display.MovieClip handling */
1080 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1081 if(state->package && state->package[0]) {
1082 as3_globalclass = concat3(state->package, ".", classname);
1084 as3_globalclass = strdup(classname);
1090 static void endclass()
1093 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1095 c = abc_getlocal_0(c);
1096 c = abc_constructsuper(c, 0);
1097 state->cls->init->header = code_append(state->cls->init->header, c);
1098 state->cls->has_constructor=1;
1100 if(state->cls->init) {
1101 if(state->cls->info->flags&FLAG_INTERFACE) {
1102 if(state->cls->init->header)
1103 syntaxerror("interface can not have class-level code");
1105 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1106 code_t*c = method_header(state->cls->init);
1107 m->body->code = wrap_function(c, 0, m->body->code);
1110 if(state->cls->static_init) {
1111 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1112 code_t*c = method_header(state->cls->static_init);
1113 m->body->code = wrap_function(c, 0, m->body->code);
1120 void check_code_for_break(code_t*c)
1123 if(c->opcode == OPCODE___BREAK__) {
1124 char*name = string_cstr(c->data[0]);
1125 syntaxerror("Unresolved \"break %s\"", name);
1127 if(c->opcode == OPCODE___CONTINUE__) {
1128 char*name = string_cstr(c->data[0]);
1129 syntaxerror("Unresolved \"continue %s\"", name);
1131 if(c->opcode == OPCODE___RETHROW__) {
1132 syntaxerror("Unresolved \"rethrow\"");
1134 if(c->opcode == OPCODE___FALLTHROUGH__) {
1135 syntaxerror("Unresolved \"fallthrough\"");
1137 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1138 char*name = string_cstr(c->data[0]);
1139 syntaxerror("Can't reference a package (%s) as such", name);
1145 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1147 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1148 if(TYPE_IS_NUMBER(t)) {
1149 xassert(c->type == CONSTANT_FLOAT
1150 || c->type == CONSTANT_INT
1151 || c->type == CONSTANT_UINT);
1152 } else if(TYPE_IS_UINT(t)) {
1153 xassert(c->type == CONSTANT_UINT ||
1154 (c->type == CONSTANT_INT && c->i>=0));
1155 } else if(TYPE_IS_INT(t)) {
1156 xassert(c->type == CONSTANT_INT);
1157 } else if(TYPE_IS_BOOLEAN(t)) {
1158 xassert(c->type == CONSTANT_TRUE
1159 || c->type == CONSTANT_FALSE);
1163 static void check_override(memberinfo_t*m, int flags)
1167 if(m->parent == state->cls->info)
1168 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1170 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1171 if(m->access==ACCESS_PRIVATE)
1173 if(m->flags & FLAG_FINAL)
1174 syntaxerror("can't override final member %s", m->name);
1176 /* allow this. it's no issue.
1177 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1178 syntaxerror("can't override static member %s", m->name);*/
1180 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1181 syntaxerror("can't override non-static member %s with static declaration", m->name);
1183 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1184 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1185 if(m->kind == INFOTYPE_METHOD)
1186 syntaxerror("can't override without explicit 'override' declaration");
1188 syntaxerror("can't override '%s'", m->name);
1193 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1195 methodinfo_t*minfo = 0;
1196 namespace_t ns = modifiers2access(mod);
1199 minfo = methodinfo_register_global(ns.access, state->package, name);
1200 minfo->return_type = return_type;
1201 } else if(getset != KW_GET && getset != KW_SET) {
1203 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1205 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1207 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1208 minfo->return_type = return_type;
1209 // getslot on a member slot only returns "undefined", so no need
1210 // to actually store these
1211 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1213 //class getter/setter
1214 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1216 if(getset == KW_GET) {
1218 } else if(params->list && params->list->param && !params->list->next) {
1219 type = params->list->param->type;
1221 syntaxerror("setter function needs to take exactly one argument");
1222 // not sure wether to look into superclasses here, too
1223 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1225 if(minfo->kind!=INFOTYPE_VAR)
1226 syntaxerror("class already contains a method called '%s'", name);
1227 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1228 syntaxerror("class already contains a field called '%s'", name);
1229 if(minfo->subtype & gs)
1230 syntaxerror("getter/setter for '%s' already defined", name);
1231 /* make a setter or getter into a getset */
1232 minfo->subtype |= gs;
1235 FIXME: this check needs to be done in pass 2
1237 if((!minfo->return_type != !type) ||
1238 (minfo->return_type && type &&
1239 !strcmp(minfo->return_type->name, type->name))) {
1240 syntaxerror("different type in getter and setter: %s and %s",
1241 minfo->return_type?minfo->return_type->name:"*",
1242 type?type->name:"*");
1245 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1246 minfo->kind = INFOTYPE_VAR; //hack
1247 minfo->subtype = gs;
1248 minfo->return_type = return_type;
1251 /* can't assign a slot as getter and setter might have different slots */
1252 //minfo->slot = slot;
1254 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1255 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1256 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1261 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1263 //parserassert(state->method && state->method->info);
1265 methodstate_t*parent_method = state->method;
1268 return_type = 0; // not valid in pass 1
1272 state->new_vars = 1;
1273 state->allvars = dict_new();
1276 state->method = rfx_calloc(sizeof(methodstate_t));
1277 state->method->inner = 1;
1278 state->method->variable_count = 0;
1279 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1281 NEW(methodinfo_t,minfo);
1282 minfo->kind = INFOTYPE_METHOD;
1283 minfo->access = ACCESS_PACKAGEINTERNAL;
1285 state->method->info = minfo;
1288 list_append(parent_method->innerfunctions, state->method);
1290 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1292 function_initvars(state->method, params, 0, 1);
1296 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1297 state->method->variable_count = 0;
1298 parserassert(state->method);
1300 state->method->info->return_type = return_type;
1301 function_initvars(state->method, params, 0, 1);
1305 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1306 params_t*params, classinfo_t*return_type)
1308 if(state->method && state->method->info) {
1309 syntaxerror("not able to start another method scope");
1312 state->new_vars = 1;
1313 state->allvars = dict_new();
1316 state->method = rfx_calloc(sizeof(methodstate_t));
1317 state->method->has_super = 0;
1320 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1322 state->method->is_global = 1;
1323 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1325 if(state->method->is_constructor)
1326 name = "__as3_constructor__";
1328 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1330 function_initvars(state->method, params, mod->flags, 1);
1332 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1336 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1337 state->method->variable_count = 0;
1338 parserassert(state->method);
1341 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1342 check_override(m, mod->flags);
1346 state->cls->has_constructor |= state->method->is_constructor;
1349 function_initvars(state->method, params, mod->flags, 1);
1353 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1354 params_t*params, classinfo_t*return_type, code_t*body)
1357 // store inner methods in variables
1358 function_initvars(state->method, 0, 0, 0);
1360 methodstate_list_t*ml = state->method->innerfunctions;
1362 dict_t*xvars = dict_new();
1365 methodstate_t*m = ml->methodstate;
1366 parserassert(m->inner);
1367 if(m->unresolved_variables) {
1368 dict_t*d = m->unresolved_variables;
1370 for(t=0;t<d->hashsize;t++) {
1371 dictentry_t*l = d->slots[t];
1373 /* check parent method's variables */
1375 if((v=find_variable(state, l->key))) {
1376 m->uses_parent_function = 1;
1377 state->method->uses_slots = 1;
1378 dict_put(xvars, l->key, 0);
1385 dict_destroy(m->unresolved_variables);
1386 m->unresolved_variables = 0;
1391 if(state->method->uses_slots) {
1392 state->method->slots = dict_new();
1394 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1395 if(!name) syntaxerror("internal error");
1396 if(v->index && dict_contains(xvars, name)) {
1399 if(v->is_inner_method) {
1400 v->is_inner_method->is_a_slot = 1;
1403 dict_put(state->method->slots, name, v);
1406 state->method->uses_slots = i;
1407 dict_destroy(state->vars);state->vars = 0;
1408 parserassert(state->new_vars);
1409 dict_destroy(state->allvars);state->allvars = 0;
1416 /*if(state->method->uses_parent_function){
1417 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1422 multiname_t*type2 = sig2mname(return_type);
1424 if(state->method->inner) {
1425 f = state->method->abc;
1426 abc_method_init(f, global->file, type2, 1);
1427 } else if(state->method->is_constructor) {
1428 f = abc_class_getconstructor(state->cls->abc, type2);
1429 } else if(!state->method->is_global) {
1430 namespace_t mname_ns = modifiers2access(mod);
1431 multiname_t mname = {QNAME, &mname_ns, 0, name};
1433 if(mod->flags&FLAG_STATIC)
1434 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1436 f = abc_class_method(state->cls->abc, type2, &mname);
1437 slot = f->trait->slot_id;
1439 namespace_t mname_ns = {state->method->info->access, state->package};
1440 multiname_t mname = {QNAME, &mname_ns, 0, name};
1442 f = abc_method_new(global->file, type2, 1);
1443 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1444 //abc_code_t*c = global->init->method->body->code;
1446 //flash doesn't seem to allow us to access function slots
1447 //state->method->info->slot = slot;
1449 if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1450 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1451 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1452 if(params->varargs) f->flags |= METHOD_NEED_REST;
1456 for(p=params->list;p;p=p->next) {
1457 if(params->varargs && !p->next) {
1458 break; //varargs: omit last parameter in function signature
1460 multiname_t*m = sig2mname(p->param->type);
1461 list_append(f->parameters, m);
1462 if(p->param->value) {
1463 check_constant_against_type(p->param->type, p->param->value);
1464 opt=1;list_append(f->optional_parameters, p->param->value);
1466 syntaxerror("non-optional parameter not allowed after optional parameters");
1469 if(state->method->slots) {
1470 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1472 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1473 multiname_t*type = sig2mname(v->type);
1474 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1475 t->slot_id = v->index;
1480 check_code_for_break(body);
1482 /* Seems this works now.
1483 if(state->method->exceptions && state->method->uses_slots) {
1484 as3_warning("try/catch and activation not supported yet within the same method");
1488 f->body->code = body;
1489 f->body->exceptions = state->method->exceptions;
1490 } else { //interface
1492 syntaxerror("interface methods can't have a method body");
1502 void breakjumpsto(code_t*c, char*name, code_t*jump)
1505 if(c->opcode == OPCODE___BREAK__) {
1506 string_t*name2 = c->data[0];
1507 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1508 c->opcode = OPCODE_JUMP;
1515 void continuejumpsto(code_t*c, char*name, code_t*jump)
1518 if(c->opcode == OPCODE___CONTINUE__) {
1519 string_t*name2 = c->data[0];
1520 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1521 c->opcode = OPCODE_JUMP;
1529 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1531 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1536 return abc_coerce_a(c);
1540 // cast an "any" type to a specific type. subject to
1541 // runtime exceptions
1542 return abc_coerce2(c, &m);
1545 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1546 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1547 // allow conversion between number types
1548 if(TYPE_IS_UINT(to))
1549 return abc_convert_u(c);
1550 else if(TYPE_IS_INT(to))
1551 return abc_convert_i(c);
1552 else if(TYPE_IS_NUMBER(to))
1553 return abc_convert_d(c);
1554 return abc_coerce2(c, &m);
1557 if(TYPE_IS_BOOLEAN(to))
1558 return abc_convert_b(c);
1559 if(TYPE_IS_STRING(to))
1560 return abc_convert_s(c);
1561 if(TYPE_IS_OBJECT(to))
1562 return abc_convert_o(c);
1564 classinfo_t*supertype = from;
1566 if(supertype == to) {
1567 // target type is one of from's superclasses
1568 return abc_coerce2(c, &m);
1571 while(supertype->interfaces[t]) {
1572 if(supertype->interfaces[t]==to) {
1573 // target type is one of from's interfaces
1574 return abc_coerce2(c, &m);
1578 supertype = supertype->superclass;
1580 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1582 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1584 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1587 as3_error("can't convert type %s%s%s to %s%s%s",
1588 from->package, from->package[0]?".":"", from->name,
1589 to->package, to->package[0]?".":"", to->name);
1593 /* move to ast.c todo end */
1595 char is_pushundefined(code_t*c)
1597 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1600 static const char* get_package_from_name(const char*name)
1602 /* try explicit imports */
1603 dictentry_t* e = dict_get_slot(state->imports, name);
1605 if(!strcmp(e->key, name)) {
1606 slotinfo_t*c = (slotinfo_t*)e->data;
1607 if(c) return c->package;
1613 static namespace_list_t*get_current_imports()
1615 namespace_list_t*searchlist = 0;
1617 list_append(searchlist, namespace_new_package(state->package));
1619 import_list_t*l = state->wildcard_imports;
1621 namespace_t*ns = namespace_new_package(l->import->package);
1622 list_append(searchlist, ns);
1625 list_append(searchlist, namespace_new_package(""));
1626 list_append(searchlist, namespace_new_package(internal_filename_package));
1630 static slotinfo_t* find_class(const char*name)
1634 c = registry_find(state->package, name);
1637 /* try explicit imports */
1638 dictentry_t* e = dict_get_slot(state->imports, name);
1641 if(!strcmp(e->key, name)) {
1642 c = (slotinfo_t*)e->data;
1648 /* try package.* imports */
1649 import_list_t*l = state->wildcard_imports;
1651 //printf("does package %s contain a class %s?\n", l->import->package, name);
1652 c = registry_find(l->import->package, name);
1657 /* try global package */
1658 c = registry_find("", name);
1661 /* try local "filename" package */
1662 c = registry_find(internal_filename_package, name);
1667 typedcode_t push_class(slotinfo_t*a)
1672 if(a->access == ACCESS_PACKAGEINTERNAL &&
1673 strcmp(a->package, state->package) &&
1674 strcmp(a->package, internal_filename_package)
1676 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1677 infotypename(a), a->name, a->package, state->package);
1680 if(a->kind != INFOTYPE_CLASS) {
1682 x.c = abc_findpropstrict2(x.c, &m);
1683 x.c = abc_getproperty2(x.c, &m);
1684 if(a->kind == INFOTYPE_METHOD) {
1685 methodinfo_t*f = (methodinfo_t*)a;
1686 x.t = TYPE_FUNCTION(f);
1688 varinfo_t*v = (varinfo_t*)a;
1692 classinfo_t*c = (classinfo_t*)a;
1694 x.c = abc_getglobalscope(x.c);
1695 x.c = abc_getslot(x.c, c->slot);
1698 x.c = abc_getlex2(x.c, &m);
1700 x.t = TYPE_CLASS(c);
1706 char is_break_or_jump(code_t*c)
1710 if(c->opcode == OPCODE_JUMP ||
1711 c->opcode == OPCODE___BREAK__ ||
1712 c->opcode == OPCODE___CONTINUE__ ||
1713 c->opcode == OPCODE_THROW ||
1714 c->opcode == OPCODE_RETURNVOID ||
1715 c->opcode == OPCODE_RETURNVALUE) {
1721 #define IS_FINALLY_TARGET(op) \
1722 ((op) == OPCODE___CONTINUE__ || \
1723 (op) == OPCODE___BREAK__ || \
1724 (op) == OPCODE_RETURNVOID || \
1725 (op) == OPCODE_RETURNVALUE || \
1726 (op) == OPCODE___RETHROW__)
1728 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1730 #define NEED_EXTRA_STACK_ARG
1731 code_t*finally_label = abc_nop(0);
1732 NEW(lookupswitch_t, l);
1738 code_t*prev = i->prev;
1739 if(IS_FINALLY_TARGET(i->opcode)) {
1742 if(i->opcode == OPCODE___RETHROW__ ||
1743 i->opcode == OPCODE_RETURNVALUE) {
1744 if(i->opcode == OPCODE___RETHROW__)
1745 i->opcode = OPCODE_THROW;
1747 p = abc_coerce_a(p);
1748 p = abc_setlocal(p, tempvar);
1750 p = abc_pushbyte(p, count++);
1751 p = abc_jump(p, finally_label);
1752 code_t*target = p = abc_label(p);
1753 #ifdef NEED_EXTRA_STACK_ARG
1757 p = abc_getlocal(p, tempvar);
1760 p->next = i;i->prev = p;
1761 list_append(l->targets, target);
1767 c = abc_pushbyte(c, -1);
1768 c = code_append(c, finally_label);
1769 c = code_append(c, finally);
1771 #ifdef NEED_EXTRA_STACK_ARG
1774 c = abc_lookupswitch(c, l);
1775 c = l->def = abc_label(c);
1776 #ifdef NEED_EXTRA_STACK_ARG
1783 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1787 code_t*prev = i->prev;
1788 if(IS_FINALLY_TARGET(i->opcode)) {
1789 if(i->opcode == OPCODE___RETHROW__)
1790 i->opcode = OPCODE_THROW;
1791 code_t*end = code_dup(finally);
1792 code_t*start = code_start(end);
1793 if(prev) prev->next = start;
1800 return code_append(c, finally);
1803 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1809 int num_insertion_points=0;
1811 if(IS_FINALLY_TARGET(i->opcode))
1812 num_insertion_points++;
1819 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1824 int simple_version_cost = (1+num_insertion_points)*code_size;
1825 int lookup_version_cost = 4*num_insertion_points + 5;
1827 if(cantdup || simple_version_cost > lookup_version_cost) {
1828 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
1829 return insert_finally_lookup(c, finally, tempvar);
1831 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
1832 return insert_finally_simple(c, finally, tempvar);
1836 #define PASS1 }} if(as3_pass == 1) {{
1837 #define PASS1END }} if(as3_pass == 2) {{
1838 #define PASS2 }} if(as3_pass == 2) {{
1839 #define PASS12 }} if(as3_pass == 1 || as3_pass == 2) {{
1840 #define PASS12END }} if(as3_pass == 2) {{
1841 #define PASS_ALWAYS }} {{
1847 /* ------------ code blocks / statements ---------------- */
1849 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1851 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1852 PROGRAM_CODE_LIST: PROGRAM_CODE
1853 | PROGRAM_CODE_LIST PROGRAM_CODE
1855 PROGRAM_CODE: PACKAGE_DECLARATION
1856 | INTERFACE_DECLARATION
1858 | FUNCTION_DECLARATION
1861 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
1864 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1865 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1866 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1868 INPACKAGE_CODE: INTERFACE_DECLARATION
1870 | FUNCTION_DECLARATION
1873 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
1876 MAYBECODE: CODE {$$=$1;}
1877 MAYBECODE: {$$=code_new();}
1879 CODE: CODE CODEPIECE {
1880 $$=code_append($1,$2);
1882 CODE: CODEPIECE {$$=$1;}
1884 // code which may appear outside of methods
1885 CODE_STATEMENT: IMPORT
1887 CODE_STATEMENT: FOR_IN
1888 CODE_STATEMENT: WHILE
1889 CODE_STATEMENT: DO_WHILE
1890 CODE_STATEMENT: SWITCH
1892 CODE_STATEMENT: WITH
1894 CODE_STATEMENT: VOIDEXPRESSION
1895 CODE_STATEMENT: USE_NAMESPACE
1896 CODE_STATEMENT: NAMESPACE_DECLARATION
1897 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1898 CODE_STATEMENT: '{' '}' {$$=0;}
1900 // code which may appear in methods
1901 CODEPIECE: ';' {$$=0;}
1902 CODEPIECE: CODE_STATEMENT
1903 CODEPIECE: VARIABLE_DECLARATION
1908 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {
1918 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1919 //CODEBLOCK : '{' '}' {$$=0;}
1920 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1921 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1923 /* ------------ package init code ------------------- */
1925 PACKAGE_INITCODE: CODE_STATEMENT {
1926 code_t**cc = &global->init->method->body->code;
1927 *cc = code_append(*cc, $1);
1930 /* ------------ conditional compilation ------------- */
1932 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER {
1935 char*key = concat3($1,"::",$3);
1936 if(!definitions || !dict_contains(definitions, key)) {
1942 /* ------------ variables --------------------------- */
1945 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1951 MAYBEEXPRESSION : '=' E {$$=$2;}
1952 | {$$=mkdummynode();}
1954 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1955 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1957 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1958 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1960 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1963 if(variable_exists($1))
1964 syntaxerror("Variable %s already defined", $1);
1966 new_variable($1, 0, 1, 0);
1971 if(state->method->uses_slots) {
1972 variable_t* v = find_slot(state, $1);
1974 // this variable is stored in a slot
1982 index = new_variable($1, $2, 1, 0);
1985 $$ = slot?abc_getscopeobject(0, 1):0;
1987 typedcode_t v = node_read($3);
1988 if(!is_subtype_of(v.t, $2)) {
1989 syntaxerror("Can't convert %s to %s", v.t->name, $2->name);
1992 if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
1993 $$ = code_append($$, v.c);
1994 $$ = converttype($$, v.t, $2);
1997 $$ = defaultvalue($$, $2);
2000 if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
2001 $$ = code_append($$, v.c);
2002 $$ = abc_coerce_a($$);
2004 // don't do anything
2012 $$ = abc_setslot($$, index);
2014 $$ = abc_setlocal($$, index);
2018 /* ------------ control flow ------------------------- */
2020 MAYBEELSE: %prec below_else {$$ = code_new();}
2021 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2022 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2024 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2027 $$ = code_append($$, $4.c);
2028 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2030 $$ = code_append($$, $6);
2032 myjmp = $$ = abc_jump($$, 0);
2034 myif->branch = $$ = abc_nop($$);
2036 $$ = code_append($$, $7);
2037 myjmp->branch = $$ = abc_nop($$);
2043 FOR_INIT : {$$=code_new();}
2044 FOR_INIT : VARIABLE_DECLARATION
2045 FOR_INIT : VOIDEXPRESSION
2047 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2048 // (I don't see any easy way to revolve this conflict otherwise, as we
2049 // can't touch VAR_READ without upsetting the precedence about "return")
2050 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2051 PASS1 $$=$2;new_variable($2,0,1,0);
2052 PASS2 $$=$2;new_variable($2,$3,1,0);
2054 FOR_IN_INIT : T_IDENTIFIER {
2059 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2060 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2062 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2063 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2065 $$ = code_append($$, $2);
2066 code_t*loopstart = $$ = abc_label($$);
2067 $$ = code_append($$, $4.c);
2068 code_t*myif = $$ = abc_iffalse($$, 0);
2069 $$ = code_append($$, $8);
2070 code_t*cont = $$ = abc_nop($$);
2071 $$ = code_append($$, $6);
2072 $$ = abc_jump($$, loopstart);
2073 code_t*out = $$ = abc_nop($$);
2074 breakjumpsto($$, $1.name, out);
2075 continuejumpsto($$, $1.name, cont);
2082 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2083 variable_t*var = find_variable(state, $2);
2085 syntaxerror("variable %s not known in this scope", $2);
2088 char*tmp1name = concat2($2, "__tmp1__");
2089 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2090 char*tmp2name = concat2($2, "__array__");
2091 int array = new_variable(tmp1name, 0, 0, 0);
2094 $$ = code_append($$, $4.c);
2095 $$ = abc_coerce_a($$);
2096 $$ = abc_setlocal($$, array);
2097 $$ = abc_pushbyte($$, 0);
2098 $$ = abc_setlocal($$, it);
2100 code_t*loopstart = $$ = abc_label($$);
2102 $$ = abc_hasnext2($$, array, it);
2103 code_t*myif = $$ = abc_iffalse($$, 0);
2104 $$ = abc_getlocal($$, array);
2105 $$ = abc_getlocal($$, it);
2107 $$ = abc_nextname($$);
2109 $$ = abc_nextvalue($$);
2110 $$ = converttype($$, 0, var->type);
2111 $$ = abc_setlocal($$, var->index);
2113 $$ = code_append($$, $6);
2114 $$ = abc_jump($$, loopstart);
2116 code_t*out = $$ = abc_nop($$);
2117 breakjumpsto($$, $1.name, out);
2118 continuejumpsto($$, $1.name, loopstart);
2130 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2134 code_t*myjmp = $$ = abc_jump($$, 0);
2135 code_t*loopstart = $$ = abc_label($$);
2136 $$ = code_append($$, $6);
2137 code_t*cont = $$ = abc_nop($$);
2138 myjmp->branch = cont;
2139 $$ = code_append($$, $4.c);
2140 $$ = abc_iftrue($$, loopstart);
2141 code_t*out = $$ = abc_nop($$);
2142 breakjumpsto($$, $1, out);
2143 continuejumpsto($$, $1, cont);
2149 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2151 code_t*loopstart = $$ = abc_label($$);
2152 $$ = code_append($$, $3);
2153 code_t*cont = $$ = abc_nop($$);
2154 $$ = code_append($$, $6.c);
2155 $$ = abc_iftrue($$, loopstart);
2156 code_t*out = $$ = abc_nop($$);
2157 breakjumpsto($$, $1, out);
2158 continuejumpsto($$, $1, cont);
2164 BREAK : "break" %prec prec_none {
2165 $$ = abc___break__(0, "");
2167 BREAK : "break" T_IDENTIFIER {
2168 $$ = abc___break__(0, $2);
2170 CONTINUE : "continue" %prec prec_none {
2171 $$ = abc___continue__(0, "");
2173 CONTINUE : "continue" T_IDENTIFIER {
2174 $$ = abc___continue__(0, $2);
2177 MAYBE_CASE_LIST : {$$=0;}
2178 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2179 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2180 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2181 CASE_LIST: CASE {$$=$1;}
2182 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2184 CASE: "case" E ':' MAYBECODE {
2185 $$ = abc_getlocal(0, state->switch_var);
2186 $$ = code_append($$, node_read($2).c);
2187 code_t*j = $$ = abc_ifne($$, 0);
2188 $$ = code_append($$, $4);
2189 if($$->opcode != OPCODE___BREAK__) {
2190 $$ = abc___fallthrough__($$, "");
2192 code_t*e = $$ = abc_nop($$);
2195 DEFAULT: "default" ':' MAYBECODE {
2198 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2199 $$ = node_read($4).c;
2200 $$ = abc_setlocal($$, state->switch_var);
2201 $$ = code_append($$, $7);
2203 code_t*out = $$ = abc_kill($$, state->switch_var);
2204 breakjumpsto($$, $1, out);
2206 code_t*c = $$,*lastblock=0;
2208 if(c->opcode == OPCODE_IFNE) {
2209 if(!c->next) syntaxerror("internal error in fallthrough handling");
2211 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2213 c->opcode = OPCODE_JUMP;
2214 c->branch = lastblock;
2216 /* fall through end of switch */
2217 c->opcode = OPCODE_NOP;
2227 /* ------------ try / catch /finally ---------------- */
2229 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2230 state->exception_name=$3;
2231 PASS1 new_variable($3, 0, 0, 0);
2232 PASS2 new_variable($3, $4, 0, 0);
2235 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2236 multiname_t name = {QNAME, &name_ns, 0, $3};
2238 NEW(abc_exception_t, e)
2239 e->exc_type = sig2mname($4);
2240 e->var_name = multiname_clone(&name);
2244 int i = find_variable_safe(state, $3)->index;
2245 e->target = c = abc_nop(0);
2246 c = abc_setlocal(c, i);
2247 c = code_append(c, code_dup(state->method->scope_code));
2248 c = code_append(c, $8);
2254 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2259 NEW(abc_exception_t, e)
2260 e->exc_type = 0; //all exceptions
2261 e->var_name = 0; //no name
2264 e->to = code_append(e->to, $4);
2270 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2271 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2272 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2273 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2277 list_append($$.l,$2);
2278 $$.finally = $2->to;$2->to=0;
2281 CATCH_FINALLY_LIST: FINALLY {
2285 list_append($$.l,$1);
2286 $$.finally = $1->to;$1->to=0;
2290 TRY : "try" '{' {PASS12 new_state();
2291 state->method->has_exceptions=1;
2292 state->method->late_binding=1;//for invariant scope_code
2293 } MAYBECODE '}' CATCH_FINALLY_LIST {
2294 code_t*out = abc_nop(0);
2296 code_t*start = abc_nop(0);
2297 $$ = code_append(start, $4);
2298 if(!is_break_or_jump($4)) {
2299 $$ = abc_jump($$, out);
2301 code_t*end = $$ = abc_nop($$);
2305 tmp = new_variable("__finally__", 0, 0, 0);
2307 abc_exception_list_t*l = $6.l;
2310 abc_exception_t*e = l->abc_exception;
2312 $$ = code_append($$, e->target);
2313 $$ = abc_jump($$, out);
2315 parserassert((ptroff_t)$6.finally);
2317 e->target = $$ = abc_nop($$);
2318 $$ = code_append($$, code_dup(state->method->scope_code));
2319 $$ = abc___rethrow__($$);
2327 $$ = code_append($$, out);
2329 $$ = insert_finally($$, $6.finally, tmp);
2331 list_concat(state->method->exceptions, $6.l);
2337 /* ------------ throw ------------------------------- */
2339 THROW : "throw" EXPRESSION {
2343 THROW : "throw" %prec prec_none {
2344 if(!state->exception_name)
2345 syntaxerror("re-throw only possible within a catch block");
2346 variable_t*v = find_variable(state, state->exception_name);
2348 $$=abc_getlocal($$, v->index);
2352 /* ------------ with -------------------------------- */
2354 WITH_HEAD : "with" '(' EXPRESSION ')' {
2356 if(state->method->has_exceptions) {
2357 int v = alloc_local();
2358 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2359 state->method->scope_code = abc_pushwith(state->method->scope_code);
2364 WITH : WITH_HEAD CODEBLOCK {
2365 /* remove getlocal;pushwith from scope code again */
2366 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2369 if(state->method->has_exceptions) {
2371 $$ = abc_setlocal($$, $1.number);
2373 $$ = abc_pushwith($$);
2374 $$ = code_append($$, $2);
2375 $$ = abc_popscope($$);
2379 /* ------------ packages and imports ---------------- */
2381 X_IDENTIFIER: T_IDENTIFIER
2382 | "package" {PASS12 $$="package";}
2383 | T_NAMESPACE {PASS12 $$=$1;}
2385 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2386 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2388 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2389 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2390 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2391 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2394 static void state_has_imports()
2396 state->wildcard_imports = list_clone(state->wildcard_imports);
2397 state->imports = dict_clone(state->imports);
2398 state->has_own_imports = 1;
2400 static void import_toplevel(const char*package)
2402 char* s = strdup(package);
2404 dict_put(state->import_toplevel_packages, s, 0);
2405 char*x = strrchr(s, '.');
2413 IMPORT : "import" PACKAGEANDCLASS {
2415 slotinfo_t*s = registry_find($2->package, $2->name);
2416 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2417 as3_schedule_class($2->package, $2->name);
2421 syntaxerror("Couldn't import class\n");
2422 state_has_imports();
2423 dict_put(state->imports, c->name, c);
2424 import_toplevel(c->package);
2427 IMPORT : "import" PACKAGE '.' '*' {
2429 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2430 as3_schedule_package($2);
2435 state_has_imports();
2436 list_append(state->wildcard_imports, i);
2437 import_toplevel(i->package);
2441 /* ------------ classes and interfaces (header) -------------- */
2443 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2444 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2445 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2446 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2448 $$.flags=$1.flags|$2.flags;
2449 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2450 $$.ns=$1.ns?$1.ns:$2.ns;
2453 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2454 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2455 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2456 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2457 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2458 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2459 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2460 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2461 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2462 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2466 EXTENDS : {PASS12 $$=0;}
2467 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2469 EXTENDS_LIST : {PASS12 $$=list_new();}
2470 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2472 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2473 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2475 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2476 EXTENDS IMPLEMENTS_LIST
2477 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2479 '}' {PASS12 endclass();$$=0;}
2481 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2483 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2484 startclass(&$1,$3,0,$4);}
2485 MAYBE_INTERFACE_BODY
2486 '}' {PASS12 endclass();$$=0;}
2488 /* ------------ classes and interfaces (body) -------------- */
2491 MAYBE_CLASS_BODY : CLASS_BODY
2492 CLASS_BODY : CLASS_BODY_ITEM
2493 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2494 CLASS_BODY_ITEM : ';'
2495 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}' {PASS_ALWAYS as3_pass=$1;}
2496 CLASS_BODY_ITEM : SLOT_DECLARATION
2497 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2499 CLASS_BODY_ITEM : CODE_STATEMENT {
2500 code_t*c = state->cls->static_init->header;
2501 c = code_append(c, $1);
2502 state->cls->static_init->header = c;
2505 MAYBE_INTERFACE_BODY :
2506 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2507 INTERFACE_BODY : IDECLARATION
2508 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2510 IDECLARATION : "var" T_IDENTIFIER {
2511 syntaxerror("variable declarations not allowed in interfaces");
2513 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2515 $1.flags |= FLAG_PUBLIC;
2516 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2517 syntaxerror("invalid method modifiers: interface methods always need to be public");
2519 startfunction(&$1,$3,$4,&$6,$8);
2520 endfunction(&$1,$3,$4,&$6,$8, 0);
2521 list_deep_free($6.list);
2524 /* ------------ classes and interfaces (body, slots ) ------- */
2527 static int slotstate_varconst = 0;
2528 static modifiers_t*slotstate_flags = 0;
2529 static void setslotstate(modifiers_t* flags, int varconst)
2531 slotstate_varconst = varconst;
2532 slotstate_flags = flags;
2534 if(flags && flags->flags&FLAG_STATIC) {
2535 state->method = state->cls->static_init;
2537 state->method = state->cls->init;
2540 parserassert(state->method);
2545 VARCONST: "var" | "const"
2547 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {PASS12 setslotstate(&$1,$2);} SLOT_LIST {PASS12 $$=$4;setslotstate(0, 0);}
2549 SLOT_LIST: ONE_SLOT {PASS12 $$=0;}
2550 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {PASS12 $$=0;}
2552 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2555 int flags = slotstate_flags->flags;
2556 namespace_t ns = modifiers2access(slotstate_flags);
2560 varinfo_t* info = 0;
2562 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2564 check_override(i, flags);
2566 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2568 slotinfo_t*i = registry_find(state->package, $1);
2570 syntaxerror("package %s already contains '%s'", state->package, $1);
2572 if(ns.name && ns.name[0]) {
2573 syntaxerror("namespaces not allowed on package-level variables");
2575 info = varinfo_register_global(ns.access, state->package, $1);
2579 info->flags = flags;
2581 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, info);
2585 varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
2588 multiname_t mname = {QNAME, &ns, 0, $1};
2590 trait_list_t**traits;
2594 ns.name = state->package;
2595 traits = &global->init->traits;
2596 code = &global->init->method->body->code;
2597 } else if(flags&FLAG_STATIC) {
2599 traits = &state->cls->abc->static_traits;
2600 code = &state->cls->static_init->header;
2602 // instance variable
2603 traits = &state->cls->abc->traits;
2604 code = &state->cls->init->header;
2610 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2612 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2614 info->slot = t->slot_id;
2616 constant_t cval = $3->type->eval($3);
2617 if(cval.type!=CONSTANT_UNKNOWN) {
2618 /* compile time constant */
2619 t->value = malloc(sizeof(constant_t));
2620 memcpy(t->value, &cval, sizeof(constant_t));
2621 info->value = constant_clone(t->value);
2623 typedcode_t v = node_read($3);
2624 /* initalization code (if needed) */
2626 if(v.c && !is_pushundefined(v.c)) {
2627 c = abc_getlocal_0(c);
2628 c = code_append(c, v.c);
2629 c = converttype(c, v.t, $2);
2630 c = abc_setslot(c, t->slot_id);
2632 *code = code_append(*code, c);
2635 if(slotstate_varconst==KW_CONST) {
2636 t->kind= TRAIT_CONST;
2637 info->flags |= FLAG_CONST;
2644 /* ------------ constants -------------------------------------- */
2646 MAYBECONSTANT: {$$=0;}
2647 MAYBECONSTANT: '=' E {
2648 $$ = malloc(sizeof(constant_t));
2649 *$$ = node_eval($2);
2650 if($$->type == CONSTANT_UNKNOWN)
2651 syntaxerror("can't evaluate default parameter value (needs to be a compile-time constant)");
2654 //CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2655 CONSTANT : T_INT {$$ = constant_new_int($1);}
2657 $$ = constant_new_uint($1);
2659 CONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2660 CONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2661 CONSTANT : "true" {$$ = constant_new_true($1);}
2662 CONSTANT : "false" {$$ = constant_new_false($1);}
2663 CONSTANT : "null" {$$ = constant_new_null($1);}
2664 CONSTANT : "undefined" {$$ = constant_new_undefined($1);}
2665 CONSTANT : KW_NAN {$$ = constant_new_float(__builtin_nan(""));}
2668 CONSTANT : T_IDENTIFIER {
2669 if(!strcmp($1, "NaN")) {
2670 $$ = constant_new_float(__builtin_nan(""));
2672 as3_warning("Couldn't evaluate constant value of %s", $1);
2673 $$ = constant_new_null($1);
2677 /* ------------ classes and interfaces (body, functions) ------- */
2679 // non-vararg version
2682 memset(&$$,0,sizeof($$));
2684 MAYBE_PARAM_LIST: PARAM_LIST {
2690 MAYBE_PARAM_LIST: "..." PARAM {
2692 memset(&$$,0,sizeof($$));
2694 list_append($$.list, $2);
2696 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2700 list_append($$.list, $4);
2704 PARAM_LIST: PARAM_LIST ',' PARAM {
2707 list_append($$.list, $3);
2711 memset(&$$,0,sizeof($$));
2712 list_append($$.list, $1);
2715 PARAM: T_IDENTIFIER ':' TYPE MAYBECONSTANT {
2717 $$ = rfx_calloc(sizeof(param_t));
2723 PARAM: T_IDENTIFIER MAYBECONSTANT {
2725 $$ = rfx_calloc(sizeof(param_t));
2727 $$->type = TYPE_ANY;
2735 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2736 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2739 endfunction(&$1,$3,$4,&$6,0,0);
2741 if(!state->method->info) syntaxerror("internal error");
2743 code_t*c = method_header(state->method);
2744 c = wrap_function(c, 0, $11);
2746 endfunction(&$1,$3,$4,&$6,$8,c);
2748 list_deep_free($6.list);
2752 MAYBE_IDENTIFIER: T_IDENTIFIER
2753 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2754 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2755 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2758 endfunction(0,0,$2,&$4,0,0);
2760 methodinfo_t*f = state->method->info;
2761 if(!f || !f->kind) syntaxerror("internal error");
2763 code_t*c = method_header(state->method);
2764 c = wrap_function(c, 0, $9);
2766 int index = state->method->var_index;
2767 endfunction(0,0,$2,&$4,$6,c);
2769 $$.c = abc_getlocal(0, index);
2770 $$.t = TYPE_FUNCTION(f);
2772 PASS12 list_deep_free($4.list);
2776 /* ------------- package + class ids --------------- */
2778 CLASS: X_IDENTIFIER {
2779 PASS1 NEW(unresolvedinfo_t,c);
2780 memset(c, 0, sizeof(*c));
2781 c->kind = INFOTYPE_UNRESOLVED;
2783 c->package = get_package_from_name($1);
2785 c->nsset = get_current_imports();
2786 /* make the compiler look for this class in the current directory,
2788 as3_schedule_class_noerror(state->package, $1);
2790 $$ = (classinfo_t*)c;
2792 slotinfo_t*s = find_class($1);
2793 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2794 $$ = (classinfo_t*)s;
2797 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
2798 PASS1 NEW(unresolvedinfo_t,c);
2799 memset(c, 0, sizeof(*c));
2800 c->kind = INFOTYPE_UNRESOLVED;
2803 $$ = (classinfo_t*)c;
2805 slotinfo_t*s = registry_find($1, $3);
2806 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2808 $$ = (classinfo_t*)s;
2811 CLASS_SPEC: PACKAGEANDCLASS
2814 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2815 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2817 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2818 | '*' {PASS12 $$=TYPE_ANY;}
2819 | "void" {PASS12 $$=TYPE_ANY;}
2821 | "String" {$$=registry_getstringclass();}
2822 | "int" {$$=registry_getintclass();}
2823 | "uint" {$$=registry_getuintclass();}
2824 | "Boolean" {$$=registry_getbooleanclass();}
2825 | "Number" {$$=registry_getnumberclass();}
2828 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2829 MAYBETYPE: {PASS12 $$=0;}
2831 /* ----------function calls, delete, constructor calls ------ */
2833 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
2834 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2836 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
2837 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2838 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2840 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
2844 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2845 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2846 $$.number= $1.number+1;
2847 $$.cc = code_append($1.cc, $2.c);
2851 NEW : "new" E XX MAYBE_PARAM_VALUES {
2852 typedcode_t v = node_read($2);
2854 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2856 code_t*paramcode = $4.cc;
2857 if($$.c->opcode == OPCODE_GETPROPERTY) {
2858 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2859 $$.c = code_cutlast($$.c);
2860 $$.c = code_append($$.c, paramcode);
2861 $$.c = abc_constructprop2($$.c, name, $4.number);
2862 multiname_destroy(name);
2863 } else if($$.c->opcode == OPCODE_GETSLOT) {
2864 int slot = (int)(ptroff_t)$$.c->data[0];
2865 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2866 multiname_t*name = t->name;
2867 $$.c = code_cutlast($$.c);
2868 $$.c = code_append($$.c, paramcode);
2869 $$.c = abc_constructprop2($$.c, name, $4.number);
2871 $$.c = code_append($$.c, paramcode);
2872 $$.c = abc_construct($$.c, $4.number);
2876 if(TYPE_IS_CLASS(v.t) && v.t->data) {
2879 $$.c = abc_coerce_a($$.c);
2884 /* TODO: use abc_call (for calling local variables),
2885 abc_callstatic (for calling own methods)
2888 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2890 typedcode_t v = node_read($1);
2892 if($$.c->opcode == OPCODE_COERCE_A) {
2893 $$.c = code_cutlast($$.c);
2895 code_t*paramcode = $3.cc;
2898 if($$.c->opcode == OPCODE_GETPROPERTY) {
2899 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2900 $$.c = code_cutlast($$.c);
2901 $$.c = code_append($$.c, paramcode);
2902 $$.c = abc_callproperty2($$.c, name, $3.number);
2903 multiname_destroy(name);
2904 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
2905 int slot = (int)(ptroff_t)$$.c->data[0];
2906 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
2907 if(t->kind!=TRAIT_METHOD) {
2908 //ok: flash allows to assign closures to members.
2910 multiname_t*name = t->name;
2911 $$.c = code_cutlast($$.c);
2912 $$.c = code_append($$.c, paramcode);
2913 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2914 $$.c = abc_callproperty2($$.c, name, $3.number);
2915 } else if($$.c->opcode == OPCODE_GETSUPER) {
2916 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2917 $$.c = code_cutlast($$.c);
2918 $$.c = code_append($$.c, paramcode);
2919 $$.c = abc_callsuper2($$.c, name, $3.number);
2920 multiname_destroy(name);
2922 $$.c = abc_getglobalscope($$.c);
2923 $$.c = code_append($$.c, paramcode);
2924 $$.c = abc_call($$.c, $3.number);
2927 if(TYPE_IS_FUNCTION(v.t) && v.t->data) {
2928 $$.t = ((methodinfo_t*)(v.t->data))->return_type;
2929 } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
2930 // calling a class is like a typecast
2931 $$.t = (classinfo_t*)v.t->data;
2933 $$.c = abc_coerce_a($$.c);
2938 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2939 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2940 if(!state->method) syntaxerror("super() not allowed outside of a function");
2941 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2944 $$.c = abc_getlocal_0($$.c);
2946 $$.c = code_append($$.c, $3.cc);
2948 this is dependent on the control path, check this somewhere else
2949 if(state->method->has_super)
2950 syntaxerror("constructor may call super() only once");
2952 state->method->has_super = 1;
2954 $$.c = abc_constructsuper($$.c, $3.number);
2955 $$.c = abc_pushundefined($$.c);
2959 DELETE: "delete" E {
2960 typedcode_t v = node_read($2);
2962 if($$.c->opcode == OPCODE_COERCE_A) {
2963 $$.c = code_cutlast($$.c);
2965 multiname_t*name = 0;
2966 if($$.c->opcode == OPCODE_GETPROPERTY) {
2967 $$.c->opcode = OPCODE_DELETEPROPERTY;
2968 } else if($$.c->opcode == OPCODE_GETSLOT) {
2969 int slot = (int)(ptroff_t)$$.c->data[0];
2970 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
2971 $$.c = code_cutlast($$.c);
2972 $$.c = abc_deleteproperty2($$.c, name);
2974 $$.c = abc_getlocal_0($$.c);
2975 MULTINAME_LATE(m, v.t?v.t->access:ACCESS_PACKAGE, "");
2976 $$.c = abc_deleteproperty2($$.c, &m);
2978 $$.t = TYPE_BOOLEAN;
2981 RETURN: "return" %prec prec_none {
2982 $$ = abc_returnvoid(0);
2984 RETURN: "return" EXPRESSION {
2986 $$ = abc_returnvalue($$);
2989 // ----------------------- expression types -------------------------------------
2991 NONCOMMAEXPRESSION : E %prec below_minus {
2994 EXPRESSION : COMMA_EXPRESSION {
2997 COMMA_EXPRESSION : E %prec below_minus {
2998 $$ = mkmultinode(&node_comma, $1);
3000 COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_minus {
3001 $$ = multinode_extend($1, $3);
3003 VOIDEXPRESSION : E %prec below_minus {
3006 VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_minus {
3008 $$ = code_append($$, node_exec($3));
3011 MAYBE_DICT_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3012 MAYBE_DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST {$$=$1;}
3014 DICTLH: T_IDENTIFIER {$$=abc_pushstring(0,$1);}
3015 DICTLH: T_STRING {$$=abc_pushstring2(0,&$1);}
3017 DICT_EXPRPAIR_LIST : DICTLH ':' NONCOMMAEXPRESSION {
3019 $$.cc = code_append($$.cc, $1);
3020 $$.cc = code_append($$.cc, $3.c);
3023 DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST ',' DICTLH ':' NONCOMMAEXPRESSION {
3025 $$.number = $1.number+2;
3026 $$.cc = code_append($$.cc, $3);
3027 $$.cc = code_append($$.cc, $5.c);
3030 // ----------------------- expression evaluation -------------------------------------
3032 E : INNERFUNCTION %prec prec_none {$$ = mkcodenode($1);}
3033 E : MEMBER %prec '.' {$$ = mkcodenode($1);}
3034 E : NEW {$$ = mkcodenode($1);}
3035 E : DELETE {$$ = mkcodenode($1);}
3036 E : FUNCTIONCALL {$$ = mkcodenode($1);}
3037 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3040 $$ = mkconstnode($1);
3047 namespace_t ns = {ACCESS_PACKAGE, ""};
3048 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3050 v.c = abc_getlex2(v.c, &m);
3051 v.c = abc_pushstring(v.c, $1.pattern);
3052 v.c = abc_construct(v.c, 1);
3054 v.c = abc_getlex2(v.c, &m);
3055 v.c = abc_pushstring(v.c, $1.pattern);
3056 v.c = abc_pushstring(v.c, $1.options);
3057 v.c = abc_construct(v.c, 2);
3064 E : '[' MAYBE_EXPRESSION_LIST ']' {
3067 v.c = code_append(v.c, $2.cc);
3068 v.c = abc_newarray(v.c, $2.number);
3069 v.t = registry_getarrayclass();
3074 E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' {
3077 v.c = code_append(v.c, $2.cc);
3078 v.c = abc_newobject(v.c, $2.number/2);
3079 v.t = registry_getobjectclass();
3083 E : E '<' E {$$ = mknode2(&node_lt,$1,$3);}
3084 E : E '>' E {$$ = mknode2(&node_gt,$1,$3);}
3085 E : E "<=" E {$$ = mknode2(&node_le,$1,$3);}
3086 E : E ">=" E {$$ = mknode2(&node_ge,$1,$3);}
3087 E : E "==" E {$$ = mknode2(&node_eqeq,$1,$3);}
3088 E : E "===" E {$$ = mknode2(&node_eqeqeq,$1,$3);}
3089 E : E "!==" E {$$ = mknode2(&node_noteqeq,$1,$3);}
3090 E : E "!=" E {$$ = mknode2(&node_noteq,$1,$3);}
3091 E : E "||" E {$$ = mknode2(&node_oror,$1,$3);}
3092 E : E "&&" E {$$ = mknode2(&node_andand,$1,$3);}
3093 E : '!' E {$$ = mknode1(&node_not, $2);}
3094 E : '~' E {$$ = mknode1(&node_bitnot, $2);}
3095 E : E '&' E {$$ = mknode2(&node_bitand, $1, $3);}
3096 E : E '^' E {$$ = mknode2(&node_bitxor, $1, $3);}
3097 E : E '|' E {$$ = mknode2(&node_bitor, $1, $3);}
3098 E : E ">>" E {$$ = mknode2(&node_shr, $1, $3);}
3099 E : E ">>>" E {$$ = mknode2(&node_ushr, $1, $3);}
3100 E : E "<<" E {$$ = mknode2(&node_shl, $1, $3);}
3101 E : E '/' E {$$ = mknode2(&node_div, $1, $3);}
3102 E : E '%' E {$$ = mknode2(&node_mod, $1, $3);}
3103 E : E '+' E {$$ = mknode2(&node_plus, $1, $3);}
3104 E : E '-' E {$$ = mknode2(&node_minus, $1, $3);}
3105 E : E '*' E {$$ = mknode2(&node_multiply, $1, $3);}
3106 E : E "in" E {$$ = mknode2(&node_in, $1, $3);}
3107 E : E "as" E {$$ = mknode2(&node_as, $1, $3);}
3108 E : E "instanceof" E {$$ = mknode2(&node_instanceof, $1, $3);}
3109 E : E "is" E {$$ = mknode2(&node_is, $1, $3);}
3110 E : "typeof" '(' E ')' {$$ = mknode1(&node_typeof, $3);}
3111 E : "void" E {$$ = mknode1(&node_void, $2);}
3112 E : "void" { $$ = mkconstnode(constant_new_undefined());}
3113 E : '(' COMMA_EXPRESSION ')' { $$=$2;}
3114 E : '-' E {$$ = mknode1(&node_neg, $2);}
3115 E : E '[' E ']' {$$ = mknode2(&node_arraylookup, $1,$3);}
3116 E : E "*=" E {$$ = mknode2(&node_muleq, $1, $3);}
3117 E : E "%=" E {$$ = mknode2(&node_modeq, $1, $3);}
3118 E : E "<<=" E {$$ = mknode2(&node_shleq, $1, $3);}
3119 E : E ">>=" E {$$ = mknode2(&node_shreq, $1, $3);}
3120 E : E ">>>=" E {$$ = mknode2(&node_ushreq, $1, $3);}
3121 E : E "/=" E { $$ = mknode2(&node_diveq, $1, $3);}
3122 E : E "|=" E { $$ = mknode2(&node_bitoreq, $1, $3);}
3123 E : E "^=" E { $$ = mknode2(&node_bitxoreq, $1, $3);}
3124 E : E "&=" E { $$ = mknode2(&node_bitandeq, $1, $3);}
3125 E : E "+=" E { $$ = mknode2(&node_pluseq, $1, $3);}
3126 E : E "-=" E { $$ = mknode2(&node_minuseq, $1, $3);}
3127 E : E '=' E { $$ = mknode2(&node_assign, $1, $3);}
3128 E : E '?' E ':' E %prec below_assignment { $$ = mknode3(&node_tenary, $1, $3, $5);}
3130 E : E "++" { $$ = mknode1(&node_rplusplus, $1);}
3131 E : E "--" { $$ = mknode1(&node_rminusminus, $1);}
3132 E : "++" %prec plusplus_prefix E {$$ = mknode1(&node_lplusplus, $2); }
3133 E : "--" %prec minusminus_prefix E {$$ = mknode1(&node_lminusminus, $2); }
3135 E : "super" '.' T_IDENTIFIER
3136 { if(!state->cls->info)
3137 syntaxerror("super keyword not allowed outside a class");
3138 classinfo_t*t = state->cls->info->superclass;
3139 if(!t) t = TYPE_OBJECT;
3140 memberinfo_t*f = findmember_nsset(t, $3, 1);
3141 MEMBER_MULTINAME(m, f, $3);
3144 v.c = abc_getlocal_0(v.c);
3145 v.c = abc_getsuper2(v.c, &m);
3146 v.t = slotinfo_gettype((slotinfo_t*)f);
3150 E : '@' T_IDENTIFIER {
3153 as3_warning("ignored @ operator");
3156 E : E '.' '@' T_IDENTIFIER {
3157 // child attribute TODO
3159 as3_warning("ignored .@ operator");
3162 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3163 // namespace declaration TODO
3165 as3_warning("ignored :: operator");
3168 E : E ".." T_IDENTIFIER {
3171 as3_warning("ignored .. operator");
3174 E : E '.' '(' E ')' {
3177 as3_warning("ignored .() operator");
3180 //E : E "::" '[' E ']' {
3181 // // qualified expression TODO
3182 // $$.c = abc_pushundefined(0);
3184 // as3_warning("ignored ::[] operator");
3187 MEMBER : E '.' T_IDENTIFIER {
3188 typedcode_t v1 = node_read($1);
3190 classinfo_t*t = v1.t;
3192 if(TYPE_IS_CLASS(t) && t->data) {
3197 if(t->subtype==INFOTYPE_UNRESOLVED) {
3198 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3200 memberinfo_t*f = findmember_nsset(t, $3, 1);
3202 if(f && !is_static != !(f->flags&FLAG_STATIC))
3204 if(f && f->slot && !noslot) {
3205 $$.c = abc_getslot($$.c, f->slot);
3208 as3_softwarning("Access of undefined property '%s' in %s", $3, t->name);
3210 MEMBER_MULTINAME(m, f, $3);
3211 $$.c = abc_getproperty2($$.c, &m);
3213 /* determine type */
3214 $$.t = slotinfo_gettype((slotinfo_t*)f);
3216 $$.c = abc_coerce_a($$.c);
3218 } else if(v1.c && v1.c->opcode == OPCODE___PUSHPACKAGE__) {
3219 string_t*package = v1.c->data[0];
3220 char*package2 = concat3(package->str, ".", $3);
3222 slotinfo_t*a = registry_find(package->str, $3);
3225 } else if(dict_contains(state->import_toplevel_packages, package2) ||
3226 registry_ispackage(package2)) {
3228 $$.c->data[0] = string_new4(package2);
3231 syntaxerror("couldn't resolve %s", package2);
3234 /* when resolving a property on an unknown type, we do know the
3235 name of the property (and don't seem to need the package), but
3236 we need to make avm2 try out all access modes */
3237 as3_warning("Resolving %s on unknown type", $3);
3238 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3239 $$.c = abc_getproperty2($$.c, &m);
3240 $$.c = abc_coerce_a($$.c);
3245 VAR_READ : T_IDENTIFIER {
3247 /* Queue unresolved identifiers for checking against the parent
3248 function's variables.
3249 We consider everything which is not a local variable "unresolved".
3250 This encompasses class names, members of the surrounding class
3251 etc. which is *correct* because local variables of the parent function
3254 if(state->method->inner && !find_variable(state, $1)) {
3255 unknown_variable($1);
3258 /* let the compiler know that it might want to check the current directory/package
3259 for this identifier- maybe there's a file $1.as defining $1. */
3260 as3_schedule_class_noerror(state->package, $1);
3272 /* look at variables */
3273 if((v = find_variable(state, $1))) {
3274 // $1 is a local variable
3275 o.c = abc_getlocal(o.c, v->index);
3280 if((v = find_slot(state, $1))) {
3281 o.c = abc_getscopeobject(o.c, 1);
3282 o.c = abc_getslot(o.c, v->index);
3288 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3290 /* look at current class' members */
3291 if(!state->method->inner &&
3293 (f = findmember_nsset(state->cls->info, $1, 1)))
3295 // $1 is a member or attribute in this class
3296 int var_is_static = (f->flags&FLAG_STATIC);
3298 if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
3299 /* if the variable is a constant (and we know what is evaluates to), we
3300 can just use the value itself */
3301 varinfo_t*v = (varinfo_t*)f;
3303 $$ = mkconstnode(v->value);
3308 if(var_is_static >= i_am_static) {
3309 if(f->kind == INFOTYPE_METHOD) {
3310 o.t = TYPE_FUNCTION(f);
3315 if(var_is_static && !i_am_static) {
3316 /* access to a static member from a non-static location.
3317 do this via findpropstrict:
3318 there doesn't seem to be any non-lookup way to access
3319 static properties of a class */
3320 state->method->late_binding = 1;
3322 namespace_t ns = {f->access, f->package};
3323 multiname_t m = {QNAME, &ns, 0, $1};
3324 o.c = abc_findpropstrict2(o.c, &m);
3325 o.c = abc_getproperty2(o.c, &m);
3328 } else if(f->slot>0) {
3329 o.c = abc_getlocal_0(o.c);
3330 o.c = abc_getslot(o.c, f->slot);
3334 namespace_t ns = {f->access, f->package};
3335 multiname_t m = {QNAME, &ns, 0, $1};
3336 o.c = abc_getlocal_0(o.c);
3337 o.c = abc_getproperty2(o.c, &m);
3344 /* look at actual classes, in the current package and imported */
3345 if((a = find_class($1))) {
3351 /* look through package prefixes */
3352 if(dict_contains(state->import_toplevel_packages, $1) ||
3353 registry_ispackage($1)) {
3354 o.c = abc___pushpackage__(o.c, $1);
3356 $$ = mkcodenode(o); //?
3360 /* unknown object, let the avm2 resolve it */
3362 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3363 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3364 state->method->late_binding = 1;
3366 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3369 o.c = abc_findpropstrict2(o.c, &m);
3370 o.c = abc_getproperty2(o.c, &m);
3376 // ----------------- namespaces -------------------------------------------------
3378 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3380 NEW(namespace_decl_t,n);
3385 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3387 NEW(namespace_decl_t,n);
3392 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3394 NEW(namespace_decl_t,n);
3399 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3401 trie_put(active_namespaces, $2->name, (void*)$2->url);
3403 namespace_t access = modifiers2access(&$1);
3404 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3405 var->type = TYPE_NAMESPACE;
3407 ns.access = ACCESS_NAMESPACE;
3409 var->value = constant_new_namespace(&ns);
3415 void add_active_url(const char*url)
3419 list_append(state->active_namespace_urls, n);
3423 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3425 const char*url = $3->name;
3427 varinfo_t*s = (varinfo_t*)$3;
3428 if(s->kind == INFOTYPE_UNRESOLVED) {
3429 s = (varinfo_t*)registry_resolve((slotinfo_t*)s);
3431 syntaxerror("Couldn't resolve namespace %s", $3->name);
3434 if(!s || s->kind != INFOTYPE_VAR)
3435 syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
3436 if(!s->value || !NS_TYPE(s->value->type))
3437 syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
3438 url = s->value->ns->name;
3440 trie_put(active_namespaces, $3->name, (void*)url);
3441 add_active_url(url);