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 namespace_list_t*active_namespace_urls;
360 char has_own_imports;
361 char new_vars; // e.g. transition between two functions
364 methodstate_t*method;
373 typedef struct _global {
377 dict_t*file2token2info;
380 static global_t*global = 0;
381 static state_t* state = 0;
385 #define MULTINAME(m,x) \
389 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
391 #define MEMBER_MULTINAME(m,f,n) \
395 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
396 m##_ns.name = ((slotinfo_t*)(f))->package; \
401 m.namespace_set = 0; \
402 m.name = ((slotinfo_t*)(f))->name; \
404 m.type = MULTINAME; \
406 m.namespace_set = &nopackage_namespace_set; \
410 /* warning: list length of namespace set is undefined */
411 #define MULTINAME_LATE(m, access, package) \
412 namespace_t m##_ns = {access, package}; \
413 namespace_set_t m##_nsset; \
414 namespace_list_t m##_l;m##_l.next = 0; \
415 m##_nsset.namespaces = &m##_l; \
416 m##_nsset = m##_nsset; \
417 m##_l.namespace = &m##_ns; \
418 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
420 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
421 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
422 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
423 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
424 static namespace_list_t nl4 = {&ns4,0};
425 static namespace_list_t nl3 = {&ns3,&nl4};
426 static namespace_list_t nl2 = {&ns2,&nl3};
427 static namespace_list_t nl1 = {&ns1,&nl2};
428 static namespace_set_t nopackage_namespace_set = {&nl1};
430 static void new_state()
433 state_t*oldstate = state;
435 memcpy(s, state, sizeof(state_t)); //shallow copy
437 s->imports = dict_new();
439 if(!s->import_toplevel_packages) {
440 s->import_toplevel_packages = dict_new();
444 state->has_own_imports = 0;
445 state->vars = dict_new();
446 state->old = oldstate;
449 trie_remember(active_namespaces);
452 state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
454 static void state_has_imports()
456 state->wildcard_imports = list_clone(state->wildcard_imports);
457 state->imports = dict_clone(state->imports);
458 state->has_own_imports = 1;
460 static void import_toplevel(const char*package)
462 char* s = strdup(package);
464 dict_put(state->import_toplevel_packages, s, 0);
465 char*x = strrchr(s, '.');
473 static void state_destroy(state_t*state)
475 if(state->has_own_imports) {
476 list_free(state->wildcard_imports);
477 dict_destroy(state->imports);state->imports=0;
479 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
480 dict_destroy(state->imports);state->imports=0;
484 for(t=0;t<state->vars->hashsize;t++) {
485 dictentry_t*e =state->vars->slots[t];
487 free(e->data);e->data=0;
491 dict_destroy(state->vars);state->vars=0;
494 list_free(state->active_namespace_urls)
495 state->active_namespace_urls = 0;
500 static void old_state()
502 trie_rollback(active_namespaces);
504 if(!state || !state->old)
505 syntaxerror("invalid nesting");
506 state_t*leaving = state;
510 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
511 free(leaving->method);
514 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
519 state_destroy(leaving);
522 static code_t* method_header(methodstate_t*m);
523 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
524 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
527 static char* internal_filename_package = 0;
528 void initialize_file(char*filename)
531 syntaxerror("invalid call to initialize_file during parsing of another file");
534 active_namespaces = trie_new();
537 state->package = internal_filename_package = strdup(filename);
539 global->token2info = dict_lookup(global->file2token2info,
540 current_filename // use long version
542 if(!global->token2info) {
543 global->token2info = dict_new2(&ptr_type);
544 dict_put(global->file2token2info, current_filename, global->token2info);
548 state->method = rfx_calloc(sizeof(methodstate_t));
549 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
550 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
552 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
553 function_initvars(state->method, 0, 0, 1);
554 global->init = abc_initscript(global->file);
560 if(!state || state->level!=1) {
561 syntaxerror("unexpected end of file in pass %d", as3_pass);
565 code_t*header = method_header(state->method);
566 code_t*c = wrap_function(header, 0, global->init->method->body->code);
567 global->init->method->body->code = c;
568 free(state->method);state->method=0;
571 //free(state->package);state->package=0; // used in registry
572 state_destroy(state);state=0;
575 void initialize_parser()
577 global = rfx_calloc(sizeof(global_t));
578 global->file = abc_file_new();
579 global->file->flags &= ~ABCFILE_LAZY;
580 global->file2token2info = dict_new();
581 global->token2info = 0;
584 void* finish_parser()
586 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
588 global->token2info=0;
594 static void xx_scopetest()
596 /* findpropstrict doesn't just return a scope object- it
597 also makes it "active" somehow. Push local_0 on the
598 scope stack and read it back with findpropstrict, it'll
599 contain properties like "trace". Trying to find the same
600 property on a "vanilla" local_0 yields only a "undefined" */
601 //c = abc_findpropstrict(c, "[package]::trace");
603 /*c = abc_getlocal_0(c);
604 c = abc_findpropstrict(c, "[package]::trace");
606 c = abc_setlocal_1(c);
608 c = abc_pushbyte(c, 0);
609 c = abc_setlocal_2(c);
611 code_t*xx = c = abc_label(c);
612 c = abc_findpropstrict(c, "[package]::trace");
613 c = abc_pushstring(c, "prop:");
614 c = abc_hasnext2(c, 1, 2);
616 c = abc_setlocal_3(c);
617 c = abc_callpropvoid(c, "[package]::trace", 2);
618 c = abc_getlocal_3(c);
620 c = abc_iftrue(c,xx);*/
623 typedef struct _variable {
627 methodstate_t*is_inner_method;
630 static variable_t* find_variable(state_t*s, char*name)
634 v = dict_lookup(s->vars, name);
636 if(s->new_vars) break;
641 static variable_t* find_slot(state_t*s, const char*name)
643 if(s->method && s->method->slots)
644 return dict_lookup(s->method->slots, name);
648 static variable_t* find_variable_safe(state_t*s, char*name)
650 variable_t* v = find_variable(s, name);
652 syntaxerror("undefined variable: %s", name);
655 static char variable_exists(char*name)
657 return dict_contains(state->vars, name);
659 code_t*defaultvalue(code_t*c, classinfo_t*type);
661 static int alloc_local()
663 return state->method->variable_count++;
666 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
669 variable_t*v = find_slot(state, name);
675 v->index = alloc_local();
680 dict_put(state->vars, name, v);
684 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
686 return new_variable2(name, type, init, maybeslot)->index;
689 #define TEMPVARNAME "__as3_temp__"
690 static int gettempvar()
692 variable_t*v = find_variable(state, TEMPVARNAME);
695 return new_variable(TEMPVARNAME, 0, 0, 0);
698 code_t* var_block(code_t*body)
704 for(t=0;t<state->vars->hashsize;t++) {
705 dictentry_t*e = state->vars->slots[t];
707 variable_t*v = (variable_t*)e->data;
708 if(v->type && v->init) {
709 c = defaultvalue(c, v->type);
710 c = abc_setlocal(c, v->index);
711 k = abc_kill(k, v->index);
721 if(x->opcode== OPCODE___BREAK__ ||
722 x->opcode== OPCODE___CONTINUE__) {
723 /* link kill code before break/continue */
724 code_t*e = code_dup(k);
725 code_t*s = code_start(e);
737 c = code_append(c, body);
738 c = code_append(c, k);
742 void unknown_variable(char*name)
744 if(!state->method->unresolved_variables)
745 state->method->unresolved_variables = dict_new();
746 if(!dict_contains(state->method->unresolved_variables, name))
747 dict_put(state->method->unresolved_variables, name, 0);
750 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
752 static void parsererror(const char*file, int line, const char*f)
754 syntaxerror("internal error in %s, %s:%d", f, file, line);
758 static code_t* add_scope_code(code_t*c, methodstate_t*m)
760 if(m->uses_slots || (m->late_binding && !m->inner)) {
761 c = abc_getlocal_0(c);
762 c = abc_pushscope(c);
765 /* FIXME: does this need to be the same activation object as
766 in the function header? */
767 c = abc_newactivation(c);
768 c = abc_pushscope(c);
773 static code_t* method_header(methodstate_t*m)
777 c = add_scope_code(c, m);
779 methodstate_list_t*l = m->innerfunctions;
781 parserassert(l->methodstate->abc);
782 if(m->uses_slots && l->methodstate->is_a_slot) {
783 c = abc_getscopeobject(c, 1);
784 c = abc_newfunction(c, l->methodstate->abc);
786 c = abc_setlocal(c, l->methodstate->var_index);
787 c = abc_setslot(c, l->methodstate->slot_index);
789 c = abc_newfunction(c, l->methodstate->abc);
790 c = abc_setlocal(c, l->methodstate->var_index);
792 free(l->methodstate);l->methodstate=0;
796 c = code_append(c, m->header);
799 if(m->is_constructor && !m->has_super) {
800 // call default constructor
801 c = abc_getlocal_0(c);
802 c = abc_constructsuper(c, 0);
804 list_free(m->innerfunctions);
805 m->innerfunctions = 0;
810 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
812 c = code_append(c, header);
813 c = code_append(c, var_block(body));
814 /* append return if necessary */
815 if(!c || (c->opcode != OPCODE_RETURNVOID &&
816 c->opcode != OPCODE_RETURNVALUE)) {
817 c = abc_returnvoid(c);
823 static void startpackage(char*name)
826 /*printf("entering package \"%s\"\n", name);*/
827 state->package = strdup(name);
829 static void endpackage()
831 /*printf("leaving package \"%s\"\n", state->package);*/
833 //used e.g. in classinfo_register:
834 //free(state->package);state->package=0;
839 #define FLAG_PUBLIC 256
840 #define FLAG_PROTECTED 512
841 #define FLAG_PRIVATE 1024
842 #define FLAG_PACKAGEINTERNAL 2048
843 #define FLAG_NAMESPACE 4096
845 static namespace_t modifiers2access(modifiers_t*mod)
850 if(mod->flags&FLAG_NAMESPACE) {
851 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
852 syntaxerror("invalid combination of access levels and namespaces");
853 ns.access = ACCESS_NAMESPACE;
855 const char*url = (const char*)trie_lookup(active_namespaces, mod->ns);
857 /* shouldn't happen- the tokenizer only reports something as a namespace
858 if it was already registered */
859 trie_dump(active_namespaces);
860 syntaxerror("unknown namespace: %s", mod->ns);
863 } else if(mod->flags&FLAG_PUBLIC) {
864 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
865 syntaxerror("invalid combination of access levels");
866 ns.access = ACCESS_PACKAGE;
867 } else if(mod->flags&FLAG_PRIVATE) {
868 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
869 syntaxerror("invalid combination of access levels");
870 ns.access = ACCESS_PRIVATE;
871 } else if(mod->flags&FLAG_PROTECTED) {
872 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
873 syntaxerror("invalid combination of access levels");
874 ns.access = ACCESS_PROTECTED;
876 ns.access = ACCESS_PACKAGEINTERNAL;
880 static slotinfo_t* find_class(const char*name);
882 static memberinfo_t* findmember_nsset(classinfo_t*cls, const char*name, char recurse)
884 return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
887 void add_active_url(const char*url)
891 list_append(state->active_namespace_urls, n);
894 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
899 index = new_variable("this", 0, 0, 0);
900 else if(!m->is_global)
901 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
903 index = new_variable("globalscope", 0, 0, 0);
906 parserassert(!index);
910 /* as variables and slots share the same number, make sure
911 that those variable indices are reserved. It's up to the
912 optimizer to later shuffle the variables down to lower
914 m->variable_count = m->uses_slots;
919 for(p=params->list;p;p=p->next) {
920 new_variable(p->param->name, p->param->type, 0, 1);
925 m->scope_code = add_scope_code(m->scope_code, m);
929 methodstate_list_t*l = m->innerfunctions;
931 methodstate_t*m = l->methodstate;
933 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
934 m->var_index = v->index;
935 m->slot_index = v->index;
936 v->is_inner_method = m;
941 if(as3_pass==2 && m->slots) {
942 /* exchange unresolved identifiers with the actual objects */
943 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
944 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
945 v->type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
946 if(!v->type || v->type->kind != INFOTYPE_CLASS) {
947 syntaxerror("Couldn't find class %s", v->type->name);
955 char*as3_globalclass=0;
956 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
959 syntaxerror("inner classes now allowed");
964 classinfo_list_t*mlist=0;
966 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
967 syntaxerror("invalid modifier(s)");
969 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
970 syntaxerror("public and internal not supported at the same time.");
972 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
973 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
974 // all classes extend object
975 extends = registry_getobjectclass();
978 /* create the class name, together with the proper attributes */
982 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
983 access = ACCESS_PRIVATE; package = internal_filename_package;
984 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
985 access = ACCESS_PACKAGEINTERNAL; package = state->package;
986 } else if(state->package!=internal_filename_package) {
987 access = ACCESS_PACKAGE; package = state->package;
989 syntaxerror("public classes only allowed inside a package");
993 state->cls = rfx_calloc(sizeof(classstate_t));
994 state->cls->init = rfx_calloc(sizeof(methodstate_t));
995 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
996 /* notice: we make no effort to initialize the top variable (local0) here,
997 even though it has special meaning. We just rely on the facat
998 that pass 1 won't do anything with variables */
1000 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
1002 /* set current method to constructor- all code within the class-level (except
1003 static variable initializations) will be executed during construction time */
1004 state->method = state->cls->init;
1006 if(registry_find(package, classname)) {
1007 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
1009 /* build info struct */
1010 int num_interfaces = (list_length(implements));
1011 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
1012 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
1015 classinfo_list_t*l = implements;
1016 for(l=implements;l;l=l->next) {
1017 state->cls->info->interfaces[pos++] = l->classinfo;
1022 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1024 state->method = state->cls->init;
1025 parserassert(state->cls && state->cls->info);
1027 function_initvars(state->cls->init, 0, 0, 1);
1028 function_initvars(state->cls->static_init, 0, 0, 0);
1030 if(extends && (extends->flags & FLAG_FINAL))
1031 syntaxerror("Can't extend final class '%s'", extends->name);
1034 while(state->cls->info->interfaces[pos]) {
1035 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1036 syntaxerror("'%s' is not an interface",
1037 state->cls->info->interfaces[pos]->name);
1041 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
1042 state->cls->info->superclass = extends;
1044 /* generate the abc code for this class */
1045 MULTINAME(classname2,state->cls->info);
1046 multiname_t*extends2 = sig2mname(extends);
1048 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1049 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1050 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1051 if(state->cls->info->flags&FLAG_INTERFACE) {
1052 abc_class_interface(state->cls->abc);
1055 abc_class_protectedNS(state->cls->abc, classname);
1057 for(mlist=implements;mlist;mlist=mlist->next) {
1058 MULTINAME(m, mlist->classinfo);
1059 abc_class_add_interface(state->cls->abc, &m);
1062 /* write the construction code for this class to the global init
1064 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1066 abc_method_body_t*m = global->init->method->body;
1067 __ getglobalscope(m);
1068 classinfo_t*s = extends;
1073 //TODO: take a look at the current scope stack, maybe
1074 // we can re-use something
1079 multiname_t*s2 = sig2mname(s);
1081 multiname_destroy(s2);
1083 __ pushscope(m); count++;
1084 m->code = m->code->prev->prev; // invert
1086 /* continue appending after last op end */
1087 while(m->code && m->code->next) m->code = m->code->next;
1089 /* TODO: if this is one of *our* classes, we can also
1090 do a getglobalscope/getslot <nr> (which references
1091 the init function's slots) */
1093 __ getlex2(m, extends2);
1095 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1096 stack is not the superclass */
1097 __ pushscope(m);count++;
1100 /* notice: we get a verify error #1107 if the top element on the scope
1101 stack is not the global object */
1103 __ pushscope(m);count++;
1105 __ newclass(m,state->cls->abc);
1109 __ setslot(m, slotindex);
1110 multiname_destroy(extends2);
1112 /* flash.display.MovieClip handling */
1114 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1115 if(state->package && state->package[0]) {
1116 as3_globalclass = concat3(state->package, ".", classname);
1118 as3_globalclass = strdup(classname);
1124 static int slotstate_varconst = 0;
1125 static modifiers_t*slotstate_flags = 0;
1126 static void setslotstate(modifiers_t* flags, int varconst)
1128 slotstate_varconst = varconst;
1129 slotstate_flags = flags;
1131 if(flags && flags->flags&FLAG_STATIC) {
1132 state->method = state->cls->static_init;
1134 state->method = state->cls->init;
1137 parserassert(state->method);
1141 static void endclass()
1144 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1146 c = abc_getlocal_0(c);
1147 c = abc_constructsuper(c, 0);
1148 state->cls->init->header = code_append(state->cls->init->header, c);
1149 state->cls->has_constructor=1;
1151 if(state->cls->init) {
1152 if(state->cls->info->flags&FLAG_INTERFACE) {
1153 if(state->cls->init->header)
1154 syntaxerror("interface can not have class-level code");
1156 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1157 code_t*c = method_header(state->cls->init);
1158 m->body->code = wrap_function(c, 0, m->body->code);
1161 if(state->cls->static_init) {
1162 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1163 code_t*c = method_header(state->cls->static_init);
1164 m->body->code = wrap_function(c, 0, m->body->code);
1171 void check_code_for_break(code_t*c)
1174 if(c->opcode == OPCODE___BREAK__) {
1175 char*name = string_cstr(c->data[0]);
1176 syntaxerror("Unresolved \"break %s\"", name);
1178 if(c->opcode == OPCODE___CONTINUE__) {
1179 char*name = string_cstr(c->data[0]);
1180 syntaxerror("Unresolved \"continue %s\"", name);
1182 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1183 char*name = string_cstr(c->data[0]);
1184 syntaxerror("Can't reference a package (%s) as such", name);
1191 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1194 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1195 if(TYPE_IS_NUMBER(t)) {
1196 xassert(c->type == CONSTANT_FLOAT
1197 || c->type == CONSTANT_INT
1198 || c->type == CONSTANT_UINT);
1199 } else if(TYPE_IS_UINT(t)) {
1200 xassert(c->type == CONSTANT_UINT ||
1201 (c->type == CONSTANT_INT && c->i>=0));
1202 } else if(TYPE_IS_INT(t)) {
1203 xassert(c->type == CONSTANT_INT);
1204 } else if(TYPE_IS_BOOLEAN(t)) {
1205 xassert(c->type == CONSTANT_TRUE
1206 || c->type == CONSTANT_FALSE);
1210 static void check_override(memberinfo_t*m, int flags)
1214 if(m->parent == state->cls->info)
1215 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1217 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1218 if(m->access==ACCESS_PRIVATE)
1220 if(m->flags & FLAG_FINAL)
1221 syntaxerror("can't override final member %s", m->name);
1223 /* allow this. it's no issue.
1224 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1225 syntaxerror("can't override static member %s", m->name);*/
1227 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1228 syntaxerror("can't override non-static member %s with static declaration", m->name);
1230 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1231 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1232 if(m->kind == INFOTYPE_METHOD)
1233 syntaxerror("can't override without explicit 'override' declaration");
1235 syntaxerror("can't override '%s'", m->name);
1240 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1242 methodinfo_t*minfo = 0;
1243 namespace_t ns = modifiers2access(mod);
1246 minfo = methodinfo_register_global(ns.access, state->package, name);
1247 minfo->return_type = 0; // save this for pass 2
1248 } else if(getset != KW_GET && getset != KW_SET) {
1250 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1252 printf("%s.%s | %s.%s\n",
1253 m->package, m->name,
1255 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1257 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1258 minfo->return_type = 0; // save this for pass 2
1259 // getslot on a member slot only returns "undefined", so no need
1260 // to actually store these
1261 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1263 //class getter/setter
1264 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1266 if(getset == KW_GET) {
1268 } else if(params->list && params->list->param && !params->list->next) {
1269 type = params->list->param->type;
1271 syntaxerror("setter function needs to take exactly one argument");
1272 // not sure wether to look into superclasses here, too
1273 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1275 if(minfo->kind!=INFOTYPE_SLOT)
1276 syntaxerror("class already contains a method called '%s'", name);
1277 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1278 syntaxerror("class already contains a field called '%s'", name);
1279 if(minfo->subtype & gs)
1280 syntaxerror("getter/setter for '%s' already defined", name);
1281 /* make a setter or getter into a getset */
1282 minfo->subtype |= gs;
1285 FIXME: this check needs to be done in pass 2
1287 if((!minfo->return_type != !type) ||
1288 (minfo->return_type && type &&
1289 !strcmp(minfo->return_type->name, type->name))) {
1290 syntaxerror("different type in getter and setter: %s and %s",
1291 minfo->return_type?minfo->return_type->name:"*",
1292 type?type->name:"*");
1295 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1296 minfo->kind = INFOTYPE_SLOT; //hack
1297 minfo->subtype = gs;
1298 minfo->return_type = 0;
1300 /* can't assign a slot as getter and setter might have different slots */
1301 //minfo->slot = slot;
1303 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1304 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1305 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1310 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1312 //parserassert(state->method && state->method->info);
1314 methodstate_t*parent_method = state->method;
1317 return_type = 0; // not valid in pass 1
1321 state->new_vars = 1;
1324 state->method = rfx_calloc(sizeof(methodstate_t));
1325 state->method->inner = 1;
1326 state->method->variable_count = 0;
1327 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1329 NEW(methodinfo_t,minfo);
1330 minfo->kind = INFOTYPE_METHOD;
1331 minfo->access = ACCESS_PACKAGEINTERNAL;
1333 state->method->info = minfo;
1336 list_append(parent_method->innerfunctions, state->method);
1338 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1340 function_initvars(state->method, params, 0, 1);
1344 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1345 state->method->variable_count = 0;
1346 parserassert(state->method);
1348 state->method->info->return_type = return_type;
1349 function_initvars(state->method, params, 0, 1);
1353 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1354 params_t*params, classinfo_t*return_type)
1356 if(state->method && state->method->info) {
1357 syntaxerror("not able to start another method scope");
1360 state->new_vars = 1;
1363 state->method = rfx_calloc(sizeof(methodstate_t));
1364 state->method->has_super = 0;
1367 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1369 state->method->is_global = 1;
1370 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1372 if(state->method->is_constructor)
1373 name = "__as3_constructor__";
1375 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1377 function_initvars(state->method, params, mod->flags, 1);
1379 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1383 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1384 state->method->variable_count = 0;
1385 parserassert(state->method);
1388 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1389 check_override(m, mod->flags);
1393 state->cls->has_constructor |= state->method->is_constructor;
1396 state->method->info->return_type = return_type;
1397 function_initvars(state->method, params, mod->flags, 1);
1401 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1402 params_t*params, classinfo_t*return_type, code_t*body)
1405 // store inner methods in variables
1406 function_initvars(state->method, 0, 0, 0);
1408 methodstate_list_t*ml = state->method->innerfunctions;
1410 dict_t*xvars = dict_new();
1413 methodstate_t*m = ml->methodstate;
1414 parserassert(m->inner);
1415 if(m->unresolved_variables) {
1416 dict_t*d = m->unresolved_variables;
1418 for(t=0;t<d->hashsize;t++) {
1419 dictentry_t*l = d->slots[t];
1421 /* check parent method's variables */
1423 if((v=find_variable(state, l->key))) {
1424 m->uses_parent_function = 1;
1425 state->method->uses_slots = 1;
1426 dict_put(xvars, l->key, 0);
1433 dict_destroy(m->unresolved_variables);
1434 m->unresolved_variables = 0;
1439 if(state->method->uses_slots) {
1440 state->method->slots = dict_new();
1442 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1443 if(!name) syntaxerror("internal error");
1444 if(v->index && dict_contains(xvars, name)) {
1447 if(v->is_inner_method) {
1448 v->is_inner_method->is_a_slot = 1;
1451 dict_put(state->method->slots, name, v);
1454 state->method->uses_slots = i;
1455 dict_destroy(state->vars);state->vars = 0;
1462 /*if(state->method->uses_parent_function){
1463 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1468 multiname_t*type2 = sig2mname(return_type);
1470 if(state->method->inner) {
1471 f = state->method->abc;
1472 abc_method_init(f, global->file, type2, 1);
1473 } else if(state->method->is_constructor) {
1474 f = abc_class_getconstructor(state->cls->abc, type2);
1475 } else if(!state->method->is_global) {
1476 namespace_t mname_ns = modifiers2access(mod);
1477 multiname_t mname = {QNAME, &mname_ns, 0, name};
1479 if(mod->flags&FLAG_STATIC)
1480 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1482 f = abc_class_method(state->cls->abc, type2, &mname);
1483 slot = f->trait->slot_id;
1485 namespace_t mname_ns = {state->method->info->access, state->package};
1486 multiname_t mname = {QNAME, &mname_ns, 0, name};
1488 f = abc_method_new(global->file, type2, 1);
1489 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1490 //abc_code_t*c = global->init->method->body->code;
1492 //flash doesn't seem to allow us to access function slots
1493 //state->method->info->slot = slot;
1495 if(mod && mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1496 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1497 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1498 if(params->varargs) f->flags |= METHOD_NEED_REST;
1502 for(p=params->list;p;p=p->next) {
1503 if(params->varargs && !p->next) {
1504 break; //varargs: omit last parameter in function signature
1506 multiname_t*m = sig2mname(p->param->type);
1507 list_append(f->parameters, m);
1508 if(p->param->value) {
1509 check_constant_against_type(p->param->type, p->param->value);
1510 opt=1;list_append(f->optional_parameters, p->param->value);
1512 syntaxerror("non-optional parameter not allowed after optional parameters");
1515 if(state->method->slots) {
1516 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1518 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1519 multiname_t*type = sig2mname(v->type);
1520 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1521 t->slot_id = v->index;
1526 check_code_for_break(body);
1528 /* Seems this works now.
1529 if(state->method->exceptions && state->method->uses_slots) {
1530 as3_warning("try/catch and activation not supported yet within the same method");
1534 f->body->code = body;
1535 f->body->exceptions = state->method->exceptions;
1536 } else { //interface
1538 syntaxerror("interface methods can't have a method body");
1548 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1553 void breakjumpsto(code_t*c, char*name, code_t*jump)
1556 if(c->opcode == OPCODE___BREAK__) {
1557 string_t*name2 = c->data[0];
1558 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1559 c->opcode = OPCODE_JUMP;
1566 void continuejumpsto(code_t*c, char*name, code_t*jump)
1569 if(c->opcode == OPCODE___CONTINUE__) {
1570 string_t*name2 = c->data[0];
1571 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1572 c->opcode = OPCODE_JUMP;
1580 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1581 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1582 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1584 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1586 if(!type1 || !type2)
1587 return registry_getanytype();
1588 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1589 return registry_getanytype();
1592 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1601 return registry_getanytype();
1603 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1608 return abc_coerce_a(c);
1612 // cast an "any" type to a specific type. subject to
1613 // runtime exceptions
1614 return abc_coerce2(c, &m);
1617 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1618 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1619 // allow conversion between number types
1620 return abc_coerce2(c, &m);
1622 //printf("%s.%s\n", from.package, from.name);
1623 //printf("%s.%s\n", to.package, to.name);
1625 classinfo_t*supertype = from;
1627 if(supertype == to) {
1628 // target type is one of from's superclasses
1629 return abc_coerce2(c, &m);
1632 while(supertype->interfaces[t]) {
1633 if(supertype->interfaces[t]==to) {
1634 // target type is one of from's interfaces
1635 return abc_coerce2(c, &m);
1639 supertype = supertype->superclass;
1641 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1643 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1645 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1648 as3_error("can't convert type %s%s%s to %s%s%s",
1649 from->package, from->package?".":"", from->name,
1650 to->package, to->package?".":"", to->name);
1654 code_t*defaultvalue(code_t*c, classinfo_t*type)
1656 if(TYPE_IS_INT(type)) {
1657 c = abc_pushbyte(c, 0);
1658 } else if(TYPE_IS_UINT(type)) {
1659 c = abc_pushuint(c, 0);
1660 } else if(TYPE_IS_FLOAT(type)) {
1662 } else if(TYPE_IS_BOOLEAN(type)) {
1663 c = abc_pushfalse(c);
1665 //c = abc_pushundefined(c);
1667 c = abc_pushnull(c);
1669 c = abc_coerce2(c, &m);
1674 char is_pushundefined(code_t*c)
1676 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1679 static const char* get_package_from_name(const char*name)
1681 /* try explicit imports */
1682 dictentry_t* e = dict_get_slot(state->imports, name);
1684 if(!strcmp(e->key, name)) {
1685 slotinfo_t*c = (slotinfo_t*)e->data;
1686 if(c) return c->package;
1692 static namespace_list_t*get_current_imports()
1694 namespace_list_t*searchlist = 0;
1696 list_append(searchlist, namespace_new_package(state->package));
1698 import_list_t*l = state->wildcard_imports;
1700 namespace_t*ns = namespace_new_package(l->import->package);
1701 list_append(searchlist, ns);
1704 list_append(searchlist, namespace_new_package(""));
1705 list_append(searchlist, namespace_new_package(internal_filename_package));
1709 static slotinfo_t* find_class(const char*name)
1713 c = registry_find(state->package, name);
1716 /* try explicit imports */
1717 dictentry_t* e = dict_get_slot(state->imports, name);
1720 if(!strcmp(e->key, name)) {
1721 c = (slotinfo_t*)e->data;
1727 /* try package.* imports */
1728 import_list_t*l = state->wildcard_imports;
1730 //printf("does package %s contain a class %s?\n", l->import->package, name);
1731 c = registry_find(l->import->package, name);
1736 /* try global package */
1737 c = registry_find("", name);
1740 /* try local "filename" package */
1741 c = registry_find(internal_filename_package, name);
1746 typedcode_t push_class(slotinfo_t*a)
1751 if(a->access == ACCESS_PACKAGEINTERNAL &&
1752 strcmp(a->package, state->package) &&
1753 strcmp(a->package, internal_filename_package)
1755 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1756 infotypename(a), a->name, a->package, state->package);
1759 if(a->kind != INFOTYPE_CLASS) {
1761 x.c = abc_findpropstrict2(x.c, &m);
1762 x.c = abc_getproperty2(x.c, &m);
1763 if(a->kind == INFOTYPE_METHOD) {
1764 methodinfo_t*f = (methodinfo_t*)a;
1765 x.t = TYPE_FUNCTION(f);
1767 varinfo_t*v = (varinfo_t*)a;
1771 classinfo_t*c = (classinfo_t*)a;
1773 x.c = abc_getglobalscope(x.c);
1774 x.c = abc_getslot(x.c, c->slot);
1777 x.c = abc_getlex2(x.c, &m);
1779 x.t = TYPE_CLASS(c);
1784 static char is_getlocal(code_t*c)
1786 if(!c || c->prev || c->next)
1788 return(c->opcode == OPCODE_GETLOCAL
1789 || c->opcode == OPCODE_GETLOCAL_0
1790 || c->opcode == OPCODE_GETLOCAL_1
1791 || c->opcode == OPCODE_GETLOCAL_2
1792 || c->opcode == OPCODE_GETLOCAL_3);
1794 static int getlocalnr(code_t*c)
1796 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1797 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1798 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1799 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1800 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1801 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1805 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1809 [prefix code] [read instruction]
1813 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1815 if(in && in->opcode == OPCODE_COERCE_A) {
1816 in = code_cutlast(in);
1819 syntaxerror("internal error");
1821 /* chop off read instruction */
1825 prefix = r->prev;r->prev = 0;
1831 char use_temp_var = readbefore;
1833 /* generate the write instruction, and maybe append a dup to the prefix code */
1834 code_t* write = abc_nop(0);
1835 if(r->opcode == OPCODE_GETPROPERTY) {
1836 write->opcode = OPCODE_SETPROPERTY;
1837 multiname_t*m = (multiname_t*)r->data[0];
1838 write->data[0] = multiname_clone(m);
1839 if(m->type == QNAME || m->type == MULTINAME) {
1841 prefix = abc_dup(prefix); // we need the object, too
1844 } else if(m->type == MULTINAMEL) {
1846 /* dupping two values on the stack requires 5 operations and one register-
1847 couldn't adobe just have given us a dup2? */
1848 int temp = gettempvar();
1849 prefix = abc_setlocal(prefix, temp);
1850 prefix = abc_dup(prefix);
1851 prefix = abc_getlocal(prefix, temp);
1852 prefix = abc_swap(prefix);
1853 prefix = abc_getlocal(prefix, temp);
1855 prefix = abc_kill(prefix, temp);
1859 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1861 } else if(r->opcode == OPCODE_GETSLOT) {
1862 write->opcode = OPCODE_SETSLOT;
1863 write->data[0] = r->data[0];
1865 prefix = abc_dup(prefix); // we need the object, too
1868 } else if(r->opcode == OPCODE_GETLOCAL) {
1869 write->opcode = OPCODE_SETLOCAL;
1870 write->data[0] = r->data[0];
1871 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1872 write->opcode = OPCODE_SETLOCAL_0;
1873 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1874 write->opcode = OPCODE_SETLOCAL_1;
1875 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1876 write->opcode = OPCODE_SETLOCAL_2;
1877 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1878 write->opcode = OPCODE_SETLOCAL_3;
1879 } else if(r->opcode == OPCODE_GETSUPER) {
1880 write->opcode = OPCODE_SETSUPER;
1881 multiname_t*m = (multiname_t*)r->data[0];
1882 write->data[0] = multiname_clone(m);
1885 syntaxerror("illegal lvalue: can't assign a value to this expression");
1892 /* with getproperty/getslot, we have to be extra careful not
1893 to execute the read code twice, as it might have side-effects
1894 (e.g. if the property is in fact a setter/getter combination)
1896 So read the value, modify it, and write it again,
1897 using prefix only once and making sure (by using a temporary
1898 register) that the return value is what we just wrote */
1899 temp = gettempvar();
1900 c = code_append(c, prefix);
1901 c = code_append(c, r);
1904 c = abc_setlocal(c, temp);
1906 c = code_append(c, middlepart);
1909 c = abc_setlocal(c, temp);
1911 c = code_append(c, write);
1912 c = abc_getlocal(c, temp);
1913 c = abc_kill(c, temp);
1915 /* if we're allowed to execute the read code twice *and*
1916 the middlepart doesn't modify the code, things are easier.
1918 code_t* r2 = code_dup(r);
1919 //c = code_append(c, prefix);
1920 parserassert(!prefix);
1921 c = code_append(c, r);
1922 c = code_append(c, middlepart);
1923 c = code_append(c, write);
1924 c = code_append(c, r2);
1927 /* even smaller version: overwrite the value without reading
1931 c = code_append(c, prefix);
1934 c = code_append(c, middlepart);
1935 c = code_append(c, write);
1936 c = code_append(c, r);
1939 temp = gettempvar();
1941 c = code_append(c, prefix);
1943 c = code_append(c, middlepart);
1945 c = abc_setlocal(c, temp);
1946 c = code_append(c, write);
1947 c = abc_getlocal(c, temp);
1948 c = abc_kill(c, temp);
1954 char is_break_or_jump(code_t*c)
1958 if(c->opcode == OPCODE_JUMP ||
1959 c->opcode == OPCODE___BREAK__ ||
1960 c->opcode == OPCODE___CONTINUE__ ||
1961 c->opcode == OPCODE_THROW ||
1962 c->opcode == OPCODE_RETURNVOID ||
1963 c->opcode == OPCODE_RETURNVALUE) {
1969 #define IS_FINALLY_TARGET(op) \
1970 ((op) == OPCODE___CONTINUE__ || \
1971 (op) == OPCODE___BREAK__ || \
1972 (op) == OPCODE_RETURNVOID || \
1973 (op) == OPCODE_RETURNVALUE || \
1974 (op) == OPCODE___RETHROW__)
1976 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1978 #define NEED_EXTRA_STACK_ARG
1979 code_t*finally_label = abc_nop(0);
1980 NEW(lookupswitch_t, l);
1986 code_t*prev = i->prev;
1987 if(IS_FINALLY_TARGET(i->opcode)) {
1990 if(i->opcode == OPCODE___RETHROW__ ||
1991 i->opcode == OPCODE_RETURNVALUE) {
1992 if(i->opcode == OPCODE___RETHROW__)
1993 i->opcode = OPCODE_THROW;
1995 p = abc_coerce_a(p);
1996 p = abc_setlocal(p, tempvar);
1998 p = abc_pushbyte(p, count++);
1999 p = abc_jump(p, finally_label);
2000 code_t*target = p = abc_label(p);
2001 #ifdef NEED_EXTRA_STACK_ARG
2005 p = abc_getlocal(p, tempvar);
2008 p->next = i;i->prev = p;
2009 list_append(l->targets, target);
2015 c = abc_pushbyte(c, -1);
2016 c = code_append(c, finally_label);
2017 c = code_append(c, finally);
2019 #ifdef NEED_EXTRA_STACK_ARG
2022 c = abc_lookupswitch(c, l);
2023 c = l->def = abc_label(c);
2024 #ifdef NEED_EXTRA_STACK_ARG
2031 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
2035 code_t*prev = i->prev;
2036 if(IS_FINALLY_TARGET(i->opcode)) {
2037 if(i->opcode == OPCODE___RETHROW__)
2038 i->opcode = OPCODE_THROW;
2039 code_t*end = code_dup(finally);
2040 code_t*start = code_start(end);
2041 if(prev) prev->next = start;
2048 return code_append(c, finally);
2051 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
2057 int num_insertion_points=0;
2059 if(IS_FINALLY_TARGET(i->opcode))
2060 num_insertion_points++;
2067 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
2072 int simple_version_cost = (1+num_insertion_points)*code_size;
2073 int lookup_version_cost = 4*num_insertion_points + 5;
2075 if(cantdup || simple_version_cost > lookup_version_cost) {
2076 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
2077 return insert_finally_lookup(c, finally, tempvar);
2079 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
2080 return insert_finally_simple(c, finally, tempvar);
2084 #define PASS1 }} if(as3_pass == 1) {{
2085 #define PASS1END }} if(as3_pass == 2) {{
2086 #define PASS2 }} if(as3_pass == 2) {{
2087 #define PASS12 }} {{
2088 #define PASS12END }} if(as3_pass == 2) {{
2094 /* ------------ code blocks / statements ---------------- */
2096 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2098 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2099 PROGRAM_CODE_LIST: PROGRAM_CODE
2100 | PROGRAM_CODE_LIST PROGRAM_CODE
2102 PROGRAM_CODE: PACKAGE_DECLARATION
2103 | INTERFACE_DECLARATION
2105 | FUNCTION_DECLARATION
2108 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
2111 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2112 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2113 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2115 INPACKAGE_CODE: INTERFACE_DECLARATION
2117 | FUNCTION_DECLARATION
2120 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
2123 MAYBECODE: CODE {$$=$1;}
2124 MAYBECODE: {$$=code_new();}
2126 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2127 CODE: CODEPIECE {$$=$1;}
2129 // code which may appear outside of methods
2130 CODE_STATEMENT: IMPORT
2132 CODE_STATEMENT: FOR_IN
2133 CODE_STATEMENT: WHILE
2134 CODE_STATEMENT: DO_WHILE
2135 CODE_STATEMENT: SWITCH
2137 CODE_STATEMENT: WITH
2139 CODE_STATEMENT: VOIDEXPRESSION
2140 CODE_STATEMENT: USE_NAMESPACE
2141 CODE_STATEMENT: NAMESPACE_DECLARATION
2142 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2143 CODE_STATEMENT: '{' '}' {$$=0;}
2145 // code which may appear in methods
2146 CODEPIECE: ';' {$$=0;}
2147 CODEPIECE: CODE_STATEMENT
2148 CODEPIECE: VARIABLE_DECLARATION
2153 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
2155 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2156 //CODEBLOCK : '{' '}' {$$=0;}
2157 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2158 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2160 /* ------------ package init code ------------------- */
2162 PACKAGE_INITCODE: CODE_STATEMENT {
2163 code_t**cc = &global->init->method->body->code;
2164 *cc = code_append(*cc, $1);
2167 /* ------------ conditional compilation ------------- */
2169 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
2171 /* ------------ variables --------------------------- */
2173 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2174 | {$$.c=abc_pushundefined(0);
2178 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2179 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2181 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2182 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2184 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2187 if(variable_exists($1))
2188 syntaxerror("Variable %s already defined", $1);
2190 new_variable($1, 0, 1, 0);
2193 if(!is_subtype_of($3.t, $2)) {
2194 syntaxerror("Can't convert %s to %s", $3.t->name,
2200 if(state->method->uses_slots) {
2201 variable_t* v = find_slot(state, $1);
2203 // this variable is stored in a slot
2211 index = new_variable($1, $2, 1, 0);
2214 $$ = slot?abc_getscopeobject(0, 1):0;
2217 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2218 $$ = code_append($$, $3.c);
2219 $$ = converttype($$, $3.t, $2);
2222 $$ = defaultvalue($$, $2);
2225 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2226 $$ = code_append($$, $3.c);
2227 $$ = abc_coerce_a($$);
2229 // don't do anything
2237 $$ = abc_setslot($$, index);
2239 $$ = abc_setlocal($$, index);
2243 /* ------------ control flow ------------------------- */
2245 MAYBEELSE: %prec below_else {$$ = code_new();}
2246 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2247 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2249 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2252 $$ = code_append($$, $4.c);
2253 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2255 $$ = code_append($$, $6);
2257 myjmp = $$ = abc_jump($$, 0);
2259 myif->branch = $$ = abc_nop($$);
2261 $$ = code_append($$, $7);
2262 myjmp->branch = $$ = abc_nop($$);
2268 FOR_INIT : {$$=code_new();}
2269 FOR_INIT : VARIABLE_DECLARATION
2270 FOR_INIT : VOIDEXPRESSION
2272 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2273 // (I don't see any easy way to revolve this conflict otherwise, as we
2274 // can't touch VAR_READ without upsetting the precedence about "return")
2275 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2276 PASS1 $$=$2;new_variable($2,0,1,0);
2277 PASS2 $$=$2;new_variable($2,$3,1,0);
2279 FOR_IN_INIT : T_IDENTIFIER {
2284 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2285 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2287 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2288 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2290 $$ = code_append($$, $2);
2291 code_t*loopstart = $$ = abc_label($$);
2292 $$ = code_append($$, $4.c);
2293 code_t*myif = $$ = abc_iffalse($$, 0);
2294 $$ = code_append($$, $8);
2295 code_t*cont = $$ = abc_nop($$);
2296 $$ = code_append($$, $6);
2297 $$ = abc_jump($$, loopstart);
2298 code_t*out = $$ = abc_nop($$);
2299 breakjumpsto($$, $1.name, out);
2300 continuejumpsto($$, $1.name, cont);
2307 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2308 variable_t*var = find_variable(state, $2);
2310 syntaxerror("variable %s not known in this scope", $2);
2313 char*tmp1name = concat2($2, "__tmp1__");
2314 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2315 char*tmp2name = concat2($2, "__array__");
2316 int array = new_variable(tmp1name, 0, 0, 0);
2319 $$ = code_append($$, $4.c);
2320 $$ = abc_coerce_a($$);
2321 $$ = abc_setlocal($$, array);
2322 $$ = abc_pushbyte($$, 0);
2323 $$ = abc_setlocal($$, it);
2325 code_t*loopstart = $$ = abc_label($$);
2327 $$ = abc_hasnext2($$, array, it);
2328 code_t*myif = $$ = abc_iffalse($$, 0);
2329 $$ = abc_getlocal($$, array);
2330 $$ = abc_getlocal($$, it);
2332 $$ = abc_nextname($$);
2334 $$ = abc_nextvalue($$);
2335 $$ = converttype($$, 0, var->type);
2336 $$ = abc_setlocal($$, var->index);
2338 $$ = code_append($$, $6);
2339 $$ = abc_jump($$, loopstart);
2341 code_t*out = $$ = abc_nop($$);
2342 breakjumpsto($$, $1.name, out);
2343 continuejumpsto($$, $1.name, loopstart);
2355 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2359 code_t*myjmp = $$ = abc_jump($$, 0);
2360 code_t*loopstart = $$ = abc_label($$);
2361 $$ = code_append($$, $6);
2362 code_t*cont = $$ = abc_nop($$);
2363 myjmp->branch = cont;
2364 $$ = code_append($$, $4.c);
2365 $$ = abc_iftrue($$, loopstart);
2366 code_t*out = $$ = abc_nop($$);
2367 breakjumpsto($$, $1, out);
2368 continuejumpsto($$, $1, cont);
2374 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2376 code_t*loopstart = $$ = abc_label($$);
2377 $$ = code_append($$, $3);
2378 code_t*cont = $$ = abc_nop($$);
2379 $$ = code_append($$, $6.c);
2380 $$ = abc_iftrue($$, loopstart);
2381 code_t*out = $$ = abc_nop($$);
2382 breakjumpsto($$, $1, out);
2383 continuejumpsto($$, $1, cont);
2389 BREAK : "break" %prec prec_none {
2390 $$ = abc___break__(0, "");
2392 BREAK : "break" T_IDENTIFIER {
2393 $$ = abc___break__(0, $2);
2395 CONTINUE : "continue" %prec prec_none {
2396 $$ = abc___continue__(0, "");
2398 CONTINUE : "continue" T_IDENTIFIER {
2399 $$ = abc___continue__(0, $2);
2402 MAYBE_CASE_LIST : {$$=0;}
2403 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2404 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2405 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2406 CASE_LIST: CASE {$$=$1;}
2407 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2409 CASE: "case" E ':' MAYBECODE {
2410 $$ = abc_getlocal(0, state->switch_var);
2411 $$ = code_append($$, $2.c);
2412 code_t*j = $$ = abc_ifne($$, 0);
2413 $$ = code_append($$, $4);
2414 if($$->opcode != OPCODE___BREAK__) {
2415 $$ = abc___fallthrough__($$, "");
2417 code_t*e = $$ = abc_nop($$);
2420 DEFAULT: "default" ':' MAYBECODE {
2423 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2425 $$ = abc_setlocal($$, state->switch_var);
2426 $$ = code_append($$, $7);
2428 code_t*out = $$ = abc_kill($$, state->switch_var);
2429 breakjumpsto($$, $1, out);
2431 code_t*c = $$,*lastblock=0;
2433 if(c->opcode == OPCODE_IFNE) {
2434 if(!c->next) syntaxerror("internal error in fallthrough handling");
2436 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2438 c->opcode = OPCODE_JUMP;
2439 c->branch = lastblock;
2441 /* fall through end of switch */
2442 c->opcode = OPCODE_NOP;
2452 /* ------------ try / catch /finally ---------------- */
2454 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2455 state->exception_name=$3;
2456 PASS1 new_variable($3, 0, 0, 0);
2457 PASS2 new_variable($3, $4, 0, 0);
2460 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2461 multiname_t name = {QNAME, &name_ns, 0, $3};
2463 NEW(abc_exception_t, e)
2464 e->exc_type = sig2mname($4);
2465 e->var_name = multiname_clone(&name);
2469 int i = find_variable_safe(state, $3)->index;
2470 e->target = c = abc_nop(0);
2471 c = abc_setlocal(c, i);
2472 c = code_append(c, code_dup(state->method->scope_code));
2473 c = code_append(c, $8);
2479 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2484 NEW(abc_exception_t, e)
2485 e->exc_type = 0; //all exceptions
2486 e->var_name = 0; //no name
2489 e->to = code_append(e->to, $4);
2495 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2496 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2497 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2498 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2502 list_append($$.l,$2);
2503 $$.finally = $2->to;$2->to=0;
2506 CATCH_FINALLY_LIST: FINALLY {
2510 list_append($$.l,$1);
2511 $$.finally = $1->to;$1->to=0;
2515 TRY : "try" '{' {PASS12 new_state();
2516 state->method->has_exceptions=1;
2517 state->method->late_binding=1;//for invariant scope_code
2518 } MAYBECODE '}' CATCH_FINALLY_LIST {
2519 code_t*out = abc_nop(0);
2521 code_t*start = abc_nop(0);
2522 $$ = code_append(start, $4);
2523 if(!is_break_or_jump($4)) {
2524 $$ = abc_jump($$, out);
2526 code_t*end = $$ = abc_nop($$);
2530 tmp = new_variable("__finally__", 0, 0, 0);
2532 abc_exception_list_t*l = $6.l;
2535 abc_exception_t*e = l->abc_exception;
2537 $$ = code_append($$, e->target);
2538 $$ = abc_jump($$, out);
2540 parserassert((ptroff_t)$6.finally);
2542 e->target = $$ = abc_nop($$);
2543 $$ = code_append($$, code_dup(state->method->scope_code));
2544 $$ = abc___rethrow__($$);
2552 $$ = code_append($$, out);
2554 $$ = insert_finally($$, $6.finally, tmp);
2556 list_concat(state->method->exceptions, $6.l);
2562 /* ------------ throw ------------------------------- */
2564 THROW : "throw" EXPRESSION {
2568 THROW : "throw" %prec prec_none {
2569 if(!state->exception_name)
2570 syntaxerror("re-throw only possible within a catch block");
2571 variable_t*v = find_variable(state, state->exception_name);
2573 $$=abc_getlocal($$, v->index);
2577 /* ------------ with -------------------------------- */
2579 WITH_HEAD : "with" '(' EXPRESSION ')' {
2581 if(state->method->has_exceptions) {
2582 int v = alloc_local();
2583 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2584 state->method->scope_code = abc_pushwith(state->method->scope_code);
2589 WITH : WITH_HEAD CODEBLOCK {
2590 /* remove getlocal;pushwith from scope code again */
2591 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2594 if(state->method->has_exceptions) {
2596 $$ = abc_setlocal($$, $1.number);
2598 $$ = abc_pushwith($$);
2599 $$ = code_append($$, $2);
2600 $$ = abc_popscope($$);
2604 /* ------------ packages and imports ---------------- */
2606 X_IDENTIFIER: T_IDENTIFIER
2607 | "package" {PASS12 $$="package";}
2608 | T_NAMESPACE {PASS12 $$=$1;}
2610 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2611 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2613 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2614 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2615 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2616 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2618 IMPORT : "import" PACKAGEANDCLASS {
2620 slotinfo_t*s = registry_find($2->package, $2->name);
2621 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2622 as3_schedule_class($2->package, $2->name);
2628 syntaxerror("Couldn't import class\n");
2629 state_has_imports();
2630 dict_put(state->imports, c->name, c);
2631 import_toplevel(c->package);
2634 IMPORT : "import" PACKAGE '.' '*' {
2636 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2637 as3_schedule_package($2);
2643 state_has_imports();
2644 list_append(state->wildcard_imports, i);
2645 import_toplevel(i->package);
2649 /* ------------ classes and interfaces (header) -------------- */
2651 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2652 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2653 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2654 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2656 $$.flags=$1.flags|$2.flags;
2657 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2658 $$.ns=$1.ns?$1.ns:$2.ns;
2661 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2662 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2663 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2664 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2665 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2666 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2667 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2668 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2669 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2670 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2674 EXTENDS : {PASS12 $$=0;}
2675 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2677 EXTENDS_LIST : {PASS12 $$=list_new();}
2678 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2680 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2681 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2683 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2684 EXTENDS IMPLEMENTS_LIST
2685 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2687 '}' {PASS12 endclass();$$=0;}
2689 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2691 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2692 startclass(&$1,$3,0,$4);}
2693 MAYBE_INTERFACE_BODY
2694 '}' {PASS12 endclass();$$=0;}
2696 /* ------------ classes and interfaces (body) -------------- */
2699 MAYBE_CLASS_BODY : CLASS_BODY
2700 CLASS_BODY : CLASS_BODY_ITEM
2701 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2702 CLASS_BODY_ITEM : ';'
2703 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2704 CLASS_BODY_ITEM : SLOT_DECLARATION
2705 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2707 CLASS_BODY_ITEM : CODE_STATEMENT {
2708 code_t*c = state->cls->static_init->header;
2709 c = code_append(c, $1);
2710 state->cls->static_init->header = c;
2713 MAYBE_INTERFACE_BODY :
2714 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2715 INTERFACE_BODY : IDECLARATION
2716 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2718 IDECLARATION : "var" T_IDENTIFIER {
2719 syntaxerror("variable declarations not allowed in interfaces");
2721 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2723 $1.flags |= FLAG_PUBLIC;
2724 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2725 syntaxerror("invalid method modifiers: interface methods always need to be public");
2727 startfunction(&$1,$3,$4,&$6,$8);
2728 endfunction(&$1,$3,$4,&$6,$8, 0);
2729 list_deep_free($6.list);
2732 /* ------------ classes and interfaces (body, slots ) ------- */
2734 VARCONST: "var" | "const"
2736 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2738 SLOT_LIST: ONE_SLOT {$$ = $1;}
2739 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2741 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2743 int flags = slotstate_flags->flags;
2744 namespace_t ns = modifiers2access(slotstate_flags);
2746 varinfo_t* info = 0;
2748 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2750 check_override(i, flags);
2752 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2754 slotinfo_t*i = registry_find(state->package, $1);
2756 syntaxerror("package %s already contains '%s'", state->package, $1);
2758 if(ns.name && ns.name[0]) {
2759 syntaxerror("namespaces not allowed on package-level variables");
2761 info = varinfo_register_global(ns.access, state->package, $1);
2765 info->flags = flags;
2768 multiname_t mname = {QNAME, &ns, 0, $1};
2770 trait_list_t**traits;
2774 ns.name = state->package;
2775 traits = &global->init->traits;
2776 code = &global->init->method->body->code;
2777 } else if(flags&FLAG_STATIC) {
2779 traits = &state->cls->abc->static_traits;
2780 code = &state->cls->static_init->header;
2782 // instance variable
2783 traits = &state->cls->abc->traits;
2784 code = &state->cls->init->header;
2790 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2792 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2794 info->slot = t->slot_id;
2796 /* initalization code (if needed) */
2798 if($3.c && !is_pushundefined($3.c)) {
2799 c = abc_getlocal_0(c);
2800 c = code_append(c, $3.c);
2801 c = converttype(c, $3.t, $2);
2802 c = abc_setslot(c, t->slot_id);
2805 *code = code_append(*code, c);
2807 if(slotstate_varconst==KW_CONST) {
2808 t->kind= TRAIT_CONST;
2814 /* ------------ constants -------------------------------------- */
2816 MAYBESTATICCONSTANT: {$$=0;}
2817 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2819 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2820 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2821 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2822 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2823 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2824 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2825 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2826 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2827 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2828 STATICCONSTANT : T_IDENTIFIER {
2829 if(!strcmp($1, "NaN")) {
2830 $$ = constant_new_float(__builtin_nan(""));
2832 as3_warning("Couldn't evaluate constant value of %s", $1);
2833 $$ = constant_new_null($1);
2837 /* ------------ classes and interfaces (body, functions) ------- */
2839 // non-vararg version
2842 memset(&$$,0,sizeof($$));
2844 MAYBE_PARAM_LIST: PARAM_LIST {
2850 MAYBE_PARAM_LIST: "..." PARAM {
2852 memset(&$$,0,sizeof($$));
2854 list_append($$.list, $2);
2856 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2860 list_append($$.list, $4);
2864 PARAM_LIST: PARAM_LIST ',' PARAM {
2867 list_append($$.list, $3);
2871 memset(&$$,0,sizeof($$));
2872 list_append($$.list, $1);
2875 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2877 $$ = rfx_calloc(sizeof(param_t));
2883 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2885 $$ = rfx_calloc(sizeof(param_t));
2887 $$->type = TYPE_ANY;
2895 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2896 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2899 endfunction(&$1,$3,$4,&$6,0,0);
2901 if(!state->method->info) syntaxerror("internal error");
2903 code_t*c = method_header(state->method);
2904 c = wrap_function(c, 0, $11);
2906 endfunction(&$1,$3,$4,&$6,$8,c);
2908 list_deep_free($6.list);
2912 MAYBE_IDENTIFIER: T_IDENTIFIER
2913 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2914 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2915 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2918 endfunction(0,0,$2,&$4,0,0);
2920 methodinfo_t*f = state->method->info;
2921 if(!f || !f->kind) syntaxerror("internal error");
2923 code_t*c = method_header(state->method);
2924 c = wrap_function(c, 0, $9);
2926 int index = state->method->var_index;
2927 endfunction(0,0,$2,&$4,$6,c);
2929 $$.c = abc_getlocal(0, index);
2930 $$.t = TYPE_FUNCTION(f);
2932 PASS12 list_deep_free($4.list);
2936 /* ------------- package + class ids --------------- */
2938 CLASS: X_IDENTIFIER {
2939 PASS1 NEW(unresolvedinfo_t,c);
2940 memset(c, 0, sizeof(*c));
2941 c->kind = INFOTYPE_UNRESOLVED;
2943 c->package = get_package_from_name($1);
2945 c->nsset = get_current_imports();
2946 /* make the compiler look for this class in the current directory,
2948 //as3_schedule_class_noerror(state->package, $1);
2950 $$ = (classinfo_t*)c;
2952 slotinfo_t*s = find_class($1);
2953 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2954 $$ = (classinfo_t*)s;
2957 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
2958 PASS1 NEW(unresolvedinfo_t,c);
2959 memset(c, 0, sizeof(*c));
2960 c->kind = INFOTYPE_UNRESOLVED;
2963 $$ = (classinfo_t*)c;
2965 slotinfo_t*s = registry_find($1, $3);
2966 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2968 $$ = (classinfo_t*)s;
2971 CLASS_SPEC: PACKAGEANDCLASS
2974 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2975 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2977 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2978 | '*' {PASS12 $$=registry_getanytype();}
2979 | "void" {PASS12 $$=registry_getanytype();}
2981 | "String" {$$=registry_getstringclass();}
2982 | "int" {$$=registry_getintclass();}
2983 | "uint" {$$=registry_getuintclass();}
2984 | "Boolean" {$$=registry_getbooleanclass();}
2985 | "Number" {$$=registry_getnumberclass();}
2988 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2989 MAYBETYPE: {PASS12 $$=0;}
2991 /* ----------function calls, delete, constructor calls ------ */
2993 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
2994 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2996 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
2997 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2998 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
3000 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
3004 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
3005 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
3006 $$.number= $1.number+1;
3007 $$.cc = code_append($1.cc, $2.c);
3011 NEW : "new" E XX MAYBE_PARAM_VALUES {
3013 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
3015 code_t*paramcode = $4.cc;
3016 if($$.c->opcode == OPCODE_GETPROPERTY) {
3017 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3018 $$.c = code_cutlast($$.c);
3019 $$.c = code_append($$.c, paramcode);
3020 $$.c = abc_constructprop2($$.c, name, $4.number);
3021 multiname_destroy(name);
3022 } else if($$.c->opcode == OPCODE_GETSLOT) {
3023 int slot = (int)(ptroff_t)$$.c->data[0];
3024 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3025 multiname_t*name = t->name;
3026 $$.c = code_cutlast($$.c);
3027 $$.c = code_append($$.c, paramcode);
3028 $$.c = abc_constructprop2($$.c, name, $4.number);
3030 $$.c = code_append($$.c, paramcode);
3031 $$.c = abc_construct($$.c, $4.number);
3035 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
3038 $$.c = abc_coerce_a($$.c);
3043 /* TODO: use abc_call (for calling local variables),
3044 abc_callstatic (for calling own methods)
3047 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3050 if($$.c->opcode == OPCODE_COERCE_A) {
3051 $$.c = code_cutlast($$.c);
3053 code_t*paramcode = $3.cc;
3056 if($$.c->opcode == OPCODE_GETPROPERTY) {
3057 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3058 $$.c = code_cutlast($$.c);
3059 $$.c = code_append($$.c, paramcode);
3060 $$.c = abc_callproperty2($$.c, name, $3.number);
3061 multiname_destroy(name);
3062 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3063 int slot = (int)(ptroff_t)$$.c->data[0];
3064 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3065 if(t->kind!=TRAIT_METHOD) {
3066 //ok: flash allows to assign closures to members.
3068 multiname_t*name = t->name;
3069 $$.c = code_cutlast($$.c);
3070 $$.c = code_append($$.c, paramcode);
3071 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3072 $$.c = abc_callproperty2($$.c, name, $3.number);
3073 } else if($$.c->opcode == OPCODE_GETSUPER) {
3074 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3075 $$.c = code_cutlast($$.c);
3076 $$.c = code_append($$.c, paramcode);
3077 $$.c = abc_callsuper2($$.c, name, $3.number);
3078 multiname_destroy(name);
3080 $$.c = abc_getglobalscope($$.c);
3081 $$.c = code_append($$.c, paramcode);
3082 $$.c = abc_call($$.c, $3.number);
3085 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
3086 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
3088 $$.c = abc_coerce_a($$.c);
3093 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3094 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3095 if(!state->method) syntaxerror("super() not allowed outside of a function");
3096 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3099 $$.c = abc_getlocal_0($$.c);
3101 $$.c = code_append($$.c, $3.cc);
3103 this is dependent on the control path, check this somewhere else
3104 if(state->method->has_super)
3105 syntaxerror("constructor may call super() only once");
3107 state->method->has_super = 1;
3109 $$.c = abc_constructsuper($$.c, $3.number);
3110 $$.c = abc_pushundefined($$.c);
3114 DELETE: "delete" E {
3116 if($$.c->opcode == OPCODE_COERCE_A) {
3117 $$.c = code_cutlast($$.c);
3119 multiname_t*name = 0;
3120 if($$.c->opcode == OPCODE_GETPROPERTY) {
3121 $$.c->opcode = OPCODE_DELETEPROPERTY;
3122 } else if($$.c->opcode == OPCODE_GETSLOT) {
3123 int slot = (int)(ptroff_t)$$.c->data[0];
3124 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3125 $$.c = code_cutlast($$.c);
3126 $$.c = abc_deleteproperty2($$.c, name);
3128 $$.c = abc_getlocal_0($$.c);
3129 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3130 $$.c = abc_deleteproperty2($$.c, &m);
3132 $$.t = TYPE_BOOLEAN;
3135 RETURN: "return" %prec prec_none {
3136 $$ = abc_returnvoid(0);
3138 RETURN: "return" EXPRESSION {
3140 $$ = abc_returnvalue($$);
3143 // ----------------------- expression types -------------------------------------
3145 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3146 EXPRESSION : E %prec below_minus {$$ = $1;}
3147 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3149 $$.c = cut_last_push($$.c);
3150 $$.c = code_append($$.c,$3.c);
3153 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3154 $$=cut_last_push($1.c);
3157 // ----------------------- expression evaluation -------------------------------------
3159 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3160 //V : CONSTANT {$$ = 0;}
3162 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
3163 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3164 //V : NEW {$$ = $1.c;}
3166 //V : DELETE {$$ = $1.c;}
3167 E : DELETE {$$ = $1;}
3173 namespace_t ns = {ACCESS_PACKAGE, ""};
3174 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3176 $$.c = abc_getlex2($$.c, &m);
3177 $$.c = abc_pushstring($$.c, $1.pattern);
3178 $$.c = abc_construct($$.c, 1);
3180 $$.c = abc_getlex2($$.c, &m);
3181 $$.c = abc_pushstring($$.c, $1.pattern);
3182 $$.c = abc_pushstring($$.c, $1.options);
3183 $$.c = abc_construct($$.c, 2);
3188 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3189 //MULTINAME(m, registry_getintclass());
3190 //$$.c = abc_coerce2($$.c, &m); // FIXME
3193 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3196 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3199 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3202 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3205 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3208 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3211 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3212 $$.t = TYPE_BOOLEAN;
3214 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3215 $$.t = TYPE_BOOLEAN;
3217 CONSTANT : "null" {$$.c = abc_pushnull(0);
3221 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3222 $$.t = TYPE_BOOLEAN;
3224 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3225 $$.t = TYPE_BOOLEAN;
3227 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3228 $$.t = TYPE_BOOLEAN;
3230 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3231 $$.t = TYPE_BOOLEAN;
3233 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3234 $$.t = TYPE_BOOLEAN;
3236 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3237 $$.t = TYPE_BOOLEAN;
3239 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3240 $$.t = TYPE_BOOLEAN;
3242 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3243 $$.t = TYPE_BOOLEAN;
3246 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3248 $$.c = converttype($$.c, $1.t, $$.t);
3249 $$.c = abc_dup($$.c);
3250 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3251 $$.c = cut_last_push($$.c);
3252 $$.c = code_append($$.c,$3.c);
3253 $$.c = converttype($$.c, $3.t, $$.t);
3254 code_t*label = $$.c = abc_label($$.c);
3255 jmp->branch = label;
3258 $$.t = join_types($1.t, $3.t, 'A');
3259 /*printf("%08x:\n",$1.t);
3260 code_dump($1.c, 0, 0, "", stdout);
3261 printf("%08x:\n",$3.t);
3262 code_dump($3.c, 0, 0, "", stdout);
3263 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3265 $$.c = converttype($$.c, $1.t, $$.t);
3266 $$.c = abc_dup($$.c);
3267 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3268 $$.c = cut_last_push($$.c);
3269 $$.c = code_append($$.c,$3.c);
3270 $$.c = converttype($$.c, $3.t, $$.t);
3271 code_t*label = $$.c = abc_label($$.c);
3272 jmp->branch = label;
3275 E : '!' E {$$.c=$2.c;
3276 $$.c = abc_not($$.c);
3277 $$.t = TYPE_BOOLEAN;
3280 E : '~' E {$$.c=$2.c;
3281 $$.c = abc_bitnot($$.c);
3285 E : E '&' E {$$.c = code_append($1.c,$3.c);
3286 $$.c = abc_bitand($$.c);
3290 E : E '^' E {$$.c = code_append($1.c,$3.c);
3291 $$.c = abc_bitxor($$.c);
3295 E : E '|' E {$$.c = code_append($1.c,$3.c);
3296 $$.c = abc_bitor($$.c);
3300 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3301 $$.c = abc_rshift($$.c);
3304 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3305 $$.c = abc_urshift($$.c);
3308 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3309 $$.c = abc_lshift($$.c);
3313 E : E '/' E {$$.c = code_append($1.c,$3.c);
3314 $$.c = abc_divide($$.c);
3317 E : E '%' E {$$.c = code_append($1.c,$3.c);
3318 $$.c = abc_modulo($$.c);
3321 E : E '+' E {$$.c = code_append($1.c,$3.c);
3322 if(BOTH_INT($1.t, $3.t)) {
3323 $$.c = abc_add_i($$.c);
3326 $$.c = abc_add($$.c);
3327 $$.t = join_types($1.t,$3.t,'+');
3330 E : E '-' E {$$.c = code_append($1.c,$3.c);
3331 if(BOTH_INT($1.t,$3.t)) {
3332 $$.c = abc_subtract_i($$.c);
3335 $$.c = abc_subtract($$.c);
3339 E : E '*' E {$$.c = code_append($1.c,$3.c);
3340 if(BOTH_INT($1.t,$3.t)) {
3341 $$.c = abc_multiply_i($$.c);
3344 $$.c = abc_multiply($$.c);
3349 E : E "in" E {$$.c = code_append($1.c,$3.c);
3350 $$.c = abc_in($$.c);
3351 $$.t = TYPE_BOOLEAN;
3354 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3355 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3356 MULTINAME(m, (classinfo_t*)($3.t->data));
3357 $$.c = abc_astype2($1.c, &m);
3360 $$.c = code_append($1.c, $3.c);
3361 $$.c = abc_astypelate($$.c);
3366 E : E "instanceof" E
3367 {$$.c = code_append($1.c, $3.c);
3368 $$.c = abc_instanceof($$.c);
3369 $$.t = TYPE_BOOLEAN;
3372 E : E "is" E {$$.c = code_append($1.c, $3.c);
3373 $$.c = abc_istypelate($$.c);
3374 $$.t = TYPE_BOOLEAN;
3377 E : "typeof" '(' E ')' {
3379 $$.c = abc_typeof($$.c);
3384 $$.c = cut_last_push($2.c);
3385 $$.c = abc_pushundefined($$.c);
3389 E : "void" { $$.c = abc_pushundefined(0);
3393 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3398 $$.c=abc_negate_i($$.c);
3401 $$.c=abc_negate($$.c);
3408 $$.c = code_append($$.c, $3.c);
3410 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3411 $$.c = abc_getproperty2($$.c, &m);
3412 $$.t = 0; // array elements have unknown type
3415 E : '[' MAYBE_EXPRESSION_LIST ']' {
3417 $$.c = code_append($$.c, $2.cc);
3418 $$.c = abc_newarray($$.c, $2.number);
3419 $$.t = registry_getarrayclass();
3422 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3423 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3425 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3427 $$.cc = code_append($$.cc, $1.c);
3428 $$.cc = code_append($$.cc, $3.c);
3431 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3433 $$.number = $1.number+2;
3434 $$.cc = code_append($$.cc, $3.c);
3435 $$.cc = code_append($$.cc, $5.c);
3440 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3442 $$.c = code_append($$.c, $2.cc);
3443 $$.c = abc_newobject($$.c, $2.number/2);
3444 $$.t = registry_getobjectclass();
3449 if(BOTH_INT($1.t,$3.t)) {
3450 c=abc_multiply_i(c);
3454 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3455 $$.c = toreadwrite($1.c, c, 0, 0);
3460 code_t*c = abc_modulo($3.c);
3461 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3462 $$.c = toreadwrite($1.c, c, 0, 0);
3466 code_t*c = abc_lshift($3.c);
3467 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3468 $$.c = toreadwrite($1.c, c, 0, 0);
3472 code_t*c = abc_rshift($3.c);
3473 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3474 $$.c = toreadwrite($1.c, c, 0, 0);
3478 code_t*c = abc_urshift($3.c);
3479 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3480 $$.c = toreadwrite($1.c, c, 0, 0);
3484 code_t*c = abc_divide($3.c);
3485 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3486 $$.c = toreadwrite($1.c, c, 0, 0);
3490 code_t*c = abc_bitor($3.c);
3491 c=converttype(c, TYPE_INT, $1.t);
3492 $$.c = toreadwrite($1.c, c, 0, 0);
3496 code_t*c = abc_bitxor($3.c);
3497 c=converttype(c, TYPE_INT, $1.t);
3498 $$.c = toreadwrite($1.c, c, 0, 0);
3504 if(TYPE_IS_INT($1.t)) {
3508 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3511 $$.c = toreadwrite($1.c, c, 0, 0);
3514 E : E "-=" E { code_t*c = $3.c;
3515 if(TYPE_IS_INT($1.t)) {
3516 c=abc_subtract_i(c);
3519 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3522 $$.c = toreadwrite($1.c, c, 0, 0);
3525 E : E '=' E { code_t*c = 0;
3526 c = code_append(c, $3.c);
3527 c = converttype(c, $3.t, $1.t);
3528 $$.c = toreadwrite($1.c, c, 1, 0);
3532 E : E '?' E ':' E %prec below_assignment {
3533 $$.t = join_types($3.t,$5.t,'?');
3535 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3536 $$.c = code_append($$.c, $3.c);
3537 $$.c = converttype($$.c, $3.t, $$.t);
3538 code_t*j2 = $$.c = abc_jump($$.c, 0);
3539 $$.c = j1->branch = abc_label($$.c);
3540 $$.c = code_append($$.c, $5.c);
3541 $$.c = converttype($$.c, $5.t, $$.t);
3542 $$.c = j2->branch = abc_label($$.c);
3545 E : E "++" { code_t*c = 0;
3546 classinfo_t*type = $1.t;
3547 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3548 int nr = getlocalnr($1.c);
3549 code_free($1.c);$1.c=0;
3550 if(TYPE_IS_INT($1.t)) {
3551 $$.c = abc_getlocal(0, nr);
3552 $$.c = abc_inclocal_i($$.c, nr);
3553 } else if(TYPE_IS_NUMBER($1.t)) {
3554 $$.c = abc_getlocal(0, nr);
3555 $$.c = abc_inclocal($$.c, nr);
3556 } else syntaxerror("internal error");
3558 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3559 c=abc_increment_i(c);
3565 c=converttype(c, type, $1.t);
3566 $$.c = toreadwrite($1.c, c, 0, 1);
3571 // TODO: use inclocal, like with ++
3572 E : E "--" { code_t*c = 0;
3573 classinfo_t*type = $1.t;
3574 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3575 c=abc_decrement_i(c);
3581 c=converttype(c, type, $1.t);
3582 $$.c = toreadwrite($1.c, c, 0, 1);
3586 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3587 classinfo_t*type = $2.t;
3588 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3589 c=abc_increment_i(c);
3595 c=converttype(c, type, $2.t);
3596 $$.c = toreadwrite($2.c, c, 0, 0);
3600 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3601 classinfo_t*type = $2.t;
3602 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3603 c=abc_decrement_i(c);
3609 c=converttype(c, type, $2.t);
3610 $$.c = toreadwrite($2.c, c, 0, 0);
3614 E : "super" '.' T_IDENTIFIER
3615 { if(!state->cls->info)
3616 syntaxerror("super keyword not allowed outside a class");
3617 classinfo_t*t = state->cls->info->superclass;
3618 if(!t) t = TYPE_OBJECT;
3620 memberinfo_t*f = findmember_nsset(t, $3, 1);
3622 MEMBER_MULTINAME(m, f, $3);
3624 $$.c = abc_getlocal_0($$.c);
3625 $$.c = abc_getsuper2($$.c, &m);
3626 $$.t = slotinfo_gettype((slotinfo_t*)f);
3629 E : '@' T_IDENTIFIER {
3631 $$.c = abc_pushundefined(0);
3633 as3_warning("ignored @ operator");
3636 E : E '.' '@' T_IDENTIFIER {
3637 // child attribute TODO
3638 $$.c = abc_pushundefined(0);
3640 as3_warning("ignored .@ operator");
3643 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3644 // namespace declaration TODO
3645 $$.c = abc_pushundefined(0);
3647 as3_warning("ignored :: operator");
3650 E : E ".." T_IDENTIFIER {
3652 $$.c = abc_pushundefined(0);
3654 as3_warning("ignored .. operator");
3657 E : E '.' '(' E ')' {
3659 $$.c = abc_pushundefined(0);
3661 as3_warning("ignored .() operator");
3664 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3668 E : E '.' T_IDENTIFIER {
3670 classinfo_t*t = $1.t;
3672 if(TYPE_IS_CLASS(t) && t->data) {
3677 if(t->subtype==INFOTYPE_UNRESOLVED) {
3678 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3680 memberinfo_t*f = findmember_nsset(t, $3, 1);
3682 if(f && !is_static != !(f->flags&FLAG_STATIC))
3684 if(f && f->slot && !noslot) {
3685 $$.c = abc_getslot($$.c, f->slot);
3687 MEMBER_MULTINAME(m, f, $3);
3688 $$.c = abc_getproperty2($$.c, &m);
3690 /* determine type */
3691 $$.t = slotinfo_gettype((slotinfo_t*)f);
3693 $$.c = abc_coerce_a($$.c);
3694 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3695 string_t*package = $1.c->data[0];
3696 char*package2 = concat3(package->str, ".", $3);
3698 slotinfo_t*a = registry_find(package->str, $3);
3701 } else if(dict_contains(state->import_toplevel_packages, package2) ||
3702 registry_ispackage(package2)) {
3704 $$.c->data[0] = string_new4(package2);
3707 syntaxerror("couldn't resolve %s", package2);
3710 /* when resolving a property on an unknown type, we do know the
3711 name of the property (and don't seem to need the package), but
3712 we need to make avm2 try out all access modes */
3713 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3714 $$.c = abc_getproperty2($$.c, &m);
3715 $$.c = abc_coerce_a($$.c);
3716 $$.t = registry_getanytype();
3720 VAR_READ : T_IDENTIFIER {
3722 /* Queue unresolved identifiers for checking against the parent
3723 function's variables.
3724 We consider everything which is not a local variable "unresolved".
3725 This encompasses class names, members of the surrounding class
3726 etc. which is *correct* because local variables of the parent function
3729 if(state->method->inner && !find_variable(state, $1)) {
3730 unknown_variable($1);
3733 /* let the compiler know that it might want to check the current directory/package
3734 for this identifier- maybe there's a file $1.as defining $1. */
3735 //as3_schedule_class_noerror(state->package, $1);
3744 /* look at variables */
3745 if((v = find_variable(state, $1))) {
3746 // $1 is a local variable
3747 $$.c = abc_getlocal($$.c, v->index);
3751 if((v = find_slot(state, $1))) {
3752 $$.c = abc_getscopeobject($$.c, 1);
3753 $$.c = abc_getslot($$.c, v->index);
3758 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3760 /* look at current class' members */
3761 if(state->cls && (f = findmember_nsset(state->cls->info, $1, 1)) &&
3762 (f->flags&FLAG_STATIC) >= i_am_static) {
3763 // $1 is a function in this class
3764 int var_is_static = (f->flags&FLAG_STATIC);
3766 if(f->kind == INFOTYPE_METHOD) {
3767 $$.t = TYPE_FUNCTION(f);
3771 if(var_is_static && !i_am_static) {
3772 /* access to a static member from a non-static location.
3773 do this via findpropstrict:
3774 there doesn't seem to be any non-lookup way to access
3775 static properties of a class */
3776 state->method->late_binding = 1;
3778 namespace_t ns = {f->access, ""};
3779 multiname_t m = {QNAME, &ns, 0, $1};
3780 $$.c = abc_findpropstrict2($$.c, &m);
3781 $$.c = abc_getproperty2($$.c, &m);
3783 } else if(f->slot>0) {
3784 $$.c = abc_getlocal_0($$.c);
3785 $$.c = abc_getslot($$.c, f->slot);
3788 namespace_t ns = {f->access, ""};
3789 multiname_t m = {QNAME, &ns, 0, $1};
3790 $$.c = abc_getlocal_0($$.c);
3791 $$.c = abc_getproperty2($$.c, &m);
3796 /* look at actual classes, in the current package and imported */
3797 if((a = find_class($1))) {
3802 /* look through package prefixes */
3803 if(dict_contains(state->import_toplevel_packages, $1) ||
3804 registry_ispackage($1)) {
3805 $$.c = abc___pushpackage__($$.c, $1);
3810 /* unknown object, let the avm2 resolve it */
3812 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3813 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3814 state->method->late_binding = 1;
3816 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3819 $$.c = abc_findpropstrict2($$.c, &m);
3820 $$.c = abc_getproperty2($$.c, &m);
3824 // ----------------- namespaces -------------------------------------------------
3826 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3828 NEW(namespace_decl_t,n);
3833 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3835 NEW(namespace_decl_t,n);
3840 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3842 NEW(namespace_decl_t,n);
3847 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3849 trie_put(active_namespaces, $2->name, (void*)$2->url);
3851 namespace_t access = modifiers2access(&$1);
3852 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3853 var->type = TYPE_NAMESPACE;
3855 ns.access = ACCESS_NAMESPACE;
3857 var->value = constant_new_namespace(&ns);
3862 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3864 const char*url = $3->name;
3865 varinfo_t*s = (varinfo_t*)$3;
3866 if(!s || s->kind != INFOTYPE_SLOT)
3867 syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
3868 if(!s->value || !NS_TYPE(s->value->type))
3869 syntaxerror("%s.%s is not a namespace", $3->package, $3->name);
3870 url = s->value->ns->name;
3872 trie_put(active_namespaces, $3->name, (void*)url);
3873 add_active_url(url);