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;
372 typedef struct _global {
376 dict_t*file2token2info;
379 static global_t*global = 0;
380 static state_t* state = 0;
384 #define MULTINAME(m,x) \
388 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
390 #define MEMBER_MULTINAME(m,f,n) \
394 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
395 m##_ns.name = ((slotinfo_t*)(f))->package; \
400 m.namespace_set = 0; \
401 m.name = ((slotinfo_t*)(f))->name; \
403 m.type = MULTINAME; \
405 m.namespace_set = &nopackage_namespace_set; \
409 /* warning: list length of namespace set is undefined */
410 #define MULTINAME_LATE(m, access, package) \
411 namespace_t m##_ns = {access, package}; \
412 namespace_set_t m##_nsset; \
413 namespace_list_t m##_l;m##_l.next = 0; \
414 m##_nsset.namespaces = &m##_l; \
415 m##_nsset = m##_nsset; \
416 m##_l.namespace = &m##_ns; \
417 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
419 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
420 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
421 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
422 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
423 static namespace_list_t nl4 = {&ns4,0};
424 static namespace_list_t nl3 = {&ns3,&nl4};
425 static namespace_list_t nl2 = {&ns2,&nl3};
426 static namespace_list_t nl1 = {&ns1,&nl2};
427 static namespace_set_t nopackage_namespace_set = {&nl1};
429 static void new_state()
432 state_t*oldstate = state;
434 memcpy(s, state, sizeof(state_t)); //shallow copy
436 s->imports = dict_new();
438 if(!s->import_toplevel_packages) {
439 s->import_toplevel_packages = dict_new();
443 state->has_own_imports = 0;
444 state->new_namespaces = 0;
445 state->vars = dict_new();
446 state->old = oldstate;
449 static void state_has_imports()
451 state->wildcard_imports = list_clone(state->wildcard_imports);
452 state->imports = dict_clone(state->imports);
453 state->has_own_imports = 1;
455 static void import_toplevel(const char*package)
457 char* s = strdup(package);
459 dict_put(state->import_toplevel_packages, s, 0);
460 char*x = strrchr(s, '.');
468 static void state_destroy(state_t*state)
470 if(state->has_own_imports) {
471 list_free(state->wildcard_imports);
472 dict_destroy(state->imports);state->imports=0;
474 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
475 dict_destroy(state->imports);state->imports=0;
479 for(t=0;t<state->vars->hashsize;t++) {
480 dictentry_t*e =state->vars->slots[t];
482 free(e->data);e->data=0;
486 dict_destroy(state->vars);state->vars=0;
492 static void old_state()
494 if(!state || !state->old)
495 syntaxerror("invalid nesting");
496 state_t*leaving = state;
500 namespace_decl_list_t*nl=leaving->new_namespaces;
502 tokenizer_unregister_namespace(nl->namespace_decl->name);
506 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
507 free(leaving->method);
510 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
515 state_destroy(leaving);
518 static code_t* method_header(methodstate_t*m);
519 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
520 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
523 static char* internal_filename_package = 0;
524 void initialize_file(char*filename)
527 syntaxerror("invalid call to initialize_file during parsing of another file");
530 state->package = internal_filename_package = strdup(filename);
532 global->token2info = dict_lookup(global->file2token2info,
533 current_filename // use long version
535 if(!global->token2info) {
536 global->token2info = dict_new2(&ptr_type);
537 dict_put(global->file2token2info, current_filename, global->token2info);
541 state->method = rfx_calloc(sizeof(methodstate_t));
542 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
543 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
545 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
546 function_initvars(state->method, 0, 0, 1);
547 global->init = abc_initscript(global->file);
553 if(!state || state->level!=1) {
554 syntaxerror("unexpected end of file in pass %d", as3_pass);
558 code_t*header = method_header(state->method);
559 code_t*c = wrap_function(header, 0, global->init->method->body->code);
560 global->init->method->body->code = c;
561 free(state->method);state->method=0;
564 //free(state->package);state->package=0; // used in registry
565 state_destroy(state);state=0;
568 void initialize_parser()
570 global = rfx_calloc(sizeof(global_t));
571 global->file = abc_file_new();
572 global->file->flags &= ~ABCFILE_LAZY;
573 global->file2token2info = dict_new();
574 global->token2info = 0;
577 void* finish_parser()
579 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
581 global->token2info=0;
587 static void xx_scopetest()
589 /* findpropstrict doesn't just return a scope object- it
590 also makes it "active" somehow. Push local_0 on the
591 scope stack and read it back with findpropstrict, it'll
592 contain properties like "trace". Trying to find the same
593 property on a "vanilla" local_0 yields only a "undefined" */
594 //c = abc_findpropstrict(c, "[package]::trace");
596 /*c = abc_getlocal_0(c);
597 c = abc_findpropstrict(c, "[package]::trace");
599 c = abc_setlocal_1(c);
601 c = abc_pushbyte(c, 0);
602 c = abc_setlocal_2(c);
604 code_t*xx = c = abc_label(c);
605 c = abc_findpropstrict(c, "[package]::trace");
606 c = abc_pushstring(c, "prop:");
607 c = abc_hasnext2(c, 1, 2);
609 c = abc_setlocal_3(c);
610 c = abc_callpropvoid(c, "[package]::trace", 2);
611 c = abc_getlocal_3(c);
613 c = abc_iftrue(c,xx);*/
616 typedef struct _variable {
620 methodstate_t*is_inner_method;
623 static variable_t* find_variable(state_t*s, char*name)
627 v = dict_lookup(s->vars, name);
629 if(s->new_vars) break;
634 static variable_t* find_slot(state_t*s, const char*name)
636 if(s->method && s->method->slots)
637 return dict_lookup(s->method->slots, name);
641 static variable_t* find_variable_safe(state_t*s, char*name)
643 variable_t* v = find_variable(s, name);
645 syntaxerror("undefined variable: %s", name);
648 static char variable_exists(char*name)
650 return dict_contains(state->vars, name);
652 code_t*defaultvalue(code_t*c, classinfo_t*type);
654 static int alloc_local()
656 return state->method->variable_count++;
659 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
662 variable_t*v = find_slot(state, name);
668 v->index = alloc_local();
673 dict_put(state->vars, name, v);
677 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
679 return new_variable2(name, type, init, maybeslot)->index;
682 #define TEMPVARNAME "__as3_temp__"
683 static int gettempvar()
685 variable_t*v = find_variable(state, TEMPVARNAME);
688 return new_variable(TEMPVARNAME, 0, 0, 0);
691 code_t* var_block(code_t*body)
697 for(t=0;t<state->vars->hashsize;t++) {
698 dictentry_t*e = state->vars->slots[t];
700 variable_t*v = (variable_t*)e->data;
701 if(v->type && v->init) {
702 c = defaultvalue(c, v->type);
703 c = abc_setlocal(c, v->index);
704 k = abc_kill(k, v->index);
714 if(x->opcode== OPCODE___BREAK__ ||
715 x->opcode== OPCODE___CONTINUE__) {
716 /* link kill code before break/continue */
717 code_t*e = code_dup(k);
718 code_t*s = code_start(e);
730 c = code_append(c, body);
731 c = code_append(c, k);
735 void unknown_variable(char*name)
737 if(!state->method->unresolved_variables)
738 state->method->unresolved_variables = dict_new();
739 if(!dict_contains(state->method->unresolved_variables, name))
740 dict_put(state->method->unresolved_variables, name, 0);
743 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
745 static void parsererror(const char*file, int line, const char*f)
747 syntaxerror("internal error in %s, %s:%d", f, file, line);
751 static code_t* add_scope_code(code_t*c, methodstate_t*m)
753 if(m->uses_slots || (m->late_binding && !m->inner)) {
754 c = abc_getlocal_0(c);
755 c = abc_pushscope(c);
758 /* FIXME: does this need to be the same activation object as
759 in the function header? */
760 c = abc_newactivation(c);
761 c = abc_pushscope(c);
766 static code_t* method_header(methodstate_t*m)
770 c = add_scope_code(c, m);
772 methodstate_list_t*l = m->innerfunctions;
774 parserassert(l->methodstate->abc);
775 if(m->uses_slots && l->methodstate->is_a_slot) {
776 c = abc_getscopeobject(c, 1);
777 c = abc_newfunction(c, l->methodstate->abc);
779 c = abc_setlocal(c, l->methodstate->var_index);
780 c = abc_setslot(c, l->methodstate->slot_index);
782 c = abc_newfunction(c, l->methodstate->abc);
783 c = abc_setlocal(c, l->methodstate->var_index);
785 free(l->methodstate);l->methodstate=0;
789 c = code_append(c, m->header);
792 if(m->is_constructor && !m->has_super) {
793 // call default constructor
794 c = abc_getlocal_0(c);
795 c = abc_constructsuper(c, 0);
797 list_free(m->innerfunctions);
798 m->innerfunctions = 0;
803 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
805 c = code_append(c, header);
806 c = code_append(c, var_block(body));
807 /* append return if necessary */
808 if(!c || (c->opcode != OPCODE_RETURNVOID &&
809 c->opcode != OPCODE_RETURNVALUE)) {
810 c = abc_returnvoid(c);
816 static void startpackage(char*name)
819 /*printf("entering package \"%s\"\n", name);*/
820 state->package = strdup(name);
822 static void endpackage()
824 /*printf("leaving package \"%s\"\n", state->package);*/
826 //used e.g. in classinfo_register:
827 //free(state->package);state->package=0;
832 #define FLAG_PUBLIC 256
833 #define FLAG_PROTECTED 512
834 #define FLAG_PRIVATE 1024
835 #define FLAG_PACKAGEINTERNAL 2048
836 #define FLAG_NAMESPACE 4096
838 static namespace_t modifiers2access(modifiers_t*mod)
843 if(mod->flags&FLAG_NAMESPACE) {
844 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
845 syntaxerror("invalid combination of access levels and namespaces");
846 ns.access = ACCESS_NAMESPACE;
848 } else if(mod->flags&FLAG_PUBLIC) {
849 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
850 syntaxerror("invalid combination of access levels");
851 ns.access = ACCESS_PACKAGE;
852 } else if(mod->flags&FLAG_PRIVATE) {
853 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
854 syntaxerror("invalid combination of access levels");
855 ns.access = ACCESS_PRIVATE;
856 } else if(mod->flags&FLAG_PROTECTED) {
857 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
858 syntaxerror("invalid combination of access levels");
859 ns.access = ACCESS_PROTECTED;
861 ns.access = ACCESS_PACKAGEINTERNAL;
865 static slotinfo_t* find_class(const char*name);
867 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
872 index = new_variable("this", 0, 0, 0);
873 else if(!m->is_global)
874 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
876 index = new_variable("globalscope", 0, 0, 0);
879 parserassert(!index);
883 /* as variables and slots share the same number, make sure
884 that those variable indices are reserved. It's up to the
885 optimizer to later shuffle the variables down to lower
887 m->variable_count = m->uses_slots;
892 for(p=params->list;p;p=p->next) {
893 new_variable(p->param->name, p->param->type, 0, 1);
898 m->scope_code = add_scope_code(m->scope_code, m);
902 methodstate_list_t*l = m->innerfunctions;
904 methodstate_t*m = l->methodstate;
906 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
907 m->var_index = v->index;
908 m->slot_index = v->index;
909 v->is_inner_method = m;
914 if(as3_pass==2 && m->slots) {
915 /* exchange unresolved identifiers with the actual objects */
916 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
917 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
918 v->type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
919 if(!v->type || v->type->kind != INFOTYPE_CLASS) {
920 syntaxerror("Couldn't find class %s", v->type->name);
928 char*as3_globalclass=0;
929 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
932 syntaxerror("inner classes now allowed");
937 classinfo_list_t*mlist=0;
939 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
940 syntaxerror("invalid modifier(s)");
942 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
943 syntaxerror("public and internal not supported at the same time.");
945 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
946 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
947 // all classes extend object
948 extends = registry_getobjectclass();
951 /* create the class name, together with the proper attributes */
955 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
956 access = ACCESS_PRIVATE; package = internal_filename_package;
957 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
958 access = ACCESS_PACKAGEINTERNAL; package = state->package;
959 } else if(state->package!=internal_filename_package) {
960 access = ACCESS_PACKAGE; package = state->package;
962 syntaxerror("public classes only allowed inside a package");
966 state->cls = rfx_calloc(sizeof(classstate_t));
967 state->cls->init = rfx_calloc(sizeof(methodstate_t));
968 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
969 /* notice: we make no effort to initialize the top variable (local0) here,
970 even though it has special meaning. We just rely on the facat
971 that pass 1 won't do anything with variables */
973 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
975 /* set current method to constructor- all code within the class-level (except
976 static variable initializations) will be executed during construction time */
977 state->method = state->cls->init;
979 if(registry_find(package, classname)) {
980 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
982 /* build info struct */
983 int num_interfaces = (list_length(implements));
984 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
985 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
988 classinfo_list_t*l = implements;
989 for(l=implements;l;l=l->next) {
990 state->cls->info->interfaces[pos++] = l->classinfo;
995 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
997 state->method = state->cls->init;
998 parserassert(state->cls && state->cls->info);
1000 function_initvars(state->cls->init, 0, 0, 1);
1001 function_initvars(state->cls->static_init, 0, 0, 0);
1003 if(extends && (extends->flags & FLAG_FINAL))
1004 syntaxerror("Can't extend final class '%s'", extends->name);
1007 while(state->cls->info->interfaces[pos]) {
1008 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1009 syntaxerror("'%s' is not an interface",
1010 state->cls->info->interfaces[pos]->name);
1014 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
1015 state->cls->info->superclass = extends;
1017 /* generate the abc code for this class */
1018 MULTINAME(classname2,state->cls->info);
1019 multiname_t*extends2 = sig2mname(extends);
1021 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1022 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1023 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1024 if(state->cls->info->flags&FLAG_INTERFACE) {
1025 abc_class_interface(state->cls->abc);
1028 abc_class_protectedNS(state->cls->abc, classname);
1030 for(mlist=implements;mlist;mlist=mlist->next) {
1031 MULTINAME(m, mlist->classinfo);
1032 abc_class_add_interface(state->cls->abc, &m);
1035 /* write the construction code for this class to the global init
1037 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1039 abc_method_body_t*m = global->init->method->body;
1040 __ getglobalscope(m);
1041 classinfo_t*s = extends;
1046 //TODO: take a look at the current scope stack, maybe
1047 // we can re-use something
1052 multiname_t*s2 = sig2mname(s);
1054 multiname_destroy(s2);
1056 __ pushscope(m); count++;
1057 m->code = m->code->prev->prev; // invert
1059 /* continue appending after last op end */
1060 while(m->code && m->code->next) m->code = m->code->next;
1062 /* TODO: if this is one of *our* classes, we can also
1063 do a getglobalscope/getslot <nr> (which references
1064 the init function's slots) */
1066 __ getlex2(m, extends2);
1068 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1069 stack is not the superclass */
1070 __ pushscope(m);count++;
1073 /* notice: we get a verify error #1107 if the top element on the scope
1074 stack is not the global object */
1076 __ pushscope(m);count++;
1078 __ newclass(m,state->cls->abc);
1082 __ setslot(m, slotindex);
1083 multiname_destroy(extends2);
1085 /* flash.display.MovieClip handling */
1087 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1088 if(state->package && state->package[0]) {
1089 as3_globalclass = concat3(state->package, ".", classname);
1091 as3_globalclass = strdup(classname);
1097 static int slotstate_varconst = 0;
1098 static modifiers_t*slotstate_flags = 0;
1099 static void setslotstate(modifiers_t* flags, int varconst)
1101 slotstate_varconst = varconst;
1102 slotstate_flags = flags;
1104 if(flags && flags->flags&FLAG_STATIC) {
1105 state->method = state->cls->static_init;
1107 state->method = state->cls->init;
1110 parserassert(state->method);
1114 static void endclass()
1117 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1119 c = abc_getlocal_0(c);
1120 c = abc_constructsuper(c, 0);
1121 state->cls->init->header = code_append(state->cls->init->header, c);
1122 state->cls->has_constructor=1;
1124 if(state->cls->init) {
1125 if(state->cls->info->flags&FLAG_INTERFACE) {
1126 if(state->cls->init->header)
1127 syntaxerror("interface can not have class-level code");
1129 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1130 code_t*c = method_header(state->cls->init);
1131 m->body->code = wrap_function(c, 0, m->body->code);
1134 if(state->cls->static_init) {
1135 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1136 code_t*c = method_header(state->cls->static_init);
1137 m->body->code = wrap_function(c, 0, m->body->code);
1144 void check_code_for_break(code_t*c)
1147 if(c->opcode == OPCODE___BREAK__) {
1148 char*name = string_cstr(c->data[0]);
1149 syntaxerror("Unresolved \"break %s\"", name);
1151 if(c->opcode == OPCODE___CONTINUE__) {
1152 char*name = string_cstr(c->data[0]);
1153 syntaxerror("Unresolved \"continue %s\"", name);
1155 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1156 char*name = string_cstr(c->data[0]);
1157 syntaxerror("Can't reference a package (%s) as such", name);
1164 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1166 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1167 if(TYPE_IS_NUMBER(t)) {
1168 xassert(c->type == CONSTANT_FLOAT
1169 || c->type == CONSTANT_INT
1170 || c->type == CONSTANT_UINT);
1171 } else if(TYPE_IS_UINT(t)) {
1172 xassert(c->type == CONSTANT_UINT ||
1173 (c->type == CONSTANT_INT && c->i>=0));
1174 } else if(TYPE_IS_INT(t)) {
1175 xassert(c->type == CONSTANT_INT);
1176 } else if(TYPE_IS_BOOLEAN(t)) {
1177 xassert(c->type == CONSTANT_TRUE
1178 || c->type == CONSTANT_FALSE);
1182 static void check_override(memberinfo_t*m, int flags)
1186 if(m->parent == state->cls->info)
1187 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1189 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1190 if(m->access==ACCESS_PRIVATE)
1192 if(m->flags & FLAG_FINAL)
1193 syntaxerror("can't override final member %s", m->name);
1195 /* allow this. it's no issue.
1196 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1197 syntaxerror("can't override static member %s", m->name);*/
1199 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1200 syntaxerror("can't override non-static member %s with static declaration", m->name);
1202 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1203 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1204 if(m->kind == INFOTYPE_METHOD)
1205 syntaxerror("can't override without explicit 'override' declaration");
1207 syntaxerror("can't override '%s'", m->name);
1212 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1214 methodinfo_t*minfo = 0;
1215 namespace_t ns = modifiers2access(mod);
1218 minfo = methodinfo_register_global(ns.access, state->package, name);
1219 minfo->return_type = 0; // save this for pass 2
1220 } else if(getset != KW_GET && getset != KW_SET) {
1222 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1224 printf("%s.%s | %s.%s\n",
1225 m->package, m->name,
1227 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1229 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1230 minfo->return_type = 0; // save this for pass 2
1231 // getslot on a member slot only returns "undefined", so no need
1232 // to actually store these
1233 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1235 //class getter/setter
1236 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1238 if(getset == KW_GET) {
1240 } else if(params->list && params->list->param && !params->list->next) {
1241 type = params->list->param->type;
1243 syntaxerror("setter function needs to take exactly one argument");
1244 // not sure wether to look into superclasses here, too
1245 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1247 if(minfo->kind!=INFOTYPE_SLOT)
1248 syntaxerror("class already contains a method called '%s'", name);
1249 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1250 syntaxerror("class already contains a field called '%s'", name);
1251 if(minfo->subtype & gs)
1252 syntaxerror("getter/setter for '%s' already defined", name);
1253 /* make a setter or getter into a getset */
1254 minfo->subtype |= gs;
1257 FIXME: this check needs to be done in pass 2
1259 if((!minfo->return_type != !type) ||
1260 (minfo->return_type && type &&
1261 !strcmp(minfo->return_type->name, type->name))) {
1262 syntaxerror("different type in getter and setter: %s and %s",
1263 minfo->return_type?minfo->return_type->name:"*",
1264 type?type->name:"*");
1267 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1268 minfo->kind = INFOTYPE_SLOT; //hack
1269 minfo->subtype = gs;
1270 minfo->return_type = 0;
1272 /* can't assign a slot as getter and setter might have different slots */
1273 //minfo->slot = slot;
1275 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1276 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1277 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1282 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1284 //parserassert(state->method && state->method->info);
1286 methodstate_t*parent_method = state->method;
1289 return_type = 0; // not valid in pass 1
1293 state->new_vars = 1;
1296 state->method = rfx_calloc(sizeof(methodstate_t));
1297 state->method->inner = 1;
1298 state->method->variable_count = 0;
1299 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1301 NEW(methodinfo_t,minfo);
1302 minfo->kind = INFOTYPE_METHOD;
1303 minfo->access = ACCESS_PACKAGEINTERNAL;
1305 state->method->info = minfo;
1308 list_append(parent_method->innerfunctions, state->method);
1310 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1312 function_initvars(state->method, params, 0, 1);
1316 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1317 state->method->variable_count = 0;
1318 parserassert(state->method);
1320 state->method->info->return_type = return_type;
1321 function_initvars(state->method, params, 0, 1);
1325 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1326 params_t*params, classinfo_t*return_type)
1328 if(state->method && state->method->info) {
1329 syntaxerror("not able to start another method scope");
1332 state->new_vars = 1;
1335 state->method = rfx_calloc(sizeof(methodstate_t));
1336 state->method->has_super = 0;
1339 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1341 state->method->is_global = 1;
1342 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1344 if(state->method->is_constructor)
1345 name = "__as3_constructor__";
1347 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1349 function_initvars(state->method, params, mod->flags, 1);
1351 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1355 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1356 state->method->variable_count = 0;
1357 parserassert(state->method);
1360 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1361 check_override(m, mod->flags);
1365 state->cls->has_constructor |= state->method->is_constructor;
1368 state->method->info->return_type = return_type;
1369 function_initvars(state->method, params, mod->flags, 1);
1373 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1374 params_t*params, classinfo_t*return_type, code_t*body)
1376 int flags = mod?mod->flags:0;
1379 // store inner methods in variables
1380 function_initvars(state->method, 0, 0, 0);
1382 methodstate_list_t*ml = state->method->innerfunctions;
1384 dict_t*xvars = dict_new();
1387 methodstate_t*m = ml->methodstate;
1388 parserassert(m->inner);
1389 if(m->unresolved_variables) {
1390 dict_t*d = m->unresolved_variables;
1392 for(t=0;t<d->hashsize;t++) {
1393 dictentry_t*l = d->slots[t];
1395 /* check parent method's variables */
1397 if((v=find_variable(state, l->key))) {
1398 m->uses_parent_function = 1;
1399 state->method->uses_slots = 1;
1400 dict_put(xvars, l->key, 0);
1407 dict_destroy(m->unresolved_variables);
1408 m->unresolved_variables = 0;
1413 if(state->method->uses_slots) {
1414 state->method->slots = dict_new();
1416 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1417 if(!name) syntaxerror("internal error");
1418 if(v->index && dict_contains(xvars, name)) {
1421 if(v->is_inner_method) {
1422 v->is_inner_method->is_a_slot = 1;
1425 dict_put(state->method->slots, name, v);
1428 state->method->uses_slots = i;
1429 dict_destroy(state->vars);state->vars = 0;
1436 /*if(state->method->uses_parent_function){
1437 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1442 multiname_t*type2 = sig2mname(return_type);
1444 if(state->method->inner) {
1445 f = state->method->abc;
1446 abc_method_init(f, global->file, type2, 1);
1447 } else if(state->method->is_constructor) {
1448 f = abc_class_getconstructor(state->cls->abc, type2);
1449 } else if(!state->method->is_global) {
1450 namespace_t mname_ns = {state->method->info->access, ""};
1451 multiname_t mname = {QNAME, &mname_ns, 0, name};
1453 if(flags&FLAG_STATIC)
1454 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1456 f = abc_class_method(state->cls->abc, type2, &mname);
1457 slot = f->trait->slot_id;
1459 namespace_t mname_ns = {state->method->info->access, state->package};
1460 multiname_t mname = {QNAME, &mname_ns, 0, name};
1462 f = abc_method_new(global->file, type2, 1);
1463 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1464 //abc_code_t*c = global->init->method->body->code;
1466 //flash doesn't seem to allow us to access function slots
1467 //state->method->info->slot = slot;
1469 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1470 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1471 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1472 if(params->varargs) f->flags |= METHOD_NEED_REST;
1476 for(p=params->list;p;p=p->next) {
1477 if(params->varargs && !p->next) {
1478 break; //varargs: omit last parameter in function signature
1480 multiname_t*m = sig2mname(p->param->type);
1481 list_append(f->parameters, m);
1482 if(p->param->value) {
1483 check_constant_against_type(p->param->type, p->param->value);
1484 opt=1;list_append(f->optional_parameters, p->param->value);
1486 syntaxerror("non-optional parameter not allowed after optional parameters");
1489 if(state->method->slots) {
1490 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1492 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1493 multiname_t*type = sig2mname(v->type);
1494 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1495 t->slot_id = v->index;
1500 check_code_for_break(body);
1502 /* Seems this works now.
1503 if(state->method->exceptions && state->method->uses_slots) {
1504 as3_warning("try/catch and activation not supported yet within the same method");
1508 f->body->code = body;
1509 f->body->exceptions = state->method->exceptions;
1510 } else { //interface
1512 syntaxerror("interface methods can't have a method body");
1522 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1527 void breakjumpsto(code_t*c, char*name, code_t*jump)
1530 if(c->opcode == OPCODE___BREAK__) {
1531 string_t*name2 = c->data[0];
1532 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1533 c->opcode = OPCODE_JUMP;
1540 void continuejumpsto(code_t*c, char*name, code_t*jump)
1543 if(c->opcode == OPCODE___CONTINUE__) {
1544 string_t*name2 = c->data[0];
1545 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1546 c->opcode = OPCODE_JUMP;
1554 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1555 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1556 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1558 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1560 if(!type1 || !type2)
1561 return registry_getanytype();
1562 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1563 return registry_getanytype();
1566 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1575 return registry_getanytype();
1577 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1582 return abc_coerce_a(c);
1586 // cast an "any" type to a specific type. subject to
1587 // runtime exceptions
1588 return abc_coerce2(c, &m);
1591 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1592 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1593 // allow conversion between number types
1594 return abc_coerce2(c, &m);
1596 //printf("%s.%s\n", from.package, from.name);
1597 //printf("%s.%s\n", to.package, to.name);
1599 classinfo_t*supertype = from;
1601 if(supertype == to) {
1602 // target type is one of from's superclasses
1603 return abc_coerce2(c, &m);
1606 while(supertype->interfaces[t]) {
1607 if(supertype->interfaces[t]==to) {
1608 // target type is one of from's interfaces
1609 return abc_coerce2(c, &m);
1613 supertype = supertype->superclass;
1615 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1617 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1619 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1622 as3_error("can't convert type %s%s%s to %s%s%s",
1623 from->package, from->package?".":"", from->name,
1624 to->package, to->package?".":"", to->name);
1628 code_t*defaultvalue(code_t*c, classinfo_t*type)
1630 if(TYPE_IS_INT(type)) {
1631 c = abc_pushbyte(c, 0);
1632 } else if(TYPE_IS_UINT(type)) {
1633 c = abc_pushuint(c, 0);
1634 } else if(TYPE_IS_FLOAT(type)) {
1636 } else if(TYPE_IS_BOOLEAN(type)) {
1637 c = abc_pushfalse(c);
1639 //c = abc_pushundefined(c);
1641 c = abc_pushnull(c);
1643 c = abc_coerce2(c, &m);
1648 char is_pushundefined(code_t*c)
1650 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1653 static const char* get_package_from_name(const char*name)
1655 /* try explicit imports */
1656 dictentry_t* e = dict_get_slot(state->imports, name);
1658 if(!strcmp(e->key, name)) {
1659 slotinfo_t*c = (slotinfo_t*)e->data;
1660 if(c) return c->package;
1666 static namespace_list_t*get_current_imports()
1668 namespace_list_t*searchlist = 0;
1670 list_append(searchlist, namespace_new_package(state->package));
1672 import_list_t*l = state->wildcard_imports;
1674 namespace_t*ns = namespace_new_package(l->import->package);
1675 list_append(searchlist, ns);
1678 list_append(searchlist, namespace_new_package(""));
1679 list_append(searchlist, namespace_new_package(internal_filename_package));
1683 static slotinfo_t* find_class(const char*name)
1687 c = registry_find(state->package, name);
1690 /* try explicit imports */
1691 dictentry_t* e = dict_get_slot(state->imports, name);
1694 if(!strcmp(e->key, name)) {
1695 c = (slotinfo_t*)e->data;
1701 /* try package.* imports */
1702 import_list_t*l = state->wildcard_imports;
1704 //printf("does package %s contain a class %s?\n", l->import->package, name);
1705 c = registry_find(l->import->package, name);
1710 /* try global package */
1711 c = registry_find("", name);
1714 /* try local "filename" package */
1715 c = registry_find(internal_filename_package, name);
1720 typedcode_t push_class(slotinfo_t*a)
1725 if(a->access == ACCESS_PACKAGEINTERNAL &&
1726 strcmp(a->package, state->package) &&
1727 strcmp(a->package, internal_filename_package)
1729 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1730 infotypename(a), a->name, a->package, state->package);
1733 if(a->kind != INFOTYPE_CLASS) {
1735 x.c = abc_findpropstrict2(x.c, &m);
1736 x.c = abc_getproperty2(x.c, &m);
1737 if(a->kind == INFOTYPE_METHOD) {
1738 methodinfo_t*f = (methodinfo_t*)a;
1739 x.t = TYPE_FUNCTION(f);
1741 varinfo_t*v = (varinfo_t*)a;
1745 classinfo_t*c = (classinfo_t*)a;
1747 x.c = abc_getglobalscope(x.c);
1748 x.c = abc_getslot(x.c, c->slot);
1751 x.c = abc_getlex2(x.c, &m);
1753 x.t = TYPE_CLASS(c);
1758 static char is_getlocal(code_t*c)
1760 if(!c || c->prev || c->next)
1762 return(c->opcode == OPCODE_GETLOCAL
1763 || c->opcode == OPCODE_GETLOCAL_0
1764 || c->opcode == OPCODE_GETLOCAL_1
1765 || c->opcode == OPCODE_GETLOCAL_2
1766 || c->opcode == OPCODE_GETLOCAL_3);
1768 static int getlocalnr(code_t*c)
1770 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1771 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1772 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1773 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1774 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1775 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1779 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1783 [prefix code] [read instruction]
1787 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1789 if(in && in->opcode == OPCODE_COERCE_A) {
1790 in = code_cutlast(in);
1793 syntaxerror("internal error");
1795 /* chop off read instruction */
1799 prefix = r->prev;r->prev = 0;
1805 char use_temp_var = readbefore;
1807 /* generate the write instruction, and maybe append a dup to the prefix code */
1808 code_t* write = abc_nop(0);
1809 if(r->opcode == OPCODE_GETPROPERTY) {
1810 write->opcode = OPCODE_SETPROPERTY;
1811 multiname_t*m = (multiname_t*)r->data[0];
1812 write->data[0] = multiname_clone(m);
1813 if(m->type == QNAME || m->type == MULTINAME) {
1815 prefix = abc_dup(prefix); // we need the object, too
1818 } else if(m->type == MULTINAMEL) {
1820 /* dupping two values on the stack requires 5 operations and one register-
1821 couldn't adobe just have given us a dup2? */
1822 int temp = gettempvar();
1823 prefix = abc_setlocal(prefix, temp);
1824 prefix = abc_dup(prefix);
1825 prefix = abc_getlocal(prefix, temp);
1826 prefix = abc_swap(prefix);
1827 prefix = abc_getlocal(prefix, temp);
1829 prefix = abc_kill(prefix, temp);
1833 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1835 } else if(r->opcode == OPCODE_GETSLOT) {
1836 write->opcode = OPCODE_SETSLOT;
1837 write->data[0] = r->data[0];
1839 prefix = abc_dup(prefix); // we need the object, too
1842 } else if(r->opcode == OPCODE_GETLOCAL) {
1843 write->opcode = OPCODE_SETLOCAL;
1844 write->data[0] = r->data[0];
1845 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1846 write->opcode = OPCODE_SETLOCAL_0;
1847 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1848 write->opcode = OPCODE_SETLOCAL_1;
1849 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1850 write->opcode = OPCODE_SETLOCAL_2;
1851 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1852 write->opcode = OPCODE_SETLOCAL_3;
1853 } else if(r->opcode == OPCODE_GETSUPER) {
1854 write->opcode = OPCODE_SETSUPER;
1855 multiname_t*m = (multiname_t*)r->data[0];
1856 write->data[0] = multiname_clone(m);
1859 syntaxerror("illegal lvalue: can't assign a value to this expression");
1866 /* with getproperty/getslot, we have to be extra careful not
1867 to execute the read code twice, as it might have side-effects
1868 (e.g. if the property is in fact a setter/getter combination)
1870 So read the value, modify it, and write it again,
1871 using prefix only once and making sure (by using a temporary
1872 register) that the return value is what we just wrote */
1873 temp = gettempvar();
1874 c = code_append(c, prefix);
1875 c = code_append(c, r);
1878 c = abc_setlocal(c, temp);
1880 c = code_append(c, middlepart);
1883 c = abc_setlocal(c, temp);
1885 c = code_append(c, write);
1886 c = abc_getlocal(c, temp);
1887 c = abc_kill(c, temp);
1889 /* if we're allowed to execute the read code twice *and*
1890 the middlepart doesn't modify the code, things are easier.
1892 code_t* r2 = code_dup(r);
1893 //c = code_append(c, prefix);
1894 parserassert(!prefix);
1895 c = code_append(c, r);
1896 c = code_append(c, middlepart);
1897 c = code_append(c, write);
1898 c = code_append(c, r2);
1901 /* even smaller version: overwrite the value without reading
1905 c = code_append(c, prefix);
1908 c = code_append(c, middlepart);
1909 c = code_append(c, write);
1910 c = code_append(c, r);
1913 temp = gettempvar();
1915 c = code_append(c, prefix);
1917 c = code_append(c, middlepart);
1919 c = abc_setlocal(c, temp);
1920 c = code_append(c, write);
1921 c = abc_getlocal(c, temp);
1922 c = abc_kill(c, temp);
1928 char is_break_or_jump(code_t*c)
1932 if(c->opcode == OPCODE_JUMP ||
1933 c->opcode == OPCODE___BREAK__ ||
1934 c->opcode == OPCODE___CONTINUE__ ||
1935 c->opcode == OPCODE_THROW ||
1936 c->opcode == OPCODE_RETURNVOID ||
1937 c->opcode == OPCODE_RETURNVALUE) {
1944 #define IS_FINALLY_TARGET(op) \
1945 ((op) == OPCODE___CONTINUE__ || \
1946 (op) == OPCODE___BREAK__ || \
1947 (op) == OPCODE_RETURNVOID || \
1948 (op) == OPCODE_RETURNVALUE || \
1949 (op) == OPCODE___RETHROW__)
1951 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1953 #define NEED_EXTRA_STACK_ARG
1954 code_t*finally_label = abc_nop(0);
1955 NEW(lookupswitch_t, l);
1961 code_t*prev = i->prev;
1962 if(IS_FINALLY_TARGET(i->opcode)) {
1965 if(i->opcode == OPCODE___RETHROW__ ||
1966 i->opcode == OPCODE_RETURNVALUE) {
1967 if(i->opcode == OPCODE___RETHROW__)
1968 i->opcode = OPCODE_THROW;
1970 p = abc_coerce_a(p);
1971 p = abc_setlocal(p, tempvar);
1973 p = abc_pushbyte(p, count++);
1974 p = abc_jump(p, finally_label);
1975 code_t*target = p = abc_label(p);
1976 #ifdef NEED_EXTRA_STACK_ARG
1980 p = abc_getlocal(p, tempvar);
1983 p->next = i;i->prev = p;
1984 list_append(l->targets, target);
1990 c = abc_pushbyte(c, -1);
1991 c = code_append(c, finally_label);
1992 c = code_append(c, finally);
1994 #ifdef NEED_EXTRA_STACK_ARG
1997 c = abc_lookupswitch(c, l);
1998 c = l->def = abc_label(c);
1999 #ifdef NEED_EXTRA_STACK_ARG
2006 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
2010 code_t*prev = i->prev;
2011 if(IS_FINALLY_TARGET(i->opcode)) {
2012 if(i->opcode == OPCODE___RETHROW__)
2013 i->opcode = OPCODE_THROW;
2014 code_t*end = code_dup(finally);
2015 code_t*start = code_start(end);
2016 if(prev) prev->next = start;
2023 return code_append(c, finally);
2026 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
2032 int num_insertion_points=0;
2034 if(IS_FINALLY_TARGET(i->opcode))
2035 num_insertion_points++;
2042 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
2047 int simple_version_cost = (1+num_insertion_points)*code_size;
2048 int lookup_version_cost = 4*num_insertion_points + 5;
2050 if(cantdup || simple_version_cost > lookup_version_cost) {
2051 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
2052 return insert_finally_lookup(c, finally, tempvar);
2054 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
2055 return insert_finally_simple(c, finally, tempvar);
2059 #define PASS1 }} if(as3_pass == 1) {{
2060 #define PASS1END }} if(as3_pass == 2) {{
2061 #define PASS2 }} if(as3_pass == 2) {{
2062 #define PASS12 }} {{
2063 #define PASS12END }} if(as3_pass == 2) {{
2069 /* ------------ code blocks / statements ---------------- */
2071 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2073 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2074 PROGRAM_CODE_LIST: PROGRAM_CODE
2075 | PROGRAM_CODE_LIST PROGRAM_CODE
2077 PROGRAM_CODE: PACKAGE_DECLARATION
2078 | INTERFACE_DECLARATION
2080 | FUNCTION_DECLARATION
2083 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
2086 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2087 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2088 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2090 INPACKAGE_CODE: INTERFACE_DECLARATION
2092 | FUNCTION_DECLARATION
2095 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
2098 MAYBECODE: CODE {$$=$1;}
2099 MAYBECODE: {$$=code_new();}
2101 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2102 CODE: CODEPIECE {$$=$1;}
2104 // code which may appear outside of methods
2105 CODE_STATEMENT: IMPORT
2107 CODE_STATEMENT: FOR_IN
2108 CODE_STATEMENT: WHILE
2109 CODE_STATEMENT: DO_WHILE
2110 CODE_STATEMENT: SWITCH
2112 CODE_STATEMENT: WITH
2114 CODE_STATEMENT: VOIDEXPRESSION
2115 CODE_STATEMENT: USE_NAMESPACE
2116 CODE_STATEMENT: NAMESPACE_DECLARATION
2117 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2118 CODE_STATEMENT: '{' '}' {$$=0;}
2120 // code which may appear in methods
2121 CODEPIECE: ';' {$$=0;}
2122 CODEPIECE: CODE_STATEMENT
2123 CODEPIECE: VARIABLE_DECLARATION
2128 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
2130 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2131 //CODEBLOCK : '{' '}' {$$=0;}
2132 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2133 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2135 /* ------------ package init code ------------------- */
2137 PACKAGE_INITCODE: CODE_STATEMENT {
2138 code_t**cc = &global->init->method->body->code;
2139 *cc = code_append(*cc, $1);
2142 /* ------------ conditional compilation ------------- */
2144 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
2146 /* ------------ variables --------------------------- */
2148 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2149 | {$$.c=abc_pushundefined(0);
2153 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2154 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2156 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2157 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2159 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2162 if(variable_exists($1))
2163 syntaxerror("Variable %s already defined", $1);
2165 new_variable($1, 0, 1, 0);
2168 if(!is_subtype_of($3.t, $2)) {
2169 syntaxerror("Can't convert %s to %s", $3.t->name,
2175 if(state->method->uses_slots) {
2176 variable_t* v = find_slot(state, $1);
2178 // this variable is stored in a slot
2186 index = new_variable($1, $2, 1, 0);
2189 $$ = slot?abc_getscopeobject(0, 1):0;
2192 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2193 $$ = code_append($$, $3.c);
2194 $$ = converttype($$, $3.t, $2);
2197 $$ = defaultvalue($$, $2);
2200 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2201 $$ = code_append($$, $3.c);
2202 $$ = abc_coerce_a($$);
2204 // don't do anything
2212 $$ = abc_setslot($$, index);
2214 $$ = abc_setlocal($$, index);
2218 /* ------------ control flow ------------------------- */
2220 MAYBEELSE: %prec below_else {$$ = code_new();}
2221 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2222 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2224 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2227 $$ = code_append($$, $4.c);
2228 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2230 $$ = code_append($$, $6);
2232 myjmp = $$ = abc_jump($$, 0);
2234 myif->branch = $$ = abc_nop($$);
2236 $$ = code_append($$, $7);
2237 myjmp->branch = $$ = abc_nop($$);
2243 FOR_INIT : {$$=code_new();}
2244 FOR_INIT : VARIABLE_DECLARATION
2245 FOR_INIT : VOIDEXPRESSION
2247 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2248 // (I don't see any easy way to revolve this conflict otherwise, as we
2249 // can't touch VAR_READ without upsetting the precedence about "return")
2250 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2251 PASS1 $$=$2;new_variable($2,0,1,0);
2252 PASS2 $$=$2;new_variable($2,$3,1,0);
2254 FOR_IN_INIT : T_IDENTIFIER {
2259 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2260 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2262 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2263 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2265 $$ = code_append($$, $2);
2266 code_t*loopstart = $$ = abc_label($$);
2267 $$ = code_append($$, $4.c);
2268 code_t*myif = $$ = abc_iffalse($$, 0);
2269 $$ = code_append($$, $8);
2270 code_t*cont = $$ = abc_nop($$);
2271 $$ = code_append($$, $6);
2272 $$ = abc_jump($$, loopstart);
2273 code_t*out = $$ = abc_nop($$);
2274 breakjumpsto($$, $1.name, out);
2275 continuejumpsto($$, $1.name, cont);
2282 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2283 variable_t*var = find_variable(state, $2);
2285 syntaxerror("variable %s not known in this scope", $2);
2288 char*tmp1name = concat2($2, "__tmp1__");
2289 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2290 char*tmp2name = concat2($2, "__array__");
2291 int array = new_variable(tmp1name, 0, 0, 0);
2294 $$ = code_append($$, $4.c);
2295 $$ = abc_coerce_a($$);
2296 $$ = abc_setlocal($$, array);
2297 $$ = abc_pushbyte($$, 0);
2298 $$ = abc_setlocal($$, it);
2300 code_t*loopstart = $$ = abc_label($$);
2302 $$ = abc_hasnext2($$, array, it);
2303 code_t*myif = $$ = abc_iffalse($$, 0);
2304 $$ = abc_getlocal($$, array);
2305 $$ = abc_getlocal($$, it);
2307 $$ = abc_nextname($$);
2309 $$ = abc_nextvalue($$);
2310 $$ = converttype($$, 0, var->type);
2311 $$ = abc_setlocal($$, var->index);
2313 $$ = code_append($$, $6);
2314 $$ = abc_jump($$, loopstart);
2316 code_t*out = $$ = abc_nop($$);
2317 breakjumpsto($$, $1.name, out);
2318 continuejumpsto($$, $1.name, loopstart);
2330 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2334 code_t*myjmp = $$ = abc_jump($$, 0);
2335 code_t*loopstart = $$ = abc_label($$);
2336 $$ = code_append($$, $6);
2337 code_t*cont = $$ = abc_nop($$);
2338 myjmp->branch = cont;
2339 $$ = code_append($$, $4.c);
2340 $$ = abc_iftrue($$, loopstart);
2341 code_t*out = $$ = abc_nop($$);
2342 breakjumpsto($$, $1, out);
2343 continuejumpsto($$, $1, cont);
2349 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2351 code_t*loopstart = $$ = abc_label($$);
2352 $$ = code_append($$, $3);
2353 code_t*cont = $$ = abc_nop($$);
2354 $$ = code_append($$, $6.c);
2355 $$ = abc_iftrue($$, loopstart);
2356 code_t*out = $$ = abc_nop($$);
2357 breakjumpsto($$, $1, out);
2358 continuejumpsto($$, $1, cont);
2364 BREAK : "break" %prec prec_none {
2365 $$ = abc___break__(0, "");
2367 BREAK : "break" T_IDENTIFIER {
2368 $$ = abc___break__(0, $2);
2370 CONTINUE : "continue" %prec prec_none {
2371 $$ = abc___continue__(0, "");
2373 CONTINUE : "continue" T_IDENTIFIER {
2374 $$ = abc___continue__(0, $2);
2377 MAYBE_CASE_LIST : {$$=0;}
2378 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2379 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2380 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2381 CASE_LIST: CASE {$$=$1;}
2382 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2384 CASE: "case" E ':' MAYBECODE {
2385 $$ = abc_getlocal(0, state->switch_var);
2386 $$ = code_append($$, $2.c);
2387 code_t*j = $$ = abc_ifne($$, 0);
2388 $$ = code_append($$, $4);
2389 if($$->opcode != OPCODE___BREAK__) {
2390 $$ = abc___fallthrough__($$, "");
2392 code_t*e = $$ = abc_nop($$);
2395 DEFAULT: "default" ':' MAYBECODE {
2398 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2400 $$ = abc_setlocal($$, state->switch_var);
2401 $$ = code_append($$, $7);
2403 code_t*out = $$ = abc_kill($$, state->switch_var);
2404 breakjumpsto($$, $1, out);
2406 code_t*c = $$,*lastblock=0;
2408 if(c->opcode == OPCODE_IFNE) {
2409 if(!c->next) syntaxerror("internal error in fallthrough handling");
2411 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2413 c->opcode = OPCODE_JUMP;
2414 c->branch = lastblock;
2416 /* fall through end of switch */
2417 c->opcode = OPCODE_NOP;
2427 /* ------------ try / catch /finally ---------------- */
2429 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2430 state->exception_name=$3;
2431 PASS1 new_variable($3, 0, 0, 0);
2432 PASS2 new_variable($3, $4, 0, 0);
2435 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2436 multiname_t name = {QNAME, &name_ns, 0, $3};
2438 NEW(abc_exception_t, e)
2439 e->exc_type = sig2mname($4);
2440 e->var_name = multiname_clone(&name);
2444 int i = find_variable_safe(state, $3)->index;
2445 e->target = c = abc_nop(0);
2446 c = abc_setlocal(c, i);
2447 c = code_append(c, code_dup(state->method->scope_code));
2448 c = code_append(c, $8);
2454 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2459 NEW(abc_exception_t, e)
2460 e->exc_type = 0; //all exceptions
2461 e->var_name = 0; //no name
2464 e->to = code_append(e->to, $4);
2470 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2471 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2472 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2473 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2477 list_append($$.l,$2);
2478 $$.finally = $2->to;$2->to=0;
2481 CATCH_FINALLY_LIST: FINALLY {
2485 list_append($$.l,$1);
2486 $$.finally = $1->to;$1->to=0;
2490 TRY : "try" '{' {PASS12 new_state();
2491 state->method->has_exceptions=1;
2492 state->method->late_binding=1;//for invariant scope_code
2493 } MAYBECODE '}' CATCH_FINALLY_LIST {
2494 code_t*out = abc_nop(0);
2496 code_t*start = abc_nop(0);
2497 $$ = code_append(start, $4);
2498 if(!is_break_or_jump($4)) {
2499 $$ = abc_jump($$, out);
2501 code_t*end = $$ = abc_nop($$);
2505 tmp = new_variable("__finally__", 0, 0, 0);
2507 abc_exception_list_t*l = $6.l;
2510 abc_exception_t*e = l->abc_exception;
2512 $$ = code_append($$, e->target);
2513 $$ = abc_jump($$, out);
2515 parserassert((ptroff_t)$6.finally);
2517 e->target = $$ = abc_nop($$);
2518 $$ = code_append($$, code_dup(state->method->scope_code));
2519 $$ = abc___rethrow__($$);
2527 $$ = code_append($$, out);
2529 $$ = insert_finally($$, $6.finally, tmp);
2531 list_concat(state->method->exceptions, $6.l);
2537 /* ------------ throw ------------------------------- */
2539 THROW : "throw" EXPRESSION {
2543 THROW : "throw" %prec prec_none {
2544 if(!state->exception_name)
2545 syntaxerror("re-throw only possible within a catch block");
2546 variable_t*v = find_variable(state, state->exception_name);
2548 $$=abc_getlocal($$, v->index);
2552 /* ------------ with -------------------------------- */
2554 WITH_HEAD : "with" '(' EXPRESSION ')' {
2556 if(state->method->has_exceptions) {
2557 int v = alloc_local();
2558 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2559 state->method->scope_code = abc_pushwith(state->method->scope_code);
2564 WITH : WITH_HEAD CODEBLOCK {
2565 /* remove getlocal;pushwith from scope code again */
2566 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2569 if(state->method->has_exceptions) {
2571 $$ = abc_setlocal($$, $1.number);
2573 $$ = abc_pushwith($$);
2574 $$ = code_append($$, $2);
2575 $$ = abc_popscope($$);
2579 /* ------------ packages and imports ---------------- */
2581 X_IDENTIFIER: T_IDENTIFIER
2582 | "package" {PASS12 $$="package";}
2584 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2585 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2587 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2588 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2589 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2590 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2592 IMPORT : "import" PACKAGEANDCLASS {
2594 slotinfo_t*s = registry_find($2->package, $2->name);
2595 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2596 as3_schedule_class($2->package, $2->name);
2602 syntaxerror("Couldn't import class\n");
2603 state_has_imports();
2604 dict_put(state->imports, c->name, c);
2605 import_toplevel(c->package);
2608 IMPORT : "import" PACKAGE '.' '*' {
2610 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2611 as3_schedule_package($2);
2617 state_has_imports();
2618 list_append(state->wildcard_imports, i);
2619 import_toplevel(i->package);
2623 /* ------------ classes and interfaces (header) -------------- */
2625 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2626 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2627 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2628 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2630 $$.flags=$1.flags|$2.flags;
2631 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2632 $$.ns=$1.ns?$1.ns:$2.ns;
2636 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2637 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2638 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2639 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2640 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2641 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2642 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2643 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2644 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2645 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2649 EXTENDS : {PASS12 $$=0;}
2650 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2652 EXTENDS_LIST : {PASS12 $$=list_new();}
2653 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2655 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2656 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2658 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2659 EXTENDS IMPLEMENTS_LIST
2660 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2662 '}' {PASS12 endclass();$$=0;}
2664 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2666 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2667 startclass(&$1,$3,0,$4);}
2668 MAYBE_INTERFACE_BODY
2669 '}' {PASS12 endclass();$$=0;}
2671 /* ------------ classes and interfaces (body) -------------- */
2674 MAYBE_CLASS_BODY : CLASS_BODY
2675 CLASS_BODY : CLASS_BODY_ITEM
2676 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2677 CLASS_BODY_ITEM : ';'
2678 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2679 CLASS_BODY_ITEM : SLOT_DECLARATION
2680 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2682 CLASS_BODY_ITEM : CODE_STATEMENT {
2683 code_t*c = state->cls->static_init->header;
2684 c = code_append(c, $1);
2685 state->cls->static_init->header = c;
2688 MAYBE_INTERFACE_BODY :
2689 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2690 INTERFACE_BODY : IDECLARATION
2691 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2693 IDECLARATION : "var" T_IDENTIFIER {
2694 syntaxerror("variable declarations not allowed in interfaces");
2696 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2698 $1.flags |= FLAG_PUBLIC;
2699 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2700 syntaxerror("invalid method modifiers: interface methods always need to be public");
2702 startfunction(&$1,$3,$4,&$6,$8);
2703 endfunction(&$1,$3,$4,&$6,$8, 0);
2704 list_deep_free($6.list);
2707 /* ------------ classes and interfaces (body, slots ) ------- */
2709 VARCONST: "var" | "const"
2711 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2713 SLOT_LIST: ONE_SLOT {$$ = $1;}
2714 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2716 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2718 int flags = slotstate_flags->flags;
2719 namespace_t ns = modifiers2access(slotstate_flags);
2721 varinfo_t* info = 0;
2723 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2725 check_override(i, flags);
2727 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2729 slotinfo_t*i = registry_find(state->package, $1);
2731 syntaxerror("package %s already contains '%s'", state->package, $1);
2733 if(ns.name && ns.name[0]) {
2734 syntaxerror("namespaces not allowed on package-level variables");
2736 info = varinfo_register_global(ns.access, state->package, $1);
2740 info->flags = flags;
2743 multiname_t mname = {QNAME, &ns, 0, $1};
2745 trait_list_t**traits;
2749 ns.name = state->package;
2750 traits = &global->init->traits;
2751 code = &global->init->method->body->code;
2752 } else if(flags&FLAG_STATIC) {
2754 traits = &state->cls->abc->static_traits;
2755 code = &state->cls->static_init->header;
2757 // instance variable
2758 traits = &state->cls->abc->traits;
2759 code = &state->cls->init->header;
2765 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2767 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2769 info->slot = t->slot_id;
2771 /* initalization code (if needed) */
2773 if($3.c && !is_pushundefined($3.c)) {
2774 c = abc_getlocal_0(c);
2775 c = code_append(c, $3.c);
2776 c = converttype(c, $3.t, $2);
2777 c = abc_setslot(c, t->slot_id);
2780 *code = code_append(*code, c);
2782 if(slotstate_varconst==KW_CONST) {
2783 t->kind= TRAIT_CONST;
2789 /* ------------ constants -------------------------------------- */
2791 MAYBESTATICCONSTANT: {$$=0;}
2792 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2794 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2795 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2796 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2797 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2798 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2799 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2800 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2801 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2802 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2803 STATICCONSTANT : T_IDENTIFIER {
2804 if(!strcmp($1, "NaN")) {
2805 $$ = constant_new_float(__builtin_nan(""));
2807 as3_warning("Couldn't evaluate constant value of %s", $1);
2808 $$ = constant_new_null($1);
2812 /* ------------ classes and interfaces (body, functions) ------- */
2814 // non-vararg version
2817 memset(&$$,0,sizeof($$));
2819 MAYBE_PARAM_LIST: PARAM_LIST {
2825 MAYBE_PARAM_LIST: "..." PARAM {
2827 memset(&$$,0,sizeof($$));
2829 list_append($$.list, $2);
2831 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2835 list_append($$.list, $4);
2839 PARAM_LIST: PARAM_LIST ',' PARAM {
2842 list_append($$.list, $3);
2846 memset(&$$,0,sizeof($$));
2847 list_append($$.list, $1);
2850 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2852 $$ = rfx_calloc(sizeof(param_t));
2858 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2860 $$ = rfx_calloc(sizeof(param_t));
2862 $$->type = TYPE_ANY;
2870 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2871 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2874 endfunction(&$1,$3,$4,&$6,0,0);
2876 if(!state->method->info) syntaxerror("internal error");
2878 code_t*c = method_header(state->method);
2879 c = wrap_function(c, 0, $11);
2881 endfunction(&$1,$3,$4,&$6,$8,c);
2883 list_deep_free($6.list);
2887 MAYBE_IDENTIFIER: T_IDENTIFIER
2888 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2889 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2890 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2893 endfunction(0,0,$2,&$4,0,0);
2895 methodinfo_t*f = state->method->info;
2896 if(!f || !f->kind) syntaxerror("internal error");
2898 code_t*c = method_header(state->method);
2899 c = wrap_function(c, 0, $9);
2901 int index = state->method->var_index;
2902 endfunction(0,0,$2,&$4,$6,c);
2904 $$.c = abc_getlocal(0, index);
2905 $$.t = TYPE_FUNCTION(f);
2907 PASS12 list_deep_free($4.list);
2911 /* ------------- package + class ids --------------- */
2913 CLASS: T_IDENTIFIER {
2914 PASS1 NEW(unresolvedinfo_t,c);
2915 memset(c, 0, sizeof(*c));
2916 c->kind = INFOTYPE_UNRESOLVED;
2918 c->package = get_package_from_name($1);
2920 c->nsset = get_current_imports();
2921 /* make the compiler look for this class in the current directory,
2923 as3_schedule_class_noerror(state->package, $1);
2925 $$ = (classinfo_t*)c;
2927 slotinfo_t*s = find_class($1);
2928 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2929 $$ = (classinfo_t*)s;
2932 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2933 PASS1 NEW(unresolvedinfo_t,c);
2934 memset(c, 0, sizeof(*c));
2935 c->kind = INFOTYPE_UNRESOLVED;
2938 $$ = (classinfo_t*)c;
2940 slotinfo_t*s = registry_find($1, $3);
2941 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2943 $$ = (classinfo_t*)s;
2946 CLASS_SPEC: PACKAGEANDCLASS
2949 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2950 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2952 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2953 | '*' {PASS12 $$=registry_getanytype();}
2954 | "void" {PASS12 $$=registry_getanytype();}
2956 | "String" {$$=registry_getstringclass();}
2957 | "int" {$$=registry_getintclass();}
2958 | "uint" {$$=registry_getuintclass();}
2959 | "Boolean" {$$=registry_getbooleanclass();}
2960 | "Number" {$$=registry_getnumberclass();}
2963 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2964 MAYBETYPE: {PASS12 $$=0;}
2966 /* ----------function calls, delete, constructor calls ------ */
2968 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
2969 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2971 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
2972 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2973 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2975 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
2979 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2980 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2981 $$.number= $1.number+1;
2982 $$.cc = code_append($1.cc, $2.c);
2986 NEW : "new" E XX MAYBE_PARAM_VALUES {
2988 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2990 code_t*paramcode = $4.cc;
2991 if($$.c->opcode == OPCODE_GETPROPERTY) {
2992 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2993 $$.c = code_cutlast($$.c);
2994 $$.c = code_append($$.c, paramcode);
2995 $$.c = abc_constructprop2($$.c, name, $4.number);
2996 multiname_destroy(name);
2997 } else if($$.c->opcode == OPCODE_GETSLOT) {
2998 int slot = (int)(ptroff_t)$$.c->data[0];
2999 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3000 multiname_t*name = t->name;
3001 $$.c = code_cutlast($$.c);
3002 $$.c = code_append($$.c, paramcode);
3003 $$.c = abc_constructprop2($$.c, name, $4.number);
3005 $$.c = code_append($$.c, paramcode);
3006 $$.c = abc_construct($$.c, $4.number);
3010 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
3013 $$.c = abc_coerce_a($$.c);
3018 /* TODO: use abc_call (for calling local variables),
3019 abc_callstatic (for calling own methods)
3022 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3025 if($$.c->opcode == OPCODE_COERCE_A) {
3026 $$.c = code_cutlast($$.c);
3028 code_t*paramcode = $3.cc;
3031 if($$.c->opcode == OPCODE_GETPROPERTY) {
3032 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3033 $$.c = code_cutlast($$.c);
3034 $$.c = code_append($$.c, paramcode);
3035 $$.c = abc_callproperty2($$.c, name, $3.number);
3036 multiname_destroy(name);
3037 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3038 int slot = (int)(ptroff_t)$$.c->data[0];
3039 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3040 if(t->kind!=TRAIT_METHOD) {
3041 //ok: flash allows to assign closures to members.
3043 multiname_t*name = t->name;
3044 $$.c = code_cutlast($$.c);
3045 $$.c = code_append($$.c, paramcode);
3046 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3047 $$.c = abc_callproperty2($$.c, name, $3.number);
3048 } else if($$.c->opcode == OPCODE_GETSUPER) {
3049 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3050 $$.c = code_cutlast($$.c);
3051 $$.c = code_append($$.c, paramcode);
3052 $$.c = abc_callsuper2($$.c, name, $3.number);
3053 multiname_destroy(name);
3055 $$.c = abc_getglobalscope($$.c);
3056 $$.c = code_append($$.c, paramcode);
3057 $$.c = abc_call($$.c, $3.number);
3060 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
3061 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
3063 $$.c = abc_coerce_a($$.c);
3068 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3069 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3070 if(!state->method) syntaxerror("super() not allowed outside of a function");
3071 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3074 $$.c = abc_getlocal_0($$.c);
3076 $$.c = code_append($$.c, $3.cc);
3078 this is dependent on the control path, check this somewhere else
3079 if(state->method->has_super)
3080 syntaxerror("constructor may call super() only once");
3082 state->method->has_super = 1;
3084 $$.c = abc_constructsuper($$.c, $3.number);
3085 $$.c = abc_pushundefined($$.c);
3089 DELETE: "delete" E {
3091 if($$.c->opcode == OPCODE_COERCE_A) {
3092 $$.c = code_cutlast($$.c);
3094 multiname_t*name = 0;
3095 if($$.c->opcode == OPCODE_GETPROPERTY) {
3096 $$.c->opcode = OPCODE_DELETEPROPERTY;
3097 } else if($$.c->opcode == OPCODE_GETSLOT) {
3098 int slot = (int)(ptroff_t)$$.c->data[0];
3099 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3100 $$.c = code_cutlast($$.c);
3101 $$.c = abc_deleteproperty2($$.c, name);
3103 $$.c = abc_getlocal_0($$.c);
3104 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3105 $$.c = abc_deleteproperty2($$.c, &m);
3107 $$.t = TYPE_BOOLEAN;
3110 RETURN: "return" %prec prec_none {
3111 $$ = abc_returnvoid(0);
3113 RETURN: "return" EXPRESSION {
3115 $$ = abc_returnvalue($$);
3118 // ----------------------- expression types -------------------------------------
3120 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3121 EXPRESSION : E %prec below_minus {$$ = $1;}
3122 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3124 $$.c = cut_last_push($$.c);
3125 $$.c = code_append($$.c,$3.c);
3128 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3129 $$=cut_last_push($1.c);
3132 // ----------------------- expression evaluation -------------------------------------
3134 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3135 //V : CONSTANT {$$ = 0;}
3137 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
3138 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3139 //V : NEW {$$ = $1.c;}
3141 //V : DELETE {$$ = $1.c;}
3142 E : DELETE {$$ = $1;}
3148 namespace_t ns = {ACCESS_PACKAGE, ""};
3149 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3151 $$.c = abc_getlex2($$.c, &m);
3152 $$.c = abc_pushstring($$.c, $1.pattern);
3153 $$.c = abc_construct($$.c, 1);
3155 $$.c = abc_getlex2($$.c, &m);
3156 $$.c = abc_pushstring($$.c, $1.pattern);
3157 $$.c = abc_pushstring($$.c, $1.options);
3158 $$.c = abc_construct($$.c, 2);
3163 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3164 //MULTINAME(m, registry_getintclass());
3165 //$$.c = abc_coerce2($$.c, &m); // FIXME
3168 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3171 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3174 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3177 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3180 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3183 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3186 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3187 $$.t = TYPE_BOOLEAN;
3189 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3190 $$.t = TYPE_BOOLEAN;
3192 CONSTANT : "null" {$$.c = abc_pushnull(0);
3196 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3197 $$.t = TYPE_BOOLEAN;
3199 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3200 $$.t = TYPE_BOOLEAN;
3202 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3203 $$.t = TYPE_BOOLEAN;
3205 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3206 $$.t = TYPE_BOOLEAN;
3208 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3209 $$.t = TYPE_BOOLEAN;
3211 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3212 $$.t = TYPE_BOOLEAN;
3214 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3215 $$.t = TYPE_BOOLEAN;
3217 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3218 $$.t = TYPE_BOOLEAN;
3221 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3223 $$.c = converttype($$.c, $1.t, $$.t);
3224 $$.c = abc_dup($$.c);
3225 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3226 $$.c = cut_last_push($$.c);
3227 $$.c = code_append($$.c,$3.c);
3228 $$.c = converttype($$.c, $3.t, $$.t);
3229 code_t*label = $$.c = abc_label($$.c);
3230 jmp->branch = label;
3233 $$.t = join_types($1.t, $3.t, 'A');
3234 /*printf("%08x:\n",$1.t);
3235 code_dump($1.c, 0, 0, "", stdout);
3236 printf("%08x:\n",$3.t);
3237 code_dump($3.c, 0, 0, "", stdout);
3238 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3240 $$.c = converttype($$.c, $1.t, $$.t);
3241 $$.c = abc_dup($$.c);
3242 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3243 $$.c = cut_last_push($$.c);
3244 $$.c = code_append($$.c,$3.c);
3245 $$.c = converttype($$.c, $3.t, $$.t);
3246 code_t*label = $$.c = abc_label($$.c);
3247 jmp->branch = label;
3250 E : '!' E {$$.c=$2.c;
3251 $$.c = abc_not($$.c);
3252 $$.t = TYPE_BOOLEAN;
3255 E : '~' E {$$.c=$2.c;
3256 $$.c = abc_bitnot($$.c);
3260 E : E '&' E {$$.c = code_append($1.c,$3.c);
3261 $$.c = abc_bitand($$.c);
3265 E : E '^' E {$$.c = code_append($1.c,$3.c);
3266 $$.c = abc_bitxor($$.c);
3270 E : E '|' E {$$.c = code_append($1.c,$3.c);
3271 $$.c = abc_bitor($$.c);
3275 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3276 $$.c = abc_rshift($$.c);
3279 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3280 $$.c = abc_urshift($$.c);
3283 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3284 $$.c = abc_lshift($$.c);
3288 E : E '/' E {$$.c = code_append($1.c,$3.c);
3289 $$.c = abc_divide($$.c);
3292 E : E '%' E {$$.c = code_append($1.c,$3.c);
3293 $$.c = abc_modulo($$.c);
3296 E : E '+' E {$$.c = code_append($1.c,$3.c);
3297 if(BOTH_INT($1.t, $3.t)) {
3298 $$.c = abc_add_i($$.c);
3301 $$.c = abc_add($$.c);
3302 $$.t = join_types($1.t,$3.t,'+');
3305 E : E '-' E {$$.c = code_append($1.c,$3.c);
3306 if(BOTH_INT($1.t,$3.t)) {
3307 $$.c = abc_subtract_i($$.c);
3310 $$.c = abc_subtract($$.c);
3314 E : E '*' E {$$.c = code_append($1.c,$3.c);
3315 if(BOTH_INT($1.t,$3.t)) {
3316 $$.c = abc_multiply_i($$.c);
3319 $$.c = abc_multiply($$.c);
3324 E : E "in" E {$$.c = code_append($1.c,$3.c);
3325 $$.c = abc_in($$.c);
3326 $$.t = TYPE_BOOLEAN;
3329 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3330 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3331 MULTINAME(m, (classinfo_t*)($3.t->data));
3332 $$.c = abc_astype2($1.c, &m);
3335 $$.c = code_append($1.c, $3.c);
3336 $$.c = abc_astypelate($$.c);
3341 E : E "instanceof" E
3342 {$$.c = code_append($1.c, $3.c);
3343 $$.c = abc_instanceof($$.c);
3344 $$.t = TYPE_BOOLEAN;
3347 E : E "is" E {$$.c = code_append($1.c, $3.c);
3348 $$.c = abc_istypelate($$.c);
3349 $$.t = TYPE_BOOLEAN;
3352 E : "typeof" '(' E ')' {
3354 $$.c = abc_typeof($$.c);
3359 $$.c = cut_last_push($2.c);
3360 $$.c = abc_pushundefined($$.c);
3364 E : "void" { $$.c = abc_pushundefined(0);
3368 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3373 $$.c=abc_negate_i($$.c);
3376 $$.c=abc_negate($$.c);
3383 $$.c = code_append($$.c, $3.c);
3385 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3386 $$.c = abc_getproperty2($$.c, &m);
3387 $$.t = 0; // array elements have unknown type
3390 E : '[' MAYBE_EXPRESSION_LIST ']' {
3392 $$.c = code_append($$.c, $2.cc);
3393 $$.c = abc_newarray($$.c, $2.number);
3394 $$.t = registry_getarrayclass();
3397 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3398 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3400 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3402 $$.cc = code_append($$.cc, $1.c);
3403 $$.cc = code_append($$.cc, $3.c);
3406 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3408 $$.number = $1.number+2;
3409 $$.cc = code_append($$.cc, $3.c);
3410 $$.cc = code_append($$.cc, $5.c);
3415 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3417 $$.c = code_append($$.c, $2.cc);
3418 $$.c = abc_newobject($$.c, $2.number/2);
3419 $$.t = registry_getobjectclass();
3424 if(BOTH_INT($1.t,$3.t)) {
3425 c=abc_multiply_i(c);
3429 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3430 $$.c = toreadwrite($1.c, c, 0, 0);
3435 code_t*c = abc_modulo($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_lshift($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_rshift($3.c);
3448 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3449 $$.c = toreadwrite($1.c, c, 0, 0);
3453 code_t*c = abc_urshift($3.c);
3454 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3455 $$.c = toreadwrite($1.c, c, 0, 0);
3459 code_t*c = abc_divide($3.c);
3460 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3461 $$.c = toreadwrite($1.c, c, 0, 0);
3465 code_t*c = abc_bitor($3.c);
3466 c=converttype(c, TYPE_INT, $1.t);
3467 $$.c = toreadwrite($1.c, c, 0, 0);
3471 code_t*c = abc_bitxor($3.c);
3472 c=converttype(c, TYPE_INT, $1.t);
3473 $$.c = toreadwrite($1.c, c, 0, 0);
3479 if(TYPE_IS_INT($1.t)) {
3483 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3486 $$.c = toreadwrite($1.c, c, 0, 0);
3489 E : E "-=" E { code_t*c = $3.c;
3490 if(TYPE_IS_INT($1.t)) {
3491 c=abc_subtract_i(c);
3494 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3497 $$.c = toreadwrite($1.c, c, 0, 0);
3500 E : E '=' E { code_t*c = 0;
3501 c = code_append(c, $3.c);
3502 c = converttype(c, $3.t, $1.t);
3503 $$.c = toreadwrite($1.c, c, 1, 0);
3507 E : E '?' E ':' E %prec below_assignment {
3508 $$.t = join_types($3.t,$5.t,'?');
3510 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3511 $$.c = code_append($$.c, $3.c);
3512 $$.c = converttype($$.c, $3.t, $$.t);
3513 code_t*j2 = $$.c = abc_jump($$.c, 0);
3514 $$.c = j1->branch = abc_label($$.c);
3515 $$.c = code_append($$.c, $5.c);
3516 $$.c = converttype($$.c, $5.t, $$.t);
3517 $$.c = j2->branch = abc_label($$.c);
3520 E : E "++" { code_t*c = 0;
3521 classinfo_t*type = $1.t;
3522 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3523 int nr = getlocalnr($1.c);
3524 code_free($1.c);$1.c=0;
3525 if(TYPE_IS_INT($1.t)) {
3526 $$.c = abc_getlocal(0, nr);
3527 $$.c = abc_inclocal_i($$.c, nr);
3528 } else if(TYPE_IS_NUMBER($1.t)) {
3529 $$.c = abc_getlocal(0, nr);
3530 $$.c = abc_inclocal($$.c, nr);
3531 } else syntaxerror("internal error");
3533 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3534 c=abc_increment_i(c);
3540 c=converttype(c, type, $1.t);
3541 $$.c = toreadwrite($1.c, c, 0, 1);
3546 // TODO: use inclocal, like with ++
3547 E : E "--" { code_t*c = 0;
3548 classinfo_t*type = $1.t;
3549 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3550 c=abc_decrement_i(c);
3556 c=converttype(c, type, $1.t);
3557 $$.c = toreadwrite($1.c, c, 0, 1);
3561 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3562 classinfo_t*type = $2.t;
3563 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3564 c=abc_increment_i(c);
3570 c=converttype(c, type, $2.t);
3571 $$.c = toreadwrite($2.c, c, 0, 0);
3575 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3576 classinfo_t*type = $2.t;
3577 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3578 c=abc_decrement_i(c);
3584 c=converttype(c, type, $2.t);
3585 $$.c = toreadwrite($2.c, c, 0, 0);
3589 E : "super" '.' T_IDENTIFIER
3590 { if(!state->cls->info)
3591 syntaxerror("super keyword not allowed outside a class");
3592 classinfo_t*t = state->cls->info->superclass;
3593 if(!t) t = TYPE_OBJECT;
3595 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3597 MEMBER_MULTINAME(m, f, $3);
3599 $$.c = abc_getlocal_0($$.c);
3600 $$.c = abc_getsuper2($$.c, &m);
3601 $$.t = slotinfo_gettype((slotinfo_t*)f);
3604 E : '@' T_IDENTIFIER {
3606 $$.c = abc_pushundefined(0);
3608 as3_warning("ignored @ operator");
3611 E : E '.' '@' T_IDENTIFIER {
3612 // child attribute TODO
3613 $$.c = abc_pushundefined(0);
3615 as3_warning("ignored .@ operator");
3618 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3619 // namespace declaration TODO
3620 $$.c = abc_pushundefined(0);
3622 as3_warning("ignored :: operator");
3625 E : E ".." T_IDENTIFIER {
3627 $$.c = abc_pushundefined(0);
3629 as3_warning("ignored .. operator");
3632 E : E '.' '(' E ')' {
3634 $$.c = abc_pushundefined(0);
3636 as3_warning("ignored .() operator");
3639 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3643 E : E '.' T_IDENTIFIER {
3645 classinfo_t*t = $1.t;
3647 if(TYPE_IS_CLASS(t) && t->data) {
3652 if(t->subtype==INFOTYPE_UNRESOLVED) {
3653 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3655 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3657 if(f && !is_static != !(f->flags&FLAG_STATIC))
3659 if(f && f->slot && !noslot) {
3660 $$.c = abc_getslot($$.c, f->slot);
3662 MEMBER_MULTINAME(m, f, $3);
3663 $$.c = abc_getproperty2($$.c, &m);
3665 /* determine type */
3666 $$.t = slotinfo_gettype((slotinfo_t*)f);
3668 $$.c = abc_coerce_a($$.c);
3669 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3670 string_t*package = $1.c->data[0];
3671 char*package2 = concat3(package->str, ".", $3);
3672 if(dict_contains(state->import_toplevel_packages, package2)) {
3674 $$.c->data[0] = string_new4(package2);
3677 slotinfo_t*a = registry_find(package->str, $3);
3679 syntaxerror("couldn't resolve %s", package2);
3683 /* when resolving a property on an unknown type, we do know the
3684 name of the property (and don't seem to need the package), but
3685 we need to make avm2 try out all access modes */
3686 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3687 $$.c = abc_getproperty2($$.c, &m);
3688 $$.c = abc_coerce_a($$.c);
3689 $$.t = registry_getanytype();
3693 VAR_READ : T_IDENTIFIER {
3695 /* Queue unresolved identifiers for checking against the parent
3696 function's variables.
3697 We consider everything which is not a local variable "unresolved".
3698 This encompasses class names, members of the surrounding class
3699 etc. which is *correct* because local variables of the parent function
3702 if(state->method->inner && !find_variable(state, $1)) {
3703 unknown_variable($1);
3706 /* let the compiler know that it might check the current directory/package
3707 for this identifier- maybe there's a file $1.as defining $1. */
3708 as3_schedule_class_noerror(state->package, $1);
3717 /* look at variables */
3718 if((v = find_variable(state, $1))) {
3719 // $1 is a local variable
3720 $$.c = abc_getlocal($$.c, v->index);
3724 if((v = find_slot(state, $1))) {
3725 $$.c = abc_getscopeobject($$.c, 1);
3726 $$.c = abc_getslot($$.c, v->index);
3731 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3733 /* look at current class' members */
3734 if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) &&
3735 (f->flags&FLAG_STATIC) >= i_am_static) {
3736 // $1 is a function in this class
3737 int var_is_static = (f->flags&FLAG_STATIC);
3739 if(f->kind == INFOTYPE_METHOD) {
3740 $$.t = TYPE_FUNCTION(f);
3744 if(var_is_static && !i_am_static) {
3745 /* access to a static member from a non-static location.
3746 do this via findpropstrict:
3747 there doesn't seem to be any non-lookup way to access
3748 static properties of a class */
3749 state->method->late_binding = 1;
3751 namespace_t ns = {f->access, ""};
3752 multiname_t m = {QNAME, &ns, 0, $1};
3753 $$.c = abc_findpropstrict2($$.c, &m);
3754 $$.c = abc_getproperty2($$.c, &m);
3756 } else if(f->slot>0) {
3757 $$.c = abc_getlocal_0($$.c);
3758 $$.c = abc_getslot($$.c, f->slot);
3761 namespace_t ns = {f->access, ""};
3762 multiname_t m = {QNAME, &ns, 0, $1};
3763 $$.c = abc_getlocal_0($$.c);
3764 $$.c = abc_getproperty2($$.c, &m);
3769 /* look at actual classes, in the current package and imported */
3770 if((a = find_class($1))) {
3775 /* look through package prefixes */
3776 if(dict_contains(state->import_toplevel_packages, $1)) {
3777 $$.c = abc___pushpackage__($$.c, $1);
3782 /* unknown object, let the avm2 resolve it */
3784 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3785 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3786 state->method->late_binding = 1;
3788 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3791 $$.c = abc_findpropstrict2($$.c, &m);
3792 $$.c = abc_getproperty2($$.c, &m);
3796 // ----------------- namespaces -------------------------------------------------
3798 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3800 NEW(namespace_decl_t,n);
3805 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3807 NEW(namespace_decl_t,n);
3812 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3814 NEW(namespace_decl_t,n);
3819 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3821 list_append(state->new_namespaces, $2);
3822 tokenizer_register_namespace($2->name);
3826 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3828 NEW(namespace_decl_t,n);
3831 /* FIXME: for pass2, we should now try to figure out what the URL of
3833 list_append(state->new_namespaces, n);
3834 tokenizer_register_namespace($3->name);