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 SLOT_LIST ONE_SLOT
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 int slotstate_varconst = 0;
1016 static modifiers_t*slotstate_flags = 0;
1017 static void setslotstate(modifiers_t* flags, int varconst)
1019 slotstate_varconst = varconst;
1020 slotstate_flags = flags;
1022 if(flags && flags->flags&FLAG_STATIC) {
1023 state->method = state->cls->static_init;
1025 state->method = state->cls->init;
1028 parserassert(state->method);
1032 static void endclass()
1035 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1037 c = abc_getlocal_0(c);
1038 c = abc_constructsuper(c, 0);
1039 state->cls->init->header = code_append(state->cls->init->header, c);
1040 state->cls->has_constructor=1;
1042 if(state->cls->init) {
1043 if(state->cls->info->flags&FLAG_INTERFACE) {
1044 if(state->cls->init->header)
1045 syntaxerror("interface can not have class-level code");
1047 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1048 code_t*c = method_header(state->cls->init);
1049 m->body->code = wrap_function(c, 0, m->body->code);
1052 if(state->cls->static_init) {
1053 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1054 code_t*c = method_header(state->cls->static_init);
1055 m->body->code = wrap_function(c, 0, m->body->code);
1062 void check_code_for_break(code_t*c)
1065 if(c->opcode == OPCODE___BREAK__) {
1066 char*name = string_cstr(c->data[0]);
1067 syntaxerror("Unresolved \"break %s\"", name);
1069 if(c->opcode == OPCODE___CONTINUE__) {
1070 char*name = string_cstr(c->data[0]);
1071 syntaxerror("Unresolved \"continue %s\"", name);
1078 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1080 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1081 if(TYPE_IS_NUMBER(t)) {
1082 xassert(c->type == CONSTANT_FLOAT
1083 || c->type == CONSTANT_INT
1084 || c->type == CONSTANT_UINT);
1085 } else if(TYPE_IS_UINT(t)) {
1086 xassert(c->type == CONSTANT_UINT ||
1087 (c->type == CONSTANT_INT && c->i>=0));
1088 } else if(TYPE_IS_INT(t)) {
1089 xassert(c->type == CONSTANT_INT);
1090 } else if(TYPE_IS_BOOLEAN(t)) {
1091 xassert(c->type == CONSTANT_TRUE
1092 || c->type == CONSTANT_FALSE);
1096 static void check_override(memberinfo_t*m, int flags)
1100 if(m->parent == state->cls->info)
1101 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1103 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1104 if(m->access==ACCESS_PRIVATE)
1106 if(m->flags & FLAG_FINAL)
1107 syntaxerror("can't override final member %s", m->name);
1108 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1109 syntaxerror("can't override static member %s", m->name);
1110 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1111 syntaxerror("can't override non-static member %s with static declaration", m->name);
1113 if(!(flags&FLAG_OVERRIDE)) {
1114 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1115 if(m->kind == INFOTYPE_METHOD)
1116 syntaxerror("can't override without explicit 'override' declaration");
1118 syntaxerror("can't override '%s'", m->name);
1123 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1125 methodinfo_t*minfo = 0;
1126 namespace_t ns = modifiers2access(mod);
1129 minfo = methodinfo_register_global(ns.access, state->package, name);
1130 minfo->return_type = 0; // save this for pass 2
1131 } else if(getset != KW_GET && getset != KW_SET) {
1133 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1135 printf("%s.%s | %s.%s\n",
1136 m->package, m->name,
1138 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1140 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1141 minfo->return_type = 0; // save this for pass 2
1142 // getslot on a member slot only returns "undefined", so no need
1143 // to actually store these
1144 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1146 //class getter/setter
1147 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1149 if(getset == KW_GET) {
1151 } else if(params->list && params->list->param && !params->list->next) {
1152 type = params->list->param->type;
1154 syntaxerror("setter function needs to take exactly one argument");
1155 // not sure wether to look into superclasses here, too
1156 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1158 if(minfo->kind!=INFOTYPE_SLOT)
1159 syntaxerror("class already contains a method called '%s'", name);
1160 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1161 syntaxerror("class already contains a field called '%s'", name);
1162 if(minfo->subtype & gs)
1163 syntaxerror("getter/setter for '%s' already defined", name);
1164 /* make a setter or getter into a getset */
1165 minfo->subtype |= gs;
1168 FIXME: this check needs to be done in pass 2
1170 if((!minfo->return_type != !type) ||
1171 (minfo->return_type && type &&
1172 !strcmp(minfo->return_type->name, type->name))) {
1173 syntaxerror("different type in getter and setter: %s and %s",
1174 minfo->return_type?minfo->return_type->name:"*",
1175 type?type->name:"*");
1178 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1179 minfo->kind = INFOTYPE_SLOT; //hack
1180 minfo->subtype = gs;
1181 minfo->return_type = 0;
1183 /* can't assign a slot as getter and setter might have different slots */
1184 //minfo->slot = slot;
1186 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1187 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1188 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1193 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1195 //parserassert(state->method && state->method->info);
1197 methodstate_t*parent_method = state->method;
1200 return_type = 0; // not valid in pass 1
1204 state->new_vars = 1;
1207 state->method = rfx_calloc(sizeof(methodstate_t));
1208 state->method->inner = 1;
1209 state->method->variable_count = 0;
1210 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1212 NEW(methodinfo_t,minfo);
1213 minfo->kind = INFOTYPE_METHOD;
1214 minfo->access = ACCESS_PACKAGEINTERNAL;
1216 state->method->info = minfo;
1219 list_append(parent_method->innerfunctions, state->method);
1221 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1223 function_initvars(state->method, params, 0, 1);
1227 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1228 state->method->variable_count = 0;
1229 parserassert(state->method);
1231 state->method->info->return_type = return_type;
1232 function_initvars(state->method, params, 0, 1);
1236 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1237 params_t*params, classinfo_t*return_type)
1239 if(state->method && state->method->info) {
1240 syntaxerror("not able to start another method scope");
1243 state->new_vars = 1;
1246 state->method = rfx_calloc(sizeof(methodstate_t));
1247 state->method->has_super = 0;
1250 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1252 state->method->is_global = 1;
1253 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1255 if(state->method->is_constructor)
1256 name = "__as3_constructor__";
1258 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1260 function_initvars(state->method, params, mod->flags, 1);
1262 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1266 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1267 state->method->variable_count = 0;
1268 parserassert(state->method);
1271 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1272 check_override(m, mod->flags);
1276 state->cls->has_constructor |= state->method->is_constructor;
1279 state->method->info->return_type = return_type;
1280 function_initvars(state->method, params, mod->flags, 1);
1284 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1285 params_t*params, classinfo_t*return_type, code_t*body)
1287 int flags = mod?mod->flags:0;
1290 // store inner methods in variables
1291 function_initvars(state->method, 0, 0, 0);
1293 methodstate_list_t*ml = state->method->innerfunctions;
1294 dict_t*xvars = dict_new();
1296 methodstate_t*m = ml->methodstate;
1297 parserassert(m->inner);
1298 if(m->unresolved_variables) {
1299 dict_t*d = m->unresolved_variables;
1301 for(t=0;t<d->hashsize;t++) {
1302 dictentry_t*l = d->slots[t];
1304 /* check parent method's variables */
1305 if(find_variable(state, l->key)) {
1306 m->uses_parent_function = 1;
1307 state->method->uses_slots = 1;
1308 dict_put(xvars, l->key, 0);
1315 dict_destroy(m->unresolved_variables);
1316 m->unresolved_variables = 0;
1320 if(state->method->uses_slots) {
1321 state->method->slots = dict_new();
1323 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1324 if(v->index && dict_contains(xvars, name)) {
1327 if(v->is_inner_method) {
1328 v->is_inner_method->is_a_slot = 1;
1331 dict_put(state->method->slots, name, v);
1334 state->method->uses_slots = i;
1335 dict_destroy(state->vars);state->vars = 0;
1337 dict_destroy(xvars);
1344 /*if(state->method->uses_parent_function){
1345 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1350 multiname_t*type2 = sig2mname(return_type);
1352 if(state->method->inner) {
1353 f = state->method->abc;
1354 abc_method_init(f, global->file, type2, 1);
1355 } else if(state->method->is_constructor) {
1356 f = abc_class_getconstructor(state->cls->abc, type2);
1357 } else if(!state->method->is_global) {
1358 namespace_t mname_ns = {state->method->info->access, ""};
1359 multiname_t mname = {QNAME, &mname_ns, 0, name};
1361 if(flags&FLAG_STATIC)
1362 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1364 f = abc_class_method(state->cls->abc, type2, &mname);
1365 slot = f->trait->slot_id;
1367 namespace_t mname_ns = {state->method->info->access, state->package};
1368 multiname_t mname = {QNAME, &mname_ns, 0, name};
1370 f = abc_method_new(global->file, type2, 1);
1371 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1372 //abc_code_t*c = global->init->method->body->code;
1374 //flash doesn't seem to allow us to access function slots
1375 //state->method->info->slot = slot;
1377 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1378 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1379 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1380 if(params->varargs) f->flags |= METHOD_NEED_REST;
1384 for(p=params->list;p;p=p->next) {
1385 if(params->varargs && !p->next) {
1386 break; //varargs: omit last parameter in function signature
1388 multiname_t*m = sig2mname(p->param->type);
1389 list_append(f->parameters, m);
1390 if(p->param->value) {
1391 check_constant_against_type(p->param->type, p->param->value);
1392 opt=1;list_append(f->optional_parameters, p->param->value);
1394 syntaxerror("non-optional parameter not allowed after optional parameters");
1397 if(state->method->slots) {
1398 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1400 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1401 multiname_t*type = sig2mname(v->type);
1402 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1403 t->slot_id = v->index;
1408 check_code_for_break(body);
1410 if(state->method->exceptions &&
1411 (state->method->late_binding || state->method->uses_slots))
1412 syntaxerror("try/catch and activation or late binding not supported yet within the same method");
1415 f->body->code = body;
1416 f->body->exceptions = state->method->exceptions;
1417 } else { //interface
1419 syntaxerror("interface methods can't have a method body");
1429 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1434 void breakjumpsto(code_t*c, char*name, code_t*jump)
1437 if(c->opcode == OPCODE___BREAK__) {
1438 string_t*name2 = c->data[0];
1439 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1440 c->opcode = OPCODE_JUMP;
1447 void continuejumpsto(code_t*c, char*name, code_t*jump)
1450 if(c->opcode == OPCODE___CONTINUE__) {
1451 string_t*name2 = c->data[0];
1452 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1453 c->opcode = OPCODE_JUMP;
1461 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1462 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1463 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1465 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1467 if(!type1 || !type2)
1468 return registry_getanytype();
1469 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1470 return registry_getanytype();
1473 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1482 return registry_getanytype();
1484 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1489 return abc_coerce_a(c);
1493 // cast an "any" type to a specific type. subject to
1494 // runtime exceptions
1495 return abc_coerce2(c, &m);
1498 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1499 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1500 // allow conversion between number types
1501 return abc_coerce2(c, &m);
1503 //printf("%s.%s\n", from.package, from.name);
1504 //printf("%s.%s\n", to.package, to.name);
1506 classinfo_t*supertype = from;
1508 if(supertype == to) {
1509 // target type is one of from's superclasses
1510 return abc_coerce2(c, &m);
1513 while(supertype->interfaces[t]) {
1514 if(supertype->interfaces[t]==to) {
1515 // target type is one of from's interfaces
1516 return abc_coerce2(c, &m);
1520 supertype = supertype->superclass;
1522 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1524 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1526 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1529 as3_error("can't convert type %s%s%s to %s%s%s",
1530 from->package, from->package?".":"", from->name,
1531 to->package, to->package?".":"", to->name);
1535 code_t*defaultvalue(code_t*c, classinfo_t*type)
1537 if(TYPE_IS_INT(type)) {
1538 c = abc_pushbyte(c, 0);
1539 } else if(TYPE_IS_UINT(type)) {
1540 c = abc_pushuint(c, 0);
1541 } else if(TYPE_IS_FLOAT(type)) {
1543 } else if(TYPE_IS_BOOLEAN(type)) {
1544 c = abc_pushfalse(c);
1546 //c = abc_pushundefined(c);
1548 c = abc_pushnull(c);
1550 c = abc_coerce2(c, &m);
1555 char is_pushundefined(code_t*c)
1557 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1560 static slotinfo_t* find_class(char*name)
1564 c = registry_find(state->package, name);
1567 /* try explicit imports */
1568 dictentry_t* e = dict_get_slot(state->imports, name);
1571 if(!strcmp(e->key, name)) {
1572 c = (slotinfo_t*)e->data;
1578 /* try package.* imports */
1579 import_list_t*l = state->wildcard_imports;
1581 //printf("does package %s contain a class %s?\n", l->import->package, name);
1582 c = registry_find(l->import->package, name);
1587 /* try global package */
1588 c = registry_find("", name);
1591 /* try local "filename" package */
1592 c = registry_find(internal_filename_package, name);
1598 static char is_getlocal(code_t*c)
1600 if(!c || c->prev || c->next)
1602 return(c->opcode == OPCODE_GETLOCAL
1603 || c->opcode == OPCODE_GETLOCAL_0
1604 || c->opcode == OPCODE_GETLOCAL_1
1605 || c->opcode == OPCODE_GETLOCAL_2
1606 || c->opcode == OPCODE_GETLOCAL_3);
1608 static int getlocalnr(code_t*c)
1610 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1611 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1612 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1613 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1614 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1615 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1619 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1623 [prefix code] [read instruction]
1627 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1629 if(in && in->opcode == OPCODE_COERCE_A) {
1630 in = code_cutlast(in);
1633 syntaxerror("internal error");
1635 /* chop off read instruction */
1639 prefix = r->prev;r->prev = 0;
1645 char use_temp_var = readbefore;
1647 /* generate the write instruction, and maybe append a dup to the prefix code */
1648 code_t* write = abc_nop(0);
1649 if(r->opcode == OPCODE_GETPROPERTY) {
1650 write->opcode = OPCODE_SETPROPERTY;
1651 multiname_t*m = (multiname_t*)r->data[0];
1652 write->data[0] = multiname_clone(m);
1653 if(m->type == QNAME || m->type == MULTINAME) {
1655 prefix = abc_dup(prefix); // we need the object, too
1658 } else if(m->type == MULTINAMEL) {
1660 /* dupping two values on the stack requires 5 operations and one register-
1661 couldn't adobe just have given us a dup2? */
1662 int temp = gettempvar();
1663 prefix = abc_setlocal(prefix, temp);
1664 prefix = abc_dup(prefix);
1665 prefix = abc_getlocal(prefix, temp);
1666 prefix = abc_swap(prefix);
1667 prefix = abc_getlocal(prefix, temp);
1669 prefix = abc_kill(prefix, temp);
1673 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1675 } else if(r->opcode == OPCODE_GETSLOT) {
1676 write->opcode = OPCODE_SETSLOT;
1677 write->data[0] = r->data[0];
1679 prefix = abc_dup(prefix); // we need the object, too
1682 } else if(r->opcode == OPCODE_GETLOCAL) {
1683 write->opcode = OPCODE_SETLOCAL;
1684 write->data[0] = r->data[0];
1685 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1686 write->opcode = OPCODE_SETLOCAL_0;
1687 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1688 write->opcode = OPCODE_SETLOCAL_1;
1689 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1690 write->opcode = OPCODE_SETLOCAL_2;
1691 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1692 write->opcode = OPCODE_SETLOCAL_3;
1695 syntaxerror("illegal lvalue: can't assign a value to this expression");
1702 /* with getproperty/getslot, we have to be extra careful not
1703 to execute the read code twice, as it might have side-effects
1704 (e.g. if the property is in fact a setter/getter combination)
1706 So read the value, modify it, and write it again,
1707 using prefix only once and making sure (by using a temporary
1708 register) that the return value is what we just wrote */
1709 temp = gettempvar();
1710 c = code_append(c, prefix);
1711 c = code_append(c, r);
1714 c = abc_setlocal(c, temp);
1716 c = code_append(c, middlepart);
1719 c = abc_setlocal(c, temp);
1721 c = code_append(c, write);
1722 c = abc_getlocal(c, temp);
1723 c = abc_kill(c, temp);
1725 /* if we're allowed to execute the read code twice *and*
1726 the middlepart doesn't modify the code, things are easier.
1728 code_t* r2 = code_dup(r);
1729 //c = code_append(c, prefix);
1730 parserassert(!prefix);
1731 c = code_append(c, r);
1732 c = code_append(c, middlepart);
1733 c = code_append(c, write);
1734 c = code_append(c, r2);
1737 /* even smaller version: overwrite the value without reading
1741 c = code_append(c, prefix);
1744 c = code_append(c, middlepart);
1745 c = code_append(c, write);
1746 c = code_append(c, r);
1749 temp = gettempvar();
1751 c = code_append(c, prefix);
1753 c = code_append(c, middlepart);
1755 c = abc_setlocal(c, temp);
1756 c = code_append(c, write);
1757 c = abc_getlocal(c, temp);
1758 c = abc_kill(c, temp);
1764 char is_break_or_jump(code_t*c)
1768 if(c->opcode == OPCODE_JUMP ||
1769 c->opcode == OPCODE___BREAK__ ||
1770 c->opcode == OPCODE___CONTINUE__ ||
1771 c->opcode == OPCODE_THROW ||
1772 c->opcode == OPCODE_RETURNVOID ||
1773 c->opcode == OPCODE_RETURNVALUE) {
1780 #define IS_FINALLY_TARGET(op) \
1781 ((op) == OPCODE___CONTINUE__ || \
1782 (op) == OPCODE___BREAK__ || \
1783 (op) == OPCODE_RETURNVOID || \
1784 (op) == OPCODE_RETURNVALUE || \
1785 (op) == OPCODE___RETHROW__)
1787 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1789 #define NEED_EXTRA_STACK_ARG
1790 code_t*finally_label = abc_nop(0);
1791 NEW(lookupswitch_t, l);
1797 code_t*prev = i->prev;
1798 if(IS_FINALLY_TARGET(i->opcode)) {
1801 if(i->opcode == OPCODE___RETHROW__ ||
1802 i->opcode == OPCODE_RETURNVALUE) {
1803 if(i->opcode == OPCODE___RETHROW__)
1804 i->opcode = OPCODE_THROW;
1806 p = abc_coerce_a(p);
1807 p = abc_setlocal(p, tempvar);
1809 p = abc_pushbyte(p, count++);
1810 p = abc_jump(p, finally_label);
1811 code_t*target = p = abc_label(p);
1812 #ifdef NEED_EXTRA_STACK_ARG
1816 p = abc_getlocal(p, tempvar);
1819 p->next = i;i->prev = p;
1820 list_append(l->targets, target);
1826 c = abc_pushbyte(c, -1);
1827 c = code_append(c, finally_label);
1828 c = code_append(c, finally);
1830 #ifdef NEED_EXTRA_STACK_ARG
1833 c = abc_lookupswitch(c, l);
1834 c = l->def = abc_label(c);
1835 #ifdef NEED_EXTRA_STACK_ARG
1842 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1846 code_t*prev = i->prev;
1847 if(IS_FINALLY_TARGET(i->opcode)) {
1848 if(i->opcode == OPCODE___RETHROW__)
1849 i->opcode = OPCODE_THROW;
1850 code_t*end = code_dup(finally);
1851 code_t*start = code_start(end);
1852 if(prev) prev->next = start;
1859 return code_append(c, finally);
1862 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1868 int num_insertion_points=0;
1870 if(IS_FINALLY_TARGET(i->opcode))
1871 num_insertion_points++;
1878 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1883 int simple_version_cost = (1+num_insertion_points)*code_size;
1884 int lookup_version_cost = 4*num_insertion_points + 5;
1886 if(cantdup || simple_version_cost > lookup_version_cost) {
1887 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1888 return insert_finally_lookup(c, finally, tempvar);
1890 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1891 return insert_finally_simple(c, finally, tempvar);
1895 #define PASS1 }} if(as3_pass == 1) {{
1896 #define PASS1END }} if(as3_pass == 2) {{
1897 #define PASS2 }} if(as3_pass == 2) {{
1898 #define PASS12 }} {{
1899 #define PASS12END }} if(as3_pass == 2) {{
1905 /* ------------ code blocks / statements ---------------- */
1907 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1909 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1910 PROGRAM_CODE_LIST: PROGRAM_CODE
1911 | PROGRAM_CODE_LIST PROGRAM_CODE
1913 PROGRAM_CODE: PACKAGE_DECLARATION
1914 | INTERFACE_DECLARATION
1916 | FUNCTION_DECLARATION
1919 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
1922 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1923 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1924 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1926 INPACKAGE_CODE: INTERFACE_DECLARATION
1928 | FUNCTION_DECLARATION
1931 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
1934 MAYBECODE: CODE {$$=$1;}
1935 MAYBECODE: {$$=code_new();}
1937 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1938 CODE: CODEPIECE {$$=$1;}
1940 // code which may appear outside of methods
1941 CODE_STATEMENT: IMPORT
1943 CODE_STATEMENT: FOR_IN
1944 CODE_STATEMENT: WHILE
1945 CODE_STATEMENT: DO_WHILE
1946 CODE_STATEMENT: SWITCH
1948 CODE_STATEMENT: WITH
1950 CODE_STATEMENT: VOIDEXPRESSION
1951 CODE_STATEMENT: USE_NAMESPACE
1952 CODE_STATEMENT: NAMESPACE_DECLARATION
1953 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1954 CODE_STATEMENT: '{' '}' {$$=0;}
1956 // code which may appear in methods
1957 CODEPIECE: ';' {$$=0;}
1958 CODEPIECE: CODE_STATEMENT
1959 CODEPIECE: VARIABLE_DECLARATION
1964 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
1966 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1967 //CODEBLOCK : '{' '}' {$$=0;}
1968 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1969 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1971 /* ------------ package init code ------------------- */
1973 PACKAGE_INITCODE: CODE_STATEMENT {
1974 code_t**cc = &global->init->method->body->code;
1975 *cc = code_append(*cc, $1);
1978 /* ------------ conditional compilation ------------- */
1980 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
1982 /* ------------ variables --------------------------- */
1984 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1985 | {$$.c=abc_pushundefined(0);
1989 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1990 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1992 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1993 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1995 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1998 if(variable_exists($1))
1999 syntaxerror("Variable %s already defined", $1);
2001 new_variable($1, 0, 1, 0);
2004 if(!is_subtype_of($3.t, $2)) {
2005 syntaxerror("Can't convert %s to %s", $3.t->name,
2011 if(state->method->uses_slots) {
2012 variable_t* v = find_slot(state, $1);
2014 // this variable is stored in a slot
2022 index = new_variable($1, $2, 1, 0);
2025 $$ = slot?abc_getscopeobject(0, 1):0;
2028 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2029 $$ = code_append($$, $3.c);
2030 $$ = converttype($$, $3.t, $2);
2033 $$ = defaultvalue($$, $2);
2036 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2037 $$ = code_append($$, $3.c);
2038 $$ = abc_coerce_a($$);
2040 // don't do anything
2048 $$ = abc_setslot($$, index);
2050 $$ = abc_setlocal($$, index);
2054 /* ------------ control flow ------------------------- */
2056 MAYBEELSE: %prec below_else {$$ = code_new();}
2057 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2058 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2060 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2063 $$ = code_append($$, $4.c);
2064 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2066 $$ = code_append($$, $6);
2068 myjmp = $$ = abc_jump($$, 0);
2070 myif->branch = $$ = abc_nop($$);
2072 $$ = code_append($$, $7);
2073 myjmp->branch = $$ = abc_nop($$);
2079 FOR_INIT : {$$=code_new();}
2080 FOR_INIT : VARIABLE_DECLARATION
2081 FOR_INIT : VOIDEXPRESSION
2083 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2084 // (I don't see any easy way to revolve this conflict otherwise, as we
2085 // can't touch VAR_READ without upsetting the precedence about "return")
2086 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2087 PASS1 $$=$2;new_variable($2,0,1,0);
2088 PASS2 $$=$2;new_variable($2,$3,1,0);
2090 FOR_IN_INIT : T_IDENTIFIER {
2095 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2096 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2098 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2099 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2101 $$ = code_append($$, $2);
2102 code_t*loopstart = $$ = abc_label($$);
2103 $$ = code_append($$, $4.c);
2104 code_t*myif = $$ = abc_iffalse($$, 0);
2105 $$ = code_append($$, $8);
2106 code_t*cont = $$ = abc_nop($$);
2107 $$ = code_append($$, $6);
2108 $$ = abc_jump($$, loopstart);
2109 code_t*out = $$ = abc_nop($$);
2110 breakjumpsto($$, $1.name, out);
2111 continuejumpsto($$, $1.name, cont);
2118 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2119 variable_t*var = find_variable(state, $2);
2120 char*tmp1name = concat2($2, "__tmp1__");
2121 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2122 char*tmp2name = concat2($2, "__array__");
2123 int array = new_variable(tmp1name, 0, 0, 0);
2126 $$ = code_append($$, $4.c);
2127 $$ = abc_coerce_a($$);
2128 $$ = abc_setlocal($$, array);
2129 $$ = abc_pushbyte($$, 0);
2130 $$ = abc_setlocal($$, it);
2132 code_t*loopstart = $$ = abc_label($$);
2134 $$ = abc_hasnext2($$, array, it);
2135 code_t*myif = $$ = abc_iffalse($$, 0);
2136 $$ = abc_getlocal($$, array);
2137 $$ = abc_getlocal($$, it);
2139 $$ = abc_nextname($$);
2141 $$ = abc_nextvalue($$);
2142 $$ = converttype($$, 0, var->type);
2143 $$ = abc_setlocal($$, var->index);
2145 $$ = code_append($$, $6);
2146 $$ = abc_jump($$, loopstart);
2148 code_t*out = $$ = abc_nop($$);
2149 breakjumpsto($$, $1.name, out);
2150 continuejumpsto($$, $1.name, loopstart);
2162 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2166 code_t*myjmp = $$ = abc_jump($$, 0);
2167 code_t*loopstart = $$ = abc_label($$);
2168 $$ = code_append($$, $6);
2169 code_t*cont = $$ = abc_nop($$);
2170 myjmp->branch = cont;
2171 $$ = code_append($$, $4.c);
2172 $$ = abc_iftrue($$, loopstart);
2173 code_t*out = $$ = abc_nop($$);
2174 breakjumpsto($$, $1, out);
2175 continuejumpsto($$, $1, cont);
2181 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2183 code_t*loopstart = $$ = abc_label($$);
2184 $$ = code_append($$, $3);
2185 code_t*cont = $$ = abc_nop($$);
2186 $$ = code_append($$, $6.c);
2187 $$ = abc_iftrue($$, loopstart);
2188 code_t*out = $$ = abc_nop($$);
2189 breakjumpsto($$, $1, out);
2190 continuejumpsto($$, $1, cont);
2196 BREAK : "break" %prec prec_none {
2197 $$ = abc___break__(0, "");
2199 BREAK : "break" T_IDENTIFIER {
2200 $$ = abc___break__(0, $2);
2202 CONTINUE : "continue" %prec prec_none {
2203 $$ = abc___continue__(0, "");
2205 CONTINUE : "continue" T_IDENTIFIER {
2206 $$ = abc___continue__(0, $2);
2209 MAYBE_CASE_LIST : {$$=0;}
2210 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2211 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2212 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2213 CASE_LIST: CASE {$$=$1;}
2214 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2216 CASE: "case" E ':' MAYBECODE {
2218 $$ = code_append($$, $2.c);
2219 code_t*j = $$ = abc_ifne($$, 0);
2220 $$ = code_append($$, $4);
2221 if($$->opcode != OPCODE___BREAK__) {
2222 $$ = abc___fallthrough__($$, "");
2224 code_t*e = $$ = abc_nop($$);
2227 DEFAULT: "default" ':' MAYBECODE {
2230 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2232 $$ = code_append($$, $7);
2233 code_t*out = $$ = abc_pop($$);
2234 breakjumpsto($$, $1, out);
2236 code_t*c = $$,*lastblock=0;
2238 if(c->opcode == OPCODE_IFNE) {
2239 if(!c->next) syntaxerror("internal error in fallthrough handling");
2241 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2243 c->opcode = OPCODE_JUMP;
2244 c->branch = lastblock;
2246 /* fall through end of switch */
2247 c->opcode = OPCODE_NOP;
2257 /* ------------ try / catch /finally ---------------- */
2259 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2260 state->exception_name=$3;
2261 PASS1 new_variable($3, 0, 0, 0);
2262 PASS2 new_variable($3, $4, 0, 0);
2265 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2266 multiname_t name = {QNAME, &name_ns, 0, $3};
2268 NEW(abc_exception_t, e)
2269 e->exc_type = sig2mname($4);
2270 e->var_name = multiname_clone(&name);
2274 int i = find_variable_safe(state, $3)->index;
2275 e->target = c = abc_nop(0);
2276 c = abc_setlocal(c, i);
2277 c = code_append(c, $8);
2283 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2288 NEW(abc_exception_t, e)
2289 e->exc_type = 0; //all exceptions
2290 e->var_name = 0; //no name
2293 e->to = code_append(e->to, $4);
2299 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2300 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2301 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2302 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2306 list_append($$.l,$2);
2307 $$.finally = $2->to;$2->to=0;
2310 CATCH_FINALLY_LIST: FINALLY {
2314 list_append($$.l,$1);
2315 $$.finally = $1->to;$1->to=0;
2319 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2320 code_t*out = abc_nop(0);
2322 code_t*start = abc_nop(0);
2323 $$ = code_append(start, $4);
2324 if(!is_break_or_jump($4)) {
2325 $$ = abc_jump($$, out);
2327 code_t*end = $$ = abc_nop($$);
2331 tmp = new_variable("__finally__", 0, 0, 0);
2333 abc_exception_list_t*l = $6.l;
2336 abc_exception_t*e = l->abc_exception;
2338 $$ = code_append($$, e->target);
2339 $$ = abc_jump($$, out);
2341 parserassert((ptroff_t)$6.finally);
2343 e->target = $$ = abc_nop($$);
2344 $$ = abc___rethrow__($$);
2352 $$ = code_append($$, out);
2354 $$ = insert_finally($$, $6.finally, tmp);
2356 list_concat(state->method->exceptions, $6.l);
2362 /* ------------ throw ------------------------------- */
2364 THROW : "throw" EXPRESSION {
2368 THROW : "throw" %prec prec_none {
2369 if(!state->exception_name)
2370 syntaxerror("re-throw only possible within a catch block");
2371 variable_t*v = find_variable(state, state->exception_name);
2373 $$=abc_getlocal($$, v->index);
2377 /* ------------ with -------------------------------- */
2379 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2381 $$ = abc_pushwith($$);
2382 $$ = code_append($$, $5);
2383 $$ = abc_popscope($$);
2386 /* ------------ packages and imports ---------------- */
2388 X_IDENTIFIER: T_IDENTIFIER
2389 | "package" {PASS12 $$="package";}
2391 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2392 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2394 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2395 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2396 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2397 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2399 IMPORT : "import" PACKAGEANDCLASS {
2401 slotinfo_t*s = registry_find($2->package, $2->name);
2402 if(!s) {// || !(s->flags&FLAG_BUILTIN)) {
2403 as3_schedule_class($2->package, $2->name);
2409 syntaxerror("Couldn't import class\n");
2410 state_has_imports();
2411 dict_put(state->imports, c->name, c);
2414 IMPORT : "import" PACKAGE '.' '*' {
2416 if(strncmp("flash.", $2, 6)) {
2417 as3_schedule_package($2);
2423 state_has_imports();
2424 list_append(state->wildcard_imports, i);
2428 /* ------------ classes and interfaces (header) -------------- */
2430 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2431 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2432 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2433 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2435 $$.flags=$1.flags|$2.flags;
2436 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2437 $$.ns=$1.ns?$1.ns:$2.ns;
2441 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2442 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2443 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2444 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2445 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2446 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2447 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2448 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2449 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2450 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2454 EXTENDS : {$$=registry_getobjectclass();}
2455 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2457 EXTENDS_LIST : {PASS12 $$=list_new();}
2458 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2460 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2461 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2463 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2464 EXTENDS IMPLEMENTS_LIST
2465 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2467 '}' {PASS12 endclass();$$=0;}
2469 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2471 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2472 startclass(&$1,$3,0,$4);}
2473 MAYBE_INTERFACE_BODY
2474 '}' {PASS12 endclass();$$=0;}
2476 /* ------------ classes and interfaces (body) -------------- */
2479 MAYBE_CLASS_BODY : CLASS_BODY
2480 CLASS_BODY : CLASS_BODY_ITEM
2481 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2482 CLASS_BODY_ITEM : ';'
2483 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2484 CLASS_BODY_ITEM : SLOT_DECLARATION
2485 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2487 CLASS_BODY_ITEM : CODE_STATEMENT {
2488 code_t*c = state->cls->static_init->header;
2489 c = code_append(c, $1);
2490 state->cls->static_init->header = c;
2493 MAYBE_INTERFACE_BODY :
2494 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2495 INTERFACE_BODY : IDECLARATION
2496 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2498 IDECLARATION : "var" T_IDENTIFIER {
2499 syntaxerror("variable declarations not allowed in interfaces");
2501 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2503 $1.flags |= FLAG_PUBLIC;
2504 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2505 syntaxerror("invalid method modifiers: interface methods always need to be public");
2507 startfunction(&$1,$3,$4,&$6,$8);
2508 endfunction(&$1,$3,$4,&$6,$8, 0);
2509 list_deep_free($6.list);
2512 /* ------------ classes and interfaces (body, slots ) ------- */
2514 VARCONST: "var" | "const"
2516 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2518 SLOT_LIST: ONE_SLOT {$$ = $1;}
2519 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2521 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2523 int flags = slotstate_flags->flags;
2524 namespace_t ns = modifiers2access(slotstate_flags);
2526 varinfo_t* info = 0;
2528 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2530 check_override(i, flags);
2532 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2534 slotinfo_t*i = registry_find(state->package, $1);
2536 syntaxerror("package %s already contains '%s'", state->package, $1);
2538 if(ns.name && ns.name[0]) {
2539 syntaxerror("namespaces not allowed on package-level variables");
2541 info = varinfo_register_global(ns.access, state->package, $1);
2545 info->flags = flags;
2548 multiname_t mname = {QNAME, &ns, 0, $1};
2550 trait_list_t**traits;
2554 ns.name = state->package;
2555 traits = &global->init->traits;
2556 code = &global->init->method->body->code;
2557 } else if(flags&FLAG_STATIC) {
2559 traits = &state->cls->abc->static_traits;
2560 code = &state->cls->static_init->header;
2562 // instance variable
2563 traits = &state->cls->abc->traits;
2564 code = &state->cls->init->header;
2570 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2572 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2574 info->slot = t->slot_id;
2576 /* initalization code (if needed) */
2578 if($3.c && !is_pushundefined($3.c)) {
2579 c = abc_getlocal_0(c);
2580 c = code_append(c, $3.c);
2581 c = converttype(c, $3.t, $2);
2582 c = abc_setslot(c, t->slot_id);
2585 *code = code_append(*code, c);
2587 if(slotstate_varconst==KW_CONST) {
2588 t->kind= TRAIT_CONST;
2594 /* ------------ constants -------------------------------------- */
2596 MAYBESTATICCONSTANT: {$$=0;}
2597 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2599 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2600 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2601 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2602 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2603 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2604 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2605 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2606 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2607 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2608 STATICCONSTANT : T_IDENTIFIER {
2610 as3_warning("Couldn't evaluate constant value of %s", $1);
2611 $$ = constant_new_null($1);
2614 /* ------------ classes and interfaces (body, functions) ------- */
2616 // non-vararg version
2619 memset(&$$,0,sizeof($$));
2621 MAYBE_PARAM_LIST: PARAM_LIST {
2627 MAYBE_PARAM_LIST: "..." PARAM {
2629 memset(&$$,0,sizeof($$));
2631 list_append($$.list, $2);
2633 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2637 list_append($$.list, $4);
2641 PARAM_LIST: PARAM_LIST ',' PARAM {
2644 list_append($$.list, $3);
2648 memset(&$$,0,sizeof($$));
2649 list_append($$.list, $1);
2652 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2654 $$ = rfx_calloc(sizeof(param_t));
2660 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2662 $$ = rfx_calloc(sizeof(param_t));
2664 $$->type = TYPE_ANY;
2672 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2673 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2676 endfunction(&$1,$3,$4,&$6,0,0);
2678 if(!state->method->info) syntaxerror("internal error");
2680 code_t*c = method_header(state->method);
2681 c = wrap_function(c, 0, $11);
2683 endfunction(&$1,$3,$4,&$6,$8,c);
2685 list_deep_free($6.list);
2689 MAYBE_IDENTIFIER: T_IDENTIFIER
2690 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2691 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2692 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2695 endfunction(0,0,$2,&$4,0,0);
2697 methodinfo_t*f = state->method->info;
2698 if(!f || !f->kind) syntaxerror("internal error");
2700 code_t*c = method_header(state->method);
2701 c = wrap_function(c, 0, $9);
2703 int index = state->method->var_index;
2704 endfunction(0,0,$2,&$4,$6,c);
2706 $$.c = abc_getlocal(0, index);
2707 $$.t = TYPE_FUNCTION(f);
2709 PASS12 list_deep_free($4.list);
2713 /* ------------- package + class ids --------------- */
2715 CLASS: T_IDENTIFIER {
2716 PASS1 static slotinfo_t c;
2717 memset(&c, 0, sizeof(c));
2719 $$ = (classinfo_t*)&c;
2721 slotinfo_t*s = find_class($1);
2722 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2723 $$ = (classinfo_t*)s;
2726 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2727 PASS1 static slotinfo_t c;
2728 memset(&c, 0, sizeof(c));
2731 $$=(classinfo_t*)&c;
2733 slotinfo_t*s = registry_find($1, $3);
2734 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2736 $$ = (classinfo_t*)s;
2739 CLASS_SPEC: PACKAGEANDCLASS
2742 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2743 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2745 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2746 | '*' {PASS12 $$=registry_getanytype();}
2747 | "void" {PASS12 $$=registry_getanytype();}
2749 | "String" {$$=registry_getstringclass();}
2750 | "int" {$$=registry_getintclass();}
2751 | "uint" {$$=registry_getuintclass();}
2752 | "Boolean" {$$=registry_getbooleanclass();}
2753 | "Number" {$$=registry_getnumberclass();}
2756 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2757 MAYBETYPE: {PASS12 $$=0;}
2759 /* ----------function calls, delete, constructor calls ------ */
2761 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2762 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2764 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2765 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2766 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2768 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2772 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2773 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2775 $$.cc = code_append($1.cc, $2.c);
2779 NEW : "new" E XX MAYBE_PARAM_VALUES {
2781 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2783 code_t*paramcode = $4.cc;
2784 if($$.c->opcode == OPCODE_GETPROPERTY) {
2785 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2786 $$.c = code_cutlast($$.c);
2787 $$.c = code_append($$.c, paramcode);
2788 $$.c = abc_constructprop2($$.c, name, $4.len);
2789 multiname_destroy(name);
2790 } else if($$.c->opcode == OPCODE_GETSLOT) {
2791 int slot = (int)(ptroff_t)$$.c->data[0];
2792 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2793 multiname_t*name = t->name;
2794 $$.c = code_cutlast($$.c);
2795 $$.c = code_append($$.c, paramcode);
2796 $$.c = abc_constructprop2($$.c, name, $4.len);
2798 $$.c = code_append($$.c, paramcode);
2799 $$.c = abc_construct($$.c, $4.len);
2803 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2806 $$.c = abc_coerce_a($$.c);
2811 /* TODO: use abc_call (for calling local variables),
2812 abc_callstatic (for calling own methods)
2815 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2818 if($$.c->opcode == OPCODE_COERCE_A) {
2819 $$.c = code_cutlast($$.c);
2821 code_t*paramcode = $3.cc;
2824 if($$.c->opcode == OPCODE_GETPROPERTY) {
2825 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2826 $$.c = code_cutlast($$.c);
2827 $$.c = code_append($$.c, paramcode);
2828 $$.c = abc_callproperty2($$.c, name, $3.len);
2829 multiname_destroy(name);
2830 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
2831 int slot = (int)(ptroff_t)$$.c->data[0];
2832 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
2833 if(t->kind!=TRAIT_METHOD) {
2834 //ok: flash allows to assign closures to members.
2836 multiname_t*name = t->name;
2837 $$.c = code_cutlast($$.c);
2838 $$.c = code_append($$.c, paramcode);
2839 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2840 $$.c = abc_callproperty2($$.c, name, $3.len);
2841 } else if($$.c->opcode == OPCODE_GETSUPER) {
2842 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2843 $$.c = code_cutlast($$.c);
2844 $$.c = code_append($$.c, paramcode);
2845 $$.c = abc_callsuper2($$.c, name, $3.len);
2846 multiname_destroy(name);
2848 $$.c = abc_getglobalscope($$.c);
2849 $$.c = code_append($$.c, paramcode);
2850 $$.c = abc_call($$.c, $3.len);
2853 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2854 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2856 $$.c = abc_coerce_a($$.c);
2861 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2862 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2863 if(!state->method) syntaxerror("super() not allowed outside of a function");
2864 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2867 $$.c = abc_getlocal_0($$.c);
2869 $$.c = code_append($$.c, $3.cc);
2871 this is dependent on the control path, check this somewhere else
2872 if(state->method->has_super)
2873 syntaxerror("constructor may call super() only once");
2875 state->method->has_super = 1;
2877 $$.c = abc_constructsuper($$.c, $3.len);
2878 $$.c = abc_pushundefined($$.c);
2882 DELETE: "delete" E {
2884 if($$.c->opcode == OPCODE_COERCE_A) {
2885 $$.c = code_cutlast($$.c);
2887 multiname_t*name = 0;
2888 if($$.c->opcode == OPCODE_GETPROPERTY) {
2889 $$.c->opcode = OPCODE_DELETEPROPERTY;
2890 } else if($$.c->opcode == OPCODE_GETSLOT) {
2891 int slot = (int)(ptroff_t)$$.c->data[0];
2892 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
2893 $$.c = code_cutlast($$.c);
2894 $$.c = abc_deleteproperty2($$.c, name);
2896 $$.c = abc_getlocal_0($$.c);
2897 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2898 $$.c = abc_deleteproperty2($$.c, &m);
2900 $$.t = TYPE_BOOLEAN;
2903 RETURN: "return" %prec prec_none {
2904 $$ = abc_returnvoid(0);
2906 RETURN: "return" EXPRESSION {
2908 $$ = abc_returnvalue($$);
2911 // ----------------------- expression types -------------------------------------
2913 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2914 EXPRESSION : E %prec below_minus {$$ = $1;}
2915 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2917 $$.c = cut_last_push($$.c);
2918 $$.c = code_append($$.c,$3.c);
2921 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2922 $$=cut_last_push($1.c);
2925 // ----------------------- expression evaluation -------------------------------------
2927 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2928 //V : CONSTANT {$$ = 0;}
2930 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2931 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2932 //V : NEW {$$ = $1.c;}
2934 //V : DELETE {$$ = $1.c;}
2935 E : DELETE {$$ = $1;}
2941 namespace_t ns = {ACCESS_PACKAGE, ""};
2942 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2944 $$.c = abc_getlex2($$.c, &m);
2945 $$.c = abc_pushstring($$.c, $1.pattern);
2946 $$.c = abc_construct($$.c, 1);
2948 $$.c = abc_getlex2($$.c, &m);
2949 $$.c = abc_pushstring($$.c, $1.pattern);
2950 $$.c = abc_pushstring($$.c, $1.options);
2951 $$.c = abc_construct($$.c, 2);
2956 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2957 //MULTINAME(m, registry_getintclass());
2958 //$$.c = abc_coerce2($$.c, &m); // FIXME
2961 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2964 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2967 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2970 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2973 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
2976 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2979 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2980 $$.t = TYPE_BOOLEAN;
2982 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2983 $$.t = TYPE_BOOLEAN;
2985 CONSTANT : "null" {$$.c = abc_pushnull(0);
2989 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2990 $$.t = TYPE_BOOLEAN;
2992 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2993 $$.t = TYPE_BOOLEAN;
2995 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2996 $$.t = TYPE_BOOLEAN;
2998 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2999 $$.t = TYPE_BOOLEAN;
3001 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3002 $$.t = TYPE_BOOLEAN;
3004 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3005 $$.t = TYPE_BOOLEAN;
3007 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3008 $$.t = TYPE_BOOLEAN;
3010 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3011 $$.t = TYPE_BOOLEAN;
3014 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3016 $$.c = converttype($$.c, $1.t, $$.t);
3017 $$.c = abc_dup($$.c);
3018 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3019 $$.c = cut_last_push($$.c);
3020 $$.c = code_append($$.c,$3.c);
3021 $$.c = converttype($$.c, $3.t, $$.t);
3022 code_t*label = $$.c = abc_label($$.c);
3023 jmp->branch = label;
3026 $$.t = join_types($1.t, $3.t, 'A');
3027 /*printf("%08x:\n",$1.t);
3028 code_dump($1.c, 0, 0, "", stdout);
3029 printf("%08x:\n",$3.t);
3030 code_dump($3.c, 0, 0, "", stdout);
3031 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3033 $$.c = converttype($$.c, $1.t, $$.t);
3034 $$.c = abc_dup($$.c);
3035 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3036 $$.c = cut_last_push($$.c);
3037 $$.c = code_append($$.c,$3.c);
3038 $$.c = converttype($$.c, $3.t, $$.t);
3039 code_t*label = $$.c = abc_label($$.c);
3040 jmp->branch = label;
3043 E : '!' E {$$.c=$2.c;
3044 $$.c = abc_not($$.c);
3045 $$.t = TYPE_BOOLEAN;
3048 E : '~' E {$$.c=$2.c;
3049 $$.c = abc_bitnot($$.c);
3053 E : E '&' E {$$.c = code_append($1.c,$3.c);
3054 $$.c = abc_bitand($$.c);
3058 E : E '^' E {$$.c = code_append($1.c,$3.c);
3059 $$.c = abc_bitxor($$.c);
3063 E : E '|' E {$$.c = code_append($1.c,$3.c);
3064 $$.c = abc_bitor($$.c);
3068 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3069 $$.c = abc_rshift($$.c);
3072 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3073 $$.c = abc_urshift($$.c);
3076 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3077 $$.c = abc_lshift($$.c);
3081 E : E '/' E {$$.c = code_append($1.c,$3.c);
3082 $$.c = abc_divide($$.c);
3085 E : E '%' E {$$.c = code_append($1.c,$3.c);
3086 $$.c = abc_modulo($$.c);
3089 E : E '+' E {$$.c = code_append($1.c,$3.c);
3090 if(BOTH_INT($1.t, $3.t)) {
3091 $$.c = abc_add_i($$.c);
3094 $$.c = abc_add($$.c);
3095 $$.t = join_types($1.t,$3.t,'+');
3098 E : E '-' E {$$.c = code_append($1.c,$3.c);
3099 if(BOTH_INT($1.t,$3.t)) {
3100 $$.c = abc_subtract_i($$.c);
3103 $$.c = abc_subtract($$.c);
3107 E : E '*' E {$$.c = code_append($1.c,$3.c);
3108 if(BOTH_INT($1.t,$3.t)) {
3109 $$.c = abc_multiply_i($$.c);
3112 $$.c = abc_multiply($$.c);
3117 E : E "in" E {$$.c = code_append($1.c,$3.c);
3118 $$.c = abc_in($$.c);
3119 $$.t = TYPE_BOOLEAN;
3122 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3123 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3124 MULTINAME(m, (classinfo_t*)($3.t->data));
3125 $$.c = abc_astype2($1.c, &m);
3128 $$.c = code_append($1.c, $3.c);
3129 $$.c = abc_astypelate($$.c);
3134 E : E "instanceof" E
3135 {$$.c = code_append($1.c, $3.c);
3136 $$.c = abc_instanceof($$.c);
3137 $$.t = TYPE_BOOLEAN;
3140 E : E "is" E {$$.c = code_append($1.c, $3.c);
3141 $$.c = abc_istypelate($$.c);
3142 $$.t = TYPE_BOOLEAN;
3145 E : "typeof" '(' E ')' {
3147 $$.c = abc_typeof($$.c);
3152 $$.c = cut_last_push($2.c);
3153 $$.c = abc_pushundefined($$.c);
3157 E : "void" { $$.c = abc_pushundefined(0);
3161 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3166 $$.c=abc_negate_i($$.c);
3169 $$.c=abc_negate($$.c);
3176 $$.c = code_append($$.c, $3.c);
3178 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3179 $$.c = abc_getproperty2($$.c, &m);
3180 $$.t = 0; // array elements have unknown type
3183 E : '[' MAYBE_EXPRESSION_LIST ']' {
3185 $$.c = code_append($$.c, $2.cc);
3186 $$.c = abc_newarray($$.c, $2.len);
3187 $$.t = registry_getarrayclass();
3190 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
3191 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3193 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3195 $$.cc = code_append($$.cc, $1.c);
3196 $$.cc = code_append($$.cc, $3.c);
3199 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3202 $$.cc = code_append($$.cc, $3.c);
3203 $$.cc = code_append($$.cc, $5.c);
3208 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3210 $$.c = code_append($$.c, $2.cc);
3211 $$.c = abc_newobject($$.c, $2.len/2);
3212 $$.t = registry_getobjectclass();
3217 if(BOTH_INT($1.t,$3.t)) {
3218 c=abc_multiply_i(c);
3222 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3223 $$.c = toreadwrite($1.c, c, 0, 0);
3228 code_t*c = abc_modulo($3.c);
3229 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3230 $$.c = toreadwrite($1.c, c, 0, 0);
3234 code_t*c = abc_lshift($3.c);
3235 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3236 $$.c = toreadwrite($1.c, c, 0, 0);
3240 code_t*c = abc_rshift($3.c);
3241 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3242 $$.c = toreadwrite($1.c, c, 0, 0);
3246 code_t*c = abc_urshift($3.c);
3247 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3248 $$.c = toreadwrite($1.c, c, 0, 0);
3252 code_t*c = abc_divide($3.c);
3253 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3254 $$.c = toreadwrite($1.c, c, 0, 0);
3258 code_t*c = abc_bitor($3.c);
3259 c=converttype(c, TYPE_INT, $1.t);
3260 $$.c = toreadwrite($1.c, c, 0, 0);
3264 code_t*c = abc_bitxor($3.c);
3265 c=converttype(c, TYPE_INT, $1.t);
3266 $$.c = toreadwrite($1.c, c, 0, 0);
3272 if(TYPE_IS_INT($1.t)) {
3276 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3279 $$.c = toreadwrite($1.c, c, 0, 0);
3282 E : E "-=" E { code_t*c = $3.c;
3283 if(TYPE_IS_INT($1.t)) {
3284 c=abc_subtract_i(c);
3287 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3290 $$.c = toreadwrite($1.c, c, 0, 0);
3293 E : E '=' E { code_t*c = 0;
3294 c = code_append(c, $3.c);
3295 c = converttype(c, $3.t, $1.t);
3296 $$.c = toreadwrite($1.c, c, 1, 0);
3300 E : E '?' E ':' E %prec below_assignment {
3301 $$.t = join_types($3.t,$5.t,'?');
3303 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3304 $$.c = code_append($$.c, $3.c);
3305 $$.c = converttype($$.c, $3.t, $$.t);
3306 code_t*j2 = $$.c = abc_jump($$.c, 0);
3307 $$.c = j1->branch = abc_label($$.c);
3308 $$.c = code_append($$.c, $5.c);
3309 $$.c = converttype($$.c, $5.t, $$.t);
3310 $$.c = j2->branch = abc_label($$.c);
3313 E : E "++" { code_t*c = 0;
3314 classinfo_t*type = $1.t;
3315 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
3316 int nr = getlocalnr($1.c);
3317 code_free($1.c);$1.c=0;
3318 if(TYPE_IS_INT($1.t)) {
3319 $$.c = abc_getlocal(0, nr);
3320 $$.c = abc_inclocal_i($$.c, nr);
3321 } else if(TYPE_IS_NUMBER($1.t)) {
3322 $$.c = abc_getlocal(0, nr);
3323 $$.c = abc_inclocal($$.c, nr);
3324 } else syntaxerror("internal error");
3326 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3327 c=abc_increment_i(c);
3333 c=converttype(c, type, $1.t);
3334 $$.c = toreadwrite($1.c, c, 0, 1);
3339 // TODO: use inclocal, like with ++
3340 E : E "--" { code_t*c = 0;
3341 classinfo_t*type = $1.t;
3342 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3343 c=abc_decrement_i(c);
3349 c=converttype(c, type, $1.t);
3350 $$.c = toreadwrite($1.c, c, 0, 1);
3354 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3355 classinfo_t*type = $2.t;
3356 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3357 c=abc_increment_i(c);
3363 c=converttype(c, type, $2.t);
3364 $$.c = toreadwrite($2.c, c, 0, 0);
3368 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3369 classinfo_t*type = $2.t;
3370 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3371 c=abc_decrement_i(c);
3377 c=converttype(c, type, $2.t);
3378 $$.c = toreadwrite($2.c, c, 0, 0);
3382 E : "super" '.' T_IDENTIFIER
3383 { if(!state->cls->info)
3384 syntaxerror("super keyword not allowed outside a class");
3385 classinfo_t*t = state->cls->info->superclass;
3386 if(!t) t = TYPE_OBJECT;
3388 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3390 MEMBER_MULTINAME(m, f, $3);
3392 $$.c = abc_getlocal_0($$.c);
3393 $$.c = abc_getsuper2($$.c, &m);
3394 $$.t = slotinfo_gettype((slotinfo_t*)f);
3397 E : '@' T_IDENTIFIER {
3399 $$.c = abc_pushundefined(0);
3401 as3_warning("ignored @ operator");
3404 E : E '.' '@' T_IDENTIFIER {
3405 // child attribute TODO
3406 $$.c = abc_pushundefined(0);
3408 as3_warning("ignored .@ operator");
3411 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3412 // namespace declaration TODO
3413 $$.c = abc_pushundefined(0);
3415 as3_warning("ignored :: operator");
3418 E : E ".." T_IDENTIFIER {
3420 $$.c = abc_pushundefined(0);
3422 as3_warning("ignored .. operator");
3425 E : E '.' '(' E ')' {
3427 $$.c = abc_pushundefined(0);
3429 as3_warning("ignored .() operator");
3432 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3436 E : E '.' T_IDENTIFIER
3438 classinfo_t*t = $1.t;
3440 if(TYPE_IS_CLASS(t) && t->data) {
3445 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3447 if(f && !is_static != !(f->flags&FLAG_STATIC))
3449 if(f && f->slot && !noslot) {
3450 $$.c = abc_getslot($$.c, f->slot);
3452 MEMBER_MULTINAME(m, f, $3);
3453 $$.c = abc_getproperty2($$.c, &m);
3455 /* determine type */
3456 $$.t = slotinfo_gettype((slotinfo_t*)f);
3458 $$.c = abc_coerce_a($$.c);
3460 /* when resolving a property on an unknown type, we do know the
3461 name of the property (and don't seem to need the package), but
3462 we need to make avm2 try out all access modes */
3463 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3464 $$.c = abc_getproperty2($$.c, &m);
3465 $$.c = abc_coerce_a($$.c);
3466 $$.t = registry_getanytype();
3470 VAR_READ : T_IDENTIFIER {
3472 /* Queue unresolved identifiers for checking against the parent
3473 function's variables.
3474 We consider everything which is not a local variable "unresolved".
3475 This encompasses class names, members of the surrounding class
3476 etc. which *correct* because local variables of the parent function
3479 if(state->method->inner && !find_variable(state, $1)) {
3480 unknown_variable($1);
3490 /* look at variables */
3491 if((v = find_variable(state, $1))) {
3492 // $1 is a local variable
3493 $$.c = abc_getlocal($$.c, v->index);
3497 if((v = find_slot(state, $1))) {
3498 $$.c = abc_getscopeobject($$.c, 1);
3499 $$.c = abc_getslot($$.c, v->index);
3504 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3506 /* look at current class' members */
3507 if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) &&
3508 (f->flags&FLAG_STATIC) >= i_am_static) {
3509 // $1 is a function in this class
3510 int var_is_static = (f->flags&FLAG_STATIC);
3512 if(f->kind == INFOTYPE_METHOD) {
3513 $$.t = TYPE_FUNCTION(f);
3517 if(var_is_static && !i_am_static) {
3518 /* access to a static member from a non-static location.
3519 do this via findpropstrict:
3520 there doesn't seem to be any non-lookup way to access
3521 static properties of a class */
3522 state->method->late_binding = 1;
3524 namespace_t ns = {f->access, ""};
3525 multiname_t m = {QNAME, &ns, 0, $1};
3526 $$.c = abc_findpropstrict2($$.c, &m);
3527 $$.c = abc_getproperty2($$.c, &m);
3529 } else if(f->slot>0) {
3530 $$.c = abc_getlocal_0($$.c);
3531 $$.c = abc_getslot($$.c, f->slot);
3534 namespace_t ns = {f->access, ""};
3535 multiname_t m = {QNAME, &ns, 0, $1};
3536 $$.c = abc_getlocal_0($$.c);
3537 $$.c = abc_getproperty2($$.c, &m);
3542 /* look at actual classes, in the current package and imported */
3543 if((a = find_class($1))) {
3544 if(a->access == ACCESS_PACKAGEINTERNAL &&
3545 strcmp(a->package, state->package) &&
3546 strcmp(a->package, internal_filename_package)
3548 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
3549 infotypename(a),$1, a->package, state->package);
3551 if(a->kind != INFOTYPE_CLASS) {
3553 $$.c = abc_findpropstrict2($$.c, &m);
3554 $$.c = abc_getproperty2($$.c, &m);
3555 if(a->kind == INFOTYPE_METHOD) {
3556 methodinfo_t*f = (methodinfo_t*)a;
3557 $$.t = TYPE_FUNCTION(f);
3559 varinfo_t*v = (varinfo_t*)a;
3563 classinfo_t*c = (classinfo_t*)a;
3565 $$.c = abc_getglobalscope($$.c);
3566 $$.c = abc_getslot($$.c, c->slot);
3569 $$.c = abc_getlex2($$.c, &m);
3571 $$.t = TYPE_CLASS(c);
3576 /* unknown object, let the avm2 resolve it */
3578 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3579 state->method->late_binding = 1;
3581 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3584 $$.c = abc_findpropstrict2($$.c, &m);
3585 $$.c = abc_getproperty2($$.c, &m);
3589 // ----------------- namespaces -------------------------------------------------
3591 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3593 tokenizer_register_namespace($2);
3597 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3600 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID '=' T_IDENTIFIER {
3603 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID '=' T_STRING {
3606 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3608 tokenizer_register_namespace($3->name);