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;
68 namespace_decl_t* namespace_decl;
70 abc_exception_list_t *l;
76 %token<id> T_IDENTIFIER T_NAMESPACE
78 %token<regexp> T_REGEXP
80 %token<number_int> T_INT
81 %token<number_uint> T_UINT
82 %token<number_uint> T_BYTE
83 %token<number_uint> T_SHORT
84 %token<number_float> T_FLOAT
86 %token<id> T_FOR "for"
87 %token<id> T_WHILE "while"
89 %token<id> T_SWITCH "switch"
91 %token<token> KW_IMPLEMENTS "implements"
92 %token<token> KW_NAMESPACE "namespace"
93 %token<token> KW_PACKAGE "package"
94 %token<token> KW_PROTECTED "protected"
95 %token<token> KW_PUBLIC "public"
96 %token<token> KW_PRIVATE "private"
97 %token<token> KW_USE "use"
98 %token<token> KW_INTERNAL "internal"
99 %token<token> KW_NEW "new"
100 %token<token> KW_NATIVE "native"
101 %token<token> KW_FUNCTION "function"
102 %token<token> KW_FINALLY "finally"
103 %token<token> KW_UNDEFINED "undefined"
104 %token<token> KW_CONTINUE "continue"
105 %token<token> KW_CLASS "class"
106 %token<token> KW_CONST "const"
107 %token<token> KW_CATCH "catch"
108 %token<token> KW_CASE "case"
109 %token<token> KW_SET "set"
110 %token<token> KW_VOID "void"
111 %token<token> KW_THROW "throw"
112 %token<token> KW_STATIC "static"
113 %token<token> KW_WITH "with"
114 %token<token> KW_INSTANCEOF "instanceof"
115 %token<token> KW_IMPORT "import"
116 %token<token> KW_RETURN "return"
117 %token<token> KW_TYPEOF "typeof"
118 %token<token> KW_INTERFACE "interface"
119 %token<token> KW_NULL "null"
120 %token<token> KW_VAR "var"
121 %token<token> KW_DYNAMIC "dynamic"
122 %token<token> KW_OVERRIDE "override"
123 %token<token> KW_FINAL "final"
124 %token<token> KW_EACH "each"
125 %token<token> KW_GET "get"
126 %token<token> KW_TRY "try"
127 %token<token> KW_SUPER "super"
128 %token<token> KW_EXTENDS "extends"
129 %token<token> KW_FALSE "false"
130 %token<token> KW_TRUE "true"
131 %token<token> KW_BOOLEAN "Boolean"
132 %token<token> KW_UINT "uint"
133 %token<token> KW_INT "int"
134 %token<token> KW_NUMBER "Number"
135 %token<token> KW_STRING "String"
136 %token<token> KW_DEFAULT "default"
137 %token<token> KW_DELETE "delete"
138 %token<token> KW_IF "if"
139 %token<token> KW_ELSE "else"
140 %token<token> KW_BREAK "break"
141 %token<token> KW_IS "is"
142 %token<token> KW_IN "in"
143 %token<token> KW_AS "as"
145 %token<token> T_DICTSTART "{ (dictionary)"
146 %token<token> T_EQEQ "=="
147 %token<token> T_EQEQEQ "==="
148 %token<token> T_NE "!="
149 %token<token> T_NEE "!=="
150 %token<token> T_LE "<="
151 %token<token> T_GE ">="
152 %token<token> T_ORBY "|="
153 %token<token> T_DIVBY "/="
154 %token<token> T_MODBY "%="
155 %token<token> T_MULBY "*="
156 %token<token> T_PLUSBY "+="
157 %token<token> T_MINUSBY "-="
158 %token<token> T_XORBY "^="
159 %token<token> T_SHRBY ">>="
160 %token<token> T_SHLBY "<<="
161 %token<token> T_USHRBY ">>>="
162 %token<token> T_OROR "||"
163 %token<token> T_ANDAND "&&"
164 %token<token> T_COLONCOLON "::"
165 %token<token> T_MINUSMINUS "--"
166 %token<token> T_PLUSPLUS "++"
167 %token<token> T_DOTDOT ".."
168 %token<token> T_DOTDOTDOT "..."
169 %token<token> T_SHL "<<"
170 %token<token> T_USHR ">>>"
171 %token<token> T_SHR ">>"
173 %type <for_start> FOR_START
174 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
175 %type <namespace_decl> NAMESPACE_ID
176 %type <token> VARCONST
178 %type <code> CODEPIECE CODE_STATEMENT
179 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
180 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
181 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
182 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
183 %type <exception> CATCH FINALLY
184 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
185 %type <code> CLASS_DECLARATION
186 %type <code> NAMESPACE_DECLARATION
187 %type <code> INTERFACE_DECLARATION
188 %type <code> VOIDEXPRESSION
189 %type <value> EXPRESSION NONCOMMAEXPRESSION
190 %type <value> MAYBEEXPRESSION
191 %type <value> E DELETE
192 %type <value> CONSTANT
193 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
194 %type <value> INNERFUNCTION
195 %type <code> USE_NAMESPACE
196 %type <code> FOR_INIT
198 %type <classinfo> MAYBETYPE
201 %type <params> PARAM_LIST
202 %type <params> MAYBE_PARAM_LIST
203 %type <flags> MAYBE_MODIFIERS
204 %type <flags> MODIFIER_LIST
205 %type <flags> MODIFIER
206 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
207 %type <classinfo_list> IMPLEMENTS_LIST
208 %type <classinfo> EXTENDS CLASS_SPEC
209 %type <classinfo_list> EXTENDS_LIST
211 %type <classinfo> CLASS PACKAGEANDCLASS
212 %type <classinfo_list> CLASS_SPEC_LIST
214 %type <classinfo> TYPE
215 //%type <token> VARIABLE
216 %type <value> VAR_READ
218 //%type <token> T_IDENTIFIER
219 %type <value> FUNCTIONCALL
220 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD
222 // precedence: from low to high
226 %left below_semicolon
229 %nonassoc below_assignment // for ?:, contrary to spec
230 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
237 %nonassoc "==" "!=" "===" "!=="
238 %nonassoc "is" "as" "in"
239 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
240 %left "<<" ">>" ">>>"
244 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
246 %nonassoc below_curly
250 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
253 %left above_identifier
257 // needed for "return" precedence:
258 %nonassoc T_STRING T_REGEXP
259 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
260 %nonassoc "false" "true" "null" "undefined" "super" "function"
267 static int a3_error(char*s)
269 syntaxerror("%s", s);
270 return 0; //make gcc happy
274 static char* concat2(const char* t1, const char* t2)
278 char*text = malloc(l1+l2+1);
279 memcpy(text , t1, l1);
280 memcpy(text+l1, t2, l2);
284 static char* concat3(const char* t1, const char* t2, const char* t3)
289 char*text = malloc(l1+l2+l3+1);
290 memcpy(text , t1, l1);
291 memcpy(text+l1, t2, l2);
292 memcpy(text+l1+l2, t3, l3);
297 typedef struct _import {
301 DECLARE_LIST(import);
303 DECLARE(methodstate);
304 DECLARE_LIST(methodstate);
306 typedef struct _classstate {
312 methodstate_t*static_init;
314 //code_t*static_init;
316 char has_constructor;
319 struct _methodstate {
329 dict_t*unresolved_variables;
332 char uses_parent_function;
337 int var_index; // for inner methods
338 int slot_index; // for inner methods
339 char is_a_slot; // for inner methods
344 abc_exception_list_t*exceptions;
346 methodstate_list_t*innerfunctions;
349 typedef struct _state {
354 import_list_t*wildcard_imports;
355 dict_t*import_toplevel_packages;
357 namespace_list_t*active_namespaces;
358 namespace_decl_list_t*new_namespaces;
359 char has_own_imports;
360 char new_vars; // e.g. transition between two functions
363 methodstate_t*method;
370 typedef struct _global {
374 dict_t*file2token2info;
377 static global_t*global = 0;
378 static state_t* state = 0;
382 #define MULTINAME(m,x) \
386 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
388 #define MEMBER_MULTINAME(m,f,n) \
392 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
393 m##_ns.name = ((slotinfo_t*)(f))->package; \
398 m.namespace_set = 0; \
399 m.name = ((slotinfo_t*)(f))->name; \
401 m.type = MULTINAME; \
403 m.namespace_set = &nopackage_namespace_set; \
407 /* warning: list length of namespace set is undefined */
408 #define MULTINAME_LATE(m, access, package) \
409 namespace_t m##_ns = {access, package}; \
410 namespace_set_t m##_nsset; \
411 namespace_list_t m##_l;m##_l.next = 0; \
412 m##_nsset.namespaces = &m##_l; \
413 m##_nsset = m##_nsset; \
414 m##_l.namespace = &m##_ns; \
415 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
417 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
418 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
419 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
420 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
421 static namespace_list_t nl4 = {&ns4,0};
422 static namespace_list_t nl3 = {&ns3,&nl4};
423 static namespace_list_t nl2 = {&ns2,&nl3};
424 static namespace_list_t nl1 = {&ns1,&nl2};
425 static namespace_set_t nopackage_namespace_set = {&nl1};
427 static void new_state()
430 state_t*oldstate = state;
432 memcpy(s, state, sizeof(state_t)); //shallow copy
434 s->imports = dict_new();
436 if(!s->import_toplevel_packages) {
437 s->import_toplevel_packages = dict_new();
441 state->has_own_imports = 0;
442 state->new_namespaces = 0;
443 state->vars = dict_new();
444 state->old = oldstate;
447 static void state_has_imports()
449 state->wildcard_imports = list_clone(state->wildcard_imports);
450 state->imports = dict_clone(state->imports);
451 state->has_own_imports = 1;
453 static void import_toplevel(const char*package)
455 char* s = strdup(package);
457 dict_put(state->import_toplevel_packages, s, 0);
458 char*x = strrchr(s, '.');
466 static void state_destroy(state_t*state)
468 if(state->has_own_imports) {
469 list_free(state->wildcard_imports);
470 dict_destroy(state->imports);state->imports=0;
472 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
473 dict_destroy(state->imports);state->imports=0;
477 for(t=0;t<state->vars->hashsize;t++) {
478 dictentry_t*e =state->vars->slots[t];
480 free(e->data);e->data=0;
484 dict_destroy(state->vars);state->vars=0;
490 static void old_state()
492 if(!state || !state->old)
493 syntaxerror("invalid nesting");
494 state_t*leaving = state;
498 namespace_decl_list_t*nl=leaving->new_namespaces;
500 tokenizer_unregister_namespace(nl->namespace_decl->name);
504 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
505 free(leaving->method);
508 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
513 state_destroy(leaving);
516 static code_t* method_header(methodstate_t*m);
517 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
518 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
521 static char* internal_filename_package = 0;
522 void initialize_file(char*filename)
525 syntaxerror("invalid call to initialize_file during parsing of another file");
528 state->package = internal_filename_package = strdup(filename);
530 global->token2info = dict_lookup(global->file2token2info,
531 current_filename // use long version
533 if(!global->token2info) {
534 global->token2info = dict_new2(&ptr_type);
535 dict_put(global->file2token2info, current_filename, global->token2info);
539 state->method = rfx_calloc(sizeof(methodstate_t));
540 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
541 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
543 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
544 function_initvars(state->method, 0, 0, 1);
545 global->init = abc_initscript(global->file);
551 if(!state || state->level!=1) {
552 syntaxerror("unexpected end of file in pass %d", as3_pass);
556 code_t*header = method_header(state->method);
557 code_t*c = wrap_function(header, 0, global->init->method->body->code);
558 global->init->method->body->code = c;
559 free(state->method);state->method=0;
562 //free(state->package);state->package=0; // used in registry
563 state_destroy(state);state=0;
566 void initialize_parser()
568 global = rfx_calloc(sizeof(global_t));
569 global->file = abc_file_new();
570 global->file->flags &= ~ABCFILE_LAZY;
571 global->file2token2info = dict_new();
572 global->token2info = 0;
575 void* finish_parser()
577 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
579 global->token2info=0;
585 static void xx_scopetest()
587 /* findpropstrict doesn't just return a scope object- it
588 also makes it "active" somehow. Push local_0 on the
589 scope stack and read it back with findpropstrict, it'll
590 contain properties like "trace". Trying to find the same
591 property on a "vanilla" local_0 yields only a "undefined" */
592 //c = abc_findpropstrict(c, "[package]::trace");
594 /*c = abc_getlocal_0(c);
595 c = abc_findpropstrict(c, "[package]::trace");
597 c = abc_setlocal_1(c);
599 c = abc_pushbyte(c, 0);
600 c = abc_setlocal_2(c);
602 code_t*xx = c = abc_label(c);
603 c = abc_findpropstrict(c, "[package]::trace");
604 c = abc_pushstring(c, "prop:");
605 c = abc_hasnext2(c, 1, 2);
607 c = abc_setlocal_3(c);
608 c = abc_callpropvoid(c, "[package]::trace", 2);
609 c = abc_getlocal_3(c);
611 c = abc_iftrue(c,xx);*/
614 typedef struct _variable {
618 methodstate_t*is_inner_method;
621 static variable_t* find_variable(state_t*s, char*name)
625 v = dict_lookup(s->vars, name);
627 if(s->new_vars) break;
632 static variable_t* find_slot(state_t*s, const char*name)
634 if(s->method && s->method->slots)
635 return dict_lookup(s->method->slots, name);
639 static variable_t* find_variable_safe(state_t*s, char*name)
641 variable_t* v = find_variable(s, name);
643 syntaxerror("undefined variable: %s", name);
646 static char variable_exists(char*name)
648 return dict_contains(state->vars, name);
650 code_t*defaultvalue(code_t*c, classinfo_t*type);
652 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
655 variable_t*v = find_slot(state, name);
661 v->index = state->method->variable_count++;
666 dict_put(state->vars, name, v);
670 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
672 return new_variable2(name, type, init, maybeslot)->index;
675 #define TEMPVARNAME "__as3_temp__"
676 static int gettempvar()
678 variable_t*v = find_variable(state, TEMPVARNAME);
681 return new_variable(TEMPVARNAME, 0, 0, 0);
684 code_t* var_block(code_t*body)
690 for(t=0;t<state->vars->hashsize;t++) {
691 dictentry_t*e = state->vars->slots[t];
693 variable_t*v = (variable_t*)e->data;
694 if(v->type && v->init) {
695 c = defaultvalue(c, v->type);
696 c = abc_setlocal(c, v->index);
697 k = abc_kill(k, v->index);
707 if(x->opcode== OPCODE___BREAK__ ||
708 x->opcode== OPCODE___CONTINUE__) {
709 /* link kill code before break/continue */
710 code_t*e = code_dup(k);
711 code_t*s = code_start(e);
723 c = code_append(c, body);
724 c = code_append(c, k);
728 void unknown_variable(char*name)
730 if(!state->method->unresolved_variables)
731 state->method->unresolved_variables = dict_new();
732 if(!dict_contains(state->method->unresolved_variables, name))
733 dict_put(state->method->unresolved_variables, name, 0);
736 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
738 static void parsererror(const char*file, int line, const char*f)
740 syntaxerror("internal error in %s, %s:%d", f, file, line);
744 static code_t* add_scope_code(code_t*c, methodstate_t*m)
746 if(m->uses_slots || (m->late_binding && !m->inner)) {
747 c = abc_getlocal_0(c);
748 c = abc_pushscope(c);
751 /* FIXME: does this need to be the same activation object as
752 in the function header? */
753 c = abc_newactivation(c);
754 c = abc_pushscope(c);
759 static code_t* method_header(methodstate_t*m)
763 c = add_scope_code(c, m);
765 methodstate_list_t*l = m->innerfunctions;
767 parserassert(l->methodstate->abc);
768 if(m->uses_slots && l->methodstate->is_a_slot) {
769 c = abc_getscopeobject(c, 1);
770 c = abc_newfunction(c, l->methodstate->abc);
772 c = abc_setlocal(c, l->methodstate->var_index);
773 c = abc_setslot(c, l->methodstate->slot_index);
775 c = abc_newfunction(c, l->methodstate->abc);
776 c = abc_setlocal(c, l->methodstate->var_index);
778 free(l->methodstate);l->methodstate=0;
782 c = code_append(c, m->header);
785 if(m->is_constructor && !m->has_super) {
786 // call default constructor
787 c = abc_getlocal_0(c);
788 c = abc_constructsuper(c, 0);
790 list_free(m->innerfunctions);
791 m->innerfunctions = 0;
796 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
798 c = code_append(c, header);
799 c = code_append(c, var_block(body));
800 /* append return if necessary */
801 if(!c || (c->opcode != OPCODE_RETURNVOID &&
802 c->opcode != OPCODE_RETURNVALUE)) {
803 c = abc_returnvoid(c);
809 static void startpackage(char*name)
812 /*printf("entering package \"%s\"\n", name);*/
813 state->package = strdup(name);
815 static void endpackage()
817 /*printf("leaving package \"%s\"\n", state->package);*/
819 //used e.g. in classinfo_register:
820 //free(state->package);state->package=0;
825 #define FLAG_PUBLIC 256
826 #define FLAG_PROTECTED 512
827 #define FLAG_PRIVATE 1024
828 #define FLAG_PACKAGEINTERNAL 2048
829 #define FLAG_NAMESPACE 4096
831 static namespace_t modifiers2access(modifiers_t*mod)
836 if(mod->flags&FLAG_NAMESPACE) {
837 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
838 syntaxerror("invalid combination of access levels and namespaces");
839 ns.access = ACCESS_NAMESPACE;
841 } else if(mod->flags&FLAG_PUBLIC) {
842 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
843 syntaxerror("invalid combination of access levels");
844 ns.access = ACCESS_PACKAGE;
845 } else if(mod->flags&FLAG_PRIVATE) {
846 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
847 syntaxerror("invalid combination of access levels");
848 ns.access = ACCESS_PRIVATE;
849 } else if(mod->flags&FLAG_PROTECTED) {
850 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
851 syntaxerror("invalid combination of access levels");
852 ns.access = ACCESS_PROTECTED;
854 ns.access = ACCESS_PACKAGEINTERNAL;
858 static slotinfo_t* find_class(const char*name);
860 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
865 index = new_variable("this", 0, 0, 0);
866 else if(!m->is_global)
867 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
869 index = new_variable("globalscope", 0, 0, 0);
872 parserassert(!index);
876 /* as variables and slots share the same number, make sure
877 that those variable indices are reserved. It's up to the
878 optimizer to later shuffle the variables down to lower
880 m->variable_count = m->uses_slots;
885 for(p=params->list;p;p=p->next) {
886 new_variable(p->param->name, p->param->type, 0, 1);
891 m->scope_code = add_scope_code(m->scope_code, m);
895 methodstate_list_t*l = m->innerfunctions;
897 methodstate_t*m = l->methodstate;
899 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
900 m->var_index = v->index;
901 m->slot_index = v->index;
902 v->is_inner_method = m;
907 if(as3_pass==2 && m->slots) {
908 /* exchange unresolved identifiers with the actual objects */
909 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
910 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
911 v->type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
912 if(!v->type || v->type->kind != INFOTYPE_CLASS) {
913 syntaxerror("Couldn't find class %s", v->type->name);
921 char*as3_globalclass=0;
922 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
925 syntaxerror("inner classes now allowed");
930 classinfo_list_t*mlist=0;
932 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
933 syntaxerror("invalid modifier(s)");
935 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
936 syntaxerror("public and internal not supported at the same time.");
938 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
939 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
940 // all classes extend object
941 extends = registry_getobjectclass();
944 /* create the class name, together with the proper attributes */
948 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
949 access = ACCESS_PRIVATE; package = internal_filename_package;
950 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
951 access = ACCESS_PACKAGEINTERNAL; package = state->package;
952 } else if(state->package!=internal_filename_package) {
953 access = ACCESS_PACKAGE; package = state->package;
955 syntaxerror("public classes only allowed inside a package");
959 state->cls = rfx_calloc(sizeof(classstate_t));
960 state->cls->init = rfx_calloc(sizeof(methodstate_t));
961 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
962 /* notice: we make no effort to initialize the top variable (local0) here,
963 even though it has special meaning. We just rely on the facat
964 that pass 1 won't do anything with variables */
966 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
968 /* set current method to constructor- all code within the class-level (except
969 static variable initializations) will be executed during construction time */
970 state->method = state->cls->init;
972 if(registry_find(package, classname)) {
973 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
975 /* build info struct */
976 int num_interfaces = (list_length(implements));
977 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
978 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
981 classinfo_list_t*l = implements;
982 for(l=implements;l;l=l->next) {
983 state->cls->info->interfaces[pos++] = l->classinfo;
988 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
990 state->method = state->cls->init;
991 parserassert(state->cls && state->cls->info);
993 function_initvars(state->cls->init, 0, 0, 1);
994 function_initvars(state->cls->static_init, 0, 0, 0);
996 if(extends && (extends->flags & FLAG_FINAL))
997 syntaxerror("Can't extend final class '%s'", extends->name);
1000 while(state->cls->info->interfaces[pos]) {
1001 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1002 syntaxerror("'%s' is not an interface",
1003 state->cls->info->interfaces[pos]->name);
1007 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
1008 state->cls->info->superclass = extends;
1010 /* generate the abc code for this class */
1011 MULTINAME(classname2,state->cls->info);
1012 multiname_t*extends2 = sig2mname(extends);
1014 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1015 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1016 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1017 if(state->cls->info->flags&FLAG_INTERFACE) {
1018 abc_class_interface(state->cls->abc);
1021 abc_class_protectedNS(state->cls->abc, classname);
1023 for(mlist=implements;mlist;mlist=mlist->next) {
1024 MULTINAME(m, mlist->classinfo);
1025 abc_class_add_interface(state->cls->abc, &m);
1028 /* write the construction code for this class to the global init
1030 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1032 abc_method_body_t*m = global->init->method->body;
1033 __ getglobalscope(m);
1034 classinfo_t*s = extends;
1039 //TODO: take a look at the current scope stack, maybe
1040 // we can re-use something
1045 multiname_t*s2 = sig2mname(s);
1047 multiname_destroy(s2);
1049 __ pushscope(m); count++;
1050 m->code = m->code->prev->prev; // invert
1052 /* continue appending after last op end */
1053 while(m->code && m->code->next) m->code = m->code->next;
1055 /* TODO: if this is one of *our* classes, we can also
1056 do a getglobalscope/getslot <nr> (which references
1057 the init function's slots) */
1059 __ getlex2(m, extends2);
1061 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1062 stack is not the superclass */
1063 __ pushscope(m);count++;
1066 /* notice: we get a verify error #1107 if the top element on the scope
1067 stack is not the global object */
1069 __ pushscope(m);count++;
1071 __ newclass(m,state->cls->abc);
1075 __ setslot(m, slotindex);
1076 multiname_destroy(extends2);
1078 /* flash.display.MovieClip handling */
1080 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1081 if(state->package && state->package[0]) {
1082 as3_globalclass = concat3(state->package, ".", classname);
1084 as3_globalclass = strdup(classname);
1090 static int slotstate_varconst = 0;
1091 static modifiers_t*slotstate_flags = 0;
1092 static void setslotstate(modifiers_t* flags, int varconst)
1094 slotstate_varconst = varconst;
1095 slotstate_flags = flags;
1097 if(flags && flags->flags&FLAG_STATIC) {
1098 state->method = state->cls->static_init;
1100 state->method = state->cls->init;
1103 parserassert(state->method);
1107 static void endclass()
1110 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1112 c = abc_getlocal_0(c);
1113 c = abc_constructsuper(c, 0);
1114 state->cls->init->header = code_append(state->cls->init->header, c);
1115 state->cls->has_constructor=1;
1117 if(state->cls->init) {
1118 if(state->cls->info->flags&FLAG_INTERFACE) {
1119 if(state->cls->init->header)
1120 syntaxerror("interface can not have class-level code");
1122 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1123 code_t*c = method_header(state->cls->init);
1124 m->body->code = wrap_function(c, 0, m->body->code);
1127 if(state->cls->static_init) {
1128 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1129 code_t*c = method_header(state->cls->static_init);
1130 m->body->code = wrap_function(c, 0, m->body->code);
1137 void check_code_for_break(code_t*c)
1140 if(c->opcode == OPCODE___BREAK__) {
1141 char*name = string_cstr(c->data[0]);
1142 syntaxerror("Unresolved \"break %s\"", name);
1144 if(c->opcode == OPCODE___CONTINUE__) {
1145 char*name = string_cstr(c->data[0]);
1146 syntaxerror("Unresolved \"continue %s\"", name);
1148 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1149 char*name = string_cstr(c->data[0]);
1150 syntaxerror("Can't reference a package (%s) as such", name);
1157 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1160 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1161 if(TYPE_IS_NUMBER(t)) {
1162 xassert(c->type == CONSTANT_FLOAT
1163 || c->type == CONSTANT_INT
1164 || c->type == CONSTANT_UINT);
1165 } else if(TYPE_IS_UINT(t)) {
1166 xassert(c->type == CONSTANT_UINT ||
1167 (c->type == CONSTANT_INT && c->i>=0));
1168 } else if(TYPE_IS_INT(t)) {
1169 xassert(c->type == CONSTANT_INT);
1170 } else if(TYPE_IS_BOOLEAN(t)) {
1171 xassert(c->type == CONSTANT_TRUE
1172 || c->type == CONSTANT_FALSE);
1176 static void check_override(memberinfo_t*m, int flags)
1180 if(m->parent == state->cls->info)
1181 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1183 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1184 if(m->access==ACCESS_PRIVATE)
1186 if(m->flags & FLAG_FINAL)
1187 syntaxerror("can't override final member %s", m->name);
1189 /* allow this. it's no issue.
1190 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1191 syntaxerror("can't override static member %s", m->name);*/
1193 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1194 syntaxerror("can't override non-static member %s with static declaration", m->name);
1196 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1197 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1198 if(m->kind == INFOTYPE_METHOD)
1199 syntaxerror("can't override without explicit 'override' declaration");
1201 syntaxerror("can't override '%s'", m->name);
1206 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1208 methodinfo_t*minfo = 0;
1209 namespace_t ns = modifiers2access(mod);
1212 minfo = methodinfo_register_global(ns.access, state->package, name);
1213 minfo->return_type = 0; // save this for pass 2
1214 } else if(getset != KW_GET && getset != KW_SET) {
1216 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1218 printf("%s.%s | %s.%s\n",
1219 m->package, m->name,
1221 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1223 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1224 minfo->return_type = 0; // save this for pass 2
1225 // getslot on a member slot only returns "undefined", so no need
1226 // to actually store these
1227 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1229 //class getter/setter
1230 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1232 if(getset == KW_GET) {
1234 } else if(params->list && params->list->param && !params->list->next) {
1235 type = params->list->param->type;
1237 syntaxerror("setter function needs to take exactly one argument");
1238 // not sure wether to look into superclasses here, too
1239 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1241 if(minfo->kind!=INFOTYPE_SLOT)
1242 syntaxerror("class already contains a method called '%s'", name);
1243 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1244 syntaxerror("class already contains a field called '%s'", name);
1245 if(minfo->subtype & gs)
1246 syntaxerror("getter/setter for '%s' already defined", name);
1247 /* make a setter or getter into a getset */
1248 minfo->subtype |= gs;
1251 FIXME: this check needs to be done in pass 2
1253 if((!minfo->return_type != !type) ||
1254 (minfo->return_type && type &&
1255 !strcmp(minfo->return_type->name, type->name))) {
1256 syntaxerror("different type in getter and setter: %s and %s",
1257 minfo->return_type?minfo->return_type->name:"*",
1258 type?type->name:"*");
1261 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1262 minfo->kind = INFOTYPE_SLOT; //hack
1263 minfo->subtype = gs;
1264 minfo->return_type = 0;
1266 /* can't assign a slot as getter and setter might have different slots */
1267 //minfo->slot = slot;
1269 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1270 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1271 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1276 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1278 //parserassert(state->method && state->method->info);
1280 methodstate_t*parent_method = state->method;
1283 return_type = 0; // not valid in pass 1
1287 state->new_vars = 1;
1290 state->method = rfx_calloc(sizeof(methodstate_t));
1291 state->method->inner = 1;
1292 state->method->variable_count = 0;
1293 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1295 NEW(methodinfo_t,minfo);
1296 minfo->kind = INFOTYPE_METHOD;
1297 minfo->access = ACCESS_PACKAGEINTERNAL;
1299 state->method->info = minfo;
1302 list_append(parent_method->innerfunctions, state->method);
1304 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1306 function_initvars(state->method, params, 0, 1);
1310 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1311 state->method->variable_count = 0;
1312 parserassert(state->method);
1314 state->method->info->return_type = return_type;
1315 function_initvars(state->method, params, 0, 1);
1319 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1320 params_t*params, classinfo_t*return_type)
1322 if(state->method && state->method->info) {
1323 syntaxerror("not able to start another method scope");
1326 state->new_vars = 1;
1329 state->method = rfx_calloc(sizeof(methodstate_t));
1330 state->method->has_super = 0;
1333 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1335 state->method->is_global = 1;
1336 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1338 if(state->method->is_constructor)
1339 name = "__as3_constructor__";
1341 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1343 function_initvars(state->method, params, mod->flags, 1);
1345 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1349 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1350 state->method->variable_count = 0;
1351 parserassert(state->method);
1354 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1355 check_override(m, mod->flags);
1359 state->cls->has_constructor |= state->method->is_constructor;
1362 state->method->info->return_type = return_type;
1363 function_initvars(state->method, params, mod->flags, 1);
1367 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1368 params_t*params, classinfo_t*return_type, code_t*body)
1370 int flags = mod?mod->flags:0;
1373 // store inner methods in variables
1374 function_initvars(state->method, 0, 0, 0);
1376 methodstate_list_t*ml = state->method->innerfunctions;
1378 dict_t*xvars = dict_new();
1381 methodstate_t*m = ml->methodstate;
1382 parserassert(m->inner);
1383 if(m->unresolved_variables) {
1384 dict_t*d = m->unresolved_variables;
1386 for(t=0;t<d->hashsize;t++) {
1387 dictentry_t*l = d->slots[t];
1389 /* check parent method's variables */
1391 if((v=find_variable(state, l->key))) {
1392 m->uses_parent_function = 1;
1393 state->method->uses_slots = 1;
1394 dict_put(xvars, l->key, 0);
1401 dict_destroy(m->unresolved_variables);
1402 m->unresolved_variables = 0;
1407 if(state->method->uses_slots) {
1408 state->method->slots = dict_new();
1410 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1411 if(!name) syntaxerror("internal error");
1412 if(v->index && dict_contains(xvars, name)) {
1415 if(v->is_inner_method) {
1416 v->is_inner_method->is_a_slot = 1;
1419 dict_put(state->method->slots, name, v);
1422 state->method->uses_slots = i;
1423 dict_destroy(state->vars);state->vars = 0;
1430 /*if(state->method->uses_parent_function){
1431 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1436 multiname_t*type2 = sig2mname(return_type);
1438 if(state->method->inner) {
1439 f = state->method->abc;
1440 abc_method_init(f, global->file, type2, 1);
1441 } else if(state->method->is_constructor) {
1442 f = abc_class_getconstructor(state->cls->abc, type2);
1443 } else if(!state->method->is_global) {
1444 namespace_t mname_ns = {state->method->info->access, ""};
1445 multiname_t mname = {QNAME, &mname_ns, 0, name};
1447 if(flags&FLAG_STATIC)
1448 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1450 f = abc_class_method(state->cls->abc, type2, &mname);
1451 slot = f->trait->slot_id;
1453 namespace_t mname_ns = {state->method->info->access, state->package};
1454 multiname_t mname = {QNAME, &mname_ns, 0, name};
1456 f = abc_method_new(global->file, type2, 1);
1457 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1458 //abc_code_t*c = global->init->method->body->code;
1460 //flash doesn't seem to allow us to access function slots
1461 //state->method->info->slot = slot;
1463 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1464 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1465 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1466 if(params->varargs) f->flags |= METHOD_NEED_REST;
1470 for(p=params->list;p;p=p->next) {
1471 if(params->varargs && !p->next) {
1472 break; //varargs: omit last parameter in function signature
1474 multiname_t*m = sig2mname(p->param->type);
1475 list_append(f->parameters, m);
1476 if(p->param->value) {
1477 check_constant_against_type(p->param->type, p->param->value);
1478 opt=1;list_append(f->optional_parameters, p->param->value);
1480 syntaxerror("non-optional parameter not allowed after optional parameters");
1483 if(state->method->slots) {
1484 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1486 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1487 multiname_t*type = sig2mname(v->type);
1488 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1489 t->slot_id = v->index;
1494 check_code_for_break(body);
1496 /* Seems this works now.
1497 if(state->method->exceptions && state->method->uses_slots) {
1498 as3_warning("try/catch and activation not supported yet within the same method");
1502 f->body->code = body;
1503 f->body->exceptions = state->method->exceptions;
1504 } else { //interface
1506 syntaxerror("interface methods can't have a method body");
1516 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1521 void breakjumpsto(code_t*c, char*name, code_t*jump)
1524 if(c->opcode == OPCODE___BREAK__) {
1525 string_t*name2 = c->data[0];
1526 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1527 c->opcode = OPCODE_JUMP;
1534 void continuejumpsto(code_t*c, char*name, code_t*jump)
1537 if(c->opcode == OPCODE___CONTINUE__) {
1538 string_t*name2 = c->data[0];
1539 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1540 c->opcode = OPCODE_JUMP;
1548 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1549 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1550 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1552 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1554 if(!type1 || !type2)
1555 return registry_getanytype();
1556 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1557 return registry_getanytype();
1560 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1569 return registry_getanytype();
1571 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1576 return abc_coerce_a(c);
1580 // cast an "any" type to a specific type. subject to
1581 // runtime exceptions
1582 return abc_coerce2(c, &m);
1585 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1586 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1587 // allow conversion between number types
1588 return abc_coerce2(c, &m);
1590 //printf("%s.%s\n", from.package, from.name);
1591 //printf("%s.%s\n", to.package, to.name);
1593 classinfo_t*supertype = from;
1595 if(supertype == to) {
1596 // target type is one of from's superclasses
1597 return abc_coerce2(c, &m);
1600 while(supertype->interfaces[t]) {
1601 if(supertype->interfaces[t]==to) {
1602 // target type is one of from's interfaces
1603 return abc_coerce2(c, &m);
1607 supertype = supertype->superclass;
1609 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1611 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1613 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1616 as3_error("can't convert type %s%s%s to %s%s%s",
1617 from->package, from->package?".":"", from->name,
1618 to->package, to->package?".":"", to->name);
1622 code_t*defaultvalue(code_t*c, classinfo_t*type)
1624 if(TYPE_IS_INT(type)) {
1625 c = abc_pushbyte(c, 0);
1626 } else if(TYPE_IS_UINT(type)) {
1627 c = abc_pushuint(c, 0);
1628 } else if(TYPE_IS_FLOAT(type)) {
1630 } else if(TYPE_IS_BOOLEAN(type)) {
1631 c = abc_pushfalse(c);
1633 //c = abc_pushundefined(c);
1635 c = abc_pushnull(c);
1637 c = abc_coerce2(c, &m);
1642 char is_pushundefined(code_t*c)
1644 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1647 static const char* get_package_from_name(const char*name)
1649 /* try explicit imports */
1650 dictentry_t* e = dict_get_slot(state->imports, name);
1652 if(!strcmp(e->key, name)) {
1653 slotinfo_t*c = (slotinfo_t*)e->data;
1654 if(c) return c->package;
1660 static namespace_list_t*get_current_imports()
1662 namespace_list_t*searchlist = 0;
1664 list_append(searchlist, namespace_new_package(state->package));
1666 import_list_t*l = state->wildcard_imports;
1668 namespace_t*ns = namespace_new_package(l->import->package);
1669 list_append(searchlist, ns);
1672 list_append(searchlist, namespace_new_package(""));
1673 list_append(searchlist, namespace_new_package(internal_filename_package));
1677 static slotinfo_t* find_class(const char*name)
1681 c = registry_find(state->package, name);
1684 /* try explicit imports */
1685 dictentry_t* e = dict_get_slot(state->imports, name);
1688 if(!strcmp(e->key, name)) {
1689 c = (slotinfo_t*)e->data;
1695 /* try package.* imports */
1696 import_list_t*l = state->wildcard_imports;
1698 //printf("does package %s contain a class %s?\n", l->import->package, name);
1699 c = registry_find(l->import->package, name);
1704 /* try global package */
1705 c = registry_find("", name);
1708 /* try local "filename" package */
1709 c = registry_find(internal_filename_package, name);
1714 typedcode_t push_class(slotinfo_t*a)
1719 if(a->access == ACCESS_PACKAGEINTERNAL &&
1720 strcmp(a->package, state->package) &&
1721 strcmp(a->package, internal_filename_package)
1723 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1724 infotypename(a), a->name, a->package, state->package);
1727 if(a->kind != INFOTYPE_CLASS) {
1729 x.c = abc_findpropstrict2(x.c, &m);
1730 x.c = abc_getproperty2(x.c, &m);
1731 if(a->kind == INFOTYPE_METHOD) {
1732 methodinfo_t*f = (methodinfo_t*)a;
1733 x.t = TYPE_FUNCTION(f);
1735 varinfo_t*v = (varinfo_t*)a;
1739 classinfo_t*c = (classinfo_t*)a;
1741 x.c = abc_getglobalscope(x.c);
1742 x.c = abc_getslot(x.c, c->slot);
1745 x.c = abc_getlex2(x.c, &m);
1747 x.t = TYPE_CLASS(c);
1752 static char is_getlocal(code_t*c)
1754 if(!c || c->prev || c->next)
1756 return(c->opcode == OPCODE_GETLOCAL
1757 || c->opcode == OPCODE_GETLOCAL_0
1758 || c->opcode == OPCODE_GETLOCAL_1
1759 || c->opcode == OPCODE_GETLOCAL_2
1760 || c->opcode == OPCODE_GETLOCAL_3);
1762 static int getlocalnr(code_t*c)
1764 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1765 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1766 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1767 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1768 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1769 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1773 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1777 [prefix code] [read instruction]
1781 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1783 if(in && in->opcode == OPCODE_COERCE_A) {
1784 in = code_cutlast(in);
1787 syntaxerror("internal error");
1789 /* chop off read instruction */
1793 prefix = r->prev;r->prev = 0;
1799 char use_temp_var = readbefore;
1801 /* generate the write instruction, and maybe append a dup to the prefix code */
1802 code_t* write = abc_nop(0);
1803 if(r->opcode == OPCODE_GETPROPERTY) {
1804 write->opcode = OPCODE_SETPROPERTY;
1805 multiname_t*m = (multiname_t*)r->data[0];
1806 write->data[0] = multiname_clone(m);
1807 if(m->type == QNAME || m->type == MULTINAME) {
1809 prefix = abc_dup(prefix); // we need the object, too
1812 } else if(m->type == MULTINAMEL) {
1814 /* dupping two values on the stack requires 5 operations and one register-
1815 couldn't adobe just have given us a dup2? */
1816 int temp = gettempvar();
1817 prefix = abc_setlocal(prefix, temp);
1818 prefix = abc_dup(prefix);
1819 prefix = abc_getlocal(prefix, temp);
1820 prefix = abc_swap(prefix);
1821 prefix = abc_getlocal(prefix, temp);
1823 prefix = abc_kill(prefix, temp);
1827 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1829 } else if(r->opcode == OPCODE_GETSLOT) {
1830 write->opcode = OPCODE_SETSLOT;
1831 write->data[0] = r->data[0];
1833 prefix = abc_dup(prefix); // we need the object, too
1836 } else if(r->opcode == OPCODE_GETLOCAL) {
1837 write->opcode = OPCODE_SETLOCAL;
1838 write->data[0] = r->data[0];
1839 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1840 write->opcode = OPCODE_SETLOCAL_0;
1841 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1842 write->opcode = OPCODE_SETLOCAL_1;
1843 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1844 write->opcode = OPCODE_SETLOCAL_2;
1845 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1846 write->opcode = OPCODE_SETLOCAL_3;
1847 } else if(r->opcode == OPCODE_GETSUPER) {
1848 write->opcode = OPCODE_SETSUPER;
1849 multiname_t*m = (multiname_t*)r->data[0];
1850 write->data[0] = multiname_clone(m);
1853 syntaxerror("illegal lvalue: can't assign a value to this expression");
1860 /* with getproperty/getslot, we have to be extra careful not
1861 to execute the read code twice, as it might have side-effects
1862 (e.g. if the property is in fact a setter/getter combination)
1864 So read the value, modify it, and write it again,
1865 using prefix only once and making sure (by using a temporary
1866 register) that the return value is what we just wrote */
1867 temp = gettempvar();
1868 c = code_append(c, prefix);
1869 c = code_append(c, r);
1872 c = abc_setlocal(c, temp);
1874 c = code_append(c, middlepart);
1877 c = abc_setlocal(c, temp);
1879 c = code_append(c, write);
1880 c = abc_getlocal(c, temp);
1881 c = abc_kill(c, temp);
1883 /* if we're allowed to execute the read code twice *and*
1884 the middlepart doesn't modify the code, things are easier.
1886 code_t* r2 = code_dup(r);
1887 //c = code_append(c, prefix);
1888 parserassert(!prefix);
1889 c = code_append(c, r);
1890 c = code_append(c, middlepart);
1891 c = code_append(c, write);
1892 c = code_append(c, r2);
1895 /* even smaller version: overwrite the value without reading
1899 c = code_append(c, prefix);
1902 c = code_append(c, middlepart);
1903 c = code_append(c, write);
1904 c = code_append(c, r);
1907 temp = gettempvar();
1909 c = code_append(c, prefix);
1911 c = code_append(c, middlepart);
1913 c = abc_setlocal(c, temp);
1914 c = code_append(c, write);
1915 c = abc_getlocal(c, temp);
1916 c = abc_kill(c, temp);
1922 char is_break_or_jump(code_t*c)
1926 if(c->opcode == OPCODE_JUMP ||
1927 c->opcode == OPCODE___BREAK__ ||
1928 c->opcode == OPCODE___CONTINUE__ ||
1929 c->opcode == OPCODE_THROW ||
1930 c->opcode == OPCODE_RETURNVOID ||
1931 c->opcode == OPCODE_RETURNVALUE) {
1938 #define IS_FINALLY_TARGET(op) \
1939 ((op) == OPCODE___CONTINUE__ || \
1940 (op) == OPCODE___BREAK__ || \
1941 (op) == OPCODE_RETURNVOID || \
1942 (op) == OPCODE_RETURNVALUE || \
1943 (op) == OPCODE___RETHROW__)
1945 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1947 #define NEED_EXTRA_STACK_ARG
1948 code_t*finally_label = abc_nop(0);
1949 NEW(lookupswitch_t, l);
1955 code_t*prev = i->prev;
1956 if(IS_FINALLY_TARGET(i->opcode)) {
1959 if(i->opcode == OPCODE___RETHROW__ ||
1960 i->opcode == OPCODE_RETURNVALUE) {
1961 if(i->opcode == OPCODE___RETHROW__)
1962 i->opcode = OPCODE_THROW;
1964 p = abc_coerce_a(p);
1965 p = abc_setlocal(p, tempvar);
1967 p = abc_pushbyte(p, count++);
1968 p = abc_jump(p, finally_label);
1969 code_t*target = p = abc_label(p);
1970 #ifdef NEED_EXTRA_STACK_ARG
1974 p = abc_getlocal(p, tempvar);
1977 p->next = i;i->prev = p;
1978 list_append(l->targets, target);
1984 c = abc_pushbyte(c, -1);
1985 c = code_append(c, finally_label);
1986 c = code_append(c, finally);
1988 #ifdef NEED_EXTRA_STACK_ARG
1991 c = abc_lookupswitch(c, l);
1992 c = l->def = abc_label(c);
1993 #ifdef NEED_EXTRA_STACK_ARG
2000 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
2004 code_t*prev = i->prev;
2005 if(IS_FINALLY_TARGET(i->opcode)) {
2006 if(i->opcode == OPCODE___RETHROW__)
2007 i->opcode = OPCODE_THROW;
2008 code_t*end = code_dup(finally);
2009 code_t*start = code_start(end);
2010 if(prev) prev->next = start;
2017 return code_append(c, finally);
2020 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
2026 int num_insertion_points=0;
2028 if(IS_FINALLY_TARGET(i->opcode))
2029 num_insertion_points++;
2036 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
2041 int simple_version_cost = (1+num_insertion_points)*code_size;
2042 int lookup_version_cost = 4*num_insertion_points + 5;
2044 if(cantdup || simple_version_cost > lookup_version_cost) {
2045 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
2046 return insert_finally_lookup(c, finally, tempvar);
2048 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
2049 return insert_finally_simple(c, finally, tempvar);
2053 #define PASS1 }} if(as3_pass == 1) {{
2054 #define PASS1END }} if(as3_pass == 2) {{
2055 #define PASS2 }} if(as3_pass == 2) {{
2056 #define PASS12 }} {{
2057 #define PASS12END }} if(as3_pass == 2) {{
2063 /* ------------ code blocks / statements ---------------- */
2065 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2067 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2068 PROGRAM_CODE_LIST: PROGRAM_CODE
2069 | PROGRAM_CODE_LIST PROGRAM_CODE
2071 PROGRAM_CODE: PACKAGE_DECLARATION
2072 | INTERFACE_DECLARATION
2074 | FUNCTION_DECLARATION
2077 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
2080 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2081 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2082 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2084 INPACKAGE_CODE: INTERFACE_DECLARATION
2086 | FUNCTION_DECLARATION
2089 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
2092 MAYBECODE: CODE {$$=$1;}
2093 MAYBECODE: {$$=code_new();}
2095 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2096 CODE: CODEPIECE {$$=$1;}
2098 // code which may appear outside of methods
2099 CODE_STATEMENT: IMPORT
2101 CODE_STATEMENT: FOR_IN
2102 CODE_STATEMENT: WHILE
2103 CODE_STATEMENT: DO_WHILE
2104 CODE_STATEMENT: SWITCH
2106 CODE_STATEMENT: WITH
2108 CODE_STATEMENT: VOIDEXPRESSION
2109 CODE_STATEMENT: USE_NAMESPACE
2110 CODE_STATEMENT: NAMESPACE_DECLARATION
2111 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2112 CODE_STATEMENT: '{' '}' {$$=0;}
2114 // code which may appear in methods
2115 CODEPIECE: ';' {$$=0;}
2116 CODEPIECE: CODE_STATEMENT
2117 CODEPIECE: VARIABLE_DECLARATION
2122 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
2124 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2125 //CODEBLOCK : '{' '}' {$$=0;}
2126 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2127 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2129 /* ------------ package init code ------------------- */
2131 PACKAGE_INITCODE: CODE_STATEMENT {
2132 code_t**cc = &global->init->method->body->code;
2133 *cc = code_append(*cc, $1);
2136 /* ------------ conditional compilation ------------- */
2138 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
2140 /* ------------ variables --------------------------- */
2142 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2143 | {$$.c=abc_pushundefined(0);
2147 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2148 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2150 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2151 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2153 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2156 if(variable_exists($1))
2157 syntaxerror("Variable %s already defined", $1);
2159 new_variable($1, 0, 1, 0);
2162 if(!is_subtype_of($3.t, $2)) {
2163 syntaxerror("Can't convert %s to %s", $3.t->name,
2169 if(state->method->uses_slots) {
2170 variable_t* v = find_slot(state, $1);
2172 // this variable is stored in a slot
2180 index = new_variable($1, $2, 1, 0);
2183 $$ = slot?abc_getscopeobject(0, 1):0;
2186 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2187 $$ = code_append($$, $3.c);
2188 $$ = converttype($$, $3.t, $2);
2191 $$ = defaultvalue($$, $2);
2194 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2195 $$ = code_append($$, $3.c);
2196 $$ = abc_coerce_a($$);
2198 // don't do anything
2206 $$ = abc_setslot($$, index);
2208 $$ = abc_setlocal($$, index);
2212 /* ------------ control flow ------------------------- */
2214 MAYBEELSE: %prec below_else {$$ = code_new();}
2215 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2216 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2218 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2221 $$ = code_append($$, $4.c);
2222 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2224 $$ = code_append($$, $6);
2226 myjmp = $$ = abc_jump($$, 0);
2228 myif->branch = $$ = abc_nop($$);
2230 $$ = code_append($$, $7);
2231 myjmp->branch = $$ = abc_nop($$);
2237 FOR_INIT : {$$=code_new();}
2238 FOR_INIT : VARIABLE_DECLARATION
2239 FOR_INIT : VOIDEXPRESSION
2241 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2242 // (I don't see any easy way to revolve this conflict otherwise, as we
2243 // can't touch VAR_READ without upsetting the precedence about "return")
2244 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2245 PASS1 $$=$2;new_variable($2,0,1,0);
2246 PASS2 $$=$2;new_variable($2,$3,1,0);
2248 FOR_IN_INIT : T_IDENTIFIER {
2253 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2254 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2256 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2257 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2259 $$ = code_append($$, $2);
2260 code_t*loopstart = $$ = abc_label($$);
2261 $$ = code_append($$, $4.c);
2262 code_t*myif = $$ = abc_iffalse($$, 0);
2263 $$ = code_append($$, $8);
2264 code_t*cont = $$ = abc_nop($$);
2265 $$ = code_append($$, $6);
2266 $$ = abc_jump($$, loopstart);
2267 code_t*out = $$ = abc_nop($$);
2268 breakjumpsto($$, $1.name, out);
2269 continuejumpsto($$, $1.name, cont);
2276 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2277 variable_t*var = find_variable(state, $2);
2279 syntaxerror("variable %s not known in this scope", $2);
2282 char*tmp1name = concat2($2, "__tmp1__");
2283 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2284 char*tmp2name = concat2($2, "__array__");
2285 int array = new_variable(tmp1name, 0, 0, 0);
2288 $$ = code_append($$, $4.c);
2289 $$ = abc_coerce_a($$);
2290 $$ = abc_setlocal($$, array);
2291 $$ = abc_pushbyte($$, 0);
2292 $$ = abc_setlocal($$, it);
2294 code_t*loopstart = $$ = abc_label($$);
2296 $$ = abc_hasnext2($$, array, it);
2297 code_t*myif = $$ = abc_iffalse($$, 0);
2298 $$ = abc_getlocal($$, array);
2299 $$ = abc_getlocal($$, it);
2301 $$ = abc_nextname($$);
2303 $$ = abc_nextvalue($$);
2304 $$ = converttype($$, 0, var->type);
2305 $$ = abc_setlocal($$, var->index);
2307 $$ = code_append($$, $6);
2308 $$ = abc_jump($$, loopstart);
2310 code_t*out = $$ = abc_nop($$);
2311 breakjumpsto($$, $1.name, out);
2312 continuejumpsto($$, $1.name, loopstart);
2324 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2328 code_t*myjmp = $$ = abc_jump($$, 0);
2329 code_t*loopstart = $$ = abc_label($$);
2330 $$ = code_append($$, $6);
2331 code_t*cont = $$ = abc_nop($$);
2332 myjmp->branch = cont;
2333 $$ = code_append($$, $4.c);
2334 $$ = abc_iftrue($$, loopstart);
2335 code_t*out = $$ = abc_nop($$);
2336 breakjumpsto($$, $1, out);
2337 continuejumpsto($$, $1, cont);
2343 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2345 code_t*loopstart = $$ = abc_label($$);
2346 $$ = code_append($$, $3);
2347 code_t*cont = $$ = abc_nop($$);
2348 $$ = code_append($$, $6.c);
2349 $$ = abc_iftrue($$, loopstart);
2350 code_t*out = $$ = abc_nop($$);
2351 breakjumpsto($$, $1, out);
2352 continuejumpsto($$, $1, cont);
2358 BREAK : "break" %prec prec_none {
2359 $$ = abc___break__(0, "");
2361 BREAK : "break" T_IDENTIFIER {
2362 $$ = abc___break__(0, $2);
2364 CONTINUE : "continue" %prec prec_none {
2365 $$ = abc___continue__(0, "");
2367 CONTINUE : "continue" T_IDENTIFIER {
2368 $$ = abc___continue__(0, $2);
2371 MAYBE_CASE_LIST : {$$=0;}
2372 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2373 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2374 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2375 CASE_LIST: CASE {$$=$1;}
2376 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2378 CASE: "case" E ':' MAYBECODE {
2380 $$ = code_append($$, $2.c);
2381 code_t*j = $$ = abc_ifne($$, 0);
2382 $$ = code_append($$, $4);
2383 if($$->opcode != OPCODE___BREAK__) {
2384 $$ = abc___fallthrough__($$, "");
2386 code_t*e = $$ = abc_nop($$);
2389 DEFAULT: "default" ':' MAYBECODE {
2392 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2394 $$ = code_append($$, $7);
2395 code_t*out = $$ = abc_pop($$);
2396 breakjumpsto($$, $1, out);
2398 code_t*c = $$,*lastblock=0;
2400 if(c->opcode == OPCODE_IFNE) {
2401 if(!c->next) syntaxerror("internal error in fallthrough handling");
2403 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2405 c->opcode = OPCODE_JUMP;
2406 c->branch = lastblock;
2408 /* fall through end of switch */
2409 c->opcode = OPCODE_NOP;
2419 /* ------------ try / catch /finally ---------------- */
2421 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2422 state->exception_name=$3;
2423 PASS1 new_variable($3, 0, 0, 0);
2424 PASS2 new_variable($3, $4, 0, 0);
2427 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2428 multiname_t name = {QNAME, &name_ns, 0, $3};
2430 NEW(abc_exception_t, e)
2431 e->exc_type = sig2mname($4);
2432 e->var_name = multiname_clone(&name);
2436 int i = find_variable_safe(state, $3)->index;
2437 e->target = c = abc_nop(0);
2438 c = abc_setlocal(c, i);
2439 c = code_append(c, code_dup(state->method->scope_code));
2440 c = code_append(c, $8);
2446 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2451 NEW(abc_exception_t, e)
2452 e->exc_type = 0; //all exceptions
2453 e->var_name = 0; //no name
2456 e->to = code_append(e->to, $4);
2462 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2463 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2464 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2465 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2469 list_append($$.l,$2);
2470 $$.finally = $2->to;$2->to=0;
2473 CATCH_FINALLY_LIST: FINALLY {
2477 list_append($$.l,$1);
2478 $$.finally = $1->to;$1->to=0;
2482 TRY : "try" '{' {PASS12 new_state();
2483 state->method->has_exceptions=1;
2484 state->method->late_binding=1;//for invariant scope_code
2485 } MAYBECODE '}' CATCH_FINALLY_LIST {
2486 code_t*out = abc_nop(0);
2488 code_t*start = abc_nop(0);
2489 $$ = code_append(start, $4);
2490 if(!is_break_or_jump($4)) {
2491 $$ = abc_jump($$, out);
2493 code_t*end = $$ = abc_nop($$);
2497 tmp = new_variable("__finally__", 0, 0, 0);
2499 abc_exception_list_t*l = $6.l;
2502 abc_exception_t*e = l->abc_exception;
2504 $$ = code_append($$, e->target);
2505 $$ = abc_jump($$, out);
2507 parserassert((ptroff_t)$6.finally);
2509 e->target = $$ = abc_nop($$);
2510 $$ = code_append($$, code_dup(state->method->scope_code));
2511 $$ = abc___rethrow__($$);
2519 $$ = code_append($$, out);
2521 $$ = insert_finally($$, $6.finally, tmp);
2523 list_concat(state->method->exceptions, $6.l);
2529 /* ------------ throw ------------------------------- */
2531 THROW : "throw" EXPRESSION {
2535 THROW : "throw" %prec prec_none {
2536 if(!state->exception_name)
2537 syntaxerror("re-throw only possible within a catch block");
2538 variable_t*v = find_variable(state, state->exception_name);
2540 $$=abc_getlocal($$, v->index);
2544 /* ------------ with -------------------------------- */
2546 WITH_HEAD : "with" '(' EXPRESSION ')' {
2548 if(state->method->has_exceptions) {
2550 sprintf(var, "#with#_%d", as3_tokencount);
2551 int v = new_variable(var,$3.t,0,0);
2552 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2553 state->method->scope_code = abc_pushwith(state->method->scope_code);
2558 WITH : WITH_HEAD CODEBLOCK {
2559 /* remove getlocal;pushwith from scope code again */
2560 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2563 if(state->method->has_exceptions) {
2565 $$ = abc_setlocal($$, $1.number);
2567 $$ = abc_pushwith($$);
2568 $$ = code_append($$, $2);
2569 $$ = abc_popscope($$);
2573 /* ------------ packages and imports ---------------- */
2575 X_IDENTIFIER: T_IDENTIFIER
2576 | "package" {PASS12 $$="package";}
2578 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2579 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2581 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2582 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2583 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2584 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2586 IMPORT : "import" PACKAGEANDCLASS {
2588 slotinfo_t*s = registry_find($2->package, $2->name);
2589 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2590 as3_schedule_class($2->package, $2->name);
2596 syntaxerror("Couldn't import class\n");
2597 state_has_imports();
2598 dict_put(state->imports, c->name, c);
2599 import_toplevel(c->package);
2602 IMPORT : "import" PACKAGE '.' '*' {
2604 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2605 as3_schedule_package($2);
2611 state_has_imports();
2612 list_append(state->wildcard_imports, i);
2613 import_toplevel(i->package);
2617 /* ------------ classes and interfaces (header) -------------- */
2619 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2620 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2621 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2622 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2624 $$.flags=$1.flags|$2.flags;
2625 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2626 $$.ns=$1.ns?$1.ns:$2.ns;
2630 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2631 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2632 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2633 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2634 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2635 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2636 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2637 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2638 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2639 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2643 EXTENDS : {PASS12 $$=0;}
2644 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2646 EXTENDS_LIST : {PASS12 $$=list_new();}
2647 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2649 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2650 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2652 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2653 EXTENDS IMPLEMENTS_LIST
2654 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2656 '}' {PASS12 endclass();$$=0;}
2658 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2660 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2661 startclass(&$1,$3,0,$4);}
2662 MAYBE_INTERFACE_BODY
2663 '}' {PASS12 endclass();$$=0;}
2665 /* ------------ classes and interfaces (body) -------------- */
2668 MAYBE_CLASS_BODY : CLASS_BODY
2669 CLASS_BODY : CLASS_BODY_ITEM
2670 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2671 CLASS_BODY_ITEM : ';'
2672 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2673 CLASS_BODY_ITEM : SLOT_DECLARATION
2674 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2676 CLASS_BODY_ITEM : CODE_STATEMENT {
2677 code_t*c = state->cls->static_init->header;
2678 c = code_append(c, $1);
2679 state->cls->static_init->header = c;
2682 MAYBE_INTERFACE_BODY :
2683 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2684 INTERFACE_BODY : IDECLARATION
2685 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2687 IDECLARATION : "var" T_IDENTIFIER {
2688 syntaxerror("variable declarations not allowed in interfaces");
2690 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2692 $1.flags |= FLAG_PUBLIC;
2693 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2694 syntaxerror("invalid method modifiers: interface methods always need to be public");
2696 startfunction(&$1,$3,$4,&$6,$8);
2697 endfunction(&$1,$3,$4,&$6,$8, 0);
2698 list_deep_free($6.list);
2701 /* ------------ classes and interfaces (body, slots ) ------- */
2703 VARCONST: "var" | "const"
2705 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2707 SLOT_LIST: ONE_SLOT {$$ = $1;}
2708 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2710 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2712 int flags = slotstate_flags->flags;
2713 namespace_t ns = modifiers2access(slotstate_flags);
2715 varinfo_t* info = 0;
2717 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2719 check_override(i, flags);
2721 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2723 slotinfo_t*i = registry_find(state->package, $1);
2725 syntaxerror("package %s already contains '%s'", state->package, $1);
2727 if(ns.name && ns.name[0]) {
2728 syntaxerror("namespaces not allowed on package-level variables");
2730 info = varinfo_register_global(ns.access, state->package, $1);
2734 info->flags = flags;
2737 multiname_t mname = {QNAME, &ns, 0, $1};
2739 trait_list_t**traits;
2743 ns.name = state->package;
2744 traits = &global->init->traits;
2745 code = &global->init->method->body->code;
2746 } else if(flags&FLAG_STATIC) {
2748 traits = &state->cls->abc->static_traits;
2749 code = &state->cls->static_init->header;
2751 // instance variable
2752 traits = &state->cls->abc->traits;
2753 code = &state->cls->init->header;
2759 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2761 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2763 info->slot = t->slot_id;
2765 /* initalization code (if needed) */
2767 if($3.c && !is_pushundefined($3.c)) {
2768 c = abc_getlocal_0(c);
2769 c = code_append(c, $3.c);
2770 c = converttype(c, $3.t, $2);
2771 c = abc_setslot(c, t->slot_id);
2774 *code = code_append(*code, c);
2776 if(slotstate_varconst==KW_CONST) {
2777 t->kind= TRAIT_CONST;
2783 /* ------------ constants -------------------------------------- */
2785 MAYBESTATICCONSTANT: {$$=0;}
2786 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2788 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2789 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2790 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2791 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2792 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2793 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2794 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2795 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2796 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2797 STATICCONSTANT : T_IDENTIFIER {
2798 if(!strcmp($1, "NaN")) {
2799 $$ = constant_new_float(__builtin_nan(""));
2801 as3_warning("Couldn't evaluate constant value of %s", $1);
2802 $$ = constant_new_null($1);
2806 /* ------------ classes and interfaces (body, functions) ------- */
2808 // non-vararg version
2811 memset(&$$,0,sizeof($$));
2813 MAYBE_PARAM_LIST: PARAM_LIST {
2819 MAYBE_PARAM_LIST: "..." PARAM {
2821 memset(&$$,0,sizeof($$));
2823 list_append($$.list, $2);
2825 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2829 list_append($$.list, $4);
2833 PARAM_LIST: PARAM_LIST ',' PARAM {
2836 list_append($$.list, $3);
2840 memset(&$$,0,sizeof($$));
2841 list_append($$.list, $1);
2844 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2846 $$ = rfx_calloc(sizeof(param_t));
2852 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2854 $$ = rfx_calloc(sizeof(param_t));
2856 $$->type = TYPE_ANY;
2864 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2865 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2868 endfunction(&$1,$3,$4,&$6,0,0);
2870 if(!state->method->info) syntaxerror("internal error");
2872 code_t*c = method_header(state->method);
2873 c = wrap_function(c, 0, $11);
2875 endfunction(&$1,$3,$4,&$6,$8,c);
2877 list_deep_free($6.list);
2881 MAYBE_IDENTIFIER: T_IDENTIFIER
2882 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2883 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2884 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2887 endfunction(0,0,$2,&$4,0,0);
2889 methodinfo_t*f = state->method->info;
2890 if(!f || !f->kind) syntaxerror("internal error");
2892 code_t*c = method_header(state->method);
2893 c = wrap_function(c, 0, $9);
2895 int index = state->method->var_index;
2896 endfunction(0,0,$2,&$4,$6,c);
2898 $$.c = abc_getlocal(0, index);
2899 $$.t = TYPE_FUNCTION(f);
2901 PASS12 list_deep_free($4.list);
2905 /* ------------- package + class ids --------------- */
2907 CLASS: T_IDENTIFIER {
2908 PASS1 NEW(unresolvedinfo_t,c);
2909 memset(c, 0, sizeof(*c));
2910 c->kind = INFOTYPE_UNRESOLVED;
2912 c->package = get_package_from_name($1);
2914 c->nsset = get_current_imports();
2915 /* make the compiler look for this class in the current directory,
2917 as3_schedule_class_noerror(state->package, $1);
2919 $$ = (classinfo_t*)c;
2921 slotinfo_t*s = find_class($1);
2922 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2923 $$ = (classinfo_t*)s;
2926 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2927 PASS1 NEW(unresolvedinfo_t,c);
2928 memset(c, 0, sizeof(*c));
2929 c->kind = INFOTYPE_UNRESOLVED;
2932 $$ = (classinfo_t*)c;
2934 slotinfo_t*s = registry_find($1, $3);
2935 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2937 $$ = (classinfo_t*)s;
2940 CLASS_SPEC: PACKAGEANDCLASS
2943 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2944 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2946 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2947 | '*' {PASS12 $$=registry_getanytype();}
2948 | "void" {PASS12 $$=registry_getanytype();}
2950 | "String" {$$=registry_getstringclass();}
2951 | "int" {$$=registry_getintclass();}
2952 | "uint" {$$=registry_getuintclass();}
2953 | "Boolean" {$$=registry_getbooleanclass();}
2954 | "Number" {$$=registry_getnumberclass();}
2957 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2958 MAYBETYPE: {PASS12 $$=0;}
2960 /* ----------function calls, delete, constructor calls ------ */
2962 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
2963 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2965 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
2966 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2967 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2969 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
2973 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2974 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2975 $$.number= $1.number+1;
2976 $$.cc = code_append($1.cc, $2.c);
2980 NEW : "new" E XX MAYBE_PARAM_VALUES {
2982 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2984 code_t*paramcode = $4.cc;
2985 if($$.c->opcode == OPCODE_GETPROPERTY) {
2986 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2987 $$.c = code_cutlast($$.c);
2988 $$.c = code_append($$.c, paramcode);
2989 $$.c = abc_constructprop2($$.c, name, $4.number);
2990 multiname_destroy(name);
2991 } else if($$.c->opcode == OPCODE_GETSLOT) {
2992 int slot = (int)(ptroff_t)$$.c->data[0];
2993 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2994 multiname_t*name = t->name;
2995 $$.c = code_cutlast($$.c);
2996 $$.c = code_append($$.c, paramcode);
2997 $$.c = abc_constructprop2($$.c, name, $4.number);
2999 $$.c = code_append($$.c, paramcode);
3000 $$.c = abc_construct($$.c, $4.number);
3004 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
3007 $$.c = abc_coerce_a($$.c);
3012 /* TODO: use abc_call (for calling local variables),
3013 abc_callstatic (for calling own methods)
3016 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3019 if($$.c->opcode == OPCODE_COERCE_A) {
3020 $$.c = code_cutlast($$.c);
3022 code_t*paramcode = $3.cc;
3025 if($$.c->opcode == OPCODE_GETPROPERTY) {
3026 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3027 $$.c = code_cutlast($$.c);
3028 $$.c = code_append($$.c, paramcode);
3029 $$.c = abc_callproperty2($$.c, name, $3.number);
3030 multiname_destroy(name);
3031 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3032 int slot = (int)(ptroff_t)$$.c->data[0];
3033 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3034 if(t->kind!=TRAIT_METHOD) {
3035 //ok: flash allows to assign closures to members.
3037 multiname_t*name = t->name;
3038 $$.c = code_cutlast($$.c);
3039 $$.c = code_append($$.c, paramcode);
3040 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3041 $$.c = abc_callproperty2($$.c, name, $3.number);
3042 } else if($$.c->opcode == OPCODE_GETSUPER) {
3043 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3044 $$.c = code_cutlast($$.c);
3045 $$.c = code_append($$.c, paramcode);
3046 $$.c = abc_callsuper2($$.c, name, $3.number);
3047 multiname_destroy(name);
3049 $$.c = abc_getglobalscope($$.c);
3050 $$.c = code_append($$.c, paramcode);
3051 $$.c = abc_call($$.c, $3.number);
3054 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
3055 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
3057 $$.c = abc_coerce_a($$.c);
3062 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3063 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3064 if(!state->method) syntaxerror("super() not allowed outside of a function");
3065 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3068 $$.c = abc_getlocal_0($$.c);
3070 $$.c = code_append($$.c, $3.cc);
3072 this is dependent on the control path, check this somewhere else
3073 if(state->method->has_super)
3074 syntaxerror("constructor may call super() only once");
3076 state->method->has_super = 1;
3078 $$.c = abc_constructsuper($$.c, $3.number);
3079 $$.c = abc_pushundefined($$.c);
3083 DELETE: "delete" E {
3085 if($$.c->opcode == OPCODE_COERCE_A) {
3086 $$.c = code_cutlast($$.c);
3088 multiname_t*name = 0;
3089 if($$.c->opcode == OPCODE_GETPROPERTY) {
3090 $$.c->opcode = OPCODE_DELETEPROPERTY;
3091 } else if($$.c->opcode == OPCODE_GETSLOT) {
3092 int slot = (int)(ptroff_t)$$.c->data[0];
3093 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3094 $$.c = code_cutlast($$.c);
3095 $$.c = abc_deleteproperty2($$.c, name);
3097 $$.c = abc_getlocal_0($$.c);
3098 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3099 $$.c = abc_deleteproperty2($$.c, &m);
3101 $$.t = TYPE_BOOLEAN;
3104 RETURN: "return" %prec prec_none {
3105 $$ = abc_returnvoid(0);
3107 RETURN: "return" EXPRESSION {
3109 $$ = abc_returnvalue($$);
3112 // ----------------------- expression types -------------------------------------
3114 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3115 EXPRESSION : E %prec below_minus {$$ = $1;}
3116 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3118 $$.c = cut_last_push($$.c);
3119 $$.c = code_append($$.c,$3.c);
3122 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3123 $$=cut_last_push($1.c);
3126 // ----------------------- expression evaluation -------------------------------------
3128 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3129 //V : CONSTANT {$$ = 0;}
3131 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
3132 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3133 //V : NEW {$$ = $1.c;}
3135 //V : DELETE {$$ = $1.c;}
3136 E : DELETE {$$ = $1;}
3142 namespace_t ns = {ACCESS_PACKAGE, ""};
3143 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3145 $$.c = abc_getlex2($$.c, &m);
3146 $$.c = abc_pushstring($$.c, $1.pattern);
3147 $$.c = abc_construct($$.c, 1);
3149 $$.c = abc_getlex2($$.c, &m);
3150 $$.c = abc_pushstring($$.c, $1.pattern);
3151 $$.c = abc_pushstring($$.c, $1.options);
3152 $$.c = abc_construct($$.c, 2);
3157 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3158 //MULTINAME(m, registry_getintclass());
3159 //$$.c = abc_coerce2($$.c, &m); // FIXME
3162 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3165 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3168 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3171 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3174 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3177 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3180 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3181 $$.t = TYPE_BOOLEAN;
3183 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3184 $$.t = TYPE_BOOLEAN;
3186 CONSTANT : "null" {$$.c = abc_pushnull(0);
3190 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3191 $$.t = TYPE_BOOLEAN;
3193 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3194 $$.t = TYPE_BOOLEAN;
3196 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3197 $$.t = TYPE_BOOLEAN;
3199 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3200 $$.t = TYPE_BOOLEAN;
3202 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3203 $$.t = TYPE_BOOLEAN;
3205 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3206 $$.t = TYPE_BOOLEAN;
3208 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3209 $$.t = TYPE_BOOLEAN;
3211 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3212 $$.t = TYPE_BOOLEAN;
3215 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3217 $$.c = converttype($$.c, $1.t, $$.t);
3218 $$.c = abc_dup($$.c);
3219 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3220 $$.c = cut_last_push($$.c);
3221 $$.c = code_append($$.c,$3.c);
3222 $$.c = converttype($$.c, $3.t, $$.t);
3223 code_t*label = $$.c = abc_label($$.c);
3224 jmp->branch = label;
3227 $$.t = join_types($1.t, $3.t, 'A');
3228 /*printf("%08x:\n",$1.t);
3229 code_dump($1.c, 0, 0, "", stdout);
3230 printf("%08x:\n",$3.t);
3231 code_dump($3.c, 0, 0, "", stdout);
3232 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3234 $$.c = converttype($$.c, $1.t, $$.t);
3235 $$.c = abc_dup($$.c);
3236 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3237 $$.c = cut_last_push($$.c);
3238 $$.c = code_append($$.c,$3.c);
3239 $$.c = converttype($$.c, $3.t, $$.t);
3240 code_t*label = $$.c = abc_label($$.c);
3241 jmp->branch = label;
3244 E : '!' E {$$.c=$2.c;
3245 $$.c = abc_not($$.c);
3246 $$.t = TYPE_BOOLEAN;
3249 E : '~' E {$$.c=$2.c;
3250 $$.c = abc_bitnot($$.c);
3254 E : E '&' E {$$.c = code_append($1.c,$3.c);
3255 $$.c = abc_bitand($$.c);
3259 E : E '^' E {$$.c = code_append($1.c,$3.c);
3260 $$.c = abc_bitxor($$.c);
3264 E : E '|' E {$$.c = code_append($1.c,$3.c);
3265 $$.c = abc_bitor($$.c);
3269 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3270 $$.c = abc_rshift($$.c);
3273 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3274 $$.c = abc_urshift($$.c);
3277 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3278 $$.c = abc_lshift($$.c);
3282 E : E '/' E {$$.c = code_append($1.c,$3.c);
3283 $$.c = abc_divide($$.c);
3286 E : E '%' E {$$.c = code_append($1.c,$3.c);
3287 $$.c = abc_modulo($$.c);
3290 E : E '+' E {$$.c = code_append($1.c,$3.c);
3291 if(BOTH_INT($1.t, $3.t)) {
3292 $$.c = abc_add_i($$.c);
3295 $$.c = abc_add($$.c);
3296 $$.t = join_types($1.t,$3.t,'+');
3299 E : E '-' E {$$.c = code_append($1.c,$3.c);
3300 if(BOTH_INT($1.t,$3.t)) {
3301 $$.c = abc_subtract_i($$.c);
3304 $$.c = abc_subtract($$.c);
3308 E : E '*' E {$$.c = code_append($1.c,$3.c);
3309 if(BOTH_INT($1.t,$3.t)) {
3310 $$.c = abc_multiply_i($$.c);
3313 $$.c = abc_multiply($$.c);
3318 E : E "in" E {$$.c = code_append($1.c,$3.c);
3319 $$.c = abc_in($$.c);
3320 $$.t = TYPE_BOOLEAN;
3323 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3324 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3325 MULTINAME(m, (classinfo_t*)($3.t->data));
3326 $$.c = abc_astype2($1.c, &m);
3329 $$.c = code_append($1.c, $3.c);
3330 $$.c = abc_astypelate($$.c);
3335 E : E "instanceof" E
3336 {$$.c = code_append($1.c, $3.c);
3337 $$.c = abc_instanceof($$.c);
3338 $$.t = TYPE_BOOLEAN;
3341 E : E "is" E {$$.c = code_append($1.c, $3.c);
3342 $$.c = abc_istypelate($$.c);
3343 $$.t = TYPE_BOOLEAN;
3346 E : "typeof" '(' E ')' {
3348 $$.c = abc_typeof($$.c);
3353 $$.c = cut_last_push($2.c);
3354 $$.c = abc_pushundefined($$.c);
3358 E : "void" { $$.c = abc_pushundefined(0);
3362 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3367 $$.c=abc_negate_i($$.c);
3370 $$.c=abc_negate($$.c);
3377 $$.c = code_append($$.c, $3.c);
3379 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3380 $$.c = abc_getproperty2($$.c, &m);
3381 $$.t = 0; // array elements have unknown type
3384 E : '[' MAYBE_EXPRESSION_LIST ']' {
3386 $$.c = code_append($$.c, $2.cc);
3387 $$.c = abc_newarray($$.c, $2.number);
3388 $$.t = registry_getarrayclass();
3391 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3392 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3394 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3396 $$.cc = code_append($$.cc, $1.c);
3397 $$.cc = code_append($$.cc, $3.c);
3400 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3402 $$.number = $1.number+2;
3403 $$.cc = code_append($$.cc, $3.c);
3404 $$.cc = code_append($$.cc, $5.c);
3409 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3411 $$.c = code_append($$.c, $2.cc);
3412 $$.c = abc_newobject($$.c, $2.number/2);
3413 $$.t = registry_getobjectclass();
3418 if(BOTH_INT($1.t,$3.t)) {
3419 c=abc_multiply_i(c);
3423 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3424 $$.c = toreadwrite($1.c, c, 0, 0);
3429 code_t*c = abc_modulo($3.c);
3430 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3431 $$.c = toreadwrite($1.c, c, 0, 0);
3435 code_t*c = abc_lshift($3.c);
3436 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3437 $$.c = toreadwrite($1.c, c, 0, 0);
3441 code_t*c = abc_rshift($3.c);
3442 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3443 $$.c = toreadwrite($1.c, c, 0, 0);
3447 code_t*c = abc_urshift($3.c);
3448 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3449 $$.c = toreadwrite($1.c, c, 0, 0);
3453 code_t*c = abc_divide($3.c);
3454 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3455 $$.c = toreadwrite($1.c, c, 0, 0);
3459 code_t*c = abc_bitor($3.c);
3460 c=converttype(c, TYPE_INT, $1.t);
3461 $$.c = toreadwrite($1.c, c, 0, 0);
3465 code_t*c = abc_bitxor($3.c);
3466 c=converttype(c, TYPE_INT, $1.t);
3467 $$.c = toreadwrite($1.c, c, 0, 0);
3473 if(TYPE_IS_INT($1.t)) {
3477 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3480 $$.c = toreadwrite($1.c, c, 0, 0);
3483 E : E "-=" E { code_t*c = $3.c;
3484 if(TYPE_IS_INT($1.t)) {
3485 c=abc_subtract_i(c);
3488 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3491 $$.c = toreadwrite($1.c, c, 0, 0);
3494 E : E '=' E { code_t*c = 0;
3495 c = code_append(c, $3.c);
3496 c = converttype(c, $3.t, $1.t);
3497 $$.c = toreadwrite($1.c, c, 1, 0);
3501 E : E '?' E ':' E %prec below_assignment {
3502 $$.t = join_types($3.t,$5.t,'?');
3504 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3505 $$.c = code_append($$.c, $3.c);
3506 $$.c = converttype($$.c, $3.t, $$.t);
3507 code_t*j2 = $$.c = abc_jump($$.c, 0);
3508 $$.c = j1->branch = abc_label($$.c);
3509 $$.c = code_append($$.c, $5.c);
3510 $$.c = converttype($$.c, $5.t, $$.t);
3511 $$.c = j2->branch = abc_label($$.c);
3514 E : E "++" { code_t*c = 0;
3515 classinfo_t*type = $1.t;
3516 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3517 int nr = getlocalnr($1.c);
3518 code_free($1.c);$1.c=0;
3519 if(TYPE_IS_INT($1.t)) {
3520 $$.c = abc_getlocal(0, nr);
3521 $$.c = abc_inclocal_i($$.c, nr);
3522 } else if(TYPE_IS_NUMBER($1.t)) {
3523 $$.c = abc_getlocal(0, nr);
3524 $$.c = abc_inclocal($$.c, nr);
3525 } else syntaxerror("internal error");
3527 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3528 c=abc_increment_i(c);
3534 c=converttype(c, type, $1.t);
3535 $$.c = toreadwrite($1.c, c, 0, 1);
3540 // TODO: use inclocal, like with ++
3541 E : E "--" { code_t*c = 0;
3542 classinfo_t*type = $1.t;
3543 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3544 c=abc_decrement_i(c);
3550 c=converttype(c, type, $1.t);
3551 $$.c = toreadwrite($1.c, c, 0, 1);
3555 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3556 classinfo_t*type = $2.t;
3557 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3558 c=abc_increment_i(c);
3564 c=converttype(c, type, $2.t);
3565 $$.c = toreadwrite($2.c, c, 0, 0);
3569 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3570 classinfo_t*type = $2.t;
3571 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3572 c=abc_decrement_i(c);
3578 c=converttype(c, type, $2.t);
3579 $$.c = toreadwrite($2.c, c, 0, 0);
3583 E : "super" '.' T_IDENTIFIER
3584 { if(!state->cls->info)
3585 syntaxerror("super keyword not allowed outside a class");
3586 classinfo_t*t = state->cls->info->superclass;
3587 if(!t) t = TYPE_OBJECT;
3589 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3591 MEMBER_MULTINAME(m, f, $3);
3593 $$.c = abc_getlocal_0($$.c);
3594 $$.c = abc_getsuper2($$.c, &m);
3595 $$.t = slotinfo_gettype((slotinfo_t*)f);
3598 E : '@' T_IDENTIFIER {
3600 $$.c = abc_pushundefined(0);
3602 as3_warning("ignored @ operator");
3605 E : E '.' '@' T_IDENTIFIER {
3606 // child attribute TODO
3607 $$.c = abc_pushundefined(0);
3609 as3_warning("ignored .@ operator");
3612 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3613 // namespace declaration TODO
3614 $$.c = abc_pushundefined(0);
3616 as3_warning("ignored :: operator");
3619 E : E ".." T_IDENTIFIER {
3621 $$.c = abc_pushundefined(0);
3623 as3_warning("ignored .. operator");
3626 E : E '.' '(' E ')' {
3628 $$.c = abc_pushundefined(0);
3630 as3_warning("ignored .() operator");
3633 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3637 E : E '.' T_IDENTIFIER {
3639 classinfo_t*t = $1.t;
3641 if(TYPE_IS_CLASS(t) && t->data) {
3646 if(t->subtype==INFOTYPE_UNRESOLVED) {
3647 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3649 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3651 if(f && !is_static != !(f->flags&FLAG_STATIC))
3653 if(f && f->slot && !noslot) {
3654 $$.c = abc_getslot($$.c, f->slot);
3656 MEMBER_MULTINAME(m, f, $3);
3657 $$.c = abc_getproperty2($$.c, &m);
3659 /* determine type */
3660 $$.t = slotinfo_gettype((slotinfo_t*)f);
3662 $$.c = abc_coerce_a($$.c);
3663 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3664 string_t*package = $1.c->data[0];
3665 char*package2 = concat3(package->str, ".", $3);
3666 if(dict_contains(state->import_toplevel_packages, package2)) {
3668 $$.c->data[0] = string_new4(package2);
3671 slotinfo_t*a = registry_find(package->str, $3);
3673 syntaxerror("couldn't resolve %s", package2);
3677 /* when resolving a property on an unknown type, we do know the
3678 name of the property (and don't seem to need the package), but
3679 we need to make avm2 try out all access modes */
3680 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3681 $$.c = abc_getproperty2($$.c, &m);
3682 $$.c = abc_coerce_a($$.c);
3683 $$.t = registry_getanytype();
3687 VAR_READ : T_IDENTIFIER {
3689 /* Queue unresolved identifiers for checking against the parent
3690 function's variables.
3691 We consider everything which is not a local variable "unresolved".
3692 This encompasses class names, members of the surrounding class
3693 etc. which is *correct* because local variables of the parent function
3696 if(state->method->inner && !find_variable(state, $1)) {
3697 unknown_variable($1);
3700 /* let the compiler know that it might check the current directory/package
3701 for this identifier- maybe there's a file $1.as defining $1. */
3702 as3_schedule_class_noerror(state->package, $1);
3711 /* look at variables */
3712 if((v = find_variable(state, $1))) {
3713 // $1 is a local variable
3714 $$.c = abc_getlocal($$.c, v->index);
3718 if((v = find_slot(state, $1))) {
3719 $$.c = abc_getscopeobject($$.c, 1);
3720 $$.c = abc_getslot($$.c, v->index);
3725 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3727 /* look at current class' members */
3728 if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) &&
3729 (f->flags&FLAG_STATIC) >= i_am_static) {
3730 // $1 is a function in this class
3731 int var_is_static = (f->flags&FLAG_STATIC);
3733 if(f->kind == INFOTYPE_METHOD) {
3734 $$.t = TYPE_FUNCTION(f);
3738 if(var_is_static && !i_am_static) {
3739 /* access to a static member from a non-static location.
3740 do this via findpropstrict:
3741 there doesn't seem to be any non-lookup way to access
3742 static properties of a class */
3743 state->method->late_binding = 1;
3745 namespace_t ns = {f->access, ""};
3746 multiname_t m = {QNAME, &ns, 0, $1};
3747 $$.c = abc_findpropstrict2($$.c, &m);
3748 $$.c = abc_getproperty2($$.c, &m);
3750 } else if(f->slot>0) {
3751 $$.c = abc_getlocal_0($$.c);
3752 $$.c = abc_getslot($$.c, f->slot);
3755 namespace_t ns = {f->access, ""};
3756 multiname_t m = {QNAME, &ns, 0, $1};
3757 $$.c = abc_getlocal_0($$.c);
3758 $$.c = abc_getproperty2($$.c, &m);
3763 /* look at actual classes, in the current package and imported */
3764 if((a = find_class($1))) {
3769 /* look through package prefixes */
3770 if(dict_contains(state->import_toplevel_packages, $1)) {
3771 $$.c = abc___pushpackage__($$.c, $1);
3776 /* unknown object, let the avm2 resolve it */
3778 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3779 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3780 state->method->late_binding = 1;
3782 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3785 $$.c = abc_findpropstrict2($$.c, &m);
3786 $$.c = abc_getproperty2($$.c, &m);
3790 // ----------------- namespaces -------------------------------------------------
3792 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3794 NEW(namespace_decl_t,n);
3799 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3801 NEW(namespace_decl_t,n);
3806 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3808 NEW(namespace_decl_t,n);
3813 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3815 list_append(state->new_namespaces, $2);
3816 tokenizer_register_namespace($2->name);
3820 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3822 NEW(namespace_decl_t,n);
3825 /* FIXME: for pass2, we should now try to figure out what the URL of
3827 list_append(state->new_namespaces, n);
3828 tokenizer_register_namespace($3->name);