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;
358 char has_own_imports;
359 char new_vars; // e.g. transition between two functions
362 methodstate_t*method;
371 typedef struct _global {
375 dict_t*file2token2info;
378 static global_t*global = 0;
379 static state_t* state = 0;
383 #define MULTINAME(m,x) \
387 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
389 #define MEMBER_MULTINAME(m,f,n) \
393 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
394 m##_ns.name = ((slotinfo_t*)(f))->package; \
399 m.namespace_set = 0; \
400 m.name = ((slotinfo_t*)(f))->name; \
402 m.type = MULTINAME; \
404 m.namespace_set = &nopackage_namespace_set; \
408 /* warning: list length of namespace set is undefined */
409 #define MULTINAME_LATE(m, access, package) \
410 namespace_t m##_ns = {access, package}; \
411 namespace_set_t m##_nsset; \
412 namespace_list_t m##_l;m##_l.next = 0; \
413 m##_nsset.namespaces = &m##_l; \
414 m##_nsset = m##_nsset; \
415 m##_l.namespace = &m##_ns; \
416 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
418 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
419 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
420 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
421 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
422 static namespace_list_t nl4 = {&ns4,0};
423 static namespace_list_t nl3 = {&ns3,&nl4};
424 static namespace_list_t nl2 = {&ns2,&nl3};
425 static namespace_list_t nl1 = {&ns1,&nl2};
426 static namespace_set_t nopackage_namespace_set = {&nl1};
428 static void new_state()
431 state_t*oldstate = state;
433 memcpy(s, state, sizeof(state_t)); //shallow copy
435 s->imports = dict_new();
437 if(!s->import_toplevel_packages) {
438 s->import_toplevel_packages = dict_new();
442 state->has_own_imports = 0;
443 state->vars = dict_new();
444 state->old = oldstate;
447 trie_remember(active_namespaces);
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 trie_rollback(active_namespaces);
496 if(!state || !state->old)
497 syntaxerror("invalid nesting");
498 state_t*leaving = state;
502 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
503 free(leaving->method);
506 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
511 state_destroy(leaving);
514 static code_t* method_header(methodstate_t*m);
515 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
516 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
519 static char* internal_filename_package = 0;
520 void initialize_file(char*filename)
523 syntaxerror("invalid call to initialize_file during parsing of another file");
526 active_namespaces = trie_new();
529 state->package = internal_filename_package = strdup(filename);
531 global->token2info = dict_lookup(global->file2token2info,
532 current_filename // use long version
534 if(!global->token2info) {
535 global->token2info = dict_new2(&ptr_type);
536 dict_put(global->file2token2info, current_filename, global->token2info);
540 state->method = rfx_calloc(sizeof(methodstate_t));
541 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
542 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
544 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
545 function_initvars(state->method, 0, 0, 1);
546 global->init = abc_initscript(global->file);
552 if(!state || state->level!=1) {
553 syntaxerror("unexpected end of file in pass %d", as3_pass);
557 code_t*header = method_header(state->method);
558 code_t*c = wrap_function(header, 0, global->init->method->body->code);
559 global->init->method->body->code = c;
560 free(state->method);state->method=0;
563 //free(state->package);state->package=0; // used in registry
564 state_destroy(state);state=0;
567 void initialize_parser()
569 global = rfx_calloc(sizeof(global_t));
570 global->file = abc_file_new();
571 global->file->flags &= ~ABCFILE_LAZY;
572 global->file2token2info = dict_new();
573 global->token2info = 0;
576 void* finish_parser()
578 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
580 global->token2info=0;
586 static void xx_scopetest()
588 /* findpropstrict doesn't just return a scope object- it
589 also makes it "active" somehow. Push local_0 on the
590 scope stack and read it back with findpropstrict, it'll
591 contain properties like "trace". Trying to find the same
592 property on a "vanilla" local_0 yields only a "undefined" */
593 //c = abc_findpropstrict(c, "[package]::trace");
595 /*c = abc_getlocal_0(c);
596 c = abc_findpropstrict(c, "[package]::trace");
598 c = abc_setlocal_1(c);
600 c = abc_pushbyte(c, 0);
601 c = abc_setlocal_2(c);
603 code_t*xx = c = abc_label(c);
604 c = abc_findpropstrict(c, "[package]::trace");
605 c = abc_pushstring(c, "prop:");
606 c = abc_hasnext2(c, 1, 2);
608 c = abc_setlocal_3(c);
609 c = abc_callpropvoid(c, "[package]::trace", 2);
610 c = abc_getlocal_3(c);
612 c = abc_iftrue(c,xx);*/
615 typedef struct _variable {
619 methodstate_t*is_inner_method;
622 static variable_t* find_variable(state_t*s, char*name)
626 v = dict_lookup(s->vars, name);
628 if(s->new_vars) break;
633 static variable_t* find_slot(state_t*s, const char*name)
635 if(s->method && s->method->slots)
636 return dict_lookup(s->method->slots, name);
640 static variable_t* find_variable_safe(state_t*s, char*name)
642 variable_t* v = find_variable(s, name);
644 syntaxerror("undefined variable: %s", name);
647 static char variable_exists(char*name)
649 return dict_contains(state->vars, name);
651 code_t*defaultvalue(code_t*c, classinfo_t*type);
653 static int alloc_local()
655 return state->method->variable_count++;
658 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
661 variable_t*v = find_slot(state, name);
667 v->index = alloc_local();
672 dict_put(state->vars, name, v);
676 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
678 return new_variable2(name, type, init, maybeslot)->index;
681 #define TEMPVARNAME "__as3_temp__"
682 static int gettempvar()
684 variable_t*v = find_variable(state, TEMPVARNAME);
687 return new_variable(TEMPVARNAME, 0, 0, 0);
690 code_t* var_block(code_t*body)
696 for(t=0;t<state->vars->hashsize;t++) {
697 dictentry_t*e = state->vars->slots[t];
699 variable_t*v = (variable_t*)e->data;
700 if(v->type && v->init) {
701 c = defaultvalue(c, v->type);
702 c = abc_setlocal(c, v->index);
703 k = abc_kill(k, v->index);
713 if(x->opcode== OPCODE___BREAK__ ||
714 x->opcode== OPCODE___CONTINUE__) {
715 /* link kill code before break/continue */
716 code_t*e = code_dup(k);
717 code_t*s = code_start(e);
729 c = code_append(c, body);
730 c = code_append(c, k);
734 void unknown_variable(char*name)
736 if(!state->method->unresolved_variables)
737 state->method->unresolved_variables = dict_new();
738 if(!dict_contains(state->method->unresolved_variables, name))
739 dict_put(state->method->unresolved_variables, name, 0);
742 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
744 static void parsererror(const char*file, int line, const char*f)
746 syntaxerror("internal error in %s, %s:%d", f, file, line);
750 static code_t* add_scope_code(code_t*c, methodstate_t*m)
752 if(m->uses_slots || (m->late_binding && !m->inner)) {
753 c = abc_getlocal_0(c);
754 c = abc_pushscope(c);
757 /* FIXME: does this need to be the same activation object as
758 in the function header? */
759 c = abc_newactivation(c);
760 c = abc_pushscope(c);
765 static code_t* method_header(methodstate_t*m)
769 c = add_scope_code(c, m);
771 methodstate_list_t*l = m->innerfunctions;
773 parserassert(l->methodstate->abc);
774 if(m->uses_slots && l->methodstate->is_a_slot) {
775 c = abc_getscopeobject(c, 1);
776 c = abc_newfunction(c, l->methodstate->abc);
778 c = abc_setlocal(c, l->methodstate->var_index);
779 c = abc_setslot(c, l->methodstate->slot_index);
781 c = abc_newfunction(c, l->methodstate->abc);
782 c = abc_setlocal(c, l->methodstate->var_index);
784 free(l->methodstate);l->methodstate=0;
788 c = code_append(c, m->header);
791 if(m->is_constructor && !m->has_super) {
792 // call default constructor
793 c = abc_getlocal_0(c);
794 c = abc_constructsuper(c, 0);
796 list_free(m->innerfunctions);
797 m->innerfunctions = 0;
802 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
804 c = code_append(c, header);
805 c = code_append(c, var_block(body));
806 /* append return if necessary */
807 if(!c || (c->opcode != OPCODE_RETURNVOID &&
808 c->opcode != OPCODE_RETURNVALUE)) {
809 c = abc_returnvoid(c);
815 static void startpackage(char*name)
818 /*printf("entering package \"%s\"\n", name);*/
819 state->package = strdup(name);
821 static void endpackage()
823 /*printf("leaving package \"%s\"\n", state->package);*/
825 //used e.g. in classinfo_register:
826 //free(state->package);state->package=0;
831 #define FLAG_PUBLIC 256
832 #define FLAG_PROTECTED 512
833 #define FLAG_PRIVATE 1024
834 #define FLAG_PACKAGEINTERNAL 2048
835 #define FLAG_NAMESPACE 4096
837 static namespace_t modifiers2access(modifiers_t*mod)
842 if(mod->flags&FLAG_NAMESPACE) {
843 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
844 syntaxerror("invalid combination of access levels and namespaces");
845 ns.access = ACCESS_NAMESPACE;
847 const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
849 /* shouldn't happen- the tokenizer only reports something as a namespace
850 if it was already registered */
851 syntaxerror("unknown namespace: %s", mod->ns);
854 } else if(mod->flags&FLAG_PUBLIC) {
855 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
856 syntaxerror("invalid combination of access levels");
857 ns.access = ACCESS_PACKAGE;
858 } else if(mod->flags&FLAG_PRIVATE) {
859 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
860 syntaxerror("invalid combination of access levels");
861 ns.access = ACCESS_PRIVATE;
862 } else if(mod->flags&FLAG_PROTECTED) {
863 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
864 syntaxerror("invalid combination of access levels");
865 ns.access = ACCESS_PROTECTED;
867 ns.access = ACCESS_PACKAGEINTERNAL;
871 static slotinfo_t* find_class(const char*name);
873 memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
875 /* FIXME- we need to loop through namespaces here */
876 return registry_findmember(cls, "", name, recurse);
879 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
884 index = new_variable("this", 0, 0, 0);
885 else if(!m->is_global)
886 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
888 index = new_variable("globalscope", 0, 0, 0);
891 parserassert(!index);
895 /* as variables and slots share the same number, make sure
896 that those variable indices are reserved. It's up to the
897 optimizer to later shuffle the variables down to lower
899 m->variable_count = m->uses_slots;
904 for(p=params->list;p;p=p->next) {
905 new_variable(p->param->name, p->param->type, 0, 1);
910 m->scope_code = add_scope_code(m->scope_code, m);
914 methodstate_list_t*l = m->innerfunctions;
916 methodstate_t*m = l->methodstate;
918 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
919 m->var_index = v->index;
920 m->slot_index = v->index;
921 v->is_inner_method = m;
926 if(as3_pass==2 && m->slots) {
927 /* exchange unresolved identifiers with the actual objects */
928 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
929 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
930 v->type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
931 if(!v->type || v->type->kind != INFOTYPE_CLASS) {
932 syntaxerror("Couldn't find class %s", v->type->name);
940 char*as3_globalclass=0;
941 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
944 syntaxerror("inner classes now allowed");
949 classinfo_list_t*mlist=0;
951 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
952 syntaxerror("invalid modifier(s)");
954 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
955 syntaxerror("public and internal not supported at the same time.");
957 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
958 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
959 // all classes extend object
960 extends = registry_getobjectclass();
963 /* create the class name, together with the proper attributes */
967 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
968 access = ACCESS_PRIVATE; package = internal_filename_package;
969 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
970 access = ACCESS_PACKAGEINTERNAL; package = state->package;
971 } else if(state->package!=internal_filename_package) {
972 access = ACCESS_PACKAGE; package = state->package;
974 syntaxerror("public classes only allowed inside a package");
978 state->cls = rfx_calloc(sizeof(classstate_t));
979 state->cls->init = rfx_calloc(sizeof(methodstate_t));
980 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
981 /* notice: we make no effort to initialize the top variable (local0) here,
982 even though it has special meaning. We just rely on the facat
983 that pass 1 won't do anything with variables */
985 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
987 /* set current method to constructor- all code within the class-level (except
988 static variable initializations) will be executed during construction time */
989 state->method = state->cls->init;
991 if(registry_find(package, classname)) {
992 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
994 /* build info struct */
995 int num_interfaces = (list_length(implements));
996 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
997 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
1000 classinfo_list_t*l = implements;
1001 for(l=implements;l;l=l->next) {
1002 state->cls->info->interfaces[pos++] = l->classinfo;
1007 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1009 state->method = state->cls->init;
1010 parserassert(state->cls && state->cls->info);
1012 function_initvars(state->cls->init, 0, 0, 1);
1013 function_initvars(state->cls->static_init, 0, 0, 0);
1015 if(extends && (extends->flags & FLAG_FINAL))
1016 syntaxerror("Can't extend final class '%s'", extends->name);
1019 while(state->cls->info->interfaces[pos]) {
1020 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1021 syntaxerror("'%s' is not an interface",
1022 state->cls->info->interfaces[pos]->name);
1026 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
1027 state->cls->info->superclass = extends;
1029 /* generate the abc code for this class */
1030 MULTINAME(classname2,state->cls->info);
1031 multiname_t*extends2 = sig2mname(extends);
1033 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1034 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1035 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1036 if(state->cls->info->flags&FLAG_INTERFACE) {
1037 abc_class_interface(state->cls->abc);
1040 abc_class_protectedNS(state->cls->abc, classname);
1042 for(mlist=implements;mlist;mlist=mlist->next) {
1043 MULTINAME(m, mlist->classinfo);
1044 abc_class_add_interface(state->cls->abc, &m);
1047 /* write the construction code for this class to the global init
1049 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1051 abc_method_body_t*m = global->init->method->body;
1052 __ getglobalscope(m);
1053 classinfo_t*s = extends;
1058 //TODO: take a look at the current scope stack, maybe
1059 // we can re-use something
1064 multiname_t*s2 = sig2mname(s);
1066 multiname_destroy(s2);
1068 __ pushscope(m); count++;
1069 m->code = m->code->prev->prev; // invert
1071 /* continue appending after last op end */
1072 while(m->code && m->code->next) m->code = m->code->next;
1074 /* TODO: if this is one of *our* classes, we can also
1075 do a getglobalscope/getslot <nr> (which references
1076 the init function's slots) */
1078 __ getlex2(m, extends2);
1080 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1081 stack is not the superclass */
1082 __ pushscope(m);count++;
1085 /* notice: we get a verify error #1107 if the top element on the scope
1086 stack is not the global object */
1088 __ pushscope(m);count++;
1090 __ newclass(m,state->cls->abc);
1094 __ setslot(m, slotindex);
1095 multiname_destroy(extends2);
1097 /* flash.display.MovieClip handling */
1099 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1100 if(state->package && state->package[0]) {
1101 as3_globalclass = concat3(state->package, ".", classname);
1103 as3_globalclass = strdup(classname);
1109 static int slotstate_varconst = 0;
1110 static modifiers_t*slotstate_flags = 0;
1111 static void setslotstate(modifiers_t* flags, int varconst)
1113 slotstate_varconst = varconst;
1114 slotstate_flags = flags;
1116 if(flags && flags->flags&FLAG_STATIC) {
1117 state->method = state->cls->static_init;
1119 state->method = state->cls->init;
1122 parserassert(state->method);
1126 static void endclass()
1129 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1131 c = abc_getlocal_0(c);
1132 c = abc_constructsuper(c, 0);
1133 state->cls->init->header = code_append(state->cls->init->header, c);
1134 state->cls->has_constructor=1;
1136 if(state->cls->init) {
1137 if(state->cls->info->flags&FLAG_INTERFACE) {
1138 if(state->cls->init->header)
1139 syntaxerror("interface can not have class-level code");
1141 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1142 code_t*c = method_header(state->cls->init);
1143 m->body->code = wrap_function(c, 0, m->body->code);
1146 if(state->cls->static_init) {
1147 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1148 code_t*c = method_header(state->cls->static_init);
1149 m->body->code = wrap_function(c, 0, m->body->code);
1156 void check_code_for_break(code_t*c)
1159 if(c->opcode == OPCODE___BREAK__) {
1160 char*name = string_cstr(c->data[0]);
1161 syntaxerror("Unresolved \"break %s\"", name);
1163 if(c->opcode == OPCODE___CONTINUE__) {
1164 char*name = string_cstr(c->data[0]);
1165 syntaxerror("Unresolved \"continue %s\"", name);
1167 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1168 char*name = string_cstr(c->data[0]);
1169 syntaxerror("Can't reference a package (%s) as such", name);
1176 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1179 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1180 if(TYPE_IS_NUMBER(t)) {
1181 xassert(c->type == CONSTANT_FLOAT
1182 || c->type == CONSTANT_INT
1183 || c->type == CONSTANT_UINT);
1184 } else if(TYPE_IS_UINT(t)) {
1185 xassert(c->type == CONSTANT_UINT ||
1186 (c->type == CONSTANT_INT && c->i>=0));
1187 } else if(TYPE_IS_INT(t)) {
1188 xassert(c->type == CONSTANT_INT);
1189 } else if(TYPE_IS_BOOLEAN(t)) {
1190 xassert(c->type == CONSTANT_TRUE
1191 || c->type == CONSTANT_FALSE);
1195 static void check_override(memberinfo_t*m, int flags)
1199 if(m->parent == state->cls->info)
1200 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1202 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1203 if(m->access==ACCESS_PRIVATE)
1205 if(m->flags & FLAG_FINAL)
1206 syntaxerror("can't override final member %s", m->name);
1208 /* allow this. it's no issue.
1209 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1210 syntaxerror("can't override static member %s", m->name);*/
1212 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1213 syntaxerror("can't override non-static member %s with static declaration", m->name);
1215 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1216 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1217 if(m->kind == INFOTYPE_METHOD)
1218 syntaxerror("can't override without explicit 'override' declaration");
1220 syntaxerror("can't override '%s'", m->name);
1225 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1227 methodinfo_t*minfo = 0;
1228 namespace_t ns = modifiers2access(mod);
1231 minfo = methodinfo_register_global(ns.access, state->package, name);
1232 minfo->return_type = 0; // save this for pass 2
1233 } else if(getset != KW_GET && getset != KW_SET) {
1235 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1237 printf("%s.%s | %s.%s\n",
1238 m->package, m->name,
1240 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1242 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1243 minfo->return_type = 0; // save this for pass 2
1244 // getslot on a member slot only returns "undefined", so no need
1245 // to actually store these
1246 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1248 //class getter/setter
1249 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1251 if(getset == KW_GET) {
1253 } else if(params->list && params->list->param && !params->list->next) {
1254 type = params->list->param->type;
1256 syntaxerror("setter function needs to take exactly one argument");
1257 // not sure wether to look into superclasses here, too
1258 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1260 if(minfo->kind!=INFOTYPE_SLOT)
1261 syntaxerror("class already contains a method called '%s'", name);
1262 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1263 syntaxerror("class already contains a field called '%s'", name);
1264 if(minfo->subtype & gs)
1265 syntaxerror("getter/setter for '%s' already defined", name);
1266 /* make a setter or getter into a getset */
1267 minfo->subtype |= gs;
1270 FIXME: this check needs to be done in pass 2
1272 if((!minfo->return_type != !type) ||
1273 (minfo->return_type && type &&
1274 !strcmp(minfo->return_type->name, type->name))) {
1275 syntaxerror("different type in getter and setter: %s and %s",
1276 minfo->return_type?minfo->return_type->name:"*",
1277 type?type->name:"*");
1280 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1281 minfo->kind = INFOTYPE_SLOT; //hack
1282 minfo->subtype = gs;
1283 minfo->return_type = 0;
1285 /* can't assign a slot as getter and setter might have different slots */
1286 //minfo->slot = slot;
1288 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1289 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1290 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1295 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1297 //parserassert(state->method && state->method->info);
1299 methodstate_t*parent_method = state->method;
1302 return_type = 0; // not valid in pass 1
1306 state->new_vars = 1;
1309 state->method = rfx_calloc(sizeof(methodstate_t));
1310 state->method->inner = 1;
1311 state->method->variable_count = 0;
1312 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1314 NEW(methodinfo_t,minfo);
1315 minfo->kind = INFOTYPE_METHOD;
1316 minfo->access = ACCESS_PACKAGEINTERNAL;
1318 state->method->info = minfo;
1321 list_append(parent_method->innerfunctions, state->method);
1323 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1325 function_initvars(state->method, params, 0, 1);
1329 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1330 state->method->variable_count = 0;
1331 parserassert(state->method);
1333 state->method->info->return_type = return_type;
1334 function_initvars(state->method, params, 0, 1);
1338 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1339 params_t*params, classinfo_t*return_type)
1341 if(state->method && state->method->info) {
1342 syntaxerror("not able to start another method scope");
1345 state->new_vars = 1;
1348 state->method = rfx_calloc(sizeof(methodstate_t));
1349 state->method->has_super = 0;
1352 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1354 state->method->is_global = 1;
1355 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1357 if(state->method->is_constructor)
1358 name = "__as3_constructor__";
1360 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1362 function_initvars(state->method, params, mod->flags, 1);
1364 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1368 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1369 state->method->variable_count = 0;
1370 parserassert(state->method);
1373 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1374 check_override(m, mod->flags);
1378 state->cls->has_constructor |= state->method->is_constructor;
1381 state->method->info->return_type = return_type;
1382 function_initvars(state->method, params, mod->flags, 1);
1386 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1387 params_t*params, classinfo_t*return_type, code_t*body)
1390 // store inner methods in variables
1391 function_initvars(state->method, 0, 0, 0);
1393 methodstate_list_t*ml = state->method->innerfunctions;
1395 dict_t*xvars = dict_new();
1398 methodstate_t*m = ml->methodstate;
1399 parserassert(m->inner);
1400 if(m->unresolved_variables) {
1401 dict_t*d = m->unresolved_variables;
1403 for(t=0;t<d->hashsize;t++) {
1404 dictentry_t*l = d->slots[t];
1406 /* check parent method's variables */
1408 if((v=find_variable(state, l->key))) {
1409 m->uses_parent_function = 1;
1410 state->method->uses_slots = 1;
1411 dict_put(xvars, l->key, 0);
1418 dict_destroy(m->unresolved_variables);
1419 m->unresolved_variables = 0;
1424 if(state->method->uses_slots) {
1425 state->method->slots = dict_new();
1427 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1428 if(!name) syntaxerror("internal error");
1429 if(v->index && dict_contains(xvars, name)) {
1432 if(v->is_inner_method) {
1433 v->is_inner_method->is_a_slot = 1;
1436 dict_put(state->method->slots, name, v);
1439 state->method->uses_slots = i;
1440 dict_destroy(state->vars);state->vars = 0;
1447 /*if(state->method->uses_parent_function){
1448 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1453 multiname_t*type2 = sig2mname(return_type);
1455 if(state->method->inner) {
1456 f = state->method->abc;
1457 abc_method_init(f, global->file, type2, 1);
1458 } else if(state->method->is_constructor) {
1459 f = abc_class_getconstructor(state->cls->abc, type2);
1460 } else if(!state->method->is_global) {
1461 namespace_t mname_ns = modifiers2access(mod);
1462 multiname_t mname = {QNAME, &mname_ns, 0, name};
1464 if(mod->flags&FLAG_STATIC)
1465 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1467 f = abc_class_method(state->cls->abc, type2, &mname);
1468 slot = f->trait->slot_id;
1470 namespace_t mname_ns = {state->method->info->access, state->package};
1471 multiname_t mname = {QNAME, &mname_ns, 0, name};
1473 f = abc_method_new(global->file, type2, 1);
1474 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1475 //abc_code_t*c = global->init->method->body->code;
1477 //flash doesn't seem to allow us to access function slots
1478 //state->method->info->slot = slot;
1480 if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1481 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1482 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1483 if(params->varargs) f->flags |= METHOD_NEED_REST;
1487 for(p=params->list;p;p=p->next) {
1488 if(params->varargs && !p->next) {
1489 break; //varargs: omit last parameter in function signature
1491 multiname_t*m = sig2mname(p->param->type);
1492 list_append(f->parameters, m);
1493 if(p->param->value) {
1494 check_constant_against_type(p->param->type, p->param->value);
1495 opt=1;list_append(f->optional_parameters, p->param->value);
1497 syntaxerror("non-optional parameter not allowed after optional parameters");
1500 if(state->method->slots) {
1501 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1503 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1504 multiname_t*type = sig2mname(v->type);
1505 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1506 t->slot_id = v->index;
1511 check_code_for_break(body);
1513 /* Seems this works now.
1514 if(state->method->exceptions && state->method->uses_slots) {
1515 as3_warning("try/catch and activation not supported yet within the same method");
1519 f->body->code = body;
1520 f->body->exceptions = state->method->exceptions;
1521 } else { //interface
1523 syntaxerror("interface methods can't have a method body");
1533 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1538 void breakjumpsto(code_t*c, char*name, code_t*jump)
1541 if(c->opcode == OPCODE___BREAK__) {
1542 string_t*name2 = c->data[0];
1543 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1544 c->opcode = OPCODE_JUMP;
1551 void continuejumpsto(code_t*c, char*name, code_t*jump)
1554 if(c->opcode == OPCODE___CONTINUE__) {
1555 string_t*name2 = c->data[0];
1556 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1557 c->opcode = OPCODE_JUMP;
1565 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1566 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1567 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1569 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1571 if(!type1 || !type2)
1572 return registry_getanytype();
1573 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1574 return registry_getanytype();
1577 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1586 return registry_getanytype();
1588 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1593 return abc_coerce_a(c);
1597 // cast an "any" type to a specific type. subject to
1598 // runtime exceptions
1599 return abc_coerce2(c, &m);
1602 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1603 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1604 // allow conversion between number types
1605 return abc_coerce2(c, &m);
1607 //printf("%s.%s\n", from.package, from.name);
1608 //printf("%s.%s\n", to.package, to.name);
1610 classinfo_t*supertype = from;
1612 if(supertype == to) {
1613 // target type is one of from's superclasses
1614 return abc_coerce2(c, &m);
1617 while(supertype->interfaces[t]) {
1618 if(supertype->interfaces[t]==to) {
1619 // target type is one of from's interfaces
1620 return abc_coerce2(c, &m);
1624 supertype = supertype->superclass;
1626 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1628 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1630 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1633 as3_error("can't convert type %s%s%s to %s%s%s",
1634 from->package, from->package?".":"", from->name,
1635 to->package, to->package?".":"", to->name);
1639 code_t*defaultvalue(code_t*c, classinfo_t*type)
1641 if(TYPE_IS_INT(type)) {
1642 c = abc_pushbyte(c, 0);
1643 } else if(TYPE_IS_UINT(type)) {
1644 c = abc_pushuint(c, 0);
1645 } else if(TYPE_IS_FLOAT(type)) {
1647 } else if(TYPE_IS_BOOLEAN(type)) {
1648 c = abc_pushfalse(c);
1650 //c = abc_pushundefined(c);
1652 c = abc_pushnull(c);
1654 c = abc_coerce2(c, &m);
1659 char is_pushundefined(code_t*c)
1661 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1664 static const char* get_package_from_name(const char*name)
1666 /* try explicit imports */
1667 dictentry_t* e = dict_get_slot(state->imports, name);
1669 if(!strcmp(e->key, name)) {
1670 slotinfo_t*c = (slotinfo_t*)e->data;
1671 if(c) return c->package;
1677 static namespace_list_t*get_current_imports()
1679 namespace_list_t*searchlist = 0;
1681 list_append(searchlist, namespace_new_package(state->package));
1683 import_list_t*l = state->wildcard_imports;
1685 namespace_t*ns = namespace_new_package(l->import->package);
1686 list_append(searchlist, ns);
1689 list_append(searchlist, namespace_new_package(""));
1690 list_append(searchlist, namespace_new_package(internal_filename_package));
1694 static slotinfo_t* find_class(const char*name)
1698 c = registry_find(state->package, name);
1701 /* try explicit imports */
1702 dictentry_t* e = dict_get_slot(state->imports, name);
1705 if(!strcmp(e->key, name)) {
1706 c = (slotinfo_t*)e->data;
1712 /* try package.* imports */
1713 import_list_t*l = state->wildcard_imports;
1715 //printf("does package %s contain a class %s?\n", l->import->package, name);
1716 c = registry_find(l->import->package, name);
1721 /* try global package */
1722 c = registry_find("", name);
1725 /* try local "filename" package */
1726 c = registry_find(internal_filename_package, name);
1731 typedcode_t push_class(slotinfo_t*a)
1736 if(a->access == ACCESS_PACKAGEINTERNAL &&
1737 strcmp(a->package, state->package) &&
1738 strcmp(a->package, internal_filename_package)
1740 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1741 infotypename(a), a->name, a->package, state->package);
1744 if(a->kind != INFOTYPE_CLASS) {
1746 x.c = abc_findpropstrict2(x.c, &m);
1747 x.c = abc_getproperty2(x.c, &m);
1748 if(a->kind == INFOTYPE_METHOD) {
1749 methodinfo_t*f = (methodinfo_t*)a;
1750 x.t = TYPE_FUNCTION(f);
1752 varinfo_t*v = (varinfo_t*)a;
1756 classinfo_t*c = (classinfo_t*)a;
1758 x.c = abc_getglobalscope(x.c);
1759 x.c = abc_getslot(x.c, c->slot);
1762 x.c = abc_getlex2(x.c, &m);
1764 x.t = TYPE_CLASS(c);
1769 static char is_getlocal(code_t*c)
1771 if(!c || c->prev || c->next)
1773 return(c->opcode == OPCODE_GETLOCAL
1774 || c->opcode == OPCODE_GETLOCAL_0
1775 || c->opcode == OPCODE_GETLOCAL_1
1776 || c->opcode == OPCODE_GETLOCAL_2
1777 || c->opcode == OPCODE_GETLOCAL_3);
1779 static int getlocalnr(code_t*c)
1781 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1782 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1783 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1784 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1785 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1786 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1790 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1794 [prefix code] [read instruction]
1798 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1800 if(in && in->opcode == OPCODE_COERCE_A) {
1801 in = code_cutlast(in);
1804 syntaxerror("internal error");
1806 /* chop off read instruction */
1810 prefix = r->prev;r->prev = 0;
1816 char use_temp_var = readbefore;
1818 /* generate the write instruction, and maybe append a dup to the prefix code */
1819 code_t* write = abc_nop(0);
1820 if(r->opcode == OPCODE_GETPROPERTY) {
1821 write->opcode = OPCODE_SETPROPERTY;
1822 multiname_t*m = (multiname_t*)r->data[0];
1823 write->data[0] = multiname_clone(m);
1824 if(m->type == QNAME || m->type == MULTINAME) {
1826 prefix = abc_dup(prefix); // we need the object, too
1829 } else if(m->type == MULTINAMEL) {
1831 /* dupping two values on the stack requires 5 operations and one register-
1832 couldn't adobe just have given us a dup2? */
1833 int temp = gettempvar();
1834 prefix = abc_setlocal(prefix, temp);
1835 prefix = abc_dup(prefix);
1836 prefix = abc_getlocal(prefix, temp);
1837 prefix = abc_swap(prefix);
1838 prefix = abc_getlocal(prefix, temp);
1840 prefix = abc_kill(prefix, temp);
1844 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1846 } else if(r->opcode == OPCODE_GETSLOT) {
1847 write->opcode = OPCODE_SETSLOT;
1848 write->data[0] = r->data[0];
1850 prefix = abc_dup(prefix); // we need the object, too
1853 } else if(r->opcode == OPCODE_GETLOCAL) {
1854 write->opcode = OPCODE_SETLOCAL;
1855 write->data[0] = r->data[0];
1856 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1857 write->opcode = OPCODE_SETLOCAL_0;
1858 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1859 write->opcode = OPCODE_SETLOCAL_1;
1860 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1861 write->opcode = OPCODE_SETLOCAL_2;
1862 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1863 write->opcode = OPCODE_SETLOCAL_3;
1864 } else if(r->opcode == OPCODE_GETSUPER) {
1865 write->opcode = OPCODE_SETSUPER;
1866 multiname_t*m = (multiname_t*)r->data[0];
1867 write->data[0] = multiname_clone(m);
1870 syntaxerror("illegal lvalue: can't assign a value to this expression");
1877 /* with getproperty/getslot, we have to be extra careful not
1878 to execute the read code twice, as it might have side-effects
1879 (e.g. if the property is in fact a setter/getter combination)
1881 So read the value, modify it, and write it again,
1882 using prefix only once and making sure (by using a temporary
1883 register) that the return value is what we just wrote */
1884 temp = gettempvar();
1885 c = code_append(c, prefix);
1886 c = code_append(c, r);
1889 c = abc_setlocal(c, temp);
1891 c = code_append(c, middlepart);
1894 c = abc_setlocal(c, temp);
1896 c = code_append(c, write);
1897 c = abc_getlocal(c, temp);
1898 c = abc_kill(c, temp);
1900 /* if we're allowed to execute the read code twice *and*
1901 the middlepart doesn't modify the code, things are easier.
1903 code_t* r2 = code_dup(r);
1904 //c = code_append(c, prefix);
1905 parserassert(!prefix);
1906 c = code_append(c, r);
1907 c = code_append(c, middlepart);
1908 c = code_append(c, write);
1909 c = code_append(c, r2);
1912 /* even smaller version: overwrite the value without reading
1916 c = code_append(c, prefix);
1919 c = code_append(c, middlepart);
1920 c = code_append(c, write);
1921 c = code_append(c, r);
1924 temp = gettempvar();
1926 c = code_append(c, prefix);
1928 c = code_append(c, middlepart);
1930 c = abc_setlocal(c, temp);
1931 c = code_append(c, write);
1932 c = abc_getlocal(c, temp);
1933 c = abc_kill(c, temp);
1939 char is_break_or_jump(code_t*c)
1943 if(c->opcode == OPCODE_JUMP ||
1944 c->opcode == OPCODE___BREAK__ ||
1945 c->opcode == OPCODE___CONTINUE__ ||
1946 c->opcode == OPCODE_THROW ||
1947 c->opcode == OPCODE_RETURNVOID ||
1948 c->opcode == OPCODE_RETURNVALUE) {
1954 #define IS_FINALLY_TARGET(op) \
1955 ((op) == OPCODE___CONTINUE__ || \
1956 (op) == OPCODE___BREAK__ || \
1957 (op) == OPCODE_RETURNVOID || \
1958 (op) == OPCODE_RETURNVALUE || \
1959 (op) == OPCODE___RETHROW__)
1961 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1963 #define NEED_EXTRA_STACK_ARG
1964 code_t*finally_label = abc_nop(0);
1965 NEW(lookupswitch_t, l);
1971 code_t*prev = i->prev;
1972 if(IS_FINALLY_TARGET(i->opcode)) {
1975 if(i->opcode == OPCODE___RETHROW__ ||
1976 i->opcode == OPCODE_RETURNVALUE) {
1977 if(i->opcode == OPCODE___RETHROW__)
1978 i->opcode = OPCODE_THROW;
1980 p = abc_coerce_a(p);
1981 p = abc_setlocal(p, tempvar);
1983 p = abc_pushbyte(p, count++);
1984 p = abc_jump(p, finally_label);
1985 code_t*target = p = abc_label(p);
1986 #ifdef NEED_EXTRA_STACK_ARG
1990 p = abc_getlocal(p, tempvar);
1993 p->next = i;i->prev = p;
1994 list_append(l->targets, target);
2000 c = abc_pushbyte(c, -1);
2001 c = code_append(c, finally_label);
2002 c = code_append(c, finally);
2004 #ifdef NEED_EXTRA_STACK_ARG
2007 c = abc_lookupswitch(c, l);
2008 c = l->def = abc_label(c);
2009 #ifdef NEED_EXTRA_STACK_ARG
2016 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
2020 code_t*prev = i->prev;
2021 if(IS_FINALLY_TARGET(i->opcode)) {
2022 if(i->opcode == OPCODE___RETHROW__)
2023 i->opcode = OPCODE_THROW;
2024 code_t*end = code_dup(finally);
2025 code_t*start = code_start(end);
2026 if(prev) prev->next = start;
2033 return code_append(c, finally);
2036 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
2042 int num_insertion_points=0;
2044 if(IS_FINALLY_TARGET(i->opcode))
2045 num_insertion_points++;
2052 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
2057 int simple_version_cost = (1+num_insertion_points)*code_size;
2058 int lookup_version_cost = 4*num_insertion_points + 5;
2060 if(cantdup || simple_version_cost > lookup_version_cost) {
2061 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
2062 return insert_finally_lookup(c, finally, tempvar);
2064 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
2065 return insert_finally_simple(c, finally, tempvar);
2069 #define PASS1 }} if(as3_pass == 1) {{
2070 #define PASS1END }} if(as3_pass == 2) {{
2071 #define PASS2 }} if(as3_pass == 2) {{
2072 #define PASS12 }} {{
2073 #define PASS12END }} if(as3_pass == 2) {{
2079 /* ------------ code blocks / statements ---------------- */
2081 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2083 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2084 PROGRAM_CODE_LIST: PROGRAM_CODE
2085 | PROGRAM_CODE_LIST PROGRAM_CODE
2087 PROGRAM_CODE: PACKAGE_DECLARATION
2088 | INTERFACE_DECLARATION
2090 | FUNCTION_DECLARATION
2093 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
2096 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2097 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2098 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2100 INPACKAGE_CODE: INTERFACE_DECLARATION
2102 | FUNCTION_DECLARATION
2105 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
2108 MAYBECODE: CODE {$$=$1;}
2109 MAYBECODE: {$$=code_new();}
2111 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2112 CODE: CODEPIECE {$$=$1;}
2114 // code which may appear outside of methods
2115 CODE_STATEMENT: IMPORT
2117 CODE_STATEMENT: FOR_IN
2118 CODE_STATEMENT: WHILE
2119 CODE_STATEMENT: DO_WHILE
2120 CODE_STATEMENT: SWITCH
2122 CODE_STATEMENT: WITH
2124 CODE_STATEMENT: VOIDEXPRESSION
2125 CODE_STATEMENT: USE_NAMESPACE
2126 CODE_STATEMENT: NAMESPACE_DECLARATION
2127 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2128 CODE_STATEMENT: '{' '}' {$$=0;}
2130 // code which may appear in methods
2131 CODEPIECE: ';' {$$=0;}
2132 CODEPIECE: CODE_STATEMENT
2133 CODEPIECE: VARIABLE_DECLARATION
2138 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
2140 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2141 //CODEBLOCK : '{' '}' {$$=0;}
2142 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2143 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2145 /* ------------ package init code ------------------- */
2147 PACKAGE_INITCODE: CODE_STATEMENT {
2148 code_t**cc = &global->init->method->body->code;
2149 *cc = code_append(*cc, $1);
2152 /* ------------ conditional compilation ------------- */
2154 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
2156 /* ------------ variables --------------------------- */
2158 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2159 | {$$.c=abc_pushundefined(0);
2163 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2164 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2166 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2167 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2169 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2172 if(variable_exists($1))
2173 syntaxerror("Variable %s already defined", $1);
2175 new_variable($1, 0, 1, 0);
2178 if(!is_subtype_of($3.t, $2)) {
2179 syntaxerror("Can't convert %s to %s", $3.t->name,
2185 if(state->method->uses_slots) {
2186 variable_t* v = find_slot(state, $1);
2188 // this variable is stored in a slot
2196 index = new_variable($1, $2, 1, 0);
2199 $$ = slot?abc_getscopeobject(0, 1):0;
2202 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2203 $$ = code_append($$, $3.c);
2204 $$ = converttype($$, $3.t, $2);
2207 $$ = defaultvalue($$, $2);
2210 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2211 $$ = code_append($$, $3.c);
2212 $$ = abc_coerce_a($$);
2214 // don't do anything
2222 $$ = abc_setslot($$, index);
2224 $$ = abc_setlocal($$, index);
2228 /* ------------ control flow ------------------------- */
2230 MAYBEELSE: %prec below_else {$$ = code_new();}
2231 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2232 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2234 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2237 $$ = code_append($$, $4.c);
2238 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2240 $$ = code_append($$, $6);
2242 myjmp = $$ = abc_jump($$, 0);
2244 myif->branch = $$ = abc_nop($$);
2246 $$ = code_append($$, $7);
2247 myjmp->branch = $$ = abc_nop($$);
2253 FOR_INIT : {$$=code_new();}
2254 FOR_INIT : VARIABLE_DECLARATION
2255 FOR_INIT : VOIDEXPRESSION
2257 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2258 // (I don't see any easy way to revolve this conflict otherwise, as we
2259 // can't touch VAR_READ without upsetting the precedence about "return")
2260 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2261 PASS1 $$=$2;new_variable($2,0,1,0);
2262 PASS2 $$=$2;new_variable($2,$3,1,0);
2264 FOR_IN_INIT : T_IDENTIFIER {
2269 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2270 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2272 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2273 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2275 $$ = code_append($$, $2);
2276 code_t*loopstart = $$ = abc_label($$);
2277 $$ = code_append($$, $4.c);
2278 code_t*myif = $$ = abc_iffalse($$, 0);
2279 $$ = code_append($$, $8);
2280 code_t*cont = $$ = abc_nop($$);
2281 $$ = code_append($$, $6);
2282 $$ = abc_jump($$, loopstart);
2283 code_t*out = $$ = abc_nop($$);
2284 breakjumpsto($$, $1.name, out);
2285 continuejumpsto($$, $1.name, cont);
2292 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2293 variable_t*var = find_variable(state, $2);
2295 syntaxerror("variable %s not known in this scope", $2);
2298 char*tmp1name = concat2($2, "__tmp1__");
2299 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2300 char*tmp2name = concat2($2, "__array__");
2301 int array = new_variable(tmp1name, 0, 0, 0);
2304 $$ = code_append($$, $4.c);
2305 $$ = abc_coerce_a($$);
2306 $$ = abc_setlocal($$, array);
2307 $$ = abc_pushbyte($$, 0);
2308 $$ = abc_setlocal($$, it);
2310 code_t*loopstart = $$ = abc_label($$);
2312 $$ = abc_hasnext2($$, array, it);
2313 code_t*myif = $$ = abc_iffalse($$, 0);
2314 $$ = abc_getlocal($$, array);
2315 $$ = abc_getlocal($$, it);
2317 $$ = abc_nextname($$);
2319 $$ = abc_nextvalue($$);
2320 $$ = converttype($$, 0, var->type);
2321 $$ = abc_setlocal($$, var->index);
2323 $$ = code_append($$, $6);
2324 $$ = abc_jump($$, loopstart);
2326 code_t*out = $$ = abc_nop($$);
2327 breakjumpsto($$, $1.name, out);
2328 continuejumpsto($$, $1.name, loopstart);
2340 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2344 code_t*myjmp = $$ = abc_jump($$, 0);
2345 code_t*loopstart = $$ = abc_label($$);
2346 $$ = code_append($$, $6);
2347 code_t*cont = $$ = abc_nop($$);
2348 myjmp->branch = cont;
2349 $$ = code_append($$, $4.c);
2350 $$ = abc_iftrue($$, loopstart);
2351 code_t*out = $$ = abc_nop($$);
2352 breakjumpsto($$, $1, out);
2353 continuejumpsto($$, $1, cont);
2359 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2361 code_t*loopstart = $$ = abc_label($$);
2362 $$ = code_append($$, $3);
2363 code_t*cont = $$ = abc_nop($$);
2364 $$ = code_append($$, $6.c);
2365 $$ = abc_iftrue($$, loopstart);
2366 code_t*out = $$ = abc_nop($$);
2367 breakjumpsto($$, $1, out);
2368 continuejumpsto($$, $1, cont);
2374 BREAK : "break" %prec prec_none {
2375 $$ = abc___break__(0, "");
2377 BREAK : "break" T_IDENTIFIER {
2378 $$ = abc___break__(0, $2);
2380 CONTINUE : "continue" %prec prec_none {
2381 $$ = abc___continue__(0, "");
2383 CONTINUE : "continue" T_IDENTIFIER {
2384 $$ = abc___continue__(0, $2);
2387 MAYBE_CASE_LIST : {$$=0;}
2388 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2389 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2390 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2391 CASE_LIST: CASE {$$=$1;}
2392 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2394 CASE: "case" E ':' MAYBECODE {
2395 $$ = abc_getlocal(0, state->switch_var);
2396 $$ = code_append($$, $2.c);
2397 code_t*j = $$ = abc_ifne($$, 0);
2398 $$ = code_append($$, $4);
2399 if($$->opcode != OPCODE___BREAK__) {
2400 $$ = abc___fallthrough__($$, "");
2402 code_t*e = $$ = abc_nop($$);
2405 DEFAULT: "default" ':' MAYBECODE {
2408 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2410 $$ = abc_setlocal($$, state->switch_var);
2411 $$ = code_append($$, $7);
2413 code_t*out = $$ = abc_kill($$, state->switch_var);
2414 breakjumpsto($$, $1, out);
2416 code_t*c = $$,*lastblock=0;
2418 if(c->opcode == OPCODE_IFNE) {
2419 if(!c->next) syntaxerror("internal error in fallthrough handling");
2421 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2423 c->opcode = OPCODE_JUMP;
2424 c->branch = lastblock;
2426 /* fall through end of switch */
2427 c->opcode = OPCODE_NOP;
2437 /* ------------ try / catch /finally ---------------- */
2439 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2440 state->exception_name=$3;
2441 PASS1 new_variable($3, 0, 0, 0);
2442 PASS2 new_variable($3, $4, 0, 0);
2445 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2446 multiname_t name = {QNAME, &name_ns, 0, $3};
2448 NEW(abc_exception_t, e)
2449 e->exc_type = sig2mname($4);
2450 e->var_name = multiname_clone(&name);
2454 int i = find_variable_safe(state, $3)->index;
2455 e->target = c = abc_nop(0);
2456 c = abc_setlocal(c, i);
2457 c = code_append(c, code_dup(state->method->scope_code));
2458 c = code_append(c, $8);
2464 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2469 NEW(abc_exception_t, e)
2470 e->exc_type = 0; //all exceptions
2471 e->var_name = 0; //no name
2474 e->to = code_append(e->to, $4);
2480 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2481 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2482 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2483 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2487 list_append($$.l,$2);
2488 $$.finally = $2->to;$2->to=0;
2491 CATCH_FINALLY_LIST: FINALLY {
2495 list_append($$.l,$1);
2496 $$.finally = $1->to;$1->to=0;
2500 TRY : "try" '{' {PASS12 new_state();
2501 state->method->has_exceptions=1;
2502 state->method->late_binding=1;//for invariant scope_code
2503 } MAYBECODE '}' CATCH_FINALLY_LIST {
2504 code_t*out = abc_nop(0);
2506 code_t*start = abc_nop(0);
2507 $$ = code_append(start, $4);
2508 if(!is_break_or_jump($4)) {
2509 $$ = abc_jump($$, out);
2511 code_t*end = $$ = abc_nop($$);
2515 tmp = new_variable("__finally__", 0, 0, 0);
2517 abc_exception_list_t*l = $6.l;
2520 abc_exception_t*e = l->abc_exception;
2522 $$ = code_append($$, e->target);
2523 $$ = abc_jump($$, out);
2525 parserassert((ptroff_t)$6.finally);
2527 e->target = $$ = abc_nop($$);
2528 $$ = code_append($$, code_dup(state->method->scope_code));
2529 $$ = abc___rethrow__($$);
2537 $$ = code_append($$, out);
2539 $$ = insert_finally($$, $6.finally, tmp);
2541 list_concat(state->method->exceptions, $6.l);
2547 /* ------------ throw ------------------------------- */
2549 THROW : "throw" EXPRESSION {
2553 THROW : "throw" %prec prec_none {
2554 if(!state->exception_name)
2555 syntaxerror("re-throw only possible within a catch block");
2556 variable_t*v = find_variable(state, state->exception_name);
2558 $$=abc_getlocal($$, v->index);
2562 /* ------------ with -------------------------------- */
2564 WITH_HEAD : "with" '(' EXPRESSION ')' {
2566 if(state->method->has_exceptions) {
2567 int v = alloc_local();
2568 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2569 state->method->scope_code = abc_pushwith(state->method->scope_code);
2574 WITH : WITH_HEAD CODEBLOCK {
2575 /* remove getlocal;pushwith from scope code again */
2576 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2579 if(state->method->has_exceptions) {
2581 $$ = abc_setlocal($$, $1.number);
2583 $$ = abc_pushwith($$);
2584 $$ = code_append($$, $2);
2585 $$ = abc_popscope($$);
2589 /* ------------ packages and imports ---------------- */
2591 X_IDENTIFIER: T_IDENTIFIER
2592 | "package" {PASS12 $$="package";}
2593 | T_NAMESPACE {PASS12 $$=$1;}
2595 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2596 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2598 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2599 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2600 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2601 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2603 IMPORT : "import" PACKAGEANDCLASS {
2605 slotinfo_t*s = registry_find($2->package, $2->name);
2606 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2607 as3_schedule_class($2->package, $2->name);
2613 syntaxerror("Couldn't import class\n");
2614 state_has_imports();
2615 dict_put(state->imports, c->name, c);
2616 import_toplevel(c->package);
2619 IMPORT : "import" PACKAGE '.' '*' {
2621 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2622 as3_schedule_package($2);
2628 state_has_imports();
2629 list_append(state->wildcard_imports, i);
2630 import_toplevel(i->package);
2634 /* ------------ classes and interfaces (header) -------------- */
2636 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2637 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2638 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2639 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2641 $$.flags=$1.flags|$2.flags;
2642 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2643 $$.ns=$1.ns?$1.ns:$2.ns;
2646 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2647 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2648 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2649 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2650 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2651 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2652 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2653 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2654 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2655 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2659 EXTENDS : {PASS12 $$=0;}
2660 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2662 EXTENDS_LIST : {PASS12 $$=list_new();}
2663 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2665 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2666 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2668 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2669 EXTENDS IMPLEMENTS_LIST
2670 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2672 '}' {PASS12 endclass();$$=0;}
2674 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2676 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2677 startclass(&$1,$3,0,$4);}
2678 MAYBE_INTERFACE_BODY
2679 '}' {PASS12 endclass();$$=0;}
2681 /* ------------ classes and interfaces (body) -------------- */
2684 MAYBE_CLASS_BODY : CLASS_BODY
2685 CLASS_BODY : CLASS_BODY_ITEM
2686 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2687 CLASS_BODY_ITEM : ';'
2688 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2689 CLASS_BODY_ITEM : SLOT_DECLARATION
2690 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2692 CLASS_BODY_ITEM : CODE_STATEMENT {
2693 code_t*c = state->cls->static_init->header;
2694 c = code_append(c, $1);
2695 state->cls->static_init->header = c;
2698 MAYBE_INTERFACE_BODY :
2699 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2700 INTERFACE_BODY : IDECLARATION
2701 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2703 IDECLARATION : "var" T_IDENTIFIER {
2704 syntaxerror("variable declarations not allowed in interfaces");
2706 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2708 $1.flags |= FLAG_PUBLIC;
2709 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2710 syntaxerror("invalid method modifiers: interface methods always need to be public");
2712 startfunction(&$1,$3,$4,&$6,$8);
2713 endfunction(&$1,$3,$4,&$6,$8, 0);
2714 list_deep_free($6.list);
2717 /* ------------ classes and interfaces (body, slots ) ------- */
2719 VARCONST: "var" | "const"
2721 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2723 SLOT_LIST: ONE_SLOT {$$ = $1;}
2724 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2726 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2728 int flags = slotstate_flags->flags;
2729 namespace_t ns = modifiers2access(slotstate_flags);
2731 varinfo_t* info = 0;
2733 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2735 check_override(i, flags);
2737 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2739 slotinfo_t*i = registry_find(state->package, $1);
2741 syntaxerror("package %s already contains '%s'", state->package, $1);
2743 if(ns.name && ns.name[0]) {
2744 syntaxerror("namespaces not allowed on package-level variables");
2746 info = varinfo_register_global(ns.access, state->package, $1);
2750 info->flags = flags;
2753 multiname_t mname = {QNAME, &ns, 0, $1};
2755 trait_list_t**traits;
2759 ns.name = state->package;
2760 traits = &global->init->traits;
2761 code = &global->init->method->body->code;
2762 } else if(flags&FLAG_STATIC) {
2764 traits = &state->cls->abc->static_traits;
2765 code = &state->cls->static_init->header;
2767 // instance variable
2768 traits = &state->cls->abc->traits;
2769 code = &state->cls->init->header;
2775 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2777 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2779 info->slot = t->slot_id;
2781 /* initalization code (if needed) */
2783 if($3.c && !is_pushundefined($3.c)) {
2784 c = abc_getlocal_0(c);
2785 c = code_append(c, $3.c);
2786 c = converttype(c, $3.t, $2);
2787 c = abc_setslot(c, t->slot_id);
2790 *code = code_append(*code, c);
2792 if(slotstate_varconst==KW_CONST) {
2793 t->kind= TRAIT_CONST;
2799 /* ------------ constants -------------------------------------- */
2801 MAYBESTATICCONSTANT: {$$=0;}
2802 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2804 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2805 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2806 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2807 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2808 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2809 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2810 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2811 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2812 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2813 STATICCONSTANT : T_IDENTIFIER {
2814 if(!strcmp($1, "NaN")) {
2815 $$ = constant_new_float(__builtin_nan(""));
2817 as3_warning("Couldn't evaluate constant value of %s", $1);
2818 $$ = constant_new_null($1);
2822 /* ------------ classes and interfaces (body, functions) ------- */
2824 // non-vararg version
2827 memset(&$$,0,sizeof($$));
2829 MAYBE_PARAM_LIST: PARAM_LIST {
2835 MAYBE_PARAM_LIST: "..." PARAM {
2837 memset(&$$,0,sizeof($$));
2839 list_append($$.list, $2);
2841 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2845 list_append($$.list, $4);
2849 PARAM_LIST: PARAM_LIST ',' PARAM {
2852 list_append($$.list, $3);
2856 memset(&$$,0,sizeof($$));
2857 list_append($$.list, $1);
2860 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2862 $$ = rfx_calloc(sizeof(param_t));
2868 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2870 $$ = rfx_calloc(sizeof(param_t));
2872 $$->type = TYPE_ANY;
2880 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2881 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2884 endfunction(&$1,$3,$4,&$6,0,0);
2886 if(!state->method->info) syntaxerror("internal error");
2888 code_t*c = method_header(state->method);
2889 c = wrap_function(c, 0, $11);
2891 endfunction(&$1,$3,$4,&$6,$8,c);
2893 list_deep_free($6.list);
2897 MAYBE_IDENTIFIER: T_IDENTIFIER
2898 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2899 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2900 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2903 endfunction(0,0,$2,&$4,0,0);
2905 methodinfo_t*f = state->method->info;
2906 if(!f || !f->kind) syntaxerror("internal error");
2908 code_t*c = method_header(state->method);
2909 c = wrap_function(c, 0, $9);
2911 int index = state->method->var_index;
2912 endfunction(0,0,$2,&$4,$6,c);
2914 $$.c = abc_getlocal(0, index);
2915 $$.t = TYPE_FUNCTION(f);
2917 PASS12 list_deep_free($4.list);
2921 /* ------------- package + class ids --------------- */
2923 CLASS: X_IDENTIFIER {
2924 PASS1 NEW(unresolvedinfo_t,c);
2925 memset(c, 0, sizeof(*c));
2926 c->kind = INFOTYPE_UNRESOLVED;
2928 c->package = get_package_from_name($1);
2930 c->nsset = get_current_imports();
2931 /* make the compiler look for this class in the current directory,
2933 as3_schedule_class_noerror(state->package, $1);
2935 $$ = (classinfo_t*)c;
2937 slotinfo_t*s = find_class($1);
2938 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2939 $$ = (classinfo_t*)s;
2942 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
2943 PASS1 NEW(unresolvedinfo_t,c);
2944 memset(c, 0, sizeof(*c));
2945 c->kind = INFOTYPE_UNRESOLVED;
2948 $$ = (classinfo_t*)c;
2950 slotinfo_t*s = registry_find($1, $3);
2951 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2953 $$ = (classinfo_t*)s;
2956 CLASS_SPEC: PACKAGEANDCLASS
2959 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2960 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2962 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2963 | '*' {PASS12 $$=registry_getanytype();}
2964 | "void" {PASS12 $$=registry_getanytype();}
2966 | "String" {$$=registry_getstringclass();}
2967 | "int" {$$=registry_getintclass();}
2968 | "uint" {$$=registry_getuintclass();}
2969 | "Boolean" {$$=registry_getbooleanclass();}
2970 | "Number" {$$=registry_getnumberclass();}
2973 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2974 MAYBETYPE: {PASS12 $$=0;}
2976 /* ----------function calls, delete, constructor calls ------ */
2978 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
2979 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2981 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
2982 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2983 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2985 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
2989 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2990 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2991 $$.number= $1.number+1;
2992 $$.cc = code_append($1.cc, $2.c);
2996 NEW : "new" E XX MAYBE_PARAM_VALUES {
2998 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
3000 code_t*paramcode = $4.cc;
3001 if($$.c->opcode == OPCODE_GETPROPERTY) {
3002 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3003 $$.c = code_cutlast($$.c);
3004 $$.c = code_append($$.c, paramcode);
3005 $$.c = abc_constructprop2($$.c, name, $4.number);
3006 multiname_destroy(name);
3007 } else if($$.c->opcode == OPCODE_GETSLOT) {
3008 int slot = (int)(ptroff_t)$$.c->data[0];
3009 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3010 multiname_t*name = t->name;
3011 $$.c = code_cutlast($$.c);
3012 $$.c = code_append($$.c, paramcode);
3013 $$.c = abc_constructprop2($$.c, name, $4.number);
3015 $$.c = code_append($$.c, paramcode);
3016 $$.c = abc_construct($$.c, $4.number);
3020 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
3023 $$.c = abc_coerce_a($$.c);
3028 /* TODO: use abc_call (for calling local variables),
3029 abc_callstatic (for calling own methods)
3032 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3035 if($$.c->opcode == OPCODE_COERCE_A) {
3036 $$.c = code_cutlast($$.c);
3038 code_t*paramcode = $3.cc;
3041 if($$.c->opcode == OPCODE_GETPROPERTY) {
3042 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3043 $$.c = code_cutlast($$.c);
3044 $$.c = code_append($$.c, paramcode);
3045 $$.c = abc_callproperty2($$.c, name, $3.number);
3046 multiname_destroy(name);
3047 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3048 int slot = (int)(ptroff_t)$$.c->data[0];
3049 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3050 if(t->kind!=TRAIT_METHOD) {
3051 //ok: flash allows to assign closures to members.
3053 multiname_t*name = t->name;
3054 $$.c = code_cutlast($$.c);
3055 $$.c = code_append($$.c, paramcode);
3056 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3057 $$.c = abc_callproperty2($$.c, name, $3.number);
3058 } else if($$.c->opcode == OPCODE_GETSUPER) {
3059 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3060 $$.c = code_cutlast($$.c);
3061 $$.c = code_append($$.c, paramcode);
3062 $$.c = abc_callsuper2($$.c, name, $3.number);
3063 multiname_destroy(name);
3065 $$.c = abc_getglobalscope($$.c);
3066 $$.c = code_append($$.c, paramcode);
3067 $$.c = abc_call($$.c, $3.number);
3070 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
3071 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
3073 $$.c = abc_coerce_a($$.c);
3078 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3079 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3080 if(!state->method) syntaxerror("super() not allowed outside of a function");
3081 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3084 $$.c = abc_getlocal_0($$.c);
3086 $$.c = code_append($$.c, $3.cc);
3088 this is dependent on the control path, check this somewhere else
3089 if(state->method->has_super)
3090 syntaxerror("constructor may call super() only once");
3092 state->method->has_super = 1;
3094 $$.c = abc_constructsuper($$.c, $3.number);
3095 $$.c = abc_pushundefined($$.c);
3099 DELETE: "delete" E {
3101 if($$.c->opcode == OPCODE_COERCE_A) {
3102 $$.c = code_cutlast($$.c);
3104 multiname_t*name = 0;
3105 if($$.c->opcode == OPCODE_GETPROPERTY) {
3106 $$.c->opcode = OPCODE_DELETEPROPERTY;
3107 } else if($$.c->opcode == OPCODE_GETSLOT) {
3108 int slot = (int)(ptroff_t)$$.c->data[0];
3109 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3110 $$.c = code_cutlast($$.c);
3111 $$.c = abc_deleteproperty2($$.c, name);
3113 $$.c = abc_getlocal_0($$.c);
3114 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3115 $$.c = abc_deleteproperty2($$.c, &m);
3117 $$.t = TYPE_BOOLEAN;
3120 RETURN: "return" %prec prec_none {
3121 $$ = abc_returnvoid(0);
3123 RETURN: "return" EXPRESSION {
3125 $$ = abc_returnvalue($$);
3128 // ----------------------- expression types -------------------------------------
3130 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3131 EXPRESSION : E %prec below_minus {$$ = $1;}
3132 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3134 $$.c = cut_last_push($$.c);
3135 $$.c = code_append($$.c,$3.c);
3138 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3139 $$=cut_last_push($1.c);
3142 // ----------------------- expression evaluation -------------------------------------
3144 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3145 //V : CONSTANT {$$ = 0;}
3147 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
3148 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3149 //V : NEW {$$ = $1.c;}
3151 //V : DELETE {$$ = $1.c;}
3152 E : DELETE {$$ = $1;}
3158 namespace_t ns = {ACCESS_PACKAGE, ""};
3159 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3161 $$.c = abc_getlex2($$.c, &m);
3162 $$.c = abc_pushstring($$.c, $1.pattern);
3163 $$.c = abc_construct($$.c, 1);
3165 $$.c = abc_getlex2($$.c, &m);
3166 $$.c = abc_pushstring($$.c, $1.pattern);
3167 $$.c = abc_pushstring($$.c, $1.options);
3168 $$.c = abc_construct($$.c, 2);
3173 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3174 //MULTINAME(m, registry_getintclass());
3175 //$$.c = abc_coerce2($$.c, &m); // FIXME
3178 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3181 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3184 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3187 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3190 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3193 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3196 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3197 $$.t = TYPE_BOOLEAN;
3199 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3200 $$.t = TYPE_BOOLEAN;
3202 CONSTANT : "null" {$$.c = abc_pushnull(0);
3206 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3207 $$.t = TYPE_BOOLEAN;
3209 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3210 $$.t = TYPE_BOOLEAN;
3212 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3213 $$.t = TYPE_BOOLEAN;
3215 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3216 $$.t = TYPE_BOOLEAN;
3218 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3219 $$.t = TYPE_BOOLEAN;
3221 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3222 $$.t = TYPE_BOOLEAN;
3224 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3225 $$.t = TYPE_BOOLEAN;
3227 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3228 $$.t = TYPE_BOOLEAN;
3231 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3233 $$.c = converttype($$.c, $1.t, $$.t);
3234 $$.c = abc_dup($$.c);
3235 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3236 $$.c = cut_last_push($$.c);
3237 $$.c = code_append($$.c,$3.c);
3238 $$.c = converttype($$.c, $3.t, $$.t);
3239 code_t*label = $$.c = abc_label($$.c);
3240 jmp->branch = label;
3243 $$.t = join_types($1.t, $3.t, 'A');
3244 /*printf("%08x:\n",$1.t);
3245 code_dump($1.c, 0, 0, "", stdout);
3246 printf("%08x:\n",$3.t);
3247 code_dump($3.c, 0, 0, "", stdout);
3248 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3250 $$.c = converttype($$.c, $1.t, $$.t);
3251 $$.c = abc_dup($$.c);
3252 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3253 $$.c = cut_last_push($$.c);
3254 $$.c = code_append($$.c,$3.c);
3255 $$.c = converttype($$.c, $3.t, $$.t);
3256 code_t*label = $$.c = abc_label($$.c);
3257 jmp->branch = label;
3260 E : '!' E {$$.c=$2.c;
3261 $$.c = abc_not($$.c);
3262 $$.t = TYPE_BOOLEAN;
3265 E : '~' E {$$.c=$2.c;
3266 $$.c = abc_bitnot($$.c);
3270 E : E '&' E {$$.c = code_append($1.c,$3.c);
3271 $$.c = abc_bitand($$.c);
3275 E : E '^' E {$$.c = code_append($1.c,$3.c);
3276 $$.c = abc_bitxor($$.c);
3280 E : E '|' E {$$.c = code_append($1.c,$3.c);
3281 $$.c = abc_bitor($$.c);
3285 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3286 $$.c = abc_rshift($$.c);
3289 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3290 $$.c = abc_urshift($$.c);
3293 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3294 $$.c = abc_lshift($$.c);
3298 E : E '/' E {$$.c = code_append($1.c,$3.c);
3299 $$.c = abc_divide($$.c);
3302 E : E '%' E {$$.c = code_append($1.c,$3.c);
3303 $$.c = abc_modulo($$.c);
3306 E : E '+' E {$$.c = code_append($1.c,$3.c);
3307 if(BOTH_INT($1.t, $3.t)) {
3308 $$.c = abc_add_i($$.c);
3311 $$.c = abc_add($$.c);
3312 $$.t = join_types($1.t,$3.t,'+');
3315 E : E '-' E {$$.c = code_append($1.c,$3.c);
3316 if(BOTH_INT($1.t,$3.t)) {
3317 $$.c = abc_subtract_i($$.c);
3320 $$.c = abc_subtract($$.c);
3324 E : E '*' E {$$.c = code_append($1.c,$3.c);
3325 if(BOTH_INT($1.t,$3.t)) {
3326 $$.c = abc_multiply_i($$.c);
3329 $$.c = abc_multiply($$.c);
3334 E : E "in" E {$$.c = code_append($1.c,$3.c);
3335 $$.c = abc_in($$.c);
3336 $$.t = TYPE_BOOLEAN;
3339 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3340 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3341 MULTINAME(m, (classinfo_t*)($3.t->data));
3342 $$.c = abc_astype2($1.c, &m);
3345 $$.c = code_append($1.c, $3.c);
3346 $$.c = abc_astypelate($$.c);
3351 E : E "instanceof" E
3352 {$$.c = code_append($1.c, $3.c);
3353 $$.c = abc_instanceof($$.c);
3354 $$.t = TYPE_BOOLEAN;
3357 E : E "is" E {$$.c = code_append($1.c, $3.c);
3358 $$.c = abc_istypelate($$.c);
3359 $$.t = TYPE_BOOLEAN;
3362 E : "typeof" '(' E ')' {
3364 $$.c = abc_typeof($$.c);
3369 $$.c = cut_last_push($2.c);
3370 $$.c = abc_pushundefined($$.c);
3374 E : "void" { $$.c = abc_pushundefined(0);
3378 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3383 $$.c=abc_negate_i($$.c);
3386 $$.c=abc_negate($$.c);
3393 $$.c = code_append($$.c, $3.c);
3395 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3396 $$.c = abc_getproperty2($$.c, &m);
3397 $$.t = 0; // array elements have unknown type
3400 E : '[' MAYBE_EXPRESSION_LIST ']' {
3402 $$.c = code_append($$.c, $2.cc);
3403 $$.c = abc_newarray($$.c, $2.number);
3404 $$.t = registry_getarrayclass();
3407 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3408 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3410 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3412 $$.cc = code_append($$.cc, $1.c);
3413 $$.cc = code_append($$.cc, $3.c);
3416 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3418 $$.number = $1.number+2;
3419 $$.cc = code_append($$.cc, $3.c);
3420 $$.cc = code_append($$.cc, $5.c);
3425 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3427 $$.c = code_append($$.c, $2.cc);
3428 $$.c = abc_newobject($$.c, $2.number/2);
3429 $$.t = registry_getobjectclass();
3434 if(BOTH_INT($1.t,$3.t)) {
3435 c=abc_multiply_i(c);
3439 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3440 $$.c = toreadwrite($1.c, c, 0, 0);
3445 code_t*c = abc_modulo($3.c);
3446 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3447 $$.c = toreadwrite($1.c, c, 0, 0);
3451 code_t*c = abc_lshift($3.c);
3452 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3453 $$.c = toreadwrite($1.c, c, 0, 0);
3457 code_t*c = abc_rshift($3.c);
3458 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3459 $$.c = toreadwrite($1.c, c, 0, 0);
3463 code_t*c = abc_urshift($3.c);
3464 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3465 $$.c = toreadwrite($1.c, c, 0, 0);
3469 code_t*c = abc_divide($3.c);
3470 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3471 $$.c = toreadwrite($1.c, c, 0, 0);
3475 code_t*c = abc_bitor($3.c);
3476 c=converttype(c, TYPE_INT, $1.t);
3477 $$.c = toreadwrite($1.c, c, 0, 0);
3481 code_t*c = abc_bitxor($3.c);
3482 c=converttype(c, TYPE_INT, $1.t);
3483 $$.c = toreadwrite($1.c, c, 0, 0);
3489 if(TYPE_IS_INT($1.t)) {
3493 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3496 $$.c = toreadwrite($1.c, c, 0, 0);
3499 E : E "-=" E { code_t*c = $3.c;
3500 if(TYPE_IS_INT($1.t)) {
3501 c=abc_subtract_i(c);
3504 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3507 $$.c = toreadwrite($1.c, c, 0, 0);
3510 E : E '=' E { code_t*c = 0;
3511 c = code_append(c, $3.c);
3512 c = converttype(c, $3.t, $1.t);
3513 $$.c = toreadwrite($1.c, c, 1, 0);
3517 E : E '?' E ':' E %prec below_assignment {
3518 $$.t = join_types($3.t,$5.t,'?');
3520 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3521 $$.c = code_append($$.c, $3.c);
3522 $$.c = converttype($$.c, $3.t, $$.t);
3523 code_t*j2 = $$.c = abc_jump($$.c, 0);
3524 $$.c = j1->branch = abc_label($$.c);
3525 $$.c = code_append($$.c, $5.c);
3526 $$.c = converttype($$.c, $5.t, $$.t);
3527 $$.c = j2->branch = abc_label($$.c);
3530 E : E "++" { code_t*c = 0;
3531 classinfo_t*type = $1.t;
3532 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3533 int nr = getlocalnr($1.c);
3534 code_free($1.c);$1.c=0;
3535 if(TYPE_IS_INT($1.t)) {
3536 $$.c = abc_getlocal(0, nr);
3537 $$.c = abc_inclocal_i($$.c, nr);
3538 } else if(TYPE_IS_NUMBER($1.t)) {
3539 $$.c = abc_getlocal(0, nr);
3540 $$.c = abc_inclocal($$.c, nr);
3541 } else syntaxerror("internal error");
3543 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3544 c=abc_increment_i(c);
3550 c=converttype(c, type, $1.t);
3551 $$.c = toreadwrite($1.c, c, 0, 1);
3556 // TODO: use inclocal, like with ++
3557 E : E "--" { code_t*c = 0;
3558 classinfo_t*type = $1.t;
3559 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3560 c=abc_decrement_i(c);
3566 c=converttype(c, type, $1.t);
3567 $$.c = toreadwrite($1.c, c, 0, 1);
3571 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3572 classinfo_t*type = $2.t;
3573 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3574 c=abc_increment_i(c);
3580 c=converttype(c, type, $2.t);
3581 $$.c = toreadwrite($2.c, c, 0, 0);
3585 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3586 classinfo_t*type = $2.t;
3587 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3588 c=abc_decrement_i(c);
3594 c=converttype(c, type, $2.t);
3595 $$.c = toreadwrite($2.c, c, 0, 0);
3599 E : "super" '.' T_IDENTIFIER
3600 { if(!state->cls->info)
3601 syntaxerror("super keyword not allowed outside a class");
3602 classinfo_t*t = state->cls->info->superclass;
3603 if(!t) t = TYPE_OBJECT;
3605 memberinfo_t*f = findmember_nsset(t, $3, 1);
3607 MEMBER_MULTINAME(m, f, $3);
3609 $$.c = abc_getlocal_0($$.c);
3610 $$.c = abc_getsuper2($$.c, &m);
3611 $$.t = slotinfo_gettype((slotinfo_t*)f);
3614 E : '@' T_IDENTIFIER {
3616 $$.c = abc_pushundefined(0);
3618 as3_warning("ignored @ operator");
3621 E : E '.' '@' T_IDENTIFIER {
3622 // child attribute TODO
3623 $$.c = abc_pushundefined(0);
3625 as3_warning("ignored .@ operator");
3628 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3629 // namespace declaration TODO
3630 $$.c = abc_pushundefined(0);
3632 as3_warning("ignored :: operator");
3635 E : E ".." T_IDENTIFIER {
3637 $$.c = abc_pushundefined(0);
3639 as3_warning("ignored .. operator");
3642 E : E '.' '(' E ')' {
3644 $$.c = abc_pushundefined(0);
3646 as3_warning("ignored .() operator");
3649 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3653 E : E '.' T_IDENTIFIER {
3655 classinfo_t*t = $1.t;
3657 if(TYPE_IS_CLASS(t) && t->data) {
3662 if(t->subtype==INFOTYPE_UNRESOLVED) {
3663 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3665 memberinfo_t*f = findmember_nsset(t, $3, 1);
3667 if(f && !is_static != !(f->flags&FLAG_STATIC))
3669 if(f && f->slot && !noslot) {
3670 $$.c = abc_getslot($$.c, f->slot);
3672 MEMBER_MULTINAME(m, f, $3);
3673 $$.c = abc_getproperty2($$.c, &m);
3675 /* determine type */
3676 $$.t = slotinfo_gettype((slotinfo_t*)f);
3678 $$.c = abc_coerce_a($$.c);
3679 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3680 string_t*package = $1.c->data[0];
3681 char*package2 = concat3(package->str, ".", $3);
3682 if(dict_contains(state->import_toplevel_packages, package2)) {
3684 $$.c->data[0] = string_new4(package2);
3687 slotinfo_t*a = registry_find(package->str, $3);
3689 syntaxerror("couldn't resolve %s", package2);
3693 /* when resolving a property on an unknown type, we do know the
3694 name of the property (and don't seem to need the package), but
3695 we need to make avm2 try out all access modes */
3696 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3697 $$.c = abc_getproperty2($$.c, &m);
3698 $$.c = abc_coerce_a($$.c);
3699 $$.t = registry_getanytype();
3703 VAR_READ : T_IDENTIFIER {
3705 /* Queue unresolved identifiers for checking against the parent
3706 function's variables.
3707 We consider everything which is not a local variable "unresolved".
3708 This encompasses class names, members of the surrounding class
3709 etc. which is *correct* because local variables of the parent function
3712 if(state->method->inner && !find_variable(state, $1)) {
3713 unknown_variable($1);
3716 /* let the compiler know that it might check the current directory/package
3717 for this identifier- maybe there's a file $1.as defining $1. */
3718 as3_schedule_class_noerror(state->package, $1);
3727 /* look at variables */
3728 if((v = find_variable(state, $1))) {
3729 // $1 is a local variable
3730 $$.c = abc_getlocal($$.c, v->index);
3734 if((v = find_slot(state, $1))) {
3735 $$.c = abc_getscopeobject($$.c, 1);
3736 $$.c = abc_getslot($$.c, v->index);
3741 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3743 /* look at current class' members */
3744 if(state->cls && (f = findmember_nsset(state->cls->info, $1, 1)) &&
3745 (f->flags&FLAG_STATIC) >= i_am_static) {
3746 // $1 is a function in this class
3747 int var_is_static = (f->flags&FLAG_STATIC);
3749 if(f->kind == INFOTYPE_METHOD) {
3750 $$.t = TYPE_FUNCTION(f);
3754 if(var_is_static && !i_am_static) {
3755 /* access to a static member from a non-static location.
3756 do this via findpropstrict:
3757 there doesn't seem to be any non-lookup way to access
3758 static properties of a class */
3759 state->method->late_binding = 1;
3761 namespace_t ns = {f->access, ""};
3762 multiname_t m = {QNAME, &ns, 0, $1};
3763 $$.c = abc_findpropstrict2($$.c, &m);
3764 $$.c = abc_getproperty2($$.c, &m);
3766 } else if(f->slot>0) {
3767 $$.c = abc_getlocal_0($$.c);
3768 $$.c = abc_getslot($$.c, f->slot);
3771 namespace_t ns = {f->access, ""};
3772 multiname_t m = {QNAME, &ns, 0, $1};
3773 $$.c = abc_getlocal_0($$.c);
3774 $$.c = abc_getproperty2($$.c, &m);
3779 /* look at actual classes, in the current package and imported */
3780 if((a = find_class($1))) {
3785 /* look through package prefixes */
3786 if(dict_contains(state->import_toplevel_packages, $1)) {
3787 $$.c = abc___pushpackage__($$.c, $1);
3792 /* unknown object, let the avm2 resolve it */
3794 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3795 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3796 state->method->late_binding = 1;
3798 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3801 $$.c = abc_findpropstrict2($$.c, &m);
3802 $$.c = abc_getproperty2($$.c, &m);
3806 // ----------------- namespaces -------------------------------------------------
3808 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3810 NEW(namespace_decl_t,n);
3815 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3817 NEW(namespace_decl_t,n);
3822 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3824 NEW(namespace_decl_t,n);
3829 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3831 trie_put(active_namespaces, $2->name, (void*)$2->url);
3833 namespace_t access = modifiers2access(&$1);
3834 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3835 var->type = TYPE_NAMESPACE;
3837 ns.access = ACCESS_NAMESPACE;
3839 var->value = constant_new_namespace(&ns);
3844 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3847 trie_put(active_namespaces, $3->name, url);