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
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 {
328 dict_t*unresolved_variables;
331 char uses_parent_function;
336 int var_index; // for inner methods
337 int slot_index; // for inner methods
338 char is_a_slot; // for inner methods
341 abc_exception_list_t*exceptions;
343 methodstate_list_t*innerfunctions;
346 typedef struct _state {
351 import_list_t*wildcard_imports;
352 dict_t*import_toplevel_packages;
354 namespace_list_t*active_namespaces;
355 namespace_decl_list_t*new_namespaces;
356 char has_own_imports;
357 char new_vars; // e.g. transition between two functions
360 methodstate_t*method;
367 typedef struct _global {
371 dict_t*file2token2info;
374 static global_t*global = 0;
375 static state_t* state = 0;
379 #define MULTINAME(m,x) \
383 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
385 #define MEMBER_MULTINAME(m,f,n) \
389 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
390 m##_ns.name = ((slotinfo_t*)(f))->package; \
395 m.namespace_set = 0; \
396 m.name = ((slotinfo_t*)(f))->name; \
398 m.type = MULTINAME; \
400 m.namespace_set = &nopackage_namespace_set; \
404 /* warning: list length of namespace set is undefined */
405 #define MULTINAME_LATE(m, access, package) \
406 namespace_t m##_ns = {access, package}; \
407 namespace_set_t m##_nsset; \
408 namespace_list_t m##_l;m##_l.next = 0; \
409 m##_nsset.namespaces = &m##_l; \
410 m##_nsset = m##_nsset; \
411 m##_l.namespace = &m##_ns; \
412 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
414 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
415 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
416 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
417 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
418 static namespace_list_t nl4 = {&ns4,0};
419 static namespace_list_t nl3 = {&ns3,&nl4};
420 static namespace_list_t nl2 = {&ns2,&nl3};
421 static namespace_list_t nl1 = {&ns1,&nl2};
422 static namespace_set_t nopackage_namespace_set = {&nl1};
424 static void new_state()
427 state_t*oldstate = state;
429 memcpy(s, state, sizeof(state_t)); //shallow copy
431 s->imports = dict_new();
433 if(!s->import_toplevel_packages) {
434 s->import_toplevel_packages = dict_new();
438 state->has_own_imports = 0;
439 state->new_namespaces = 0;
440 state->vars = dict_new();
441 state->old = oldstate;
444 static void state_has_imports()
446 state->wildcard_imports = list_clone(state->wildcard_imports);
447 state->imports = dict_clone(state->imports);
448 state->has_own_imports = 1;
450 static void import_toplevel(const char*package)
452 char* s = strdup(package);
454 dict_put(state->import_toplevel_packages, s, 0);
455 char*x = strrchr(s, '.');
463 static void state_destroy(state_t*state)
465 if(state->has_own_imports) {
466 list_free(state->wildcard_imports);
467 dict_destroy(state->imports);state->imports=0;
469 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
470 dict_destroy(state->imports);state->imports=0;
474 for(t=0;t<state->vars->hashsize;t++) {
475 dictentry_t*e =state->vars->slots[t];
477 free(e->data);e->data=0;
481 dict_destroy(state->vars);state->vars=0;
487 static void old_state()
489 if(!state || !state->old)
490 syntaxerror("invalid nesting");
491 state_t*leaving = state;
495 namespace_decl_list_t*nl=leaving->new_namespaces;
497 tokenizer_unregister_namespace(nl->namespace_decl->name);
501 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
502 free(leaving->method);
505 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
510 state_destroy(leaving);
513 static code_t* method_header(methodstate_t*m);
514 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
515 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
518 static char* internal_filename_package = 0;
519 void initialize_file(char*filename)
522 syntaxerror("invalid call to initialize_file during parsing of another file");
525 state->package = internal_filename_package = strdup(filename);
527 global->token2info = dict_lookup(global->file2token2info,
528 current_filename // use long version
530 if(!global->token2info) {
531 global->token2info = dict_new2(&ptr_type);
532 dict_put(global->file2token2info, current_filename, global->token2info);
536 state->method = rfx_calloc(sizeof(methodstate_t));
537 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
539 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
540 function_initvars(state->method, 0, 0, 1);
541 global->init = abc_initscript(global->file);
542 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
548 if(!state || state->level!=1) {
549 syntaxerror("unexpected end of file in pass %d", as3_pass);
553 code_t*header = method_header(state->method);
554 code_t*c = wrap_function(header, 0, global->init->method->body->code);
555 global->init->method->body->code = c;
556 free(state->method);state->method=0;
559 //free(state->package);state->package=0; // used in registry
560 state_destroy(state);state=0;
563 void initialize_parser()
565 global = rfx_calloc(sizeof(global_t));
566 global->file = abc_file_new();
567 global->file->flags &= ~ABCFILE_LAZY;
568 global->file2token2info = dict_new();
569 global->token2info = 0;
572 void* finish_parser()
574 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
576 global->token2info=0;
582 static void xx_scopetest()
584 /* findpropstrict doesn't just return a scope object- it
585 also makes it "active" somehow. Push local_0 on the
586 scope stack and read it back with findpropstrict, it'll
587 contain properties like "trace". Trying to find the same
588 property on a "vanilla" local_0 yields only a "undefined" */
589 //c = abc_findpropstrict(c, "[package]::trace");
591 /*c = abc_getlocal_0(c);
592 c = abc_findpropstrict(c, "[package]::trace");
594 c = abc_setlocal_1(c);
596 c = abc_pushbyte(c, 0);
597 c = abc_setlocal_2(c);
599 code_t*xx = c = abc_label(c);
600 c = abc_findpropstrict(c, "[package]::trace");
601 c = abc_pushstring(c, "prop:");
602 c = abc_hasnext2(c, 1, 2);
604 c = abc_setlocal_3(c);
605 c = abc_callpropvoid(c, "[package]::trace", 2);
606 c = abc_getlocal_3(c);
608 c = abc_iftrue(c,xx);*/
611 typedef struct _variable {
615 methodstate_t*is_inner_method;
618 static variable_t* find_variable(state_t*s, char*name)
622 v = dict_lookup(s->vars, name);
624 if(s->new_vars) break;
629 static variable_t* find_slot(state_t*s, const char*name)
631 if(s->method && s->method->slots)
632 return dict_lookup(s->method->slots, name);
636 static variable_t* find_variable_safe(state_t*s, char*name)
638 variable_t* v = find_variable(s, name);
640 syntaxerror("undefined variable: %s", name);
643 static char variable_exists(char*name)
645 return dict_contains(state->vars, name);
647 code_t*defaultvalue(code_t*c, classinfo_t*type);
649 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
652 variable_t*v = find_slot(state, name);
658 v->index = state->method->variable_count++;
662 dict_put(state->vars, name, v);
666 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
668 return new_variable2(name, type, init, maybeslot)->index;
671 #define TEMPVARNAME "__as3_temp__"
672 static int gettempvar()
674 variable_t*v = find_variable(state, TEMPVARNAME);
677 return new_variable(TEMPVARNAME, 0, 0, 0);
680 code_t* var_block(code_t*body)
686 for(t=0;t<state->vars->hashsize;t++) {
687 dictentry_t*e = state->vars->slots[t];
689 variable_t*v = (variable_t*)e->data;
690 if(v->type && v->init) {
691 c = defaultvalue(c, v->type);
692 c = abc_setlocal(c, v->index);
693 k = abc_kill(k, v->index);
703 if(x->opcode== OPCODE___BREAK__ ||
704 x->opcode== OPCODE___CONTINUE__) {
705 /* link kill code before break/continue */
706 code_t*e = code_dup(k);
707 code_t*s = code_start(e);
719 c = code_append(c, body);
720 c = code_append(c, k);
724 void unknown_variable(char*name)
726 if(!state->method->unresolved_variables)
727 state->method->unresolved_variables = dict_new();
728 if(!dict_contains(state->method->unresolved_variables, name))
729 dict_put(state->method->unresolved_variables, name, 0);
732 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
734 static void parsererror(const char*file, int line, const char*f)
736 syntaxerror("internal error in %s, %s:%d", f, file, line);
740 static code_t* method_header(methodstate_t*m)
743 if(m->uses_slots || (m->late_binding && !m->inner)) {
744 c = abc_getlocal_0(c);
745 c = abc_pushscope(c);
748 c = abc_newactivation(c);
749 c = abc_pushscope(c);
751 methodstate_list_t*l = m->innerfunctions;
753 parserassert(l->methodstate->abc);
754 if(m->uses_slots && l->methodstate->is_a_slot) {
755 c = abc_getscopeobject(c, 1);
756 c = abc_newfunction(c, l->methodstate->abc);
758 c = abc_setlocal(c, l->methodstate->var_index);
759 c = abc_setslot(c, l->methodstate->slot_index);
761 c = abc_newfunction(c, l->methodstate->abc);
762 c = abc_setlocal(c, l->methodstate->var_index);
764 free(l->methodstate);l->methodstate=0;
768 c = code_append(c, m->header);
771 if(m->is_constructor && !m->has_super) {
772 // call default constructor
773 c = abc_getlocal_0(c);
774 c = abc_constructsuper(c, 0);
776 list_free(m->innerfunctions);
777 m->innerfunctions = 0;
782 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
784 c = code_append(c, header);
785 c = code_append(c, var_block(body));
786 /* append return if necessary */
787 if(!c || (c->opcode != OPCODE_RETURNVOID &&
788 c->opcode != OPCODE_RETURNVALUE)) {
789 c = abc_returnvoid(c);
795 static void startpackage(char*name)
798 /*printf("entering package \"%s\"\n", name);*/
799 state->package = strdup(name);
801 static void endpackage()
803 /*printf("leaving package \"%s\"\n", state->package);*/
805 //used e.g. in classinfo_register:
806 //free(state->package);state->package=0;
811 #define FLAG_PUBLIC 256
812 #define FLAG_PROTECTED 512
813 #define FLAG_PRIVATE 1024
814 #define FLAG_PACKAGEINTERNAL 2048
815 #define FLAG_NAMESPACE 4096
817 static namespace_t modifiers2access(modifiers_t*mod)
822 if(mod->flags&FLAG_NAMESPACE) {
823 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
824 syntaxerror("invalid combination of access levels and namespaces");
825 ns.access = ACCESS_NAMESPACE;
827 } else if(mod->flags&FLAG_PUBLIC) {
828 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
829 syntaxerror("invalid combination of access levels");
830 ns.access = ACCESS_PACKAGE;
831 } else if(mod->flags&FLAG_PRIVATE) {
832 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
833 syntaxerror("invalid combination of access levels");
834 ns.access = ACCESS_PRIVATE;
835 } else if(mod->flags&FLAG_PROTECTED) {
836 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
837 syntaxerror("invalid combination of access levels");
838 ns.access = ACCESS_PROTECTED;
840 ns.access = ACCESS_PACKAGEINTERNAL;
844 static slotinfo_t* find_class(const char*name);
846 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
851 index = new_variable("this", 0, 0, 0);
852 else if(!m->is_global)
853 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
855 index = new_variable("globalscope", 0, 0, 0);
858 parserassert(!index);
861 /* as variables and slots share the same number, make sure
862 that those variable indices are reserved. It's up to the
863 optimizer to later shuffle the variables down to lower
865 m->variable_count = m->uses_slots;
866 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
869 v->type = (classinfo_t*)registry_find(v->type->package, v->type->name);
871 v->type = (classinfo_t*)find_class(v->type->name);
872 if(!v->type || v->type->kind != INFOTYPE_CLASS) {
873 syntaxerror("Couldn't find class %s", v->type->name);
881 for(p=params->list;p;p=p->next) {
882 new_variable(p->param->name, p->param->type, 0, 1);
886 methodstate_list_t*l = m->innerfunctions;
888 methodstate_t*m = l->methodstate;
889 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
890 m->var_index = v->index;
891 m->slot_index = v->index;
892 v->is_inner_method = m;
898 char*as3_globalclass=0;
899 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
902 syntaxerror("inner classes now allowed");
907 classinfo_list_t*mlist=0;
909 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
910 syntaxerror("invalid modifier(s)");
912 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
913 syntaxerror("public and internal not supported at the same time.");
915 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
916 // all classes extend object
917 extends = registry_getobjectclass();
920 /* create the class name, together with the proper attributes */
924 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
925 access = ACCESS_PRIVATE; package = internal_filename_package;
926 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
927 access = ACCESS_PACKAGEINTERNAL; package = state->package;
928 } else if(state->package!=internal_filename_package) {
929 access = ACCESS_PACKAGE; package = state->package;
931 syntaxerror("public classes only allowed inside a package");
935 state->cls = rfx_calloc(sizeof(classstate_t));
936 state->cls->init = rfx_calloc(sizeof(methodstate_t));
937 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
938 /* notice: we make no effort to initialize the top variable (local0) here,
939 even though it has special meaning. We just rely on the facat
940 that pass 1 won't do anything with variables */
942 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
944 /* set current method to constructor- all code within the class-level (except
945 static variable initializations) will be executed during construction time */
946 state->method = state->cls->init;
948 if(registry_find(package, classname)) {
949 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
951 /* build info struct */
952 int num_interfaces = (list_length(implements));
953 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
954 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
958 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
960 state->method = state->cls->init;
961 parserassert(state->cls && state->cls->info);
963 function_initvars(state->cls->init, 0, 0, 1);
964 function_initvars(state->cls->static_init, 0, 0, 0);
966 if(extends && (extends->flags & FLAG_FINAL))
967 syntaxerror("Can't extend final class '%s'", extends->name);
969 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
970 state->cls->info->superclass = extends;
972 classinfo_list_t*l = implements;
973 for(l=implements;l;l=l->next) {
974 if(!(l->classinfo->flags & FLAG_INTERFACE))
975 syntaxerror("'%s' is not an interface", l->classinfo->name);
976 state->cls->info->interfaces[pos++] = l->classinfo;
979 /* generate the abc code for this class */
980 MULTINAME(classname2,state->cls->info);
981 multiname_t*extends2 = sig2mname(extends);
983 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
984 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
985 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
986 if(state->cls->info->flags&FLAG_INTERFACE) {
987 abc_class_interface(state->cls->abc);
990 abc_class_protectedNS(state->cls->abc, classname);
992 for(mlist=implements;mlist;mlist=mlist->next) {
993 MULTINAME(m, mlist->classinfo);
994 abc_class_add_interface(state->cls->abc, &m);
997 /* write the construction code for this class to the global init
999 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1001 abc_method_body_t*m = global->init->method->body;
1002 __ getglobalscope(m);
1003 classinfo_t*s = extends;
1008 //TODO: take a look at the current scope stack, maybe
1009 // we can re-use something
1014 multiname_t*s2 = sig2mname(s);
1016 multiname_destroy(s2);
1018 __ pushscope(m); count++;
1019 m->code = m->code->prev->prev; // invert
1021 /* continue appending after last op end */
1022 while(m->code && m->code->next) m->code = m->code->next;
1024 /* TODO: if this is one of *our* classes, we can also
1025 do a getglobalscope/getslot <nr> (which references
1026 the init function's slots) */
1028 __ getlex2(m, extends2);
1030 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1031 stack is not the superclass */
1032 __ pushscope(m);count++;
1035 /* notice: we get a verify error #1107 if the top element on the scope
1036 stack is not the global object */
1038 __ pushscope(m);count++;
1040 __ newclass(m,state->cls->abc);
1044 __ setslot(m, slotindex);
1045 multiname_destroy(extends2);
1047 /* flash.display.MovieClip handling */
1049 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1050 if(state->package && state->package[0]) {
1051 as3_globalclass = concat3(state->package, ".", classname);
1053 as3_globalclass = strdup(classname);
1059 static int slotstate_varconst = 0;
1060 static modifiers_t*slotstate_flags = 0;
1061 static void setslotstate(modifiers_t* flags, int varconst)
1063 slotstate_varconst = varconst;
1064 slotstate_flags = flags;
1066 if(flags && flags->flags&FLAG_STATIC) {
1067 state->method = state->cls->static_init;
1069 state->method = state->cls->init;
1072 parserassert(state->method);
1076 static void endclass()
1079 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1081 c = abc_getlocal_0(c);
1082 c = abc_constructsuper(c, 0);
1083 state->cls->init->header = code_append(state->cls->init->header, c);
1084 state->cls->has_constructor=1;
1086 if(state->cls->init) {
1087 if(state->cls->info->flags&FLAG_INTERFACE) {
1088 if(state->cls->init->header)
1089 syntaxerror("interface can not have class-level code");
1091 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1092 code_t*c = method_header(state->cls->init);
1093 m->body->code = wrap_function(c, 0, m->body->code);
1096 if(state->cls->static_init) {
1097 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1098 code_t*c = method_header(state->cls->static_init);
1099 m->body->code = wrap_function(c, 0, m->body->code);
1106 void check_code_for_break(code_t*c)
1109 if(c->opcode == OPCODE___BREAK__) {
1110 char*name = string_cstr(c->data[0]);
1111 syntaxerror("Unresolved \"break %s\"", name);
1113 if(c->opcode == OPCODE___CONTINUE__) {
1114 char*name = string_cstr(c->data[0]);
1115 syntaxerror("Unresolved \"continue %s\"", name);
1117 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1118 char*name = string_cstr(c->data[0]);
1119 syntaxerror("Can't reference a package (%s) as such", name);
1126 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1128 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1129 if(TYPE_IS_NUMBER(t)) {
1130 xassert(c->type == CONSTANT_FLOAT
1131 || c->type == CONSTANT_INT
1132 || c->type == CONSTANT_UINT);
1133 } else if(TYPE_IS_UINT(t)) {
1134 xassert(c->type == CONSTANT_UINT ||
1135 (c->type == CONSTANT_INT && c->i>=0));
1136 } else if(TYPE_IS_INT(t)) {
1137 xassert(c->type == CONSTANT_INT);
1138 } else if(TYPE_IS_BOOLEAN(t)) {
1139 xassert(c->type == CONSTANT_TRUE
1140 || c->type == CONSTANT_FALSE);
1144 static void check_override(memberinfo_t*m, int flags)
1148 if(m->parent == state->cls->info)
1149 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1151 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1152 if(m->access==ACCESS_PRIVATE)
1154 if(m->flags & FLAG_FINAL)
1155 syntaxerror("can't override final member %s", m->name);
1157 /* allow this. it's no issue.
1158 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1159 syntaxerror("can't override static member %s", m->name);*/
1161 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1162 syntaxerror("can't override non-static member %s with static declaration", m->name);
1164 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1165 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1166 if(m->kind == INFOTYPE_METHOD)
1167 syntaxerror("can't override without explicit 'override' declaration");
1169 syntaxerror("can't override '%s'", m->name);
1174 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1176 methodinfo_t*minfo = 0;
1177 namespace_t ns = modifiers2access(mod);
1180 minfo = methodinfo_register_global(ns.access, state->package, name);
1181 minfo->return_type = 0; // save this for pass 2
1182 } else if(getset != KW_GET && getset != KW_SET) {
1184 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1186 printf("%s.%s | %s.%s\n",
1187 m->package, m->name,
1189 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1191 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1192 minfo->return_type = 0; // save this for pass 2
1193 // getslot on a member slot only returns "undefined", so no need
1194 // to actually store these
1195 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1197 //class getter/setter
1198 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1200 if(getset == KW_GET) {
1202 } else if(params->list && params->list->param && !params->list->next) {
1203 type = params->list->param->type;
1205 syntaxerror("setter function needs to take exactly one argument");
1206 // not sure wether to look into superclasses here, too
1207 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1209 if(minfo->kind!=INFOTYPE_SLOT)
1210 syntaxerror("class already contains a method called '%s'", name);
1211 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1212 syntaxerror("class already contains a field called '%s'", name);
1213 if(minfo->subtype & gs)
1214 syntaxerror("getter/setter for '%s' already defined", name);
1215 /* make a setter or getter into a getset */
1216 minfo->subtype |= gs;
1219 FIXME: this check needs to be done in pass 2
1221 if((!minfo->return_type != !type) ||
1222 (minfo->return_type && type &&
1223 !strcmp(minfo->return_type->name, type->name))) {
1224 syntaxerror("different type in getter and setter: %s and %s",
1225 minfo->return_type?minfo->return_type->name:"*",
1226 type?type->name:"*");
1229 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1230 minfo->kind = INFOTYPE_SLOT; //hack
1231 minfo->subtype = gs;
1232 minfo->return_type = 0;
1234 /* can't assign a slot as getter and setter might have different slots */
1235 //minfo->slot = slot;
1237 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1238 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1239 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1244 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1246 //parserassert(state->method && state->method->info);
1248 methodstate_t*parent_method = state->method;
1251 return_type = 0; // not valid in pass 1
1255 state->new_vars = 1;
1258 state->method = rfx_calloc(sizeof(methodstate_t));
1259 state->method->inner = 1;
1260 state->method->variable_count = 0;
1261 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1263 NEW(methodinfo_t,minfo);
1264 minfo->kind = INFOTYPE_METHOD;
1265 minfo->access = ACCESS_PACKAGEINTERNAL;
1267 state->method->info = minfo;
1270 list_append(parent_method->innerfunctions, state->method);
1272 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1274 function_initvars(state->method, params, 0, 1);
1278 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1279 state->method->variable_count = 0;
1280 parserassert(state->method);
1282 state->method->info->return_type = return_type;
1283 function_initvars(state->method, params, 0, 1);
1287 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1288 params_t*params, classinfo_t*return_type)
1290 if(state->method && state->method->info) {
1291 syntaxerror("not able to start another method scope");
1294 state->new_vars = 1;
1297 state->method = rfx_calloc(sizeof(methodstate_t));
1298 state->method->has_super = 0;
1301 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1303 state->method->is_global = 1;
1304 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1306 if(state->method->is_constructor)
1307 name = "__as3_constructor__";
1309 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1311 function_initvars(state->method, params, mod->flags, 1);
1313 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1317 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1318 state->method->variable_count = 0;
1319 parserassert(state->method);
1322 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1323 check_override(m, mod->flags);
1327 state->cls->has_constructor |= state->method->is_constructor;
1330 state->method->info->return_type = return_type;
1331 function_initvars(state->method, params, mod->flags, 1);
1335 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1336 params_t*params, classinfo_t*return_type, code_t*body)
1338 int flags = mod?mod->flags:0;
1341 // store inner methods in variables
1342 function_initvars(state->method, 0, 0, 0);
1344 methodstate_list_t*ml = state->method->innerfunctions;
1346 dict_t*xvars = dict_new();
1349 methodstate_t*m = ml->methodstate;
1350 parserassert(m->inner);
1351 if(m->unresolved_variables) {
1352 dict_t*d = m->unresolved_variables;
1354 for(t=0;t<d->hashsize;t++) {
1355 dictentry_t*l = d->slots[t];
1357 /* check parent method's variables */
1359 if((v=find_variable(state, l->key))) {
1360 m->uses_parent_function = 1;
1361 state->method->uses_slots = 1;
1362 dict_put(xvars, l->key, 0);
1369 dict_destroy(m->unresolved_variables);
1370 m->unresolved_variables = 0;
1375 if(state->method->uses_slots) {
1376 state->method->slots = dict_new();
1378 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1379 if(v->index && dict_contains(xvars, name)) {
1382 if(v->is_inner_method) {
1383 v->is_inner_method->is_a_slot = 1;
1386 dict_put(state->method->slots, name, v);
1389 state->method->uses_slots = i;
1390 dict_destroy(state->vars);state->vars = 0;
1397 /*if(state->method->uses_parent_function){
1398 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1403 multiname_t*type2 = sig2mname(return_type);
1405 if(state->method->inner) {
1406 f = state->method->abc;
1407 abc_method_init(f, global->file, type2, 1);
1408 } else if(state->method->is_constructor) {
1409 f = abc_class_getconstructor(state->cls->abc, type2);
1410 } else if(!state->method->is_global) {
1411 namespace_t mname_ns = {state->method->info->access, ""};
1412 multiname_t mname = {QNAME, &mname_ns, 0, name};
1414 if(flags&FLAG_STATIC)
1415 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1417 f = abc_class_method(state->cls->abc, type2, &mname);
1418 slot = f->trait->slot_id;
1420 namespace_t mname_ns = {state->method->info->access, state->package};
1421 multiname_t mname = {QNAME, &mname_ns, 0, name};
1423 f = abc_method_new(global->file, type2, 1);
1424 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1425 //abc_code_t*c = global->init->method->body->code;
1427 //flash doesn't seem to allow us to access function slots
1428 //state->method->info->slot = slot;
1430 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1431 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1432 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1433 if(params->varargs) f->flags |= METHOD_NEED_REST;
1437 for(p=params->list;p;p=p->next) {
1438 if(params->varargs && !p->next) {
1439 break; //varargs: omit last parameter in function signature
1441 multiname_t*m = sig2mname(p->param->type);
1442 list_append(f->parameters, m);
1443 if(p->param->value) {
1444 check_constant_against_type(p->param->type, p->param->value);
1445 opt=1;list_append(f->optional_parameters, p->param->value);
1447 syntaxerror("non-optional parameter not allowed after optional parameters");
1450 if(state->method->slots) {
1451 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1453 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1454 multiname_t*type = sig2mname(v->type);
1455 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1456 t->slot_id = v->index;
1461 check_code_for_break(body);
1463 if(state->method->exceptions &&
1464 (state->method->late_binding || state->method->uses_slots)) {
1465 //syntaxerror("try/catch and activation or late binding not supported yet within the same method");
1466 as3_warning("try/catch and activation or late binding not supported yet within the same method");
1470 f->body->code = body;
1471 f->body->exceptions = state->method->exceptions;
1472 } else { //interface
1474 syntaxerror("interface methods can't have a method body");
1484 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1489 void breakjumpsto(code_t*c, char*name, code_t*jump)
1492 if(c->opcode == OPCODE___BREAK__) {
1493 string_t*name2 = c->data[0];
1494 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1495 c->opcode = OPCODE_JUMP;
1502 void continuejumpsto(code_t*c, char*name, code_t*jump)
1505 if(c->opcode == OPCODE___CONTINUE__) {
1506 string_t*name2 = c->data[0];
1507 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1508 c->opcode = OPCODE_JUMP;
1516 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1517 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1518 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1520 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1522 if(!type1 || !type2)
1523 return registry_getanytype();
1524 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1525 return registry_getanytype();
1528 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1537 return registry_getanytype();
1539 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1544 return abc_coerce_a(c);
1548 // cast an "any" type to a specific type. subject to
1549 // runtime exceptions
1550 return abc_coerce2(c, &m);
1553 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1554 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1555 // allow conversion between number types
1556 return abc_coerce2(c, &m);
1558 //printf("%s.%s\n", from.package, from.name);
1559 //printf("%s.%s\n", to.package, to.name);
1561 classinfo_t*supertype = from;
1563 if(supertype == to) {
1564 // target type is one of from's superclasses
1565 return abc_coerce2(c, &m);
1568 while(supertype->interfaces[t]) {
1569 if(supertype->interfaces[t]==to) {
1570 // target type is one of from's interfaces
1571 return abc_coerce2(c, &m);
1575 supertype = supertype->superclass;
1577 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1579 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1581 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1584 as3_error("can't convert type %s%s%s to %s%s%s",
1585 from->package, from->package?".":"", from->name,
1586 to->package, to->package?".":"", to->name);
1590 code_t*defaultvalue(code_t*c, classinfo_t*type)
1592 if(TYPE_IS_INT(type)) {
1593 c = abc_pushbyte(c, 0);
1594 } else if(TYPE_IS_UINT(type)) {
1595 c = abc_pushuint(c, 0);
1596 } else if(TYPE_IS_FLOAT(type)) {
1598 } else if(TYPE_IS_BOOLEAN(type)) {
1599 c = abc_pushfalse(c);
1601 //c = abc_pushundefined(c);
1603 c = abc_pushnull(c);
1605 c = abc_coerce2(c, &m);
1610 char is_pushundefined(code_t*c)
1612 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1615 static slotinfo_t* find_class(const char*name)
1619 c = registry_find(state->package, name);
1622 /* try explicit imports */
1623 dictentry_t* e = dict_get_slot(state->imports, name);
1626 if(!strcmp(e->key, name)) {
1627 c = (slotinfo_t*)e->data;
1633 /* try package.* imports */
1634 import_list_t*l = state->wildcard_imports;
1636 //printf("does package %s contain a class %s?\n", l->import->package, name);
1637 c = registry_find(l->import->package, name);
1642 /* try global package */
1643 c = registry_find("", name);
1646 /* try local "filename" package */
1647 c = registry_find(internal_filename_package, name);
1652 typedcode_t push_class(slotinfo_t*a)
1657 if(a->access == ACCESS_PACKAGEINTERNAL &&
1658 strcmp(a->package, state->package) &&
1659 strcmp(a->package, internal_filename_package)
1661 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1662 infotypename(a), a->name, a->package, state->package);
1665 if(a->kind != INFOTYPE_CLASS) {
1667 x.c = abc_findpropstrict2(x.c, &m);
1668 x.c = abc_getproperty2(x.c, &m);
1669 if(a->kind == INFOTYPE_METHOD) {
1670 methodinfo_t*f = (methodinfo_t*)a;
1671 x.t = TYPE_FUNCTION(f);
1673 varinfo_t*v = (varinfo_t*)a;
1677 classinfo_t*c = (classinfo_t*)a;
1679 x.c = abc_getglobalscope(x.c);
1680 x.c = abc_getslot(x.c, c->slot);
1683 x.c = abc_getlex2(x.c, &m);
1685 x.t = TYPE_CLASS(c);
1690 static char is_getlocal(code_t*c)
1692 if(!c || c->prev || c->next)
1694 return(c->opcode == OPCODE_GETLOCAL
1695 || c->opcode == OPCODE_GETLOCAL_0
1696 || c->opcode == OPCODE_GETLOCAL_1
1697 || c->opcode == OPCODE_GETLOCAL_2
1698 || c->opcode == OPCODE_GETLOCAL_3);
1700 static int getlocalnr(code_t*c)
1702 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1703 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1704 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1705 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1706 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1707 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1711 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1715 [prefix code] [read instruction]
1719 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1721 if(in && in->opcode == OPCODE_COERCE_A) {
1722 in = code_cutlast(in);
1725 syntaxerror("internal error");
1727 /* chop off read instruction */
1731 prefix = r->prev;r->prev = 0;
1737 char use_temp_var = readbefore;
1739 /* generate the write instruction, and maybe append a dup to the prefix code */
1740 code_t* write = abc_nop(0);
1741 if(r->opcode == OPCODE_GETPROPERTY) {
1742 write->opcode = OPCODE_SETPROPERTY;
1743 multiname_t*m = (multiname_t*)r->data[0];
1744 write->data[0] = multiname_clone(m);
1745 if(m->type == QNAME || m->type == MULTINAME) {
1747 prefix = abc_dup(prefix); // we need the object, too
1750 } else if(m->type == MULTINAMEL) {
1752 /* dupping two values on the stack requires 5 operations and one register-
1753 couldn't adobe just have given us a dup2? */
1754 int temp = gettempvar();
1755 prefix = abc_setlocal(prefix, temp);
1756 prefix = abc_dup(prefix);
1757 prefix = abc_getlocal(prefix, temp);
1758 prefix = abc_swap(prefix);
1759 prefix = abc_getlocal(prefix, temp);
1761 prefix = abc_kill(prefix, temp);
1765 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1767 } else if(r->opcode == OPCODE_GETSLOT) {
1768 write->opcode = OPCODE_SETSLOT;
1769 write->data[0] = r->data[0];
1771 prefix = abc_dup(prefix); // we need the object, too
1774 } else if(r->opcode == OPCODE_GETLOCAL) {
1775 write->opcode = OPCODE_SETLOCAL;
1776 write->data[0] = r->data[0];
1777 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1778 write->opcode = OPCODE_SETLOCAL_0;
1779 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1780 write->opcode = OPCODE_SETLOCAL_1;
1781 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1782 write->opcode = OPCODE_SETLOCAL_2;
1783 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1784 write->opcode = OPCODE_SETLOCAL_3;
1785 } else if(r->opcode == OPCODE_GETSUPER) {
1786 write->opcode = OPCODE_SETSUPER;
1787 multiname_t*m = (multiname_t*)r->data[0];
1788 write->data[0] = multiname_clone(m);
1791 syntaxerror("illegal lvalue: can't assign a value to this expression");
1798 /* with getproperty/getslot, we have to be extra careful not
1799 to execute the read code twice, as it might have side-effects
1800 (e.g. if the property is in fact a setter/getter combination)
1802 So read the value, modify it, and write it again,
1803 using prefix only once and making sure (by using a temporary
1804 register) that the return value is what we just wrote */
1805 temp = gettempvar();
1806 c = code_append(c, prefix);
1807 c = code_append(c, r);
1810 c = abc_setlocal(c, temp);
1812 c = code_append(c, middlepart);
1815 c = abc_setlocal(c, temp);
1817 c = code_append(c, write);
1818 c = abc_getlocal(c, temp);
1819 c = abc_kill(c, temp);
1821 /* if we're allowed to execute the read code twice *and*
1822 the middlepart doesn't modify the code, things are easier.
1824 code_t* r2 = code_dup(r);
1825 //c = code_append(c, prefix);
1826 parserassert(!prefix);
1827 c = code_append(c, r);
1828 c = code_append(c, middlepart);
1829 c = code_append(c, write);
1830 c = code_append(c, r2);
1833 /* even smaller version: overwrite the value without reading
1837 c = code_append(c, prefix);
1840 c = code_append(c, middlepart);
1841 c = code_append(c, write);
1842 c = code_append(c, r);
1845 temp = gettempvar();
1847 c = code_append(c, prefix);
1849 c = code_append(c, middlepart);
1851 c = abc_setlocal(c, temp);
1852 c = code_append(c, write);
1853 c = abc_getlocal(c, temp);
1854 c = abc_kill(c, temp);
1860 char is_break_or_jump(code_t*c)
1864 if(c->opcode == OPCODE_JUMP ||
1865 c->opcode == OPCODE___BREAK__ ||
1866 c->opcode == OPCODE___CONTINUE__ ||
1867 c->opcode == OPCODE_THROW ||
1868 c->opcode == OPCODE_RETURNVOID ||
1869 c->opcode == OPCODE_RETURNVALUE) {
1876 #define IS_FINALLY_TARGET(op) \
1877 ((op) == OPCODE___CONTINUE__ || \
1878 (op) == OPCODE___BREAK__ || \
1879 (op) == OPCODE_RETURNVOID || \
1880 (op) == OPCODE_RETURNVALUE || \
1881 (op) == OPCODE___RETHROW__)
1883 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1885 #define NEED_EXTRA_STACK_ARG
1886 code_t*finally_label = abc_nop(0);
1887 NEW(lookupswitch_t, l);
1893 code_t*prev = i->prev;
1894 if(IS_FINALLY_TARGET(i->opcode)) {
1897 if(i->opcode == OPCODE___RETHROW__ ||
1898 i->opcode == OPCODE_RETURNVALUE) {
1899 if(i->opcode == OPCODE___RETHROW__)
1900 i->opcode = OPCODE_THROW;
1902 p = abc_coerce_a(p);
1903 p = abc_setlocal(p, tempvar);
1905 p = abc_pushbyte(p, count++);
1906 p = abc_jump(p, finally_label);
1907 code_t*target = p = abc_label(p);
1908 #ifdef NEED_EXTRA_STACK_ARG
1912 p = abc_getlocal(p, tempvar);
1915 p->next = i;i->prev = p;
1916 list_append(l->targets, target);
1922 c = abc_pushbyte(c, -1);
1923 c = code_append(c, finally_label);
1924 c = code_append(c, finally);
1926 #ifdef NEED_EXTRA_STACK_ARG
1929 c = abc_lookupswitch(c, l);
1930 c = l->def = abc_label(c);
1931 #ifdef NEED_EXTRA_STACK_ARG
1938 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1942 code_t*prev = i->prev;
1943 if(IS_FINALLY_TARGET(i->opcode)) {
1944 if(i->opcode == OPCODE___RETHROW__)
1945 i->opcode = OPCODE_THROW;
1946 code_t*end = code_dup(finally);
1947 code_t*start = code_start(end);
1948 if(prev) prev->next = start;
1955 return code_append(c, finally);
1958 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1964 int num_insertion_points=0;
1966 if(IS_FINALLY_TARGET(i->opcode))
1967 num_insertion_points++;
1974 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1979 int simple_version_cost = (1+num_insertion_points)*code_size;
1980 int lookup_version_cost = 4*num_insertion_points + 5;
1982 if(cantdup || simple_version_cost > lookup_version_cost) {
1983 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1984 return insert_finally_lookup(c, finally, tempvar);
1986 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1987 return insert_finally_simple(c, finally, tempvar);
1991 #define PASS1 }} if(as3_pass == 1) {{
1992 #define PASS1END }} if(as3_pass == 2) {{
1993 #define PASS2 }} if(as3_pass == 2) {{
1994 #define PASS12 }} {{
1995 #define PASS12END }} if(as3_pass == 2) {{
2001 /* ------------ code blocks / statements ---------------- */
2003 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2005 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2006 PROGRAM_CODE_LIST: PROGRAM_CODE
2007 | PROGRAM_CODE_LIST PROGRAM_CODE
2009 PROGRAM_CODE: PACKAGE_DECLARATION
2010 | INTERFACE_DECLARATION
2012 | FUNCTION_DECLARATION
2015 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
2018 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2019 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2020 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2022 INPACKAGE_CODE: INTERFACE_DECLARATION
2024 | FUNCTION_DECLARATION
2027 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
2030 MAYBECODE: CODE {$$=$1;}
2031 MAYBECODE: {$$=code_new();}
2033 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2034 CODE: CODEPIECE {$$=$1;}
2036 // code which may appear outside of methods
2037 CODE_STATEMENT: IMPORT
2039 CODE_STATEMENT: FOR_IN
2040 CODE_STATEMENT: WHILE
2041 CODE_STATEMENT: DO_WHILE
2042 CODE_STATEMENT: SWITCH
2044 CODE_STATEMENT: WITH
2046 CODE_STATEMENT: VOIDEXPRESSION
2047 CODE_STATEMENT: USE_NAMESPACE
2048 CODE_STATEMENT: NAMESPACE_DECLARATION
2049 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2050 CODE_STATEMENT: '{' '}' {$$=0;}
2052 // code which may appear in methods
2053 CODEPIECE: ';' {$$=0;}
2054 CODEPIECE: CODE_STATEMENT
2055 CODEPIECE: VARIABLE_DECLARATION
2060 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
2062 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2063 //CODEBLOCK : '{' '}' {$$=0;}
2064 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2065 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2067 /* ------------ package init code ------------------- */
2069 PACKAGE_INITCODE: CODE_STATEMENT {
2070 code_t**cc = &global->init->method->body->code;
2071 *cc = code_append(*cc, $1);
2074 /* ------------ conditional compilation ------------- */
2076 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
2078 /* ------------ variables --------------------------- */
2080 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2081 | {$$.c=abc_pushundefined(0);
2085 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2086 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2088 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2089 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2091 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2094 if(variable_exists($1))
2095 syntaxerror("Variable %s already defined", $1);
2097 new_variable($1, 0, 1, 0);
2100 if(!is_subtype_of($3.t, $2)) {
2101 syntaxerror("Can't convert %s to %s", $3.t->name,
2107 if(state->method->uses_slots) {
2108 variable_t* v = find_slot(state, $1);
2110 // this variable is stored in a slot
2118 index = new_variable($1, $2, 1, 0);
2121 $$ = slot?abc_getscopeobject(0, 1):0;
2124 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2125 $$ = code_append($$, $3.c);
2126 $$ = converttype($$, $3.t, $2);
2129 $$ = defaultvalue($$, $2);
2132 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2133 $$ = code_append($$, $3.c);
2134 $$ = abc_coerce_a($$);
2136 // don't do anything
2144 $$ = abc_setslot($$, index);
2146 $$ = abc_setlocal($$, index);
2150 /* ------------ control flow ------------------------- */
2152 MAYBEELSE: %prec below_else {$$ = code_new();}
2153 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2154 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2156 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2159 $$ = code_append($$, $4.c);
2160 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2162 $$ = code_append($$, $6);
2164 myjmp = $$ = abc_jump($$, 0);
2166 myif->branch = $$ = abc_nop($$);
2168 $$ = code_append($$, $7);
2169 myjmp->branch = $$ = abc_nop($$);
2175 FOR_INIT : {$$=code_new();}
2176 FOR_INIT : VARIABLE_DECLARATION
2177 FOR_INIT : VOIDEXPRESSION
2179 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2180 // (I don't see any easy way to revolve this conflict otherwise, as we
2181 // can't touch VAR_READ without upsetting the precedence about "return")
2182 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2183 PASS1 $$=$2;new_variable($2,0,1,0);
2184 PASS2 $$=$2;new_variable($2,$3,1,0);
2186 FOR_IN_INIT : T_IDENTIFIER {
2191 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2192 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2194 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2195 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2197 $$ = code_append($$, $2);
2198 code_t*loopstart = $$ = abc_label($$);
2199 $$ = code_append($$, $4.c);
2200 code_t*myif = $$ = abc_iffalse($$, 0);
2201 $$ = code_append($$, $8);
2202 code_t*cont = $$ = abc_nop($$);
2203 $$ = code_append($$, $6);
2204 $$ = abc_jump($$, loopstart);
2205 code_t*out = $$ = abc_nop($$);
2206 breakjumpsto($$, $1.name, out);
2207 continuejumpsto($$, $1.name, cont);
2214 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2215 variable_t*var = find_variable(state, $2);
2217 syntaxerror("variable %s not known in this scope", $2);
2220 char*tmp1name = concat2($2, "__tmp1__");
2221 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2222 char*tmp2name = concat2($2, "__array__");
2223 int array = new_variable(tmp1name, 0, 0, 0);
2226 $$ = code_append($$, $4.c);
2227 $$ = abc_coerce_a($$);
2228 $$ = abc_setlocal($$, array);
2229 $$ = abc_pushbyte($$, 0);
2230 $$ = abc_setlocal($$, it);
2232 code_t*loopstart = $$ = abc_label($$);
2234 $$ = abc_hasnext2($$, array, it);
2235 code_t*myif = $$ = abc_iffalse($$, 0);
2236 $$ = abc_getlocal($$, array);
2237 $$ = abc_getlocal($$, it);
2239 $$ = abc_nextname($$);
2241 $$ = abc_nextvalue($$);
2242 $$ = converttype($$, 0, var->type);
2243 $$ = abc_setlocal($$, var->index);
2245 $$ = code_append($$, $6);
2246 $$ = abc_jump($$, loopstart);
2248 code_t*out = $$ = abc_nop($$);
2249 breakjumpsto($$, $1.name, out);
2250 continuejumpsto($$, $1.name, loopstart);
2262 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2266 code_t*myjmp = $$ = abc_jump($$, 0);
2267 code_t*loopstart = $$ = abc_label($$);
2268 $$ = code_append($$, $6);
2269 code_t*cont = $$ = abc_nop($$);
2270 myjmp->branch = cont;
2271 $$ = code_append($$, $4.c);
2272 $$ = abc_iftrue($$, loopstart);
2273 code_t*out = $$ = abc_nop($$);
2274 breakjumpsto($$, $1, out);
2275 continuejumpsto($$, $1, cont);
2281 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2283 code_t*loopstart = $$ = abc_label($$);
2284 $$ = code_append($$, $3);
2285 code_t*cont = $$ = abc_nop($$);
2286 $$ = code_append($$, $6.c);
2287 $$ = abc_iftrue($$, loopstart);
2288 code_t*out = $$ = abc_nop($$);
2289 breakjumpsto($$, $1, out);
2290 continuejumpsto($$, $1, cont);
2296 BREAK : "break" %prec prec_none {
2297 $$ = abc___break__(0, "");
2299 BREAK : "break" T_IDENTIFIER {
2300 $$ = abc___break__(0, $2);
2302 CONTINUE : "continue" %prec prec_none {
2303 $$ = abc___continue__(0, "");
2305 CONTINUE : "continue" T_IDENTIFIER {
2306 $$ = abc___continue__(0, $2);
2309 MAYBE_CASE_LIST : {$$=0;}
2310 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2311 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2312 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2313 CASE_LIST: CASE {$$=$1;}
2314 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2316 CASE: "case" E ':' MAYBECODE {
2318 $$ = code_append($$, $2.c);
2319 code_t*j = $$ = abc_ifne($$, 0);
2320 $$ = code_append($$, $4);
2321 if($$->opcode != OPCODE___BREAK__) {
2322 $$ = abc___fallthrough__($$, "");
2324 code_t*e = $$ = abc_nop($$);
2327 DEFAULT: "default" ':' MAYBECODE {
2330 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2332 $$ = code_append($$, $7);
2333 code_t*out = $$ = abc_pop($$);
2334 breakjumpsto($$, $1, out);
2336 code_t*c = $$,*lastblock=0;
2338 if(c->opcode == OPCODE_IFNE) {
2339 if(!c->next) syntaxerror("internal error in fallthrough handling");
2341 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2343 c->opcode = OPCODE_JUMP;
2344 c->branch = lastblock;
2346 /* fall through end of switch */
2347 c->opcode = OPCODE_NOP;
2357 /* ------------ try / catch /finally ---------------- */
2359 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2360 state->exception_name=$3;
2361 PASS1 new_variable($3, 0, 0, 0);
2362 PASS2 new_variable($3, $4, 0, 0);
2365 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2366 multiname_t name = {QNAME, &name_ns, 0, $3};
2368 NEW(abc_exception_t, e)
2369 e->exc_type = sig2mname($4);
2370 e->var_name = multiname_clone(&name);
2374 int i = find_variable_safe(state, $3)->index;
2375 e->target = c = abc_nop(0);
2376 c = abc_setlocal(c, i);
2377 c = code_append(c, $8);
2383 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2388 NEW(abc_exception_t, e)
2389 e->exc_type = 0; //all exceptions
2390 e->var_name = 0; //no name
2393 e->to = code_append(e->to, $4);
2399 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2400 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2401 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2402 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2406 list_append($$.l,$2);
2407 $$.finally = $2->to;$2->to=0;
2410 CATCH_FINALLY_LIST: FINALLY {
2414 list_append($$.l,$1);
2415 $$.finally = $1->to;$1->to=0;
2419 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2420 code_t*out = abc_nop(0);
2422 code_t*start = abc_nop(0);
2423 $$ = code_append(start, $4);
2424 if(!is_break_or_jump($4)) {
2425 $$ = abc_jump($$, out);
2427 code_t*end = $$ = abc_nop($$);
2431 tmp = new_variable("__finally__", 0, 0, 0);
2433 abc_exception_list_t*l = $6.l;
2436 abc_exception_t*e = l->abc_exception;
2438 $$ = code_append($$, e->target);
2439 $$ = abc_jump($$, out);
2441 parserassert((ptroff_t)$6.finally);
2443 e->target = $$ = abc_nop($$);
2444 $$ = abc___rethrow__($$);
2452 $$ = code_append($$, out);
2454 $$ = insert_finally($$, $6.finally, tmp);
2456 list_concat(state->method->exceptions, $6.l);
2462 /* ------------ throw ------------------------------- */
2464 THROW : "throw" EXPRESSION {
2468 THROW : "throw" %prec prec_none {
2469 if(!state->exception_name)
2470 syntaxerror("re-throw only possible within a catch block");
2471 variable_t*v = find_variable(state, state->exception_name);
2473 $$=abc_getlocal($$, v->index);
2477 /* ------------ with -------------------------------- */
2479 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2481 $$ = abc_pushwith($$);
2482 $$ = code_append($$, $5);
2483 $$ = abc_popscope($$);
2486 /* ------------ packages and imports ---------------- */
2488 X_IDENTIFIER: T_IDENTIFIER
2489 | "package" {PASS12 $$="package";}
2491 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2492 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2494 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2495 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2496 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2497 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2499 IMPORT : "import" PACKAGEANDCLASS {
2501 slotinfo_t*s = registry_find($2->package, $2->name);
2502 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2503 as3_schedule_class($2->package, $2->name);
2509 syntaxerror("Couldn't import class\n");
2510 state_has_imports();
2511 dict_put(state->imports, c->name, c);
2512 import_toplevel(c->package);
2515 IMPORT : "import" PACKAGE '.' '*' {
2517 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2518 as3_schedule_package($2);
2524 state_has_imports();
2525 list_append(state->wildcard_imports, i);
2526 import_toplevel(i->package);
2530 /* ------------ classes and interfaces (header) -------------- */
2532 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2533 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2534 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2535 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2537 $$.flags=$1.flags|$2.flags;
2538 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2539 $$.ns=$1.ns?$1.ns:$2.ns;
2543 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2544 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2545 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2546 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2547 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2548 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2549 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2550 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2551 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2552 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2557 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2559 EXTENDS_LIST : {PASS12 $$=list_new();}
2560 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2562 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2563 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2565 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2566 EXTENDS IMPLEMENTS_LIST
2567 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2569 '}' {PASS12 endclass();$$=0;}
2571 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2573 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2574 startclass(&$1,$3,0,$4);}
2575 MAYBE_INTERFACE_BODY
2576 '}' {PASS12 endclass();$$=0;}
2578 /* ------------ classes and interfaces (body) -------------- */
2581 MAYBE_CLASS_BODY : CLASS_BODY
2582 CLASS_BODY : CLASS_BODY_ITEM
2583 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2584 CLASS_BODY_ITEM : ';'
2585 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2586 CLASS_BODY_ITEM : SLOT_DECLARATION
2587 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2589 CLASS_BODY_ITEM : CODE_STATEMENT {
2590 code_t*c = state->cls->static_init->header;
2591 c = code_append(c, $1);
2592 state->cls->static_init->header = c;
2595 MAYBE_INTERFACE_BODY :
2596 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2597 INTERFACE_BODY : IDECLARATION
2598 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2600 IDECLARATION : "var" T_IDENTIFIER {
2601 syntaxerror("variable declarations not allowed in interfaces");
2603 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2605 $1.flags |= FLAG_PUBLIC;
2606 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2607 syntaxerror("invalid method modifiers: interface methods always need to be public");
2609 startfunction(&$1,$3,$4,&$6,$8);
2610 endfunction(&$1,$3,$4,&$6,$8, 0);
2611 list_deep_free($6.list);
2614 /* ------------ classes and interfaces (body, slots ) ------- */
2616 VARCONST: "var" | "const"
2618 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2620 SLOT_LIST: ONE_SLOT {$$ = $1;}
2621 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2623 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2625 int flags = slotstate_flags->flags;
2626 namespace_t ns = modifiers2access(slotstate_flags);
2628 varinfo_t* info = 0;
2630 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2632 check_override(i, flags);
2634 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2636 slotinfo_t*i = registry_find(state->package, $1);
2638 syntaxerror("package %s already contains '%s'", state->package, $1);
2640 if(ns.name && ns.name[0]) {
2641 syntaxerror("namespaces not allowed on package-level variables");
2643 info = varinfo_register_global(ns.access, state->package, $1);
2647 info->flags = flags;
2650 multiname_t mname = {QNAME, &ns, 0, $1};
2652 trait_list_t**traits;
2656 ns.name = state->package;
2657 traits = &global->init->traits;
2658 code = &global->init->method->body->code;
2659 } else if(flags&FLAG_STATIC) {
2661 traits = &state->cls->abc->static_traits;
2662 code = &state->cls->static_init->header;
2664 // instance variable
2665 traits = &state->cls->abc->traits;
2666 code = &state->cls->init->header;
2672 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2674 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2676 info->slot = t->slot_id;
2678 /* initalization code (if needed) */
2680 if($3.c && !is_pushundefined($3.c)) {
2681 c = abc_getlocal_0(c);
2682 c = code_append(c, $3.c);
2683 c = converttype(c, $3.t, $2);
2684 c = abc_setslot(c, t->slot_id);
2687 *code = code_append(*code, c);
2689 if(slotstate_varconst==KW_CONST) {
2690 t->kind= TRAIT_CONST;
2696 /* ------------ constants -------------------------------------- */
2698 MAYBESTATICCONSTANT: {$$=0;}
2699 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2701 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2702 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2703 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2704 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2705 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2706 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2707 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2708 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2709 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2710 STATICCONSTANT : T_IDENTIFIER {
2711 if(!strcmp($1, "NaN")) {
2712 $$ = constant_new_float(__builtin_nan(""));
2714 as3_warning("Couldn't evaluate constant value of %s", $1);
2715 $$ = constant_new_null($1);
2719 /* ------------ classes and interfaces (body, functions) ------- */
2721 // non-vararg version
2724 memset(&$$,0,sizeof($$));
2726 MAYBE_PARAM_LIST: PARAM_LIST {
2732 MAYBE_PARAM_LIST: "..." PARAM {
2734 memset(&$$,0,sizeof($$));
2736 list_append($$.list, $2);
2738 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2742 list_append($$.list, $4);
2746 PARAM_LIST: PARAM_LIST ',' PARAM {
2749 list_append($$.list, $3);
2753 memset(&$$,0,sizeof($$));
2754 list_append($$.list, $1);
2757 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2759 $$ = rfx_calloc(sizeof(param_t));
2765 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2767 $$ = rfx_calloc(sizeof(param_t));
2769 $$->type = TYPE_ANY;
2777 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2778 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2781 endfunction(&$1,$3,$4,&$6,0,0);
2783 if(!state->method->info) syntaxerror("internal error");
2785 code_t*c = method_header(state->method);
2786 c = wrap_function(c, 0, $11);
2788 endfunction(&$1,$3,$4,&$6,$8,c);
2790 list_deep_free($6.list);
2794 MAYBE_IDENTIFIER: T_IDENTIFIER
2795 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2796 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2797 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2800 endfunction(0,0,$2,&$4,0,0);
2802 methodinfo_t*f = state->method->info;
2803 if(!f || !f->kind) syntaxerror("internal error");
2805 code_t*c = method_header(state->method);
2806 c = wrap_function(c, 0, $9);
2808 int index = state->method->var_index;
2809 endfunction(0,0,$2,&$4,$6,c);
2811 $$.c = abc_getlocal(0, index);
2812 $$.t = TYPE_FUNCTION(f);
2814 PASS12 list_deep_free($4.list);
2818 /* ------------- package + class ids --------------- */
2820 CLASS: T_IDENTIFIER {
2821 PASS1 static classinfo_t c;
2822 memset(&c, 0, sizeof(c));
2823 c.kind = INFOTYPE_CLASS;
2828 /* let the compiler know that we might be looking for this soon */
2829 as3_schedule_class_noerror(state->package, $1);
2831 slotinfo_t*s = find_class($1);
2832 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2833 $$ = (classinfo_t*)s;
2836 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2837 PASS1 static classinfo_t c;
2838 memset(&c, 0, sizeof(c));
2839 c.kind = INFOTYPE_CLASS;
2845 slotinfo_t*s = registry_find($1, $3);
2846 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2848 $$ = (classinfo_t*)s;
2851 CLASS_SPEC: PACKAGEANDCLASS
2854 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2855 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2857 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2858 | '*' {PASS12 $$=registry_getanytype();}
2859 | "void" {PASS12 $$=registry_getanytype();}
2861 | "String" {$$=registry_getstringclass();}
2862 | "int" {$$=registry_getintclass();}
2863 | "uint" {$$=registry_getuintclass();}
2864 | "Boolean" {$$=registry_getbooleanclass();}
2865 | "Number" {$$=registry_getnumberclass();}
2868 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2869 MAYBETYPE: {PASS12 $$=0;}
2871 /* ----------function calls, delete, constructor calls ------ */
2873 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2874 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2876 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2877 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2878 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2880 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2884 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2885 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2887 $$.cc = code_append($1.cc, $2.c);
2891 NEW : "new" E XX MAYBE_PARAM_VALUES {
2893 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2895 code_t*paramcode = $4.cc;
2896 if($$.c->opcode == OPCODE_GETPROPERTY) {
2897 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2898 $$.c = code_cutlast($$.c);
2899 $$.c = code_append($$.c, paramcode);
2900 $$.c = abc_constructprop2($$.c, name, $4.len);
2901 multiname_destroy(name);
2902 } else if($$.c->opcode == OPCODE_GETSLOT) {
2903 int slot = (int)(ptroff_t)$$.c->data[0];
2904 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2905 multiname_t*name = t->name;
2906 $$.c = code_cutlast($$.c);
2907 $$.c = code_append($$.c, paramcode);
2908 $$.c = abc_constructprop2($$.c, name, $4.len);
2910 $$.c = code_append($$.c, paramcode);
2911 $$.c = abc_construct($$.c, $4.len);
2915 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2918 $$.c = abc_coerce_a($$.c);
2923 /* TODO: use abc_call (for calling local variables),
2924 abc_callstatic (for calling own methods)
2927 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2930 if($$.c->opcode == OPCODE_COERCE_A) {
2931 $$.c = code_cutlast($$.c);
2933 code_t*paramcode = $3.cc;
2936 if($$.c->opcode == OPCODE_GETPROPERTY) {
2937 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2938 $$.c = code_cutlast($$.c);
2939 $$.c = code_append($$.c, paramcode);
2940 $$.c = abc_callproperty2($$.c, name, $3.len);
2941 multiname_destroy(name);
2942 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
2943 int slot = (int)(ptroff_t)$$.c->data[0];
2944 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
2945 if(t->kind!=TRAIT_METHOD) {
2946 //ok: flash allows to assign closures to members.
2948 multiname_t*name = t->name;
2949 $$.c = code_cutlast($$.c);
2950 $$.c = code_append($$.c, paramcode);
2951 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2952 $$.c = abc_callproperty2($$.c, name, $3.len);
2953 } else if($$.c->opcode == OPCODE_GETSUPER) {
2954 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2955 $$.c = code_cutlast($$.c);
2956 $$.c = code_append($$.c, paramcode);
2957 $$.c = abc_callsuper2($$.c, name, $3.len);
2958 multiname_destroy(name);
2960 $$.c = abc_getglobalscope($$.c);
2961 $$.c = code_append($$.c, paramcode);
2962 $$.c = abc_call($$.c, $3.len);
2965 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2966 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2968 $$.c = abc_coerce_a($$.c);
2973 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2974 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2975 if(!state->method) syntaxerror("super() not allowed outside of a function");
2976 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2979 $$.c = abc_getlocal_0($$.c);
2981 $$.c = code_append($$.c, $3.cc);
2983 this is dependent on the control path, check this somewhere else
2984 if(state->method->has_super)
2985 syntaxerror("constructor may call super() only once");
2987 state->method->has_super = 1;
2989 $$.c = abc_constructsuper($$.c, $3.len);
2990 $$.c = abc_pushundefined($$.c);
2994 DELETE: "delete" E {
2996 if($$.c->opcode == OPCODE_COERCE_A) {
2997 $$.c = code_cutlast($$.c);
2999 multiname_t*name = 0;
3000 if($$.c->opcode == OPCODE_GETPROPERTY) {
3001 $$.c->opcode = OPCODE_DELETEPROPERTY;
3002 } else if($$.c->opcode == OPCODE_GETSLOT) {
3003 int slot = (int)(ptroff_t)$$.c->data[0];
3004 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3005 $$.c = code_cutlast($$.c);
3006 $$.c = abc_deleteproperty2($$.c, name);
3008 $$.c = abc_getlocal_0($$.c);
3009 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3010 $$.c = abc_deleteproperty2($$.c, &m);
3012 $$.t = TYPE_BOOLEAN;
3015 RETURN: "return" %prec prec_none {
3016 $$ = abc_returnvoid(0);
3018 RETURN: "return" EXPRESSION {
3020 $$ = abc_returnvalue($$);
3023 // ----------------------- expression types -------------------------------------
3025 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3026 EXPRESSION : E %prec below_minus {$$ = $1;}
3027 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3029 $$.c = cut_last_push($$.c);
3030 $$.c = code_append($$.c,$3.c);
3033 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3034 $$=cut_last_push($1.c);
3037 // ----------------------- expression evaluation -------------------------------------
3039 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3040 //V : CONSTANT {$$ = 0;}
3042 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
3043 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3044 //V : NEW {$$ = $1.c;}
3046 //V : DELETE {$$ = $1.c;}
3047 E : DELETE {$$ = $1;}
3053 namespace_t ns = {ACCESS_PACKAGE, ""};
3054 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3056 $$.c = abc_getlex2($$.c, &m);
3057 $$.c = abc_pushstring($$.c, $1.pattern);
3058 $$.c = abc_construct($$.c, 1);
3060 $$.c = abc_getlex2($$.c, &m);
3061 $$.c = abc_pushstring($$.c, $1.pattern);
3062 $$.c = abc_pushstring($$.c, $1.options);
3063 $$.c = abc_construct($$.c, 2);
3068 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3069 //MULTINAME(m, registry_getintclass());
3070 //$$.c = abc_coerce2($$.c, &m); // FIXME
3073 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3076 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3079 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3082 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3085 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3088 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3091 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3092 $$.t = TYPE_BOOLEAN;
3094 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3095 $$.t = TYPE_BOOLEAN;
3097 CONSTANT : "null" {$$.c = abc_pushnull(0);
3101 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3102 $$.t = TYPE_BOOLEAN;
3104 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3105 $$.t = TYPE_BOOLEAN;
3107 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3108 $$.t = TYPE_BOOLEAN;
3110 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3111 $$.t = TYPE_BOOLEAN;
3113 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3114 $$.t = TYPE_BOOLEAN;
3116 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3117 $$.t = TYPE_BOOLEAN;
3119 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3120 $$.t = TYPE_BOOLEAN;
3122 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3123 $$.t = TYPE_BOOLEAN;
3126 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3128 $$.c = converttype($$.c, $1.t, $$.t);
3129 $$.c = abc_dup($$.c);
3130 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3131 $$.c = cut_last_push($$.c);
3132 $$.c = code_append($$.c,$3.c);
3133 $$.c = converttype($$.c, $3.t, $$.t);
3134 code_t*label = $$.c = abc_label($$.c);
3135 jmp->branch = label;
3138 $$.t = join_types($1.t, $3.t, 'A');
3139 /*printf("%08x:\n",$1.t);
3140 code_dump($1.c, 0, 0, "", stdout);
3141 printf("%08x:\n",$3.t);
3142 code_dump($3.c, 0, 0, "", stdout);
3143 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3145 $$.c = converttype($$.c, $1.t, $$.t);
3146 $$.c = abc_dup($$.c);
3147 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3148 $$.c = cut_last_push($$.c);
3149 $$.c = code_append($$.c,$3.c);
3150 $$.c = converttype($$.c, $3.t, $$.t);
3151 code_t*label = $$.c = abc_label($$.c);
3152 jmp->branch = label;
3155 E : '!' E {$$.c=$2.c;
3156 $$.c = abc_not($$.c);
3157 $$.t = TYPE_BOOLEAN;
3160 E : '~' E {$$.c=$2.c;
3161 $$.c = abc_bitnot($$.c);
3165 E : E '&' E {$$.c = code_append($1.c,$3.c);
3166 $$.c = abc_bitand($$.c);
3170 E : E '^' E {$$.c = code_append($1.c,$3.c);
3171 $$.c = abc_bitxor($$.c);
3175 E : E '|' E {$$.c = code_append($1.c,$3.c);
3176 $$.c = abc_bitor($$.c);
3180 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3181 $$.c = abc_rshift($$.c);
3184 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3185 $$.c = abc_urshift($$.c);
3188 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3189 $$.c = abc_lshift($$.c);
3193 E : E '/' E {$$.c = code_append($1.c,$3.c);
3194 $$.c = abc_divide($$.c);
3197 E : E '%' E {$$.c = code_append($1.c,$3.c);
3198 $$.c = abc_modulo($$.c);
3201 E : E '+' E {$$.c = code_append($1.c,$3.c);
3202 if(BOTH_INT($1.t, $3.t)) {
3203 $$.c = abc_add_i($$.c);
3206 $$.c = abc_add($$.c);
3207 $$.t = join_types($1.t,$3.t,'+');
3210 E : E '-' E {$$.c = code_append($1.c,$3.c);
3211 if(BOTH_INT($1.t,$3.t)) {
3212 $$.c = abc_subtract_i($$.c);
3215 $$.c = abc_subtract($$.c);
3219 E : E '*' E {$$.c = code_append($1.c,$3.c);
3220 if(BOTH_INT($1.t,$3.t)) {
3221 $$.c = abc_multiply_i($$.c);
3224 $$.c = abc_multiply($$.c);
3229 E : E "in" E {$$.c = code_append($1.c,$3.c);
3230 $$.c = abc_in($$.c);
3231 $$.t = TYPE_BOOLEAN;
3234 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3235 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3236 MULTINAME(m, (classinfo_t*)($3.t->data));
3237 $$.c = abc_astype2($1.c, &m);
3240 $$.c = code_append($1.c, $3.c);
3241 $$.c = abc_astypelate($$.c);
3246 E : E "instanceof" E
3247 {$$.c = code_append($1.c, $3.c);
3248 $$.c = abc_instanceof($$.c);
3249 $$.t = TYPE_BOOLEAN;
3252 E : E "is" E {$$.c = code_append($1.c, $3.c);
3253 $$.c = abc_istypelate($$.c);
3254 $$.t = TYPE_BOOLEAN;
3257 E : "typeof" '(' E ')' {
3259 $$.c = abc_typeof($$.c);
3264 $$.c = cut_last_push($2.c);
3265 $$.c = abc_pushundefined($$.c);
3269 E : "void" { $$.c = abc_pushundefined(0);
3273 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3278 $$.c=abc_negate_i($$.c);
3281 $$.c=abc_negate($$.c);
3288 $$.c = code_append($$.c, $3.c);
3290 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3291 $$.c = abc_getproperty2($$.c, &m);
3292 $$.t = 0; // array elements have unknown type
3295 E : '[' MAYBE_EXPRESSION_LIST ']' {
3297 $$.c = code_append($$.c, $2.cc);
3298 $$.c = abc_newarray($$.c, $2.len);
3299 $$.t = registry_getarrayclass();
3302 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
3303 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3305 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3307 $$.cc = code_append($$.cc, $1.c);
3308 $$.cc = code_append($$.cc, $3.c);
3311 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3314 $$.cc = code_append($$.cc, $3.c);
3315 $$.cc = code_append($$.cc, $5.c);
3320 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3322 $$.c = code_append($$.c, $2.cc);
3323 $$.c = abc_newobject($$.c, $2.len/2);
3324 $$.t = registry_getobjectclass();
3329 if(BOTH_INT($1.t,$3.t)) {
3330 c=abc_multiply_i(c);
3334 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3335 $$.c = toreadwrite($1.c, c, 0, 0);
3340 code_t*c = abc_modulo($3.c);
3341 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3342 $$.c = toreadwrite($1.c, c, 0, 0);
3346 code_t*c = abc_lshift($3.c);
3347 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3348 $$.c = toreadwrite($1.c, c, 0, 0);
3352 code_t*c = abc_rshift($3.c);
3353 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3354 $$.c = toreadwrite($1.c, c, 0, 0);
3358 code_t*c = abc_urshift($3.c);
3359 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3360 $$.c = toreadwrite($1.c, c, 0, 0);
3364 code_t*c = abc_divide($3.c);
3365 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3366 $$.c = toreadwrite($1.c, c, 0, 0);
3370 code_t*c = abc_bitor($3.c);
3371 c=converttype(c, TYPE_INT, $1.t);
3372 $$.c = toreadwrite($1.c, c, 0, 0);
3376 code_t*c = abc_bitxor($3.c);
3377 c=converttype(c, TYPE_INT, $1.t);
3378 $$.c = toreadwrite($1.c, c, 0, 0);
3384 if(TYPE_IS_INT($1.t)) {
3388 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3391 $$.c = toreadwrite($1.c, c, 0, 0);
3394 E : E "-=" E { code_t*c = $3.c;
3395 if(TYPE_IS_INT($1.t)) {
3396 c=abc_subtract_i(c);
3399 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3402 $$.c = toreadwrite($1.c, c, 0, 0);
3405 E : E '=' E { code_t*c = 0;
3406 c = code_append(c, $3.c);
3407 c = converttype(c, $3.t, $1.t);
3408 $$.c = toreadwrite($1.c, c, 1, 0);
3412 E : E '?' E ':' E %prec below_assignment {
3413 $$.t = join_types($3.t,$5.t,'?');
3415 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3416 $$.c = code_append($$.c, $3.c);
3417 $$.c = converttype($$.c, $3.t, $$.t);
3418 code_t*j2 = $$.c = abc_jump($$.c, 0);
3419 $$.c = j1->branch = abc_label($$.c);
3420 $$.c = code_append($$.c, $5.c);
3421 $$.c = converttype($$.c, $5.t, $$.t);
3422 $$.c = j2->branch = abc_label($$.c);
3425 E : E "++" { code_t*c = 0;
3426 classinfo_t*type = $1.t;
3427 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3428 int nr = getlocalnr($1.c);
3429 code_free($1.c);$1.c=0;
3430 if(TYPE_IS_INT($1.t)) {
3431 $$.c = abc_getlocal(0, nr);
3432 $$.c = abc_inclocal_i($$.c, nr);
3433 } else if(TYPE_IS_NUMBER($1.t)) {
3434 $$.c = abc_getlocal(0, nr);
3435 $$.c = abc_inclocal($$.c, nr);
3436 } else syntaxerror("internal error");
3438 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3439 c=abc_increment_i(c);
3445 c=converttype(c, type, $1.t);
3446 $$.c = toreadwrite($1.c, c, 0, 1);
3451 // TODO: use inclocal, like with ++
3452 E : E "--" { code_t*c = 0;
3453 classinfo_t*type = $1.t;
3454 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3455 c=abc_decrement_i(c);
3461 c=converttype(c, type, $1.t);
3462 $$.c = toreadwrite($1.c, c, 0, 1);
3466 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3467 classinfo_t*type = $2.t;
3468 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3469 c=abc_increment_i(c);
3475 c=converttype(c, type, $2.t);
3476 $$.c = toreadwrite($2.c, c, 0, 0);
3480 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3481 classinfo_t*type = $2.t;
3482 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3483 c=abc_decrement_i(c);
3489 c=converttype(c, type, $2.t);
3490 $$.c = toreadwrite($2.c, c, 0, 0);
3494 E : "super" '.' T_IDENTIFIER
3495 { if(!state->cls->info)
3496 syntaxerror("super keyword not allowed outside a class");
3497 classinfo_t*t = state->cls->info->superclass;
3498 if(!t) t = TYPE_OBJECT;
3500 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3502 MEMBER_MULTINAME(m, f, $3);
3504 $$.c = abc_getlocal_0($$.c);
3505 $$.c = abc_getsuper2($$.c, &m);
3506 $$.t = slotinfo_gettype((slotinfo_t*)f);
3509 E : '@' T_IDENTIFIER {
3511 $$.c = abc_pushundefined(0);
3513 as3_warning("ignored @ operator");
3516 E : E '.' '@' T_IDENTIFIER {
3517 // child attribute TODO
3518 $$.c = abc_pushundefined(0);
3520 as3_warning("ignored .@ operator");
3523 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3524 // namespace declaration TODO
3525 $$.c = abc_pushundefined(0);
3527 as3_warning("ignored :: operator");
3530 E : E ".." T_IDENTIFIER {
3532 $$.c = abc_pushundefined(0);
3534 as3_warning("ignored .. operator");
3537 E : E '.' '(' E ')' {
3539 $$.c = abc_pushundefined(0);
3541 as3_warning("ignored .() operator");
3544 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3548 E : E '.' T_IDENTIFIER {
3550 classinfo_t*t = $1.t;
3552 if(TYPE_IS_CLASS(t) && t->data) {
3557 if(t->subtype==0xff) {
3558 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3560 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3562 if(f && !is_static != !(f->flags&FLAG_STATIC))
3564 if(f && f->slot && !noslot) {
3565 $$.c = abc_getslot($$.c, f->slot);
3567 MEMBER_MULTINAME(m, f, $3);
3568 $$.c = abc_getproperty2($$.c, &m);
3570 /* determine type */
3571 $$.t = slotinfo_gettype((slotinfo_t*)f);
3573 $$.c = abc_coerce_a($$.c);
3574 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3575 string_t*package = $1.c->data[0];
3576 char*package2 = concat3(package->str, ".", $3);
3577 if(dict_contains(state->import_toplevel_packages, package2)) {
3579 $$.c->data[0] = string_new4(package2);
3582 slotinfo_t*a = registry_find(package->str, $3);
3584 syntaxerror("couldn't resolve %s", package2);
3588 /* when resolving a property on an unknown type, we do know the
3589 name of the property (and don't seem to need the package), but
3590 we need to make avm2 try out all access modes */
3591 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3592 $$.c = abc_getproperty2($$.c, &m);
3593 $$.c = abc_coerce_a($$.c);
3594 $$.t = registry_getanytype();
3598 VAR_READ : T_IDENTIFIER {
3600 /* Queue unresolved identifiers for checking against the parent
3601 function's variables.
3602 We consider everything which is not a local variable "unresolved".
3603 This encompasses class names, members of the surrounding class
3604 etc. which is *correct* because local variables of the parent function
3607 if(state->method->inner && !find_variable(state, $1)) {
3608 unknown_variable($1);
3611 /* let the compiler know that it might check the current directory/package
3612 for this identifier- maybe there's a file $1.as defining $1. */
3613 as3_schedule_class_noerror(state->package, $1);
3622 /* look at variables */
3623 if((v = find_variable(state, $1))) {
3624 // $1 is a local variable
3625 $$.c = abc_getlocal($$.c, v->index);
3629 if((v = find_slot(state, $1))) {
3630 $$.c = abc_getscopeobject($$.c, 1);
3631 $$.c = abc_getslot($$.c, v->index);
3636 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3638 /* look at current class' members */
3639 if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) &&
3640 (f->flags&FLAG_STATIC) >= i_am_static) {
3641 // $1 is a function in this class
3642 int var_is_static = (f->flags&FLAG_STATIC);
3644 if(f->kind == INFOTYPE_METHOD) {
3645 $$.t = TYPE_FUNCTION(f);
3649 if(var_is_static && !i_am_static) {
3650 /* access to a static member from a non-static location.
3651 do this via findpropstrict:
3652 there doesn't seem to be any non-lookup way to access
3653 static properties of a class */
3654 state->method->late_binding = 1;
3656 namespace_t ns = {f->access, ""};
3657 multiname_t m = {QNAME, &ns, 0, $1};
3658 $$.c = abc_findpropstrict2($$.c, &m);
3659 $$.c = abc_getproperty2($$.c, &m);
3661 } else if(f->slot>0) {
3662 $$.c = abc_getlocal_0($$.c);
3663 $$.c = abc_getslot($$.c, f->slot);
3666 namespace_t ns = {f->access, ""};
3667 multiname_t m = {QNAME, &ns, 0, $1};
3668 $$.c = abc_getlocal_0($$.c);
3669 $$.c = abc_getproperty2($$.c, &m);
3674 /* look at actual classes, in the current package and imported */
3675 if((a = find_class($1))) {
3680 /* look through package prefixes */
3681 if(dict_contains(state->import_toplevel_packages, $1)) {
3682 $$.c = abc___pushpackage__($$.c, $1);
3687 /* unknown object, let the avm2 resolve it */
3689 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3690 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3691 state->method->late_binding = 1;
3693 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3696 $$.c = abc_findpropstrict2($$.c, &m);
3697 $$.c = abc_getproperty2($$.c, &m);
3701 // ----------------- namespaces -------------------------------------------------
3703 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3705 NEW(namespace_decl_t,n);
3710 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3712 NEW(namespace_decl_t,n);
3717 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3719 NEW(namespace_decl_t,n);
3724 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3726 list_append(state->new_namespaces, $2);
3727 tokenizer_register_namespace($2->name);
3731 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3733 NEW(namespace_decl_t,n);
3736 /* FIXME: for pass2, we should now try to figure out what the URL of
3738 list_append(state->new_namespaces, n);
3739 tokenizer_register_namespace($3->name);