3 Routines for compiling Flash2 AVM2 ABC Actionscript
5 Extension module for the rfxswf library.
6 Part of the swftools package.
8 Copyright (c) 2008 Matthias Kramm <kramm@quiss.org>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
30 #include "tokenizer.h"
45 enum yytokentype token;
47 classinfo_t*classinfo;
48 classinfo_list_t*classinfo_list;
50 slotinfo_list_t*slotinfo_list;
53 unsigned int number_uint;
57 //typedcode_list_t*value_list;
58 codeandnumber_t value_list;
64 for_start_t for_start;
65 abc_exception_t *exception;
69 abc_exception_list_t *l;
75 %token<id> T_IDENTIFIER T_NAMESPACE
77 %token<regexp> T_REGEXP
79 %token<number_int> T_INT
80 %token<number_uint> T_UINT
81 %token<number_uint> T_BYTE
82 %token<number_uint> T_SHORT
83 %token<number_float> T_FLOAT
85 %token<id> T_FOR "for"
86 %token<id> T_WHILE "while"
88 %token<id> T_SWITCH "switch"
90 %token<token> KW_IMPLEMENTS "implements"
91 %token<token> KW_NAMESPACE "namespace"
92 %token<token> KW_PACKAGE "package"
93 %token<token> KW_PROTECTED "protected"
94 %token<token> KW_PUBLIC "public"
95 %token<token> KW_PRIVATE "private"
96 %token<token> KW_USE "use"
97 %token<token> KW_INTERNAL "internal"
98 %token<token> KW_NEW "new"
99 %token<token> KW_NATIVE "native"
100 %token<token> KW_FUNCTION "function"
101 %token<token> KW_FINALLY "finally"
102 %token<token> KW_UNDEFINED "undefined"
103 %token<token> KW_CONTINUE "continue"
104 %token<token> KW_CLASS "class"
105 %token<token> KW_CONST "const"
106 %token<token> KW_CATCH "catch"
107 %token<token> KW_CASE "case"
108 %token<token> KW_SET "set"
109 %token<token> KW_VOID "void"
110 %token<token> KW_THROW "throw"
111 %token<token> KW_STATIC "static"
112 %token<token> KW_WITH "with"
113 %token<token> KW_INSTANCEOF "instanceof"
114 %token<token> KW_IMPORT "import"
115 %token<token> KW_RETURN "return"
116 %token<token> KW_TYPEOF "typeof"
117 %token<token> KW_INTERFACE "interface"
118 %token<token> KW_NULL "null"
119 %token<token> KW_VAR "var"
120 %token<token> KW_DYNAMIC "dynamic"
121 %token<token> KW_OVERRIDE "override"
122 %token<token> KW_FINAL "final"
123 %token<token> KW_EACH "each"
124 %token<token> KW_GET "get"
125 %token<token> KW_TRY "try"
126 %token<token> KW_SUPER "super"
127 %token<token> KW_EXTENDS "extends"
128 %token<token> KW_FALSE "false"
129 %token<token> KW_TRUE "true"
130 %token<token> KW_BOOLEAN "Boolean"
131 %token<token> KW_UINT "uint"
132 %token<token> KW_INT "int"
133 %token<token> KW_NUMBER "Number"
134 %token<token> KW_STRING "String"
135 %token<token> KW_DEFAULT "default"
136 %token<token> KW_DELETE "delete"
137 %token<token> KW_IF "if"
138 %token<token> KW_ELSE "else"
139 %token<token> KW_BREAK "break"
140 %token<token> KW_IS "is"
141 %token<token> KW_IN "in"
142 %token<token> KW_AS "as"
144 %token<token> T_DICTSTART "{ (dictionary)"
145 %token<token> T_EQEQ "=="
146 %token<token> T_EQEQEQ "==="
147 %token<token> T_NE "!="
148 %token<token> T_NEE "!=="
149 %token<token> T_LE "<="
150 %token<token> T_GE ">="
151 %token<token> T_ORBY "|="
152 %token<token> T_DIVBY "/="
153 %token<token> T_MODBY "%="
154 %token<token> T_MULBY "*="
155 %token<token> T_PLUSBY "+="
156 %token<token> T_MINUSBY "-="
157 %token<token> T_XORBY "^="
158 %token<token> T_SHRBY ">>="
159 %token<token> T_SHLBY "<<="
160 %token<token> T_USHRBY ">>>="
161 %token<token> T_OROR "||"
162 %token<token> T_ANDAND "&&"
163 %token<token> T_COLONCOLON "::"
164 %token<token> T_MINUSMINUS "--"
165 %token<token> T_PLUSPLUS "++"
166 %token<token> T_DOTDOT ".."
167 %token<token> T_DOTDOTDOT "..."
168 %token<token> T_SHL "<<"
169 %token<token> T_USHR ">>>"
170 %token<token> T_SHR ">>"
172 %type <for_start> FOR_START
173 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER NAMESPACE_ID
174 %type <token> VARCONST
176 %type <code> CODEPIECE CODE_STATEMENT
177 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
178 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION
179 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
180 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
181 %type <exception> CATCH FINALLY
182 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
183 %type <code> CLASS_DECLARATION
184 %type <code> NAMESPACE_DECLARATION
185 %type <code> INTERFACE_DECLARATION
186 %type <code> VOIDEXPRESSION
187 %type <value> EXPRESSION NONCOMMAEXPRESSION
188 %type <value> MAYBEEXPRESSION
189 %type <value> E DELETE
190 %type <value> CONSTANT
191 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
192 %type <value> INNERFUNCTION
193 %type <code> USE_NAMESPACE
194 %type <code> FOR_INIT
196 %type <classinfo> MAYBETYPE
199 %type <params> PARAM_LIST
200 %type <params> MAYBE_PARAM_LIST
201 %type <flags> MAYBE_MODIFIERS
202 %type <flags> MODIFIER_LIST
203 %type <flags> MODIFIER
204 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
205 %type <classinfo_list> IMPLEMENTS_LIST
206 %type <classinfo> EXTENDS CLASS_SPEC
207 %type <classinfo_list> EXTENDS_LIST
209 %type <classinfo> CLASS PACKAGEANDCLASS
210 %type <classinfo_list> CLASS_SPEC_LIST
212 %type <classinfo> TYPE
213 //%type <token> VARIABLE
214 %type <value> VAR_READ
216 //%type <token> T_IDENTIFIER
217 %type <value> FUNCTIONCALL
218 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
220 // precedence: from low to high
224 %left below_semicolon
227 %nonassoc below_assignment // for ?:, contrary to spec
228 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
235 %nonassoc "==" "!=" "===" "!=="
236 %nonassoc "is" "as" "in"
237 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
238 %left "<<" ">>" ">>>"
242 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
244 %nonassoc below_curly
248 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
251 %left above_identifier
255 // needed for "return" precedence:
256 %nonassoc T_STRING T_REGEXP
257 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
258 %nonassoc "false" "true" "null" "undefined" "super" "function"
265 static int a3_error(char*s)
267 syntaxerror("%s", s);
268 return 0; //make gcc happy
272 static char* concat2(const char* t1, const char* t2)
276 char*text = malloc(l1+l2+1);
277 memcpy(text , t1, l1);
278 memcpy(text+l1, t2, l2);
282 static char* concat3(const char* t1, const char* t2, const char* t3)
287 char*text = malloc(l1+l2+l3+1);
288 memcpy(text , t1, l1);
289 memcpy(text+l1, t2, l2);
290 memcpy(text+l1+l2, t3, l3);
295 typedef struct _import {
299 DECLARE_LIST(import);
301 DECLARE(methodstate);
302 DECLARE_LIST(methodstate);
304 typedef struct _classstate {
310 methodstate_t*static_init;
312 //code_t*static_init;
314 char has_constructor;
317 struct _methodstate {
326 dict_t*unresolved_variables;
329 char uses_parent_function;
334 int var_index; // for inner methods
335 int slot_index; // for inner methods
336 char is_a_slot; // for inner methods
339 abc_exception_list_t*exceptions;
341 methodstate_list_t*innerfunctions;
344 typedef struct _state {
349 import_list_t*wildcard_imports;
350 namespace_list_t*active_namespaces;
352 char has_own_imports;
353 char new_vars; // e.g. transition between two functions
356 methodstate_t*method;
363 typedef struct _global {
367 dict_t*file2token2info;
370 static global_t*global = 0;
371 static state_t* state = 0;
375 #define MULTINAME(m,x) \
379 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
381 #define MEMBER_MULTINAME(m,f,n) \
385 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
386 m##_ns.name = ((slotinfo_t*)(f))->package; \
391 m.namespace_set = 0; \
392 m.name = ((slotinfo_t*)(f))->name; \
394 m.type = MULTINAME; \
396 m.namespace_set = &nopackage_namespace_set; \
400 /* warning: list length of namespace set is undefined */
401 #define MULTINAME_LATE(m, access, package) \
402 namespace_t m##_ns = {access, package}; \
403 namespace_set_t m##_nsset; \
404 namespace_list_t m##_l;m##_l.next = 0; \
405 m##_nsset.namespaces = &m##_l; \
406 m##_nsset = m##_nsset; \
407 m##_l.namespace = &m##_ns; \
408 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
410 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
411 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
412 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
413 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
414 static namespace_list_t nl4 = {&ns4,0};
415 static namespace_list_t nl3 = {&ns3,&nl4};
416 static namespace_list_t nl2 = {&ns2,&nl3};
417 static namespace_list_t nl1 = {&ns1,&nl2};
418 static namespace_set_t nopackage_namespace_set = {&nl1};
420 static void new_state()
423 state_t*oldstate = state;
425 memcpy(s, state, sizeof(state_t)); //shallow copy
427 s->imports = dict_new();
431 state->has_own_imports = 0;
432 state->vars = dict_new();
433 state->old = oldstate;
436 static void state_has_imports()
438 state->wildcard_imports = list_clone(state->wildcard_imports);
439 state->imports = dict_clone(state->imports);
440 state->has_own_imports = 1;
443 static void state_destroy(state_t*state)
445 if(state->has_own_imports) {
446 list_free(state->wildcard_imports);
447 dict_destroy(state->imports);state->imports=0;
449 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
450 dict_destroy(state->imports);state->imports=0;
454 for(t=0;t<state->vars->hashsize;t++) {
455 dictentry_t*e =state->vars->slots[t];
457 free(e->data);e->data=0;
461 dict_destroy(state->vars);state->vars=0;
467 static void old_state()
469 if(!state || !state->old)
470 syntaxerror("invalid nesting");
471 state_t*leaving = state;
475 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
476 free(leaving->method);
479 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
484 state_destroy(leaving);
487 static code_t* method_header(methodstate_t*m);
488 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
489 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
492 static char* internal_filename_package = 0;
493 void initialize_file(char*filename)
496 syntaxerror("invalid call to initialize_file during parsing of another file");
499 state->package = internal_filename_package = strdup(filename);
501 global->token2info = dict_lookup(global->file2token2info,
502 current_filename // use long version
504 if(!global->token2info) {
505 global->token2info = dict_new2(&ptr_type);
506 dict_put(global->file2token2info, current_filename, global->token2info);
510 state->method = rfx_calloc(sizeof(methodstate_t));
511 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
513 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
514 function_initvars(state->method, 0, 0, 1);
515 global->init = abc_initscript(global->file);
516 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
522 if(!state || state->level!=1) {
523 syntaxerror("unexpected end of file in pass %d", as3_pass);
527 code_t*header = method_header(state->method);
528 code_t*c = wrap_function(header, 0, global->init->method->body->code);
529 global->init->method->body->code = c;
530 free(state->method);state->method=0;
533 //free(state->package);state->package=0; // used in registry
534 state_destroy(state);state=0;
537 void initialize_parser()
539 global = rfx_calloc(sizeof(global_t));
540 global->file = abc_file_new();
541 global->file->flags &= ~ABCFILE_LAZY;
542 global->file2token2info = dict_new();
543 global->token2info = 0;
546 void* finish_parser()
548 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
550 global->token2info=0;
556 static void xx_scopetest()
558 /* findpropstrict doesn't just return a scope object- it
559 also makes it "active" somehow. Push local_0 on the
560 scope stack and read it back with findpropstrict, it'll
561 contain properties like "trace". Trying to find the same
562 property on a "vanilla" local_0 yields only a "undefined" */
563 //c = abc_findpropstrict(c, "[package]::trace");
565 /*c = abc_getlocal_0(c);
566 c = abc_findpropstrict(c, "[package]::trace");
568 c = abc_setlocal_1(c);
570 c = abc_pushbyte(c, 0);
571 c = abc_setlocal_2(c);
573 code_t*xx = c = abc_label(c);
574 c = abc_findpropstrict(c, "[package]::trace");
575 c = abc_pushstring(c, "prop:");
576 c = abc_hasnext2(c, 1, 2);
578 c = abc_setlocal_3(c);
579 c = abc_callpropvoid(c, "[package]::trace", 2);
580 c = abc_getlocal_3(c);
582 c = abc_iftrue(c,xx);*/
585 typedef struct _variable {
589 methodstate_t*is_inner_method;
592 static variable_t* find_variable(state_t*s, char*name)
596 v = dict_lookup(s->vars, name);
598 if(s->new_vars) break;
603 static variable_t* find_slot(state_t*s, const char*name)
605 if(s->method && s->method->slots)
606 return dict_lookup(s->method->slots, name);
610 static variable_t* find_variable_safe(state_t*s, char*name)
612 variable_t* v = find_variable(s, name);
614 syntaxerror("undefined variable: %s", name);
617 static char variable_exists(char*name)
619 return dict_contains(state->vars, name);
621 code_t*defaultvalue(code_t*c, classinfo_t*type);
623 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
626 variable_t*v = find_slot(state, name);
632 v->index = state->method->variable_count++;
636 dict_put(state->vars, name, v);
640 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
642 return new_variable2(name, type, init, maybeslot)->index;
645 #define TEMPVARNAME "__as3_temp__"
646 static int gettempvar()
648 variable_t*v = find_variable(state, TEMPVARNAME);
651 return new_variable(TEMPVARNAME, 0, 0, 0);
654 code_t* var_block(code_t*body)
660 for(t=0;t<state->vars->hashsize;t++) {
661 dictentry_t*e = state->vars->slots[t];
663 variable_t*v = (variable_t*)e->data;
664 if(v->type && v->init) {
665 c = defaultvalue(c, v->type);
666 c = abc_setlocal(c, v->index);
667 k = abc_kill(k, v->index);
677 if(x->opcode== OPCODE___BREAK__ ||
678 x->opcode== OPCODE___CONTINUE__) {
679 /* link kill code before break/continue */
680 code_t*e = code_dup(k);
681 code_t*s = code_start(e);
693 c = code_append(c, body);
694 c = code_append(c, k);
698 void unknown_variable(char*name)
700 if(!state->method->unresolved_variables)
701 state->method->unresolved_variables = dict_new();
702 if(!dict_contains(state->method->unresolved_variables, name))
703 dict_put(state->method->unresolved_variables, name, 0);
706 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
708 static void parsererror(const char*file, int line, const char*f)
710 syntaxerror("internal error in %s, %s:%d", f, file, line);
714 static code_t* method_header(methodstate_t*m)
717 if(m->uses_slots || (m->late_binding && !m->inner)) {
718 c = abc_getlocal_0(c);
719 c = abc_pushscope(c);
722 c = abc_newactivation(c);
723 c = abc_pushscope(c);
725 methodstate_list_t*l = m->innerfunctions;
727 parserassert(l->methodstate->abc);
728 if(m->uses_slots && l->methodstate->is_a_slot) {
729 c = abc_getscopeobject(c, 1);
730 c = abc_newfunction(c, l->methodstate->abc);
732 c = abc_setlocal(c, l->methodstate->var_index);
733 c = abc_setslot(c, l->methodstate->slot_index);
735 c = abc_newfunction(c, l->methodstate->abc);
736 c = abc_setlocal(c, l->methodstate->var_index);
738 free(l->methodstate);l->methodstate=0;
742 c = code_append(c, m->header);
745 if(m->is_constructor && !m->has_super) {
746 // call default constructor
747 c = abc_getlocal_0(c);
748 c = abc_constructsuper(c, 0);
750 list_free(m->innerfunctions);
751 m->innerfunctions = 0;
756 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
758 c = code_append(c, header);
759 c = code_append(c, var_block(body));
760 /* append return if necessary */
761 if(!c || (c->opcode != OPCODE_RETURNVOID &&
762 c->opcode != OPCODE_RETURNVALUE)) {
763 c = abc_returnvoid(c);
769 static void startpackage(char*name)
772 /*printf("entering package \"%s\"\n", name);*/
773 state->package = strdup(name);
775 static void endpackage()
777 /*printf("leaving package \"%s\"\n", state->package);*/
779 //used e.g. in classinfo_register:
780 //free(state->package);state->package=0;
785 #define FLAG_PUBLIC 256
786 #define FLAG_PROTECTED 512
787 #define FLAG_PRIVATE 1024
788 #define FLAG_PACKAGEINTERNAL 2048
789 #define FLAG_NAMESPACE 4096
791 static namespace_t modifiers2access(modifiers_t*mod)
796 if(mod->flags&FLAG_NAMESPACE) {
797 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
798 syntaxerror("invalid combination of access levels and namespaces");
799 ns.access = ACCESS_NAMESPACE;
801 } else if(mod->flags&FLAG_PUBLIC) {
802 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
803 syntaxerror("invalid combination of access levels");
804 ns.access = ACCESS_PACKAGE;
805 } else if(mod->flags&FLAG_PRIVATE) {
806 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
807 syntaxerror("invalid combination of access levels");
808 ns.access = ACCESS_PRIVATE;
809 } else if(mod->flags&FLAG_PROTECTED) {
810 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
811 syntaxerror("invalid combination of access levels");
812 ns.access = ACCESS_PROTECTED;
814 ns.access = ACCESS_PACKAGEINTERNAL;
819 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
824 index = new_variable("this", 0, 0, 0);
825 else if(!m->is_global)
826 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
828 index = new_variable("globalscope", 0, 0, 0);
831 parserassert(!index);
834 /* as variables and slots share the same number, make sure
835 that those variable indices are reserved. It's up to the
836 optimizer to later shuffle the variables down to lower
838 m->variable_count = m->uses_slots;
843 for(p=params->list;p;p=p->next) {
844 new_variable(p->param->name, p->param->type, 0, 1);
848 methodstate_list_t*l = m->innerfunctions;
850 methodstate_t*m = l->methodstate;
851 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
852 m->var_index = v->index;
853 m->slot_index = v->index;
854 v->is_inner_method = m;
860 char*as3_globalclass=0;
861 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
864 syntaxerror("inner classes now allowed");
868 classinfo_list_t*mlist=0;
870 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
871 syntaxerror("invalid modifier(s)");
873 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
874 syntaxerror("public and internal not supported at the same time.");
876 /* create the class name, together with the proper attributes */
880 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
881 access = ACCESS_PRIVATE; package = internal_filename_package;
882 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
883 access = ACCESS_PACKAGEINTERNAL; package = state->package;
884 } else if(state->package!=internal_filename_package) {
885 access = ACCESS_PACKAGE; package = state->package;
887 syntaxerror("public classes only allowed inside a package");
891 state->cls = rfx_calloc(sizeof(classstate_t));
892 state->cls->init = rfx_calloc(sizeof(methodstate_t));
893 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
894 /* notice: we make no effort to initialize the top variable (local0) here,
895 even though it has special meaning. We just rely on the facat
896 that pass 1 won't do anything with variables */
898 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
900 /* set current method to constructor- all code within the class-level (except
901 static variable initializations) will be executed during construction time */
902 state->method = state->cls->init;
904 if(registry_find(package, classname)) {
905 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
907 /* build info struct */
908 int num_interfaces = (list_length(implements));
909 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
910 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
914 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
916 state->method = state->cls->init;
917 parserassert(state->cls && state->cls->info);
919 function_initvars(state->cls->init, 0, 0, 1);
920 function_initvars(state->cls->static_init, 0, 0, 0);
922 if(extends && (extends->flags & FLAG_FINAL))
923 syntaxerror("Can't extend final class '%s'", extends->name);
925 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
926 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
928 classinfo_list_t*l = implements;
929 for(l=implements;l;l=l->next) {
930 if(!(l->classinfo->flags & FLAG_INTERFACE))
931 syntaxerror("'%s' is not an interface", l->classinfo->name);
932 state->cls->info->interfaces[pos++] = l->classinfo;
935 /* generate the abc code for this class */
936 MULTINAME(classname2,state->cls->info);
937 multiname_t*extends2 = sig2mname(extends);
939 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
940 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
941 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
942 if(state->cls->info->flags&FLAG_INTERFACE) {
943 abc_class_interface(state->cls->abc);
946 abc_class_protectedNS(state->cls->abc, classname);
948 for(mlist=implements;mlist;mlist=mlist->next) {
949 MULTINAME(m, mlist->classinfo);
950 abc_class_add_interface(state->cls->abc, &m);
953 /* write the construction code for this class to the global init
955 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
957 abc_method_body_t*m = global->init->method->body;
958 __ getglobalscope(m);
959 classinfo_t*s = extends;
964 //TODO: take a look at the current scope stack, maybe
965 // we can re-use something
970 multiname_t*s2 = sig2mname(s);
972 multiname_destroy(s2);
974 __ pushscope(m); count++;
975 m->code = m->code->prev->prev; // invert
977 /* continue appending after last op end */
978 while(m->code && m->code->next) m->code = m->code->next;
980 /* TODO: if this is one of *our* classes, we can also
981 do a getglobalscope/getslot <nr> (which references
982 the init function's slots) */
984 __ getlex2(m, extends2);
986 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
987 stack is not the superclass */
988 __ pushscope(m);count++;
991 /* notice: we get a verify error #1107 if the top element on the scope
992 stack is not the global object */
994 __ pushscope(m);count++;
996 __ newclass(m,state->cls->abc);
1000 __ setslot(m, slotindex);
1001 multiname_destroy(extends2);
1003 /* flash.display.MovieClip handling */
1005 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1006 if(state->package && state->package[0]) {
1007 as3_globalclass = concat3(state->package, ".", classname);
1009 as3_globalclass = strdup(classname);
1015 static void setstaticfunction(int x)
1019 state->method = state->cls->static_init;
1021 state->method = state->cls->init;
1024 parserassert(state->method);
1028 static void endclass()
1031 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1033 c = abc_getlocal_0(c);
1034 c = abc_constructsuper(c, 0);
1035 state->cls->init->header = code_append(state->cls->init->header, c);
1036 state->cls->has_constructor=1;
1038 if(state->cls->init) {
1039 if(state->cls->info->flags&FLAG_INTERFACE) {
1040 if(state->cls->init->header)
1041 syntaxerror("interface can not have class-level code");
1043 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1044 code_t*c = method_header(state->cls->init);
1045 m->body->code = wrap_function(c, 0, m->body->code);
1048 if(state->cls->static_init) {
1049 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1050 code_t*c = method_header(state->cls->static_init);
1051 m->body->code = wrap_function(c, 0, m->body->code);
1058 void check_code_for_break(code_t*c)
1061 if(c->opcode == OPCODE___BREAK__) {
1062 char*name = string_cstr(c->data[0]);
1063 syntaxerror("Unresolved \"break %s\"", name);
1065 if(c->opcode == OPCODE___CONTINUE__) {
1066 char*name = string_cstr(c->data[0]);
1067 syntaxerror("Unresolved \"continue %s\"", name);
1074 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1076 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1077 if(TYPE_IS_NUMBER(t)) {
1078 xassert(c->type == CONSTANT_FLOAT
1079 || c->type == CONSTANT_INT
1080 || c->type == CONSTANT_UINT);
1081 } else if(TYPE_IS_UINT(t)) {
1082 xassert(c->type == CONSTANT_UINT ||
1083 (c->type == CONSTANT_INT && c->i>=0));
1084 } else if(TYPE_IS_INT(t)) {
1085 xassert(c->type == CONSTANT_INT);
1086 } else if(TYPE_IS_BOOLEAN(t)) {
1087 xassert(c->type == CONSTANT_TRUE
1088 || c->type == CONSTANT_FALSE);
1092 static void check_override(memberinfo_t*m, int flags)
1096 if(m->parent == state->cls->info)
1097 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1099 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1100 if(m->access==ACCESS_PRIVATE)
1102 if(m->flags & FLAG_FINAL)
1103 syntaxerror("can't override final member %s", m->name);
1104 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1105 syntaxerror("can't override static member %s", m->name);
1106 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1107 syntaxerror("can't override non-static member %s with static declaration", m->name);
1109 if(!(flags&FLAG_OVERRIDE)) {
1110 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1111 if(m->kind == INFOTYPE_METHOD)
1112 syntaxerror("can't override without explicit 'override' declaration");
1114 syntaxerror("can't override '%s'", m->name);
1119 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1121 methodinfo_t*minfo = 0;
1122 namespace_t ns = modifiers2access(mod);
1125 minfo = methodinfo_register_global(ns.access, state->package, name);
1126 minfo->return_type = return_type;
1127 } else if(getset != KW_GET && getset != KW_SET) {
1129 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1131 printf("%s.%s | %s.%s\n",
1132 m->package, m->name,
1134 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1136 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1137 minfo->return_type = return_type;
1138 // getslot on a member slot only returns "undefined", so no need
1139 // to actually store these
1140 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1142 //class getter/setter
1143 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1145 if(getset == KW_GET)
1147 else if(params->list && params->list->param)
1148 type = params->list->param->type;
1149 // not sure wether to look into superclasses here, too
1150 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1152 if(minfo->kind!=INFOTYPE_SLOT)
1153 syntaxerror("class already contains a method called '%s'", name);
1154 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1155 syntaxerror("class already contains a field called '%s'", name);
1156 if(minfo->subtype & gs)
1157 syntaxerror("getter/setter for '%s' already defined", name);
1158 /* make a setter or getter into a getset */
1159 minfo->subtype |= gs;
1160 if(!minfo->return_type) {
1161 minfo->return_type = type;
1163 if(minfo && minfo->return_type != type)
1164 syntaxerror("different type in getter and setter");
1167 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1168 minfo->kind = INFOTYPE_SLOT; //hack
1169 minfo->subtype = gs;
1170 minfo->return_type = type;
1172 /* can't assign a slot as getter and setter might have different slots */
1173 //minfo->slot = slot;
1175 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1176 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1177 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1182 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1184 //parserassert(state->method && state->method->info);
1186 methodstate_t*parent_method = state->method;
1189 return_type = 0; // not valid in pass 1
1193 state->new_vars = 1;
1196 state->method = rfx_calloc(sizeof(methodstate_t));
1197 state->method->inner = 1;
1198 state->method->variable_count = 0;
1199 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1201 NEW(methodinfo_t,minfo);
1202 minfo->kind = INFOTYPE_METHOD;
1203 minfo->access = ACCESS_PACKAGEINTERNAL;
1205 state->method->info = minfo;
1208 list_append(parent_method->innerfunctions, state->method);
1210 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1212 function_initvars(state->method, params, 0, 1);
1216 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1217 state->method->variable_count = 0;
1218 parserassert(state->method);
1220 state->method->info->return_type = return_type;
1221 function_initvars(state->method, params, 0, 1);
1225 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1226 params_t*params, classinfo_t*return_type)
1228 if(state->method && state->method->info) {
1229 syntaxerror("not able to start another method scope");
1232 state->new_vars = 1;
1235 state->method = rfx_calloc(sizeof(methodstate_t));
1236 state->method->has_super = 0;
1239 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1241 state->method->is_global = 1;
1242 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1244 if(state->method->is_constructor)
1245 name = "__as3_constructor__";
1248 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1250 function_initvars(state->method, params, mod->flags, 1);
1252 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1256 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1257 state->method->variable_count = 0;
1258 parserassert(state->method);
1261 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1262 check_override(m, mod->flags);
1266 state->cls->has_constructor |= state->method->is_constructor;
1269 state->method->info->return_type = return_type;
1270 function_initvars(state->method, params, mod->flags, 1);
1274 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1275 params_t*params, classinfo_t*return_type, code_t*body)
1277 int flags = mod?mod->flags:0;
1280 // store inner methods in variables
1281 function_initvars(state->method, 0, 0, 0);
1283 methodstate_list_t*ml = state->method->innerfunctions;
1284 dict_t*xvars = dict_new();
1286 methodstate_t*m = ml->methodstate;
1287 parserassert(m->inner);
1288 if(m->unresolved_variables) {
1289 dict_t*d = m->unresolved_variables;
1291 for(t=0;t<d->hashsize;t++) {
1292 dictentry_t*l = d->slots[t];
1294 /* check parent method's variables */
1295 if(find_variable(state, l->key)) {
1296 m->uses_parent_function = 1;
1297 state->method->uses_slots = 1;
1298 dict_put(xvars, l->key, 0);
1305 dict_destroy(m->unresolved_variables);
1306 m->unresolved_variables = 0;
1310 if(state->method->uses_slots) {
1311 state->method->slots = dict_new();
1313 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1314 if(v->index && dict_contains(xvars, name)) {
1317 if(v->is_inner_method) {
1318 v->is_inner_method->is_a_slot = 1;
1321 dict_put(state->method->slots, name, v);
1324 state->method->uses_slots = i;
1325 dict_destroy(state->vars);state->vars = 0;
1327 dict_destroy(xvars);
1334 /*if(state->method->uses_parent_function){
1335 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1340 multiname_t*type2 = sig2mname(return_type);
1342 if(state->method->inner) {
1343 f = state->method->abc;
1344 abc_method_init(f, global->file, type2, 1);
1345 } else if(state->method->is_constructor) {
1346 f = abc_class_getconstructor(state->cls->abc, type2);
1347 } else if(!state->method->is_global) {
1348 namespace_t mname_ns = {state->method->info->access, ""};
1349 multiname_t mname = {QNAME, &mname_ns, 0, name};
1351 if(flags&FLAG_STATIC)
1352 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1354 f = abc_class_method(state->cls->abc, type2, &mname);
1355 slot = f->trait->slot_id;
1357 namespace_t mname_ns = {state->method->info->access, state->package};
1358 multiname_t mname = {QNAME, &mname_ns, 0, name};
1360 f = abc_method_new(global->file, type2, 1);
1361 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1362 //abc_code_t*c = global->init->method->body->code;
1364 //flash doesn't seem to allow us to access function slots
1365 //state->method->info->slot = slot;
1367 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1368 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1369 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1370 if(params->varargs) f->flags |= METHOD_NEED_REST;
1374 for(p=params->list;p;p=p->next) {
1375 if(params->varargs && !p->next) {
1376 break; //varargs: omit last parameter in function signature
1378 multiname_t*m = sig2mname(p->param->type);
1379 list_append(f->parameters, m);
1380 if(p->param->value) {
1381 check_constant_against_type(p->param->type, p->param->value);
1382 opt=1;list_append(f->optional_parameters, p->param->value);
1384 syntaxerror("non-optional parameter not allowed after optional parameters");
1387 if(state->method->slots) {
1388 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1390 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1391 multiname_t*type = sig2mname(v->type);
1392 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1393 t->slot_id = v->index;
1398 check_code_for_break(body);
1401 f->body->code = body;
1402 f->body->exceptions = state->method->exceptions;
1403 } else { //interface
1405 syntaxerror("interface methods can't have a method body");
1415 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1420 void breakjumpsto(code_t*c, char*name, code_t*jump)
1423 if(c->opcode == OPCODE___BREAK__) {
1424 string_t*name2 = c->data[0];
1425 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1426 c->opcode = OPCODE_JUMP;
1433 void continuejumpsto(code_t*c, char*name, code_t*jump)
1436 if(c->opcode == OPCODE___CONTINUE__) {
1437 string_t*name2 = c->data[0];
1438 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1439 c->opcode = OPCODE_JUMP;
1447 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1448 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1449 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1451 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1453 if(!type1 || !type2)
1454 return registry_getanytype();
1455 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1456 return registry_getanytype();
1459 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1468 return registry_getanytype();
1470 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1475 return abc_coerce_a(c);
1479 // cast an "any" type to a specific type. subject to
1480 // runtime exceptions
1481 return abc_coerce2(c, &m);
1484 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1485 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1486 // allow conversion between number types
1487 return abc_coerce2(c, &m);
1489 //printf("%s.%s\n", from.package, from.name);
1490 //printf("%s.%s\n", to.package, to.name);
1492 classinfo_t*supertype = from;
1494 if(supertype == to) {
1495 // target type is one of from's superclasses
1496 return abc_coerce2(c, &m);
1499 while(supertype->interfaces[t]) {
1500 if(supertype->interfaces[t]==to) {
1501 // target type is one of from's interfaces
1502 return abc_coerce2(c, &m);
1506 supertype = supertype->superclass;
1508 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1510 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1512 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1515 as3_error("can't convert type %s%s%s to %s%s%s",
1516 from->package, from->package?".":"", from->name,
1517 to->package, to->package?".":"", to->name);
1521 code_t*defaultvalue(code_t*c, classinfo_t*type)
1523 if(TYPE_IS_INT(type)) {
1524 c = abc_pushbyte(c, 0);
1525 } else if(TYPE_IS_UINT(type)) {
1526 c = abc_pushuint(c, 0);
1527 } else if(TYPE_IS_FLOAT(type)) {
1529 } else if(TYPE_IS_BOOLEAN(type)) {
1530 c = abc_pushfalse(c);
1532 //c = abc_pushundefined(c);
1534 c = abc_pushnull(c);
1536 c = abc_coerce2(c, &m);
1541 char is_pushundefined(code_t*c)
1543 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1546 static slotinfo_t* find_class(char*name)
1550 c = registry_find(state->package, name);
1553 /* try explicit imports */
1554 dictentry_t* e = dict_get_slot(state->imports, name);
1557 if(!strcmp(e->key, name)) {
1558 c = (slotinfo_t*)e->data;
1564 /* try package.* imports */
1565 import_list_t*l = state->wildcard_imports;
1567 //printf("does package %s contain a class %s?\n", l->import->package, name);
1568 c = registry_find(l->import->package, name);
1573 /* try global package */
1574 c = registry_find("", name);
1577 /* try local "filename" package */
1578 c = registry_find(internal_filename_package, name);
1584 static char is_getlocal(code_t*c)
1586 if(!c || c->prev || c->next)
1588 return(c->opcode == OPCODE_GETLOCAL
1589 || c->opcode == OPCODE_GETLOCAL_0
1590 || c->opcode == OPCODE_GETLOCAL_1
1591 || c->opcode == OPCODE_GETLOCAL_2
1592 || c->opcode == OPCODE_GETLOCAL_3);
1594 static int getlocalnr(code_t*c)
1596 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1597 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1598 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1599 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1600 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1601 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1605 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1609 [prefix code] [read instruction]
1613 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1615 if(in && in->opcode == OPCODE_COERCE_A) {
1616 in = code_cutlast(in);
1619 syntaxerror("internal error");
1621 /* chop off read instruction */
1625 prefix = r->prev;r->prev = 0;
1631 char use_temp_var = readbefore;
1633 /* generate the write instruction, and maybe append a dup to the prefix code */
1634 code_t* write = abc_nop(0);
1635 if(r->opcode == OPCODE_GETPROPERTY) {
1636 write->opcode = OPCODE_SETPROPERTY;
1637 multiname_t*m = (multiname_t*)r->data[0];
1638 write->data[0] = multiname_clone(m);
1639 if(m->type == QNAME || m->type == MULTINAME) {
1641 prefix = abc_dup(prefix); // we need the object, too
1644 } else if(m->type == MULTINAMEL) {
1646 /* dupping two values on the stack requires 5 operations and one register-
1647 couldn't adobe just have given us a dup2? */
1648 int temp = gettempvar();
1649 prefix = abc_setlocal(prefix, temp);
1650 prefix = abc_dup(prefix);
1651 prefix = abc_getlocal(prefix, temp);
1652 prefix = abc_swap(prefix);
1653 prefix = abc_getlocal(prefix, temp);
1655 prefix = abc_kill(prefix, temp);
1659 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1661 } else if(r->opcode == OPCODE_GETSLOT) {
1662 write->opcode = OPCODE_SETSLOT;
1663 write->data[0] = r->data[0];
1665 prefix = abc_dup(prefix); // we need the object, too
1668 } else if(r->opcode == OPCODE_GETLOCAL) {
1669 write->opcode = OPCODE_SETLOCAL;
1670 write->data[0] = r->data[0];
1671 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1672 write->opcode = OPCODE_SETLOCAL_0;
1673 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1674 write->opcode = OPCODE_SETLOCAL_1;
1675 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1676 write->opcode = OPCODE_SETLOCAL_2;
1677 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1678 write->opcode = OPCODE_SETLOCAL_3;
1681 syntaxerror("illegal lvalue: can't assign a value to this expression");
1688 /* with getproperty/getslot, we have to be extra careful not
1689 to execute the read code twice, as it might have side-effects
1690 (e.g. if the property is in fact a setter/getter combination)
1692 So read the value, modify it, and write it again,
1693 using prefix only once and making sure (by using a temporary
1694 register) that the return value is what we just wrote */
1695 temp = gettempvar();
1696 c = code_append(c, prefix);
1697 c = code_append(c, r);
1700 c = abc_setlocal(c, temp);
1702 c = code_append(c, middlepart);
1705 c = abc_setlocal(c, temp);
1707 c = code_append(c, write);
1708 c = abc_getlocal(c, temp);
1709 c = abc_kill(c, temp);
1711 /* if we're allowed to execute the read code twice *and*
1712 the middlepart doesn't modify the code, things are easier.
1714 code_t* r2 = code_dup(r);
1715 //c = code_append(c, prefix);
1716 parserassert(!prefix);
1717 c = code_append(c, r);
1718 c = code_append(c, middlepart);
1719 c = code_append(c, write);
1720 c = code_append(c, r2);
1723 /* even smaller version: overwrite the value without reading
1727 c = code_append(c, prefix);
1730 c = code_append(c, middlepart);
1731 c = code_append(c, write);
1732 c = code_append(c, r);
1735 temp = gettempvar();
1737 c = code_append(c, prefix);
1739 c = code_append(c, middlepart);
1741 c = abc_setlocal(c, temp);
1742 c = code_append(c, write);
1743 c = abc_getlocal(c, temp);
1744 c = abc_kill(c, temp);
1750 char is_break_or_jump(code_t*c)
1754 if(c->opcode == OPCODE_JUMP ||
1755 c->opcode == OPCODE___BREAK__ ||
1756 c->opcode == OPCODE___CONTINUE__ ||
1757 c->opcode == OPCODE_THROW ||
1758 c->opcode == OPCODE_RETURNVOID ||
1759 c->opcode == OPCODE_RETURNVALUE) {
1766 #define IS_FINALLY_TARGET(op) \
1767 ((op) == OPCODE___CONTINUE__ || \
1768 (op) == OPCODE___BREAK__ || \
1769 (op) == OPCODE_RETURNVOID || \
1770 (op) == OPCODE_RETURNVALUE || \
1771 (op) == OPCODE___RETHROW__)
1773 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1775 #define NEED_EXTRA_STACK_ARG
1776 code_t*finally_label = abc_nop(0);
1777 NEW(lookupswitch_t, l);
1783 code_t*prev = i->prev;
1784 if(IS_FINALLY_TARGET(i->opcode)) {
1787 if(i->opcode == OPCODE___RETHROW__ ||
1788 i->opcode == OPCODE_RETURNVALUE) {
1789 if(i->opcode == OPCODE___RETHROW__)
1790 i->opcode = OPCODE_THROW;
1792 p = abc_coerce_a(p);
1793 p = abc_setlocal(p, tempvar);
1795 p = abc_pushbyte(p, count++);
1796 p = abc_jump(p, finally_label);
1797 code_t*target = p = abc_label(p);
1798 #ifdef NEED_EXTRA_STACK_ARG
1802 p = abc_getlocal(p, tempvar);
1805 p->next = i;i->prev = p;
1806 list_append(l->targets, target);
1812 c = abc_pushbyte(c, -1);
1813 c = code_append(c, finally_label);
1814 c = code_append(c, finally);
1816 #ifdef NEED_EXTRA_STACK_ARG
1819 c = abc_lookupswitch(c, l);
1820 c = l->def = abc_label(c);
1821 #ifdef NEED_EXTRA_STACK_ARG
1828 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1832 code_t*prev = i->prev;
1833 if(IS_FINALLY_TARGET(i->opcode)) {
1834 if(i->opcode == OPCODE___RETHROW__)
1835 i->opcode = OPCODE_THROW;
1836 code_t*end = code_dup(finally);
1837 code_t*start = code_start(end);
1838 if(prev) prev->next = start;
1845 return code_append(c, finally);
1848 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1854 int num_insertion_points=0;
1856 if(IS_FINALLY_TARGET(i->opcode))
1857 num_insertion_points++;
1864 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1869 int simple_version_cost = (1+num_insertion_points)*code_size;
1870 int lookup_version_cost = 4*num_insertion_points + 5;
1872 if(cantdup || simple_version_cost > lookup_version_cost) {
1873 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1874 return insert_finally_lookup(c, finally, tempvar);
1876 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1877 return insert_finally_simple(c, finally, tempvar);
1881 #define PASS1 }} if(as3_pass == 1) {{
1882 #define PASS1END }} if(as3_pass == 2) {{
1883 #define PASS2 }} if(as3_pass == 2) {{
1884 #define PASS12 }} {{
1885 #define PASS12END }} if(as3_pass == 2) {{
1891 /* ------------ code blocks / statements ---------------- */
1893 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1895 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1896 PROGRAM_CODE_LIST: PROGRAM_CODE
1897 | PROGRAM_CODE_LIST PROGRAM_CODE
1899 PROGRAM_CODE: PACKAGE_DECLARATION
1900 | INTERFACE_DECLARATION
1902 | FUNCTION_DECLARATION
1905 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
1908 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1909 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1910 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1912 INPACKAGE_CODE: INTERFACE_DECLARATION
1914 | FUNCTION_DECLARATION
1917 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
1920 MAYBECODE: CODE {$$=$1;}
1921 MAYBECODE: {$$=code_new();}
1923 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1924 CODE: CODEPIECE {$$=$1;}
1926 // code which may appear outside of methods
1927 CODE_STATEMENT: IMPORT
1929 CODE_STATEMENT: FOR_IN
1930 CODE_STATEMENT: WHILE
1931 CODE_STATEMENT: DO_WHILE
1932 CODE_STATEMENT: SWITCH
1934 CODE_STATEMENT: WITH
1936 CODE_STATEMENT: VOIDEXPRESSION
1937 CODE_STATEMENT: USE_NAMESPACE
1938 CODE_STATEMENT: NAMESPACE_DECLARATION
1939 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1940 CODE_STATEMENT: '{' '}' {$$=0;}
1942 // code which may appear in methods
1943 CODEPIECE: ';' {$$=0;}
1944 CODEPIECE: CODE_STATEMENT
1945 CODEPIECE: VARIABLE_DECLARATION
1950 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
1952 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1953 //CODEBLOCK : '{' '}' {$$=0;}
1954 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1955 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1957 /* ------------ package init code ------------------- */
1959 PACKAGE_INITCODE: CODE_STATEMENT {
1960 code_t**cc = &global->init->method->body->code;
1961 *cc = code_append(*cc, $1);
1964 /* ------------ conditional compilation ------------- */
1966 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
1968 /* ------------ variables --------------------------- */
1970 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1971 | {$$.c=abc_pushundefined(0);
1975 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1976 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1978 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1979 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1981 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1984 if(variable_exists($1))
1985 syntaxerror("Variable %s already defined", $1);
1987 new_variable($1, 0, 1, 0);
1990 if(!is_subtype_of($3.t, $2)) {
1991 syntaxerror("Can't convert %s to %s", $3.t->name,
1997 if(state->method->uses_slots) {
1998 variable_t* v = find_slot(state, $1);
2000 // this variable is stored in a slot
2008 index = new_variable($1, $2, 1, 0);
2011 $$ = slot?abc_getscopeobject(0, 1):0;
2014 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2015 $$ = code_append($$, $3.c);
2016 $$ = converttype($$, $3.t, $2);
2019 $$ = defaultvalue($$, $2);
2022 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2023 $$ = code_append($$, $3.c);
2024 $$ = abc_coerce_a($$);
2026 // don't do anything
2034 $$ = abc_setslot($$, index);
2036 $$ = abc_setlocal($$, index);
2040 /* ------------ control flow ------------------------- */
2042 MAYBEELSE: %prec below_else {$$ = code_new();}
2043 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2044 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2046 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2049 $$ = code_append($$, $4.c);
2050 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2052 $$ = code_append($$, $6);
2054 myjmp = $$ = abc_jump($$, 0);
2056 myif->branch = $$ = abc_nop($$);
2058 $$ = code_append($$, $7);
2059 myjmp->branch = $$ = abc_nop($$);
2065 FOR_INIT : {$$=code_new();}
2066 FOR_INIT : VARIABLE_DECLARATION
2067 FOR_INIT : VOIDEXPRESSION
2069 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2070 // (I don't see any easy way to revolve this conflict otherwise, as we
2071 // can't touch VAR_READ without upsetting the precedence about "return")
2072 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2073 PASS1 $$=$2;new_variable($2,0,1,0);
2074 PASS2 $$=$2;new_variable($2,$3,1,0);
2076 FOR_IN_INIT : T_IDENTIFIER {
2081 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2082 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2084 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2085 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2087 $$ = code_append($$, $2);
2088 code_t*loopstart = $$ = abc_label($$);
2089 $$ = code_append($$, $4.c);
2090 code_t*myif = $$ = abc_iffalse($$, 0);
2091 $$ = code_append($$, $8);
2092 code_t*cont = $$ = abc_nop($$);
2093 $$ = code_append($$, $6);
2094 $$ = abc_jump($$, loopstart);
2095 code_t*out = $$ = abc_nop($$);
2096 breakjumpsto($$, $1.name, out);
2097 continuejumpsto($$, $1.name, cont);
2104 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2105 variable_t*var = find_variable(state, $2);
2106 char*tmp1name = concat2($2, "__tmp1__");
2107 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2108 char*tmp2name = concat2($2, "__array__");
2109 int array = new_variable(tmp1name, 0, 0, 0);
2112 $$ = code_append($$, $4.c);
2113 $$ = abc_coerce_a($$);
2114 $$ = abc_setlocal($$, array);
2115 $$ = abc_pushbyte($$, 0);
2116 $$ = abc_setlocal($$, it);
2118 code_t*loopstart = $$ = abc_label($$);
2120 $$ = abc_hasnext2($$, array, it);
2121 code_t*myif = $$ = abc_iffalse($$, 0);
2122 $$ = abc_getlocal($$, array);
2123 $$ = abc_getlocal($$, it);
2125 $$ = abc_nextname($$);
2127 $$ = abc_nextvalue($$);
2128 $$ = converttype($$, 0, var->type);
2129 $$ = abc_setlocal($$, var->index);
2131 $$ = code_append($$, $6);
2132 $$ = abc_jump($$, loopstart);
2134 code_t*out = $$ = abc_nop($$);
2135 breakjumpsto($$, $1.name, out);
2136 continuejumpsto($$, $1.name, loopstart);
2148 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2152 code_t*myjmp = $$ = abc_jump($$, 0);
2153 code_t*loopstart = $$ = abc_label($$);
2154 $$ = code_append($$, $6);
2155 code_t*cont = $$ = abc_nop($$);
2156 myjmp->branch = cont;
2157 $$ = code_append($$, $4.c);
2158 $$ = abc_iftrue($$, loopstart);
2159 code_t*out = $$ = abc_nop($$);
2160 breakjumpsto($$, $1, out);
2161 continuejumpsto($$, $1, cont);
2167 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2169 code_t*loopstart = $$ = abc_label($$);
2170 $$ = code_append($$, $3);
2171 code_t*cont = $$ = abc_nop($$);
2172 $$ = code_append($$, $6.c);
2173 $$ = abc_iftrue($$, loopstart);
2174 code_t*out = $$ = abc_nop($$);
2175 breakjumpsto($$, $1, out);
2176 continuejumpsto($$, $1, cont);
2182 BREAK : "break" %prec prec_none {
2183 $$ = abc___break__(0, "");
2185 BREAK : "break" T_IDENTIFIER {
2186 $$ = abc___break__(0, $2);
2188 CONTINUE : "continue" %prec prec_none {
2189 $$ = abc___continue__(0, "");
2191 CONTINUE : "continue" T_IDENTIFIER {
2192 $$ = abc___continue__(0, $2);
2195 MAYBE_CASE_LIST : {$$=0;}
2196 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2197 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2198 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2199 CASE_LIST: CASE {$$=$1;}
2200 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2202 CASE: "case" E ':' MAYBECODE {
2204 $$ = code_append($$, $2.c);
2205 code_t*j = $$ = abc_ifne($$, 0);
2206 $$ = code_append($$, $4);
2207 if($$->opcode != OPCODE___BREAK__) {
2208 $$ = abc___fallthrough__($$, "");
2210 code_t*e = $$ = abc_nop($$);
2213 DEFAULT: "default" ':' MAYBECODE {
2216 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2218 $$ = code_append($$, $7);
2219 code_t*out = $$ = abc_pop($$);
2220 breakjumpsto($$, $1, out);
2222 code_t*c = $$,*lastblock=0;
2224 if(c->opcode == OPCODE_IFNE) {
2225 if(!c->next) syntaxerror("internal error in fallthrough handling");
2227 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2229 c->opcode = OPCODE_JUMP;
2230 c->branch = lastblock;
2232 /* fall through end of switch */
2233 c->opcode = OPCODE_NOP;
2243 /* ------------ try / catch /finally ---------------- */
2245 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2246 state->exception_name=$3;
2247 PASS1 new_variable($3, 0, 0, 0);
2248 PASS2 new_variable($3, $4, 0, 0);
2251 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2252 multiname_t name = {QNAME, &name_ns, 0, $3};
2254 NEW(abc_exception_t, e)
2255 e->exc_type = sig2mname($4);
2256 e->var_name = multiname_clone(&name);
2260 int i = find_variable_safe(state, $3)->index;
2261 e->target = c = abc_nop(0);
2262 c = abc_setlocal(c, i);
2263 c = code_append(c, $8);
2269 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2274 NEW(abc_exception_t, e)
2275 e->exc_type = 0; //all exceptions
2276 e->var_name = 0; //no name
2279 e->to = code_append(e->to, $4);
2285 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2286 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2287 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2288 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2292 list_append($$.l,$2);
2293 $$.finally = $2->to;$2->to=0;
2296 CATCH_FINALLY_LIST: FINALLY {
2300 list_append($$.l,$1);
2301 $$.finally = $1->to;$1->to=0;
2305 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2306 code_t*out = abc_nop(0);
2308 code_t*start = abc_nop(0);
2309 $$ = code_append(start, $4);
2310 if(!is_break_or_jump($4)) {
2311 $$ = abc_jump($$, out);
2313 code_t*end = $$ = abc_nop($$);
2317 tmp = new_variable("__finally__", 0, 0, 0);
2319 abc_exception_list_t*l = $6.l;
2322 abc_exception_t*e = l->abc_exception;
2324 $$ = code_append($$, e->target);
2325 $$ = abc_jump($$, out);
2327 parserassert((ptroff_t)$6.finally);
2329 e->target = $$ = abc_nop($$);
2330 $$ = abc___rethrow__($$);
2338 $$ = code_append($$, out);
2340 $$ = insert_finally($$, $6.finally, tmp);
2342 list_concat(state->method->exceptions, $6.l);
2348 /* ------------ throw ------------------------------- */
2350 THROW : "throw" EXPRESSION {
2354 THROW : "throw" %prec prec_none {
2355 if(!state->exception_name)
2356 syntaxerror("re-throw only possible within a catch block");
2357 variable_t*v = find_variable(state, state->exception_name);
2359 $$=abc_getlocal($$, v->index);
2363 /* ------------ with -------------------------------- */
2365 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2367 $$ = abc_pushscope($$);
2368 $$ = code_append($$, $5);
2369 $$ = abc_popscope($$);
2372 /* ------------ packages and imports ---------------- */
2374 X_IDENTIFIER: T_IDENTIFIER
2375 | "package" {PASS12 $$="package";}
2377 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2378 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2380 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2381 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2382 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2383 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2385 IMPORT : "import" PACKAGEANDCLASS {
2387 slotinfo_t*s = registry_find($2->package, $2->name);
2388 if(!s) {// || !(s->flags&FLAG_BUILTIN)) {
2389 as3_schedule_class($2->package, $2->name);
2395 syntaxerror("Couldn't import class\n");
2396 state_has_imports();
2397 dict_put(state->imports, c->name, c);
2400 IMPORT : "import" PACKAGE '.' '*' {
2402 if(strncmp("flash.", $2, 6)) {
2403 as3_schedule_package($2);
2409 state_has_imports();
2410 list_append(state->wildcard_imports, i);
2414 /* ------------ classes and interfaces (header) -------------- */
2416 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2417 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2418 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2419 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2421 $$.flags=$1.flags|$2.flags;
2422 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2423 $$.ns=$1.ns?$1.ns:$2.ns;
2427 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2428 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2429 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2430 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2431 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2432 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2433 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2434 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2435 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2436 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2440 EXTENDS : {$$=registry_getobjectclass();}
2441 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2443 EXTENDS_LIST : {PASS12 $$=list_new();}
2444 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2446 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2447 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2449 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2450 EXTENDS IMPLEMENTS_LIST
2451 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2453 '}' {PASS12 endclass();$$=0;}
2455 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2457 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2458 startclass(&$1,$3,0,$4);}
2459 MAYBE_INTERFACE_BODY
2460 '}' {PASS12 endclass();$$=0;}
2462 /* ------------ classes and interfaces (body) -------------- */
2465 MAYBE_CLASS_BODY : CLASS_BODY
2466 CLASS_BODY : CLASS_BODY_ITEM
2467 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2468 CLASS_BODY_ITEM : ';'
2469 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2470 CLASS_BODY_ITEM : SLOT_DECLARATION
2471 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2473 CLASS_BODY_ITEM : CODE_STATEMENT {
2474 code_t*c = state->cls->static_init->header;
2475 c = code_append(c, $1);
2476 state->cls->static_init->header = c;
2479 MAYBE_INTERFACE_BODY :
2480 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2481 INTERFACE_BODY : IDECLARATION
2482 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2484 IDECLARATION : "var" T_IDENTIFIER {
2485 syntaxerror("variable declarations not allowed in interfaces");
2487 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2489 $1.flags |= FLAG_PUBLIC;
2490 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2491 syntaxerror("invalid method modifiers: interface methods always need to be public");
2493 startfunction(&$1,$3,$4,&$6,$8);
2494 endfunction(&$1,$3,$4,&$6,$8, 0);
2495 list_deep_free($6.list);
2498 /* ------------ classes and interfaces (body, slots ) ------- */
2500 VARCONST: "var" | "const"
2502 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1.flags);} MAYBETYPE MAYBEEXPRESSION {
2503 int flags = $1.flags;
2504 namespace_t ns = modifiers2access(&$1);
2506 varinfo_t* info = 0;
2508 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $3, 1);
2510 check_override(i, flags);
2512 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $3);
2514 slotinfo_t*i = registry_find(state->package, $3);
2516 syntaxerror("package %s already contains '%s'", state->package, $3);
2518 if(ns.name && ns.name[0]) {
2519 syntaxerror("namespaces not allowed on package-level variables");
2521 info = varinfo_register_global(ns.access, state->package, $3);
2525 info->flags = flags;
2528 multiname_t mname = {QNAME, &ns, 0, $3};
2530 trait_list_t**traits;
2534 ns.name = state->package;
2535 traits = &global->init->traits;
2536 code = &global->init->method->body->code;
2537 } else if(flags&FLAG_STATIC) {
2539 traits = &state->cls->abc->static_traits;
2540 code = &state->cls->static_init->header;
2542 // instance variable
2543 traits = &state->cls->abc->traits;
2544 code = &state->cls->init->header;
2550 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2552 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2554 info->slot = t->slot_id;
2556 /* initalization code (if needed) */
2558 if($6.c && !is_pushundefined($6.c)) {
2559 c = abc_getlocal_0(c);
2560 c = code_append(c, $6.c);
2561 c = converttype(c, $6.t, $5);
2562 c = abc_setslot(c, t->slot_id);
2565 *code = code_append(*code, c);
2568 t->kind= TRAIT_CONST;
2572 setstaticfunction(0);
2575 /* ------------ constants -------------------------------------- */
2577 MAYBESTATICCONSTANT: {$$=0;}
2578 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2580 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2581 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2582 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2583 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2584 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2585 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2586 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2587 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2588 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2589 STATICCONSTANT : T_IDENTIFIER {
2591 as3_warning("Couldn't resolve %s", $1);
2592 $$ = constant_new_null($1);
2595 /* ------------ classes and interfaces (body, functions) ------- */
2597 // non-vararg version
2600 memset(&$$,0,sizeof($$));
2602 MAYBE_PARAM_LIST: PARAM_LIST {
2608 MAYBE_PARAM_LIST: "..." PARAM {
2610 memset(&$$,0,sizeof($$));
2612 list_append($$.list, $2);
2614 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2618 list_append($$.list, $4);
2622 PARAM_LIST: PARAM_LIST ',' PARAM {
2625 list_append($$.list, $3);
2629 memset(&$$,0,sizeof($$));
2630 list_append($$.list, $1);
2633 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2635 $$ = rfx_calloc(sizeof(param_t));
2641 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2643 $$ = rfx_calloc(sizeof(param_t));
2645 $$->type = TYPE_ANY;
2653 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2654 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2657 endfunction(&$1,$3,$4,&$6,0,0);
2659 if(!state->method->info) syntaxerror("internal error");
2661 code_t*c = method_header(state->method);
2662 c = wrap_function(c, 0, $11);
2664 endfunction(&$1,$3,$4,&$6,$8,c);
2666 list_deep_free($6.list);
2670 MAYBE_IDENTIFIER: T_IDENTIFIER
2671 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2672 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2673 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2676 endfunction(0,0,$2,&$4,0,0);
2678 methodinfo_t*f = state->method->info;
2679 if(!f || !f->kind) syntaxerror("internal error");
2681 code_t*c = method_header(state->method);
2682 c = wrap_function(c, 0, $9);
2684 int index = state->method->var_index;
2685 endfunction(0,0,$2,&$4,$6,c);
2687 $$.c = abc_getlocal(0, index);
2688 $$.t = TYPE_FUNCTION(f);
2690 PASS12 list_deep_free($4.list);
2694 /* ------------- package + class ids --------------- */
2696 CLASS: T_IDENTIFIER {
2697 PASS1 static slotinfo_t c;
2698 memset(&c, 0, sizeof(c));
2700 $$ = (classinfo_t*)&c;
2702 slotinfo_t*s = find_class($1);
2703 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2704 $$ = (classinfo_t*)s;
2707 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2708 PASS1 static slotinfo_t c;
2709 memset(&c, 0, sizeof(c));
2712 $$=(classinfo_t*)&c;
2714 slotinfo_t*s = registry_find($1, $3);
2715 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2717 $$ = (classinfo_t*)s;
2720 CLASS_SPEC: PACKAGEANDCLASS
2723 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2724 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2726 TYPE : CLASS_SPEC {$$=$1;}
2727 | '*' {$$=registry_getanytype();}
2728 | "void" {$$=registry_getanytype();}
2730 | "String" {$$=registry_getstringclass();}
2731 | "int" {$$=registry_getintclass();}
2732 | "uint" {$$=registry_getuintclass();}
2733 | "Boolean" {$$=registry_getbooleanclass();}
2734 | "Number" {$$=registry_getnumberclass();}
2737 MAYBETYPE: ':' TYPE {$$=$2;}
2740 /* ----------function calls, delete, constructor calls ------ */
2742 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2743 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2745 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2746 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2747 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2749 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2753 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2754 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2756 $$.cc = code_append($1.cc, $2.c);
2760 NEW : "new" E XX MAYBE_PARAM_VALUES {
2762 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2764 code_t*paramcode = $4.cc;
2765 if($$.c->opcode == OPCODE_GETPROPERTY) {
2766 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2767 $$.c = code_cutlast($$.c);
2768 $$.c = code_append($$.c, paramcode);
2769 $$.c = abc_constructprop2($$.c, name, $4.len);
2770 multiname_destroy(name);
2771 } else if($$.c->opcode == OPCODE_GETSLOT) {
2772 int slot = (int)(ptroff_t)$$.c->data[0];
2773 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2774 multiname_t*name = t->name;
2775 $$.c = code_cutlast($$.c);
2776 $$.c = code_append($$.c, paramcode);
2777 $$.c = abc_constructprop2($$.c, name, $4.len);
2779 $$.c = code_append($$.c, paramcode);
2780 $$.c = abc_construct($$.c, $4.len);
2784 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2787 $$.c = abc_coerce_a($$.c);
2792 /* TODO: use abc_call (for calling local variables),
2793 abc_callstatic (for calling own methods)
2796 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2799 if($$.c->opcode == OPCODE_COERCE_A) {
2800 $$.c = code_cutlast($$.c);
2802 code_t*paramcode = $3.cc;
2805 if($$.c->opcode == OPCODE_GETPROPERTY) {
2806 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2807 $$.c = code_cutlast($$.c);
2808 $$.c = code_append($$.c, paramcode);
2809 $$.c = abc_callproperty2($$.c, name, $3.len);
2810 multiname_destroy(name);
2811 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
2812 int slot = (int)(ptroff_t)$$.c->data[0];
2813 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
2814 if(t->kind!=TRAIT_METHOD) {
2815 //ok: flash allows to assign closures to members.
2817 multiname_t*name = t->name;
2818 $$.c = code_cutlast($$.c);
2819 $$.c = code_append($$.c, paramcode);
2820 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2821 $$.c = abc_callproperty2($$.c, name, $3.len);
2822 } else if($$.c->opcode == OPCODE_GETSUPER) {
2823 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2824 $$.c = code_cutlast($$.c);
2825 $$.c = code_append($$.c, paramcode);
2826 $$.c = abc_callsuper2($$.c, name, $3.len);
2827 multiname_destroy(name);
2829 $$.c = abc_getglobalscope($$.c);
2830 $$.c = code_append($$.c, paramcode);
2831 $$.c = abc_call($$.c, $3.len);
2834 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2835 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2837 $$.c = abc_coerce_a($$.c);
2842 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2843 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2844 if(!state->method) syntaxerror("super() not allowed outside of a function");
2845 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2848 $$.c = abc_getlocal_0($$.c);
2850 $$.c = code_append($$.c, $3.cc);
2852 this is dependent on the control path, check this somewhere else
2853 if(state->method->has_super)
2854 syntaxerror("constructor may call super() only once");
2856 state->method->has_super = 1;
2858 $$.c = abc_constructsuper($$.c, $3.len);
2859 $$.c = abc_pushundefined($$.c);
2863 DELETE: "delete" E {
2865 if($$.c->opcode == OPCODE_COERCE_A) {
2866 $$.c = code_cutlast($$.c);
2868 multiname_t*name = 0;
2869 if($$.c->opcode == OPCODE_GETPROPERTY) {
2870 $$.c->opcode = OPCODE_DELETEPROPERTY;
2871 } else if($$.c->opcode == OPCODE_GETSLOT) {
2872 int slot = (int)(ptroff_t)$$.c->data[0];
2873 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
2874 $$.c = code_cutlast($$.c);
2875 $$.c = abc_deleteproperty2($$.c, name);
2877 $$.c = abc_getlocal_0($$.c);
2878 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2879 $$.c = abc_deleteproperty2($$.c, &m);
2881 $$.t = TYPE_BOOLEAN;
2884 RETURN: "return" %prec prec_none {
2885 $$ = abc_returnvoid(0);
2887 RETURN: "return" EXPRESSION {
2889 $$ = abc_returnvalue($$);
2892 // ----------------------- expression types -------------------------------------
2894 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2895 EXPRESSION : E %prec below_minus {$$ = $1;}
2896 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2898 $$.c = cut_last_push($$.c);
2899 $$.c = code_append($$.c,$3.c);
2902 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2903 $$=cut_last_push($1.c);
2906 // ----------------------- expression evaluation -------------------------------------
2908 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2909 //V : CONSTANT {$$ = 0;}
2911 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2912 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2913 //V : NEW {$$ = $1.c;}
2915 //V : DELETE {$$ = $1.c;}
2916 E : DELETE {$$ = $1;}
2922 namespace_t ns = {ACCESS_PACKAGE, ""};
2923 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2925 $$.c = abc_getlex2($$.c, &m);
2926 $$.c = abc_pushstring($$.c, $1.pattern);
2927 $$.c = abc_construct($$.c, 1);
2929 $$.c = abc_getlex2($$.c, &m);
2930 $$.c = abc_pushstring($$.c, $1.pattern);
2931 $$.c = abc_pushstring($$.c, $1.options);
2932 $$.c = abc_construct($$.c, 2);
2937 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2938 //MULTINAME(m, registry_getintclass());
2939 //$$.c = abc_coerce2($$.c, &m); // FIXME
2942 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2945 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2948 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2951 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2954 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
2957 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2960 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2961 $$.t = TYPE_BOOLEAN;
2963 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2964 $$.t = TYPE_BOOLEAN;
2966 CONSTANT : "null" {$$.c = abc_pushnull(0);
2970 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2971 $$.t = TYPE_BOOLEAN;
2973 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2974 $$.t = TYPE_BOOLEAN;
2976 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2977 $$.t = TYPE_BOOLEAN;
2979 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2980 $$.t = TYPE_BOOLEAN;
2982 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2983 $$.t = TYPE_BOOLEAN;
2985 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2986 $$.t = TYPE_BOOLEAN;
2988 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2989 $$.t = TYPE_BOOLEAN;
2991 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2992 $$.t = TYPE_BOOLEAN;
2995 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2997 $$.c = converttype($$.c, $1.t, $$.t);
2998 $$.c = abc_dup($$.c);
2999 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3000 $$.c = cut_last_push($$.c);
3001 $$.c = code_append($$.c,$3.c);
3002 $$.c = converttype($$.c, $3.t, $$.t);
3003 code_t*label = $$.c = abc_label($$.c);
3004 jmp->branch = label;
3007 $$.t = join_types($1.t, $3.t, 'A');
3008 /*printf("%08x:\n",$1.t);
3009 code_dump($1.c, 0, 0, "", stdout);
3010 printf("%08x:\n",$3.t);
3011 code_dump($3.c, 0, 0, "", stdout);
3012 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3014 $$.c = converttype($$.c, $1.t, $$.t);
3015 $$.c = abc_dup($$.c);
3016 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3017 $$.c = cut_last_push($$.c);
3018 $$.c = code_append($$.c,$3.c);
3019 $$.c = converttype($$.c, $3.t, $$.t);
3020 code_t*label = $$.c = abc_label($$.c);
3021 jmp->branch = label;
3024 E : '!' E {$$.c=$2.c;
3025 $$.c = abc_not($$.c);
3026 $$.t = TYPE_BOOLEAN;
3029 E : '~' E {$$.c=$2.c;
3030 $$.c = abc_bitnot($$.c);
3034 E : E '&' E {$$.c = code_append($1.c,$3.c);
3035 $$.c = abc_bitand($$.c);
3039 E : E '^' E {$$.c = code_append($1.c,$3.c);
3040 $$.c = abc_bitxor($$.c);
3044 E : E '|' E {$$.c = code_append($1.c,$3.c);
3045 $$.c = abc_bitor($$.c);
3049 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3050 $$.c = abc_rshift($$.c);
3053 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3054 $$.c = abc_urshift($$.c);
3057 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3058 $$.c = abc_lshift($$.c);
3062 E : E '/' E {$$.c = code_append($1.c,$3.c);
3063 $$.c = abc_divide($$.c);
3066 E : E '%' E {$$.c = code_append($1.c,$3.c);
3067 $$.c = abc_modulo($$.c);
3070 E : E '+' E {$$.c = code_append($1.c,$3.c);
3071 if(BOTH_INT($1.t, $3.t)) {
3072 $$.c = abc_add_i($$.c);
3075 $$.c = abc_add($$.c);
3076 $$.t = join_types($1.t,$3.t,'+');
3079 E : E '-' E {$$.c = code_append($1.c,$3.c);
3080 if(BOTH_INT($1.t,$3.t)) {
3081 $$.c = abc_subtract_i($$.c);
3084 $$.c = abc_subtract($$.c);
3088 E : E '*' E {$$.c = code_append($1.c,$3.c);
3089 if(BOTH_INT($1.t,$3.t)) {
3090 $$.c = abc_multiply_i($$.c);
3093 $$.c = abc_multiply($$.c);
3098 E : E "in" E {$$.c = code_append($1.c,$3.c);
3099 $$.c = abc_in($$.c);
3100 $$.t = TYPE_BOOLEAN;
3103 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3104 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3105 MULTINAME(m, (classinfo_t*)($3.t->data));
3106 $$.c = abc_astype2($1.c, &m);
3109 $$.c = code_append($1.c, $3.c);
3110 $$.c = abc_astypelate($$.c);
3115 E : E "instanceof" E
3116 {$$.c = code_append($1.c, $3.c);
3117 $$.c = abc_instanceof($$.c);
3118 $$.t = TYPE_BOOLEAN;
3121 E : E "is" E {$$.c = code_append($1.c, $3.c);
3122 $$.c = abc_istypelate($$.c);
3123 $$.t = TYPE_BOOLEAN;
3126 E : "typeof" '(' E ')' {
3128 $$.c = abc_typeof($$.c);
3133 $$.c = cut_last_push($2.c);
3134 $$.c = abc_pushundefined($$.c);
3138 E : "void" { $$.c = abc_pushundefined(0);
3142 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3147 $$.c=abc_negate_i($$.c);
3150 $$.c=abc_negate($$.c);
3157 $$.c = code_append($$.c, $3.c);
3159 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3160 $$.c = abc_getproperty2($$.c, &m);
3161 $$.t = 0; // array elements have unknown type
3164 E : '[' MAYBE_EXPRESSION_LIST ']' {
3166 $$.c = code_append($$.c, $2.cc);
3167 $$.c = abc_newarray($$.c, $2.len);
3168 $$.t = registry_getarrayclass();
3171 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
3172 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3174 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3176 $$.cc = code_append($$.cc, $1.c);
3177 $$.cc = code_append($$.cc, $3.c);
3180 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3183 $$.cc = code_append($$.cc, $3.c);
3184 $$.cc = code_append($$.cc, $5.c);
3189 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3191 $$.c = code_append($$.c, $2.cc);
3192 $$.c = abc_newobject($$.c, $2.len/2);
3193 $$.t = registry_getobjectclass();
3198 if(BOTH_INT($1.t,$3.t)) {
3199 c=abc_multiply_i(c);
3203 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3204 $$.c = toreadwrite($1.c, c, 0, 0);
3209 code_t*c = abc_modulo($3.c);
3210 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3211 $$.c = toreadwrite($1.c, c, 0, 0);
3215 code_t*c = abc_lshift($3.c);
3216 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3217 $$.c = toreadwrite($1.c, c, 0, 0);
3221 code_t*c = abc_rshift($3.c);
3222 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3223 $$.c = toreadwrite($1.c, c, 0, 0);
3227 code_t*c = abc_urshift($3.c);
3228 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3229 $$.c = toreadwrite($1.c, c, 0, 0);
3233 code_t*c = abc_divide($3.c);
3234 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3235 $$.c = toreadwrite($1.c, c, 0, 0);
3239 code_t*c = abc_bitor($3.c);
3240 c=converttype(c, TYPE_INT, $1.t);
3241 $$.c = toreadwrite($1.c, c, 0, 0);
3245 code_t*c = abc_bitxor($3.c);
3246 c=converttype(c, TYPE_INT, $1.t);
3247 $$.c = toreadwrite($1.c, c, 0, 0);
3253 if(TYPE_IS_INT($1.t)) {
3257 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3260 $$.c = toreadwrite($1.c, c, 0, 0);
3263 E : E "-=" E { code_t*c = $3.c;
3264 if(TYPE_IS_INT($1.t)) {
3265 c=abc_subtract_i(c);
3268 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3271 $$.c = toreadwrite($1.c, c, 0, 0);
3274 E : E '=' E { code_t*c = 0;
3275 c = code_append(c, $3.c);
3276 c = converttype(c, $3.t, $1.t);
3277 $$.c = toreadwrite($1.c, c, 1, 0);
3281 E : E '?' E ':' E %prec below_assignment {
3282 $$.t = join_types($3.t,$5.t,'?');
3284 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3285 $$.c = code_append($$.c, $3.c);
3286 $$.c = converttype($$.c, $3.t, $$.t);
3287 code_t*j2 = $$.c = abc_jump($$.c, 0);
3288 $$.c = j1->branch = abc_label($$.c);
3289 $$.c = code_append($$.c, $5.c);
3290 $$.c = converttype($$.c, $5.t, $$.t);
3291 $$.c = j2->branch = abc_label($$.c);
3294 E : E "++" { code_t*c = 0;
3295 classinfo_t*type = $1.t;
3296 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
3297 int nr = getlocalnr($1.c);
3298 code_free($1.c);$1.c=0;
3299 if(TYPE_IS_INT($1.t)) {
3300 $$.c = abc_getlocal(0, nr);
3301 $$.c = abc_inclocal_i($$.c, nr);
3302 } else if(TYPE_IS_NUMBER($1.t)) {
3303 $$.c = abc_getlocal(0, nr);
3304 $$.c = abc_inclocal($$.c, nr);
3305 } else syntaxerror("internal error");
3307 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3308 c=abc_increment_i(c);
3314 c=converttype(c, type, $1.t);
3315 $$.c = toreadwrite($1.c, c, 0, 1);
3320 // TODO: use inclocal, like with ++
3321 E : E "--" { code_t*c = 0;
3322 classinfo_t*type = $1.t;
3323 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3324 c=abc_decrement_i(c);
3330 c=converttype(c, type, $1.t);
3331 $$.c = toreadwrite($1.c, c, 0, 1);
3335 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3336 classinfo_t*type = $2.t;
3337 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3338 c=abc_increment_i(c);
3344 c=converttype(c, type, $2.t);
3345 $$.c = toreadwrite($2.c, c, 0, 0);
3349 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3350 classinfo_t*type = $2.t;
3351 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3352 c=abc_decrement_i(c);
3358 c=converttype(c, type, $2.t);
3359 $$.c = toreadwrite($2.c, c, 0, 0);
3363 E : "super" '.' T_IDENTIFIER
3364 { if(!state->cls->info)
3365 syntaxerror("super keyword not allowed outside a class");
3366 classinfo_t*t = state->cls->info->superclass;
3367 if(!t) t = TYPE_OBJECT;
3369 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3371 MEMBER_MULTINAME(m, f, $3);
3373 $$.c = abc_getlocal_0($$.c);
3374 $$.c = abc_getsuper2($$.c, &m);
3375 $$.t = slotinfo_gettype((slotinfo_t*)f);
3378 E : '@' T_IDENTIFIER {
3380 $$.c = abc_pushundefined(0);
3382 as3_warning("ignored @ operator");
3385 E : E '.' '@' T_IDENTIFIER {
3386 // child attribute TODO
3387 $$.c = abc_pushundefined(0);
3389 as3_warning("ignored .@ operator");
3392 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3393 // namespace declaration TODO
3394 $$.c = abc_pushundefined(0);
3396 as3_warning("ignored :: operator");
3399 E : E ".." T_IDENTIFIER {
3401 $$.c = abc_pushundefined(0);
3403 as3_warning("ignored .. operator");
3406 E : E '.' '(' E ')' {
3408 $$.c = abc_pushundefined(0);
3410 as3_warning("ignored .() operator");
3413 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3417 E : E '.' T_IDENTIFIER
3419 classinfo_t*t = $1.t;
3421 if(TYPE_IS_CLASS(t) && t->data) {
3426 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3428 if(f && !is_static != !(f->flags&FLAG_STATIC))
3430 if(f && f->slot && !noslot) {
3431 $$.c = abc_getslot($$.c, f->slot);
3433 MEMBER_MULTINAME(m, f, $3);
3434 $$.c = abc_getproperty2($$.c, &m);
3436 /* determine type */
3437 $$.t = slotinfo_gettype((slotinfo_t*)f);
3439 $$.c = abc_coerce_a($$.c);
3441 /* when resolving a property on an unknown type, we do know the
3442 name of the property (and don't seem to need the package), but
3443 we need to make avm2 try out all access modes */
3444 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3445 $$.c = abc_getproperty2($$.c, &m);
3446 $$.c = abc_coerce_a($$.c);
3447 $$.t = registry_getanytype();
3451 VAR_READ : T_IDENTIFIER {
3453 /* Queue unresolved identifiers for checking against the parent
3454 function's variables.
3455 We consider everything which is not a local variable "unresolved".
3456 This encompasses class names, members of the surrounding class
3457 etc. which *correct* because local variables of the parent function
3460 if(state->method->inner && !find_variable(state, $1)) {
3461 unknown_variable($1);
3471 /* look at variables */
3472 if((v = find_variable(state, $1))) {
3473 // $1 is a local variable
3474 $$.c = abc_getlocal($$.c, v->index);
3478 if((v = find_slot(state, $1))) {
3479 $$.c = abc_getscopeobject($$.c, 1);
3480 $$.c = abc_getslot($$.c, v->index);
3485 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3487 /* look at current class' members */
3488 if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) &&
3489 (f->flags&FLAG_STATIC) >= i_am_static) {
3490 // $1 is a function in this class
3491 int var_is_static = (f->flags&FLAG_STATIC);
3493 if(f->kind == INFOTYPE_METHOD) {
3494 $$.t = TYPE_FUNCTION(f);
3498 if(var_is_static && !i_am_static) {
3499 /* access to a static member from a non-static location.
3500 do this via findpropstrict:
3501 there doesn't seem to be any non-lookup way to access
3502 static properties of a class */
3503 state->method->late_binding = 1;
3505 namespace_t ns = {f->access, ""};
3506 multiname_t m = {QNAME, &ns, 0, $1};
3507 $$.c = abc_findpropstrict2($$.c, &m);
3508 $$.c = abc_getproperty2($$.c, &m);
3510 } else if(f->slot>0) {
3511 $$.c = abc_getlocal_0($$.c);
3512 $$.c = abc_getslot($$.c, f->slot);
3515 namespace_t ns = {f->access, ""};
3516 multiname_t m = {QNAME, &ns, 0, $1};
3517 $$.c = abc_getlocal_0($$.c);
3518 $$.c = abc_getproperty2($$.c, &m);
3523 /* look at actual classes, in the current package and imported */
3524 if((a = find_class($1))) {
3525 if(a->access == ACCESS_PACKAGEINTERNAL &&
3526 strcmp(a->package, state->package) &&
3527 strcmp(a->package, internal_filename_package)
3529 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
3530 infotypename(a),$1, a->package, state->package);
3532 if(a->kind != INFOTYPE_CLASS) {
3534 $$.c = abc_findpropstrict2($$.c, &m);
3535 $$.c = abc_getproperty2($$.c, &m);
3536 if(a->kind == INFOTYPE_METHOD) {
3537 methodinfo_t*f = (methodinfo_t*)a;
3538 $$.t = TYPE_FUNCTION(f);
3540 varinfo_t*v = (varinfo_t*)a;
3544 classinfo_t*c = (classinfo_t*)a;
3546 $$.c = abc_getglobalscope($$.c);
3547 $$.c = abc_getslot($$.c, c->slot);
3550 $$.c = abc_getlex2($$.c, &m);
3552 $$.t = TYPE_CLASS(c);
3557 /* unknown object, let the avm2 resolve it */
3559 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3560 state->method->late_binding = 1;
3562 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3565 $$.c = abc_findpropstrict2($$.c, &m);
3566 $$.c = abc_getproperty2($$.c, &m);
3570 // ----------------- namespaces -------------------------------------------------
3572 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3574 tokenizer_register_namespace($2);
3578 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3581 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID '=' T_IDENTIFIER {
3584 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID '=' T_STRING {
3587 USE_NAMESPACE : "use" PACKAGEANDCLASS {
3589 tokenizer_register_namespace($2->name);