3 Routines for compiling Flash2 AVM2 ABC Actionscript
5 Extension module for the rfxswf library.
6 Part of the swftools package.
8 Copyright (c) 2008 Matthias Kramm <kramm@quiss.org>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
30 #include "tokenizer.h"
45 enum yytokentype token;
47 classinfo_t*classinfo;
48 classinfo_list_t*classinfo_list;
50 slotinfo_list_t*slotinfo_list;
53 unsigned int number_uint;
57 //typedcode_list_t*value_list;
58 codeandnumber_t value_list;
64 for_start_t for_start;
65 abc_exception_t *exception;
68 namespace_decl_t* namespace_decl;
70 abc_exception_list_t *l;
76 %token<id> T_IDENTIFIER T_NAMESPACE
78 %token<regexp> T_REGEXP
80 %token<number_int> T_INT
81 %token<number_uint> T_UINT
82 %token<number_uint> T_BYTE
83 %token<number_uint> T_SHORT
84 %token<number_float> T_FLOAT
86 %token<id> T_FOR "for"
87 %token<id> T_WHILE "while"
89 %token<id> T_SWITCH "switch"
91 %token<token> KW_IMPLEMENTS "implements"
92 %token<token> KW_NAMESPACE "namespace"
93 %token<token> KW_PACKAGE "package"
94 %token<token> KW_PROTECTED "protected"
95 %token<token> KW_PUBLIC "public"
96 %token<token> KW_PRIVATE "private"
97 %token<token> KW_USE "use"
98 %token<token> KW_INTERNAL "internal"
99 %token<token> KW_NEW "new"
100 %token<token> KW_NATIVE "native"
101 %token<token> KW_FUNCTION "function"
102 %token<token> KW_FINALLY "finally"
103 %token<token> KW_UNDEFINED "undefined"
104 %token<token> KW_CONTINUE "continue"
105 %token<token> KW_CLASS "class"
106 %token<token> KW_CONST "const"
107 %token<token> KW_CATCH "catch"
108 %token<token> KW_CASE "case"
109 %token<token> KW_SET "set"
110 %token<token> KW_VOID "void"
111 %token<token> KW_THROW "throw"
112 %token<token> KW_STATIC "static"
113 %token<token> KW_WITH "with"
114 %token<token> KW_INSTANCEOF "instanceof"
115 %token<token> KW_IMPORT "import"
116 %token<token> KW_RETURN "return"
117 %token<token> KW_TYPEOF "typeof"
118 %token<token> KW_INTERFACE "interface"
119 %token<token> KW_NULL "null"
120 %token<token> KW_VAR "var"
121 %token<token> KW_DYNAMIC "dynamic"
122 %token<token> KW_OVERRIDE "override"
123 %token<token> KW_FINAL "final"
124 %token<token> KW_EACH "each"
125 %token<token> KW_GET "get"
126 %token<token> KW_TRY "try"
127 %token<token> KW_SUPER "super"
128 %token<token> KW_EXTENDS "extends"
129 %token<token> KW_FALSE "false"
130 %token<token> KW_TRUE "true"
131 %token<token> KW_BOOLEAN "Boolean"
132 %token<token> KW_UINT "uint"
133 %token<token> KW_INT "int"
134 %token<token> KW_NUMBER "Number"
135 %token<token> KW_STRING "String"
136 %token<token> KW_DEFAULT "default"
137 %token<token> KW_DELETE "delete"
138 %token<token> KW_IF "if"
139 %token<token> KW_ELSE "else"
140 %token<token> KW_BREAK "break"
141 %token<token> KW_IS "is"
142 %token<token> KW_IN "in"
143 %token<token> KW_AS "as"
145 %token<token> T_DICTSTART "{ (dictionary)"
146 %token<token> T_EQEQ "=="
147 %token<token> T_EQEQEQ "==="
148 %token<token> T_NE "!="
149 %token<token> T_NEE "!=="
150 %token<token> T_LE "<="
151 %token<token> T_GE ">="
152 %token<token> T_ORBY "|="
153 %token<token> T_DIVBY "/="
154 %token<token> T_MODBY "%="
155 %token<token> T_MULBY "*="
156 %token<token> T_PLUSBY "+="
157 %token<token> T_MINUSBY "-="
158 %token<token> T_XORBY "^="
159 %token<token> T_SHRBY ">>="
160 %token<token> T_SHLBY "<<="
161 %token<token> T_USHRBY ">>>="
162 %token<token> T_OROR "||"
163 %token<token> T_ANDAND "&&"
164 %token<token> T_COLONCOLON "::"
165 %token<token> T_MINUSMINUS "--"
166 %token<token> T_PLUSPLUS "++"
167 %token<token> T_DOTDOT ".."
168 %token<token> T_DOTDOTDOT "..."
169 %token<token> T_SHL "<<"
170 %token<token> T_USHR ">>>"
171 %token<token> T_SHR ">>"
173 %type <for_start> FOR_START
174 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
175 %type <namespace_decl> NAMESPACE_ID
176 %type <token> VARCONST
178 %type <code> CODEPIECE CODE_STATEMENT
179 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
180 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION SLOT_LIST ONE_SLOT
181 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
182 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
183 %type <exception> CATCH FINALLY
184 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
185 %type <code> CLASS_DECLARATION
186 %type <code> NAMESPACE_DECLARATION
187 %type <code> INTERFACE_DECLARATION
188 %type <code> VOIDEXPRESSION
189 %type <value> EXPRESSION NONCOMMAEXPRESSION
190 %type <value> MAYBEEXPRESSION
191 %type <value> E DELETE
192 %type <value> CONSTANT
193 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
194 %type <value> INNERFUNCTION
195 %type <code> USE_NAMESPACE
196 %type <code> FOR_INIT
198 %type <classinfo> MAYBETYPE
201 %type <params> PARAM_LIST
202 %type <params> MAYBE_PARAM_LIST
203 %type <flags> MAYBE_MODIFIERS
204 %type <flags> MODIFIER_LIST
205 %type <flags> MODIFIER
206 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
207 %type <classinfo_list> IMPLEMENTS_LIST
208 %type <classinfo> EXTENDS CLASS_SPEC
209 %type <classinfo_list> EXTENDS_LIST
211 %type <classinfo> CLASS PACKAGEANDCLASS
212 %type <classinfo_list> CLASS_SPEC_LIST
214 %type <classinfo> TYPE
215 //%type <token> VARIABLE
216 %type <value> VAR_READ
218 //%type <token> T_IDENTIFIER
219 %type <value> FUNCTIONCALL
220 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD
222 // precedence: from low to high
226 %left below_semicolon
229 %nonassoc below_assignment // for ?:, contrary to spec
230 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
237 %nonassoc "==" "!=" "===" "!=="
238 %nonassoc "is" "as" "in"
239 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
240 %left "<<" ">>" ">>>"
244 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
246 %nonassoc below_curly
250 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
253 %left above_identifier
257 // needed for "return" precedence:
258 %nonassoc T_STRING T_REGEXP
259 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
260 %nonassoc "false" "true" "null" "undefined" "super" "function"
267 static int a3_error(char*s)
269 syntaxerror("%s", s);
270 return 0; //make gcc happy
274 static char* concat2(const char* t1, const char* t2)
278 char*text = malloc(l1+l2+1);
279 memcpy(text , t1, l1);
280 memcpy(text+l1, t2, l2);
284 static char* concat3(const char* t1, const char* t2, const char* t3)
289 char*text = malloc(l1+l2+l3+1);
290 memcpy(text , t1, l1);
291 memcpy(text+l1, t2, l2);
292 memcpy(text+l1+l2, t3, l3);
297 typedef struct _import {
301 DECLARE_LIST(import);
303 DECLARE(methodstate);
304 DECLARE_LIST(methodstate);
306 typedef struct _classstate {
312 methodstate_t*static_init;
314 //code_t*static_init;
316 char has_constructor;
319 struct _methodstate {
329 dict_t*unresolved_variables;
332 char uses_parent_function;
337 int var_index; // for inner methods
338 int slot_index; // for inner methods
339 char is_a_slot; // for inner methods
344 abc_exception_list_t*exceptions;
346 methodstate_list_t*innerfunctions;
349 typedef struct _state {
354 import_list_t*wildcard_imports;
355 dict_t*import_toplevel_packages;
357 namespace_list_t*active_namespaces;
358 namespace_decl_list_t*new_namespaces;
359 dict_t*namespace2url;
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->new_namespaces = 0;
446 state->vars = dict_new();
447 state->old = oldstate;
449 state->namespace2url = 0;
451 static void state_has_imports()
453 state->wildcard_imports = list_clone(state->wildcard_imports);
454 state->imports = dict_clone(state->imports);
455 state->has_own_imports = 1;
457 static void import_toplevel(const char*package)
459 char* s = strdup(package);
461 dict_put(state->import_toplevel_packages, s, 0);
462 char*x = strrchr(s, '.');
470 static void state_destroy(state_t*state)
472 if(state->has_own_imports) {
473 list_free(state->wildcard_imports);
474 dict_destroy(state->imports);state->imports=0;
476 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
477 dict_destroy(state->imports);state->imports=0;
481 for(t=0;t<state->vars->hashsize;t++) {
482 dictentry_t*e =state->vars->slots[t];
484 free(e->data);e->data=0;
488 dict_destroy(state->vars);state->vars=0;
494 static void old_state()
496 if(!state || !state->old)
497 syntaxerror("invalid nesting");
498 state_t*leaving = state;
502 namespace_decl_list_t*nl=leaving->new_namespaces;
504 tokenizer_unregister_namespace(nl->namespace_decl->name);
507 if(leaving->namespace2url)
508 dict_destroy(leaving->namespace2url);
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 state->package = internal_filename_package = strdup(filename);
536 global->token2info = dict_lookup(global->file2token2info,
537 current_filename // use long version
539 if(!global->token2info) {
540 global->token2info = dict_new2(&ptr_type);
541 dict_put(global->file2token2info, current_filename, global->token2info);
545 state->method = rfx_calloc(sizeof(methodstate_t));
546 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
547 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
549 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
550 function_initvars(state->method, 0, 0, 1);
551 global->init = abc_initscript(global->file);
557 if(!state || state->level!=1) {
558 syntaxerror("unexpected end of file in pass %d", as3_pass);
562 code_t*header = method_header(state->method);
563 code_t*c = wrap_function(header, 0, global->init->method->body->code);
564 global->init->method->body->code = c;
565 free(state->method);state->method=0;
568 //free(state->package);state->package=0; // used in registry
569 state_destroy(state);state=0;
572 void initialize_parser()
574 global = rfx_calloc(sizeof(global_t));
575 global->file = abc_file_new();
576 global->file->flags &= ~ABCFILE_LAZY;
577 global->file2token2info = dict_new();
578 global->token2info = 0;
581 void* finish_parser()
583 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
585 global->token2info=0;
591 static void xx_scopetest()
593 /* findpropstrict doesn't just return a scope object- it
594 also makes it "active" somehow. Push local_0 on the
595 scope stack and read it back with findpropstrict, it'll
596 contain properties like "trace". Trying to find the same
597 property on a "vanilla" local_0 yields only a "undefined" */
598 //c = abc_findpropstrict(c, "[package]::trace");
600 /*c = abc_getlocal_0(c);
601 c = abc_findpropstrict(c, "[package]::trace");
603 c = abc_setlocal_1(c);
605 c = abc_pushbyte(c, 0);
606 c = abc_setlocal_2(c);
608 code_t*xx = c = abc_label(c);
609 c = abc_findpropstrict(c, "[package]::trace");
610 c = abc_pushstring(c, "prop:");
611 c = abc_hasnext2(c, 1, 2);
613 c = abc_setlocal_3(c);
614 c = abc_callpropvoid(c, "[package]::trace", 2);
615 c = abc_getlocal_3(c);
617 c = abc_iftrue(c,xx);*/
620 typedef struct _variable {
624 methodstate_t*is_inner_method;
627 static variable_t* find_variable(state_t*s, char*name)
631 v = dict_lookup(s->vars, name);
633 if(s->new_vars) break;
638 static variable_t* find_slot(state_t*s, const char*name)
640 if(s->method && s->method->slots)
641 return dict_lookup(s->method->slots, name);
645 static variable_t* find_variable_safe(state_t*s, char*name)
647 variable_t* v = find_variable(s, name);
649 syntaxerror("undefined variable: %s", name);
652 static char variable_exists(char*name)
654 return dict_contains(state->vars, name);
656 code_t*defaultvalue(code_t*c, classinfo_t*type);
658 static int alloc_local()
660 return state->method->variable_count++;
663 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
666 variable_t*v = find_slot(state, name);
672 v->index = alloc_local();
677 dict_put(state->vars, name, v);
681 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
683 return new_variable2(name, type, init, maybeslot)->index;
686 #define TEMPVARNAME "__as3_temp__"
687 static int gettempvar()
689 variable_t*v = find_variable(state, TEMPVARNAME);
692 return new_variable(TEMPVARNAME, 0, 0, 0);
695 code_t* var_block(code_t*body)
701 for(t=0;t<state->vars->hashsize;t++) {
702 dictentry_t*e = state->vars->slots[t];
704 variable_t*v = (variable_t*)e->data;
705 if(v->type && v->init) {
706 c = defaultvalue(c, v->type);
707 c = abc_setlocal(c, v->index);
708 k = abc_kill(k, v->index);
718 if(x->opcode== OPCODE___BREAK__ ||
719 x->opcode== OPCODE___CONTINUE__) {
720 /* link kill code before break/continue */
721 code_t*e = code_dup(k);
722 code_t*s = code_start(e);
734 c = code_append(c, body);
735 c = code_append(c, k);
739 void unknown_variable(char*name)
741 if(!state->method->unresolved_variables)
742 state->method->unresolved_variables = dict_new();
743 if(!dict_contains(state->method->unresolved_variables, name))
744 dict_put(state->method->unresolved_variables, name, 0);
747 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
749 static void parsererror(const char*file, int line, const char*f)
751 syntaxerror("internal error in %s, %s:%d", f, file, line);
755 static code_t* add_scope_code(code_t*c, methodstate_t*m)
757 if(m->uses_slots || (m->late_binding && !m->inner)) {
758 c = abc_getlocal_0(c);
759 c = abc_pushscope(c);
762 /* FIXME: does this need to be the same activation object as
763 in the function header? */
764 c = abc_newactivation(c);
765 c = abc_pushscope(c);
770 static code_t* method_header(methodstate_t*m)
774 c = add_scope_code(c, m);
776 methodstate_list_t*l = m->innerfunctions;
778 parserassert(l->methodstate->abc);
779 if(m->uses_slots && l->methodstate->is_a_slot) {
780 c = abc_getscopeobject(c, 1);
781 c = abc_newfunction(c, l->methodstate->abc);
783 c = abc_setlocal(c, l->methodstate->var_index);
784 c = abc_setslot(c, l->methodstate->slot_index);
786 c = abc_newfunction(c, l->methodstate->abc);
787 c = abc_setlocal(c, l->methodstate->var_index);
789 free(l->methodstate);l->methodstate=0;
793 c = code_append(c, m->header);
796 if(m->is_constructor && !m->has_super) {
797 // call default constructor
798 c = abc_getlocal_0(c);
799 c = abc_constructsuper(c, 0);
801 list_free(m->innerfunctions);
802 m->innerfunctions = 0;
807 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
809 c = code_append(c, header);
810 c = code_append(c, var_block(body));
811 /* append return if necessary */
812 if(!c || (c->opcode != OPCODE_RETURNVOID &&
813 c->opcode != OPCODE_RETURNVALUE)) {
814 c = abc_returnvoid(c);
820 static void startpackage(char*name)
823 /*printf("entering package \"%s\"\n", name);*/
824 state->package = strdup(name);
826 static void endpackage()
828 /*printf("leaving package \"%s\"\n", state->package);*/
830 //used e.g. in classinfo_register:
831 //free(state->package);state->package=0;
836 #define FLAG_PUBLIC 256
837 #define FLAG_PROTECTED 512
838 #define FLAG_PRIVATE 1024
839 #define FLAG_PACKAGEINTERNAL 2048
840 #define FLAG_NAMESPACE 4096
842 static namespace_t modifiers2access(modifiers_t*mod)
847 if(mod->flags&FLAG_NAMESPACE) {
848 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
849 syntaxerror("invalid combination of access levels and namespaces");
850 ns.access = ACCESS_NAMESPACE;
852 const char*url = (const char*)dict_lookup(state->namespace2url, mod->ns);
854 /* shouldn't happen- the tokenizer only reports something as a namespace
855 if it was already registered */
856 syntaxerror("unknown namespace: %s", mod->ns);
859 } else if(mod->flags&FLAG_PUBLIC) {
860 if(mod->flags&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
861 syntaxerror("invalid combination of access levels");
862 ns.access = ACCESS_PACKAGE;
863 } else if(mod->flags&FLAG_PRIVATE) {
864 if(mod->flags&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
865 syntaxerror("invalid combination of access levels");
866 ns.access = ACCESS_PRIVATE;
867 } else if(mod->flags&FLAG_PROTECTED) {
868 if(mod->flags&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
869 syntaxerror("invalid combination of access levels");
870 ns.access = ACCESS_PROTECTED;
872 ns.access = ACCESS_PACKAGEINTERNAL;
876 static slotinfo_t* find_class(const char*name);
878 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
883 index = new_variable("this", 0, 0, 0);
884 else if(!m->is_global)
885 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
887 index = new_variable("globalscope", 0, 0, 0);
890 parserassert(!index);
894 /* as variables and slots share the same number, make sure
895 that those variable indices are reserved. It's up to the
896 optimizer to later shuffle the variables down to lower
898 m->variable_count = m->uses_slots;
903 for(p=params->list;p;p=p->next) {
904 new_variable(p->param->name, p->param->type, 0, 1);
909 m->scope_code = add_scope_code(m->scope_code, m);
913 methodstate_list_t*l = m->innerfunctions;
915 methodstate_t*m = l->methodstate;
917 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
918 m->var_index = v->index;
919 m->slot_index = v->index;
920 v->is_inner_method = m;
925 if(as3_pass==2 && m->slots) {
926 /* exchange unresolved identifiers with the actual objects */
927 DICT_ITERATE_ITEMS(m->slots, char*, name, variable_t*, v) {
928 if(v->type && v->type->kind == INFOTYPE_UNRESOLVED) {
929 v->type = (classinfo_t*)registry_resolve((slotinfo_t*)v->type);
930 if(!v->type || v->type->kind != INFOTYPE_CLASS) {
931 syntaxerror("Couldn't find class %s", v->type->name);
939 char*as3_globalclass=0;
940 static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, classinfo_list_t*implements)
943 syntaxerror("inner classes now allowed");
948 classinfo_list_t*mlist=0;
950 if(mod->flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
951 syntaxerror("invalid modifier(s)");
953 if((mod->flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
954 syntaxerror("public and internal not supported at the same time.");
956 //if(!(mod->flags&FLAG_INTERFACE) && !extends) {
957 if(!(mod->flags&FLAG_INTERFACE) && !extends) {
958 // all classes extend object
959 extends = registry_getobjectclass();
962 /* create the class name, together with the proper attributes */
966 if(!(mod->flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
967 access = ACCESS_PRIVATE; package = internal_filename_package;
968 } else if(!(mod->flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
969 access = ACCESS_PACKAGEINTERNAL; package = state->package;
970 } else if(state->package!=internal_filename_package) {
971 access = ACCESS_PACKAGE; package = state->package;
973 syntaxerror("public classes only allowed inside a package");
977 state->cls = rfx_calloc(sizeof(classstate_t));
978 state->cls->init = rfx_calloc(sizeof(methodstate_t));
979 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
980 /* notice: we make no effort to initialize the top variable (local0) here,
981 even though it has special meaning. We just rely on the facat
982 that pass 1 won't do anything with variables */
984 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
986 /* set current method to constructor- all code within the class-level (except
987 static variable initializations) will be executed during construction time */
988 state->method = state->cls->init;
990 if(registry_find(package, classname)) {
991 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
993 /* build info struct */
994 int num_interfaces = (list_length(implements));
995 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
996 state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
999 classinfo_list_t*l = implements;
1000 for(l=implements;l;l=l->next) {
1001 state->cls->info->interfaces[pos++] = l->classinfo;
1006 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1008 state->method = state->cls->init;
1009 parserassert(state->cls && state->cls->info);
1011 function_initvars(state->cls->init, 0, 0, 1);
1012 function_initvars(state->cls->static_init, 0, 0, 0);
1014 if(extends && (extends->flags & FLAG_FINAL))
1015 syntaxerror("Can't extend final class '%s'", extends->name);
1018 while(state->cls->info->interfaces[pos]) {
1019 if(!(state->cls->info->interfaces[pos]->flags & FLAG_INTERFACE))
1020 syntaxerror("'%s' is not an interface",
1021 state->cls->info->interfaces[pos]->name);
1025 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
1026 state->cls->info->superclass = extends;
1028 /* generate the abc code for this class */
1029 MULTINAME(classname2,state->cls->info);
1030 multiname_t*extends2 = sig2mname(extends);
1032 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
1033 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
1034 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
1035 if(state->cls->info->flags&FLAG_INTERFACE) {
1036 abc_class_interface(state->cls->abc);
1039 abc_class_protectedNS(state->cls->abc, classname);
1041 for(mlist=implements;mlist;mlist=mlist->next) {
1042 MULTINAME(m, mlist->classinfo);
1043 abc_class_add_interface(state->cls->abc, &m);
1046 /* write the construction code for this class to the global init
1048 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
1050 abc_method_body_t*m = global->init->method->body;
1051 __ getglobalscope(m);
1052 classinfo_t*s = extends;
1057 //TODO: take a look at the current scope stack, maybe
1058 // we can re-use something
1063 multiname_t*s2 = sig2mname(s);
1065 multiname_destroy(s2);
1067 __ pushscope(m); count++;
1068 m->code = m->code->prev->prev; // invert
1070 /* continue appending after last op end */
1071 while(m->code && m->code->next) m->code = m->code->next;
1073 /* TODO: if this is one of *our* classes, we can also
1074 do a getglobalscope/getslot <nr> (which references
1075 the init function's slots) */
1077 __ getlex2(m, extends2);
1079 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
1080 stack is not the superclass */
1081 __ pushscope(m);count++;
1084 /* notice: we get a verify error #1107 if the top element on the scope
1085 stack is not the global object */
1087 __ pushscope(m);count++;
1089 __ newclass(m,state->cls->abc);
1093 __ setslot(m, slotindex);
1094 multiname_destroy(extends2);
1096 /* flash.display.MovieClip handling */
1098 if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
1099 if(state->package && state->package[0]) {
1100 as3_globalclass = concat3(state->package, ".", classname);
1102 as3_globalclass = strdup(classname);
1108 static int slotstate_varconst = 0;
1109 static modifiers_t*slotstate_flags = 0;
1110 static void setslotstate(modifiers_t* flags, int varconst)
1112 slotstate_varconst = varconst;
1113 slotstate_flags = flags;
1115 if(flags && flags->flags&FLAG_STATIC) {
1116 state->method = state->cls->static_init;
1118 state->method = state->cls->init;
1121 parserassert(state->method);
1125 static void endclass()
1128 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1130 c = abc_getlocal_0(c);
1131 c = abc_constructsuper(c, 0);
1132 state->cls->init->header = code_append(state->cls->init->header, c);
1133 state->cls->has_constructor=1;
1135 if(state->cls->init) {
1136 if(state->cls->info->flags&FLAG_INTERFACE) {
1137 if(state->cls->init->header)
1138 syntaxerror("interface can not have class-level code");
1140 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1141 code_t*c = method_header(state->cls->init);
1142 m->body->code = wrap_function(c, 0, m->body->code);
1145 if(state->cls->static_init) {
1146 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1147 code_t*c = method_header(state->cls->static_init);
1148 m->body->code = wrap_function(c, 0, m->body->code);
1155 void check_code_for_break(code_t*c)
1158 if(c->opcode == OPCODE___BREAK__) {
1159 char*name = string_cstr(c->data[0]);
1160 syntaxerror("Unresolved \"break %s\"", name);
1162 if(c->opcode == OPCODE___CONTINUE__) {
1163 char*name = string_cstr(c->data[0]);
1164 syntaxerror("Unresolved \"continue %s\"", name);
1166 if(c->opcode == OPCODE___PUSHPACKAGE__) {
1167 char*name = string_cstr(c->data[0]);
1168 syntaxerror("Can't reference a package (%s) as such", name);
1175 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1178 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1179 if(TYPE_IS_NUMBER(t)) {
1180 xassert(c->type == CONSTANT_FLOAT
1181 || c->type == CONSTANT_INT
1182 || c->type == CONSTANT_UINT);
1183 } else if(TYPE_IS_UINT(t)) {
1184 xassert(c->type == CONSTANT_UINT ||
1185 (c->type == CONSTANT_INT && c->i>=0));
1186 } else if(TYPE_IS_INT(t)) {
1187 xassert(c->type == CONSTANT_INT);
1188 } else if(TYPE_IS_BOOLEAN(t)) {
1189 xassert(c->type == CONSTANT_TRUE
1190 || c->type == CONSTANT_FALSE);
1194 static void check_override(memberinfo_t*m, int flags)
1198 if(m->parent == state->cls->info)
1199 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1201 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1202 if(m->access==ACCESS_PRIVATE)
1204 if(m->flags & FLAG_FINAL)
1205 syntaxerror("can't override final member %s", m->name);
1207 /* allow this. it's no issue.
1208 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1209 syntaxerror("can't override static member %s", m->name);*/
1211 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1212 syntaxerror("can't override non-static member %s with static declaration", m->name);
1214 if(!(flags&FLAG_OVERRIDE) && !(flags&FLAG_STATIC) && !(m->flags&FLAG_STATIC)) {
1215 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1216 if(m->kind == INFOTYPE_METHOD)
1217 syntaxerror("can't override without explicit 'override' declaration");
1219 syntaxerror("can't override '%s'", m->name);
1224 static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, char*name, params_t*params, classinfo_t*return_type, int slot)
1226 methodinfo_t*minfo = 0;
1227 namespace_t ns = modifiers2access(mod);
1230 minfo = methodinfo_register_global(ns.access, state->package, name);
1231 minfo->return_type = 0; // save this for pass 2
1232 } else if(getset != KW_GET && getset != KW_SET) {
1234 memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
1236 printf("%s.%s | %s.%s\n",
1237 m->package, m->name,
1239 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1241 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1242 minfo->return_type = 0; // save this for pass 2
1243 // getslot on a member slot only returns "undefined", so no need
1244 // to actually store these
1245 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1247 //class getter/setter
1248 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1250 if(getset == KW_GET) {
1252 } else if(params->list && params->list->param && !params->list->next) {
1253 type = params->list->param->type;
1255 syntaxerror("setter function needs to take exactly one argument");
1256 // not sure wether to look into superclasses here, too
1257 minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
1259 if(minfo->kind!=INFOTYPE_SLOT)
1260 syntaxerror("class already contains a method called '%s'", name);
1261 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1262 syntaxerror("class already contains a field called '%s'", name);
1263 if(minfo->subtype & gs)
1264 syntaxerror("getter/setter for '%s' already defined", name);
1265 /* make a setter or getter into a getset */
1266 minfo->subtype |= gs;
1269 FIXME: this check needs to be done in pass 2
1271 if((!minfo->return_type != !type) ||
1272 (minfo->return_type && type &&
1273 !strcmp(minfo->return_type->name, type->name))) {
1274 syntaxerror("different type in getter and setter: %s and %s",
1275 minfo->return_type?minfo->return_type->name:"*",
1276 type?type->name:"*");
1279 minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
1280 minfo->kind = INFOTYPE_SLOT; //hack
1281 minfo->subtype = gs;
1282 minfo->return_type = 0;
1284 /* can't assign a slot as getter and setter might have different slots */
1285 //minfo->slot = slot;
1287 if(mod->flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1288 if(mod->flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1289 if(mod->flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1294 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1296 //parserassert(state->method && state->method->info);
1298 methodstate_t*parent_method = state->method;
1301 return_type = 0; // not valid in pass 1
1305 state->new_vars = 1;
1308 state->method = rfx_calloc(sizeof(methodstate_t));
1309 state->method->inner = 1;
1310 state->method->variable_count = 0;
1311 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1313 NEW(methodinfo_t,minfo);
1314 minfo->kind = INFOTYPE_METHOD;
1315 minfo->access = ACCESS_PACKAGEINTERNAL;
1317 state->method->info = minfo;
1320 list_append(parent_method->innerfunctions, state->method);
1322 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1324 function_initvars(state->method, params, 0, 1);
1328 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1329 state->method->variable_count = 0;
1330 parserassert(state->method);
1332 state->method->info->return_type = return_type;
1333 function_initvars(state->method, params, 0, 1);
1337 static void startfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1338 params_t*params, classinfo_t*return_type)
1340 if(state->method && state->method->info) {
1341 syntaxerror("not able to start another method scope");
1344 state->new_vars = 1;
1347 state->method = rfx_calloc(sizeof(methodstate_t));
1348 state->method->has_super = 0;
1351 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1353 state->method->is_global = 1;
1354 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1356 if(state->method->is_constructor)
1357 name = "__as3_constructor__";
1359 state->method->info = registerfunction(getset, mod, name, params, return_type, 0);
1361 function_initvars(state->method, params, mod->flags, 1);
1363 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1367 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1368 state->method->variable_count = 0;
1369 parserassert(state->method);
1372 memberinfo_t*m = registry_findmember(state->cls->info, mod->ns, name, 2);
1373 check_override(m, mod->flags);
1377 state->cls->has_constructor |= state->method->is_constructor;
1380 state->method->info->return_type = return_type;
1381 function_initvars(state->method, params, mod->flags, 1);
1385 static abc_method_t* endfunction(modifiers_t*mod, enum yytokentype getset, char*name,
1386 params_t*params, classinfo_t*return_type, code_t*body)
1389 // store inner methods in variables
1390 function_initvars(state->method, 0, 0, 0);
1392 methodstate_list_t*ml = state->method->innerfunctions;
1394 dict_t*xvars = dict_new();
1397 methodstate_t*m = ml->methodstate;
1398 parserassert(m->inner);
1399 if(m->unresolved_variables) {
1400 dict_t*d = m->unresolved_variables;
1402 for(t=0;t<d->hashsize;t++) {
1403 dictentry_t*l = d->slots[t];
1405 /* check parent method's variables */
1407 if((v=find_variable(state, l->key))) {
1408 m->uses_parent_function = 1;
1409 state->method->uses_slots = 1;
1410 dict_put(xvars, l->key, 0);
1417 dict_destroy(m->unresolved_variables);
1418 m->unresolved_variables = 0;
1423 if(state->method->uses_slots) {
1424 state->method->slots = dict_new();
1426 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1427 if(!name) syntaxerror("internal error");
1428 if(v->index && dict_contains(xvars, name)) {
1431 if(v->is_inner_method) {
1432 v->is_inner_method->is_a_slot = 1;
1435 dict_put(state->method->slots, name, v);
1438 state->method->uses_slots = i;
1439 dict_destroy(state->vars);state->vars = 0;
1446 /*if(state->method->uses_parent_function){
1447 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1452 multiname_t*type2 = sig2mname(return_type);
1454 if(state->method->inner) {
1455 f = state->method->abc;
1456 abc_method_init(f, global->file, type2, 1);
1457 } else if(state->method->is_constructor) {
1458 f = abc_class_getconstructor(state->cls->abc, type2);
1459 } else if(!state->method->is_global) {
1460 namespace_t mname_ns = modifiers2access(mod);
1461 multiname_t mname = {QNAME, &mname_ns, 0, name};
1463 if(mod->flags&FLAG_STATIC)
1464 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1466 f = abc_class_method(state->cls->abc, type2, &mname);
1467 slot = f->trait->slot_id;
1469 namespace_t mname_ns = {state->method->info->access, state->package};
1470 multiname_t mname = {QNAME, &mname_ns, 0, name};
1472 f = abc_method_new(global->file, type2, 1);
1473 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1474 //abc_code_t*c = global->init->method->body->code;
1476 //flash doesn't seem to allow us to access function slots
1477 //state->method->info->slot = slot;
1479 if(mod->flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1480 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1481 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1482 if(params->varargs) f->flags |= METHOD_NEED_REST;
1486 for(p=params->list;p;p=p->next) {
1487 if(params->varargs && !p->next) {
1488 break; //varargs: omit last parameter in function signature
1490 multiname_t*m = sig2mname(p->param->type);
1491 list_append(f->parameters, m);
1492 if(p->param->value) {
1493 check_constant_against_type(p->param->type, p->param->value);
1494 opt=1;list_append(f->optional_parameters, p->param->value);
1496 syntaxerror("non-optional parameter not allowed after optional parameters");
1499 if(state->method->slots) {
1500 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1502 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1503 multiname_t*type = sig2mname(v->type);
1504 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1505 t->slot_id = v->index;
1510 check_code_for_break(body);
1512 /* Seems this works now.
1513 if(state->method->exceptions && state->method->uses_slots) {
1514 as3_warning("try/catch and activation not supported yet within the same method");
1518 f->body->code = body;
1519 f->body->exceptions = state->method->exceptions;
1520 } else { //interface
1522 syntaxerror("interface methods can't have a method body");
1532 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1537 void breakjumpsto(code_t*c, char*name, code_t*jump)
1540 if(c->opcode == OPCODE___BREAK__) {
1541 string_t*name2 = c->data[0];
1542 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1543 c->opcode = OPCODE_JUMP;
1550 void continuejumpsto(code_t*c, char*name, code_t*jump)
1553 if(c->opcode == OPCODE___CONTINUE__) {
1554 string_t*name2 = c->data[0];
1555 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1556 c->opcode = OPCODE_JUMP;
1564 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1565 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1566 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1568 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1570 if(!type1 || !type2)
1571 return registry_getanytype();
1572 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1573 return registry_getanytype();
1576 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1585 return registry_getanytype();
1587 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1592 return abc_coerce_a(c);
1596 // cast an "any" type to a specific type. subject to
1597 // runtime exceptions
1598 return abc_coerce2(c, &m);
1601 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1602 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1603 // allow conversion between number types
1604 return abc_coerce2(c, &m);
1606 //printf("%s.%s\n", from.package, from.name);
1607 //printf("%s.%s\n", to.package, to.name);
1609 classinfo_t*supertype = from;
1611 if(supertype == to) {
1612 // target type is one of from's superclasses
1613 return abc_coerce2(c, &m);
1616 while(supertype->interfaces[t]) {
1617 if(supertype->interfaces[t]==to) {
1618 // target type is one of from's interfaces
1619 return abc_coerce2(c, &m);
1623 supertype = supertype->superclass;
1625 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1627 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1629 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1632 as3_error("can't convert type %s%s%s to %s%s%s",
1633 from->package, from->package?".":"", from->name,
1634 to->package, to->package?".":"", to->name);
1638 code_t*defaultvalue(code_t*c, classinfo_t*type)
1640 if(TYPE_IS_INT(type)) {
1641 c = abc_pushbyte(c, 0);
1642 } else if(TYPE_IS_UINT(type)) {
1643 c = abc_pushuint(c, 0);
1644 } else if(TYPE_IS_FLOAT(type)) {
1646 } else if(TYPE_IS_BOOLEAN(type)) {
1647 c = abc_pushfalse(c);
1649 //c = abc_pushundefined(c);
1651 c = abc_pushnull(c);
1653 c = abc_coerce2(c, &m);
1658 char is_pushundefined(code_t*c)
1660 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1663 static const char* get_package_from_name(const char*name)
1665 /* try explicit imports */
1666 dictentry_t* e = dict_get_slot(state->imports, name);
1668 if(!strcmp(e->key, name)) {
1669 slotinfo_t*c = (slotinfo_t*)e->data;
1670 if(c) return c->package;
1676 static namespace_list_t*get_current_imports()
1678 namespace_list_t*searchlist = 0;
1680 list_append(searchlist, namespace_new_package(state->package));
1682 import_list_t*l = state->wildcard_imports;
1684 namespace_t*ns = namespace_new_package(l->import->package);
1685 list_append(searchlist, ns);
1688 list_append(searchlist, namespace_new_package(""));
1689 list_append(searchlist, namespace_new_package(internal_filename_package));
1693 static slotinfo_t* find_class(const char*name)
1697 c = registry_find(state->package, name);
1700 /* try explicit imports */
1701 dictentry_t* e = dict_get_slot(state->imports, name);
1704 if(!strcmp(e->key, name)) {
1705 c = (slotinfo_t*)e->data;
1711 /* try package.* imports */
1712 import_list_t*l = state->wildcard_imports;
1714 //printf("does package %s contain a class %s?\n", l->import->package, name);
1715 c = registry_find(l->import->package, name);
1720 /* try global package */
1721 c = registry_find("", name);
1724 /* try local "filename" package */
1725 c = registry_find(internal_filename_package, name);
1730 typedcode_t push_class(slotinfo_t*a)
1735 if(a->access == ACCESS_PACKAGEINTERNAL &&
1736 strcmp(a->package, state->package) &&
1737 strcmp(a->package, internal_filename_package)
1739 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
1740 infotypename(a), a->name, a->package, state->package);
1743 if(a->kind != INFOTYPE_CLASS) {
1745 x.c = abc_findpropstrict2(x.c, &m);
1746 x.c = abc_getproperty2(x.c, &m);
1747 if(a->kind == INFOTYPE_METHOD) {
1748 methodinfo_t*f = (methodinfo_t*)a;
1749 x.t = TYPE_FUNCTION(f);
1751 varinfo_t*v = (varinfo_t*)a;
1755 classinfo_t*c = (classinfo_t*)a;
1757 x.c = abc_getglobalscope(x.c);
1758 x.c = abc_getslot(x.c, c->slot);
1761 x.c = abc_getlex2(x.c, &m);
1763 x.t = TYPE_CLASS(c);
1768 static char is_getlocal(code_t*c)
1770 if(!c || c->prev || c->next)
1772 return(c->opcode == OPCODE_GETLOCAL
1773 || c->opcode == OPCODE_GETLOCAL_0
1774 || c->opcode == OPCODE_GETLOCAL_1
1775 || c->opcode == OPCODE_GETLOCAL_2
1776 || c->opcode == OPCODE_GETLOCAL_3);
1778 static int getlocalnr(code_t*c)
1780 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1781 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1782 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1783 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1784 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1785 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1789 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1793 [prefix code] [read instruction]
1797 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1799 if(in && in->opcode == OPCODE_COERCE_A) {
1800 in = code_cutlast(in);
1803 syntaxerror("internal error");
1805 /* chop off read instruction */
1809 prefix = r->prev;r->prev = 0;
1815 char use_temp_var = readbefore;
1817 /* generate the write instruction, and maybe append a dup to the prefix code */
1818 code_t* write = abc_nop(0);
1819 if(r->opcode == OPCODE_GETPROPERTY) {
1820 write->opcode = OPCODE_SETPROPERTY;
1821 multiname_t*m = (multiname_t*)r->data[0];
1822 write->data[0] = multiname_clone(m);
1823 if(m->type == QNAME || m->type == MULTINAME) {
1825 prefix = abc_dup(prefix); // we need the object, too
1828 } else if(m->type == MULTINAMEL) {
1830 /* dupping two values on the stack requires 5 operations and one register-
1831 couldn't adobe just have given us a dup2? */
1832 int temp = gettempvar();
1833 prefix = abc_setlocal(prefix, temp);
1834 prefix = abc_dup(prefix);
1835 prefix = abc_getlocal(prefix, temp);
1836 prefix = abc_swap(prefix);
1837 prefix = abc_getlocal(prefix, temp);
1839 prefix = abc_kill(prefix, temp);
1843 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1845 } else if(r->opcode == OPCODE_GETSLOT) {
1846 write->opcode = OPCODE_SETSLOT;
1847 write->data[0] = r->data[0];
1849 prefix = abc_dup(prefix); // we need the object, too
1852 } else if(r->opcode == OPCODE_GETLOCAL) {
1853 write->opcode = OPCODE_SETLOCAL;
1854 write->data[0] = r->data[0];
1855 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1856 write->opcode = OPCODE_SETLOCAL_0;
1857 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1858 write->opcode = OPCODE_SETLOCAL_1;
1859 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1860 write->opcode = OPCODE_SETLOCAL_2;
1861 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1862 write->opcode = OPCODE_SETLOCAL_3;
1863 } else if(r->opcode == OPCODE_GETSUPER) {
1864 write->opcode = OPCODE_SETSUPER;
1865 multiname_t*m = (multiname_t*)r->data[0];
1866 write->data[0] = multiname_clone(m);
1869 syntaxerror("illegal lvalue: can't assign a value to this expression");
1876 /* with getproperty/getslot, we have to be extra careful not
1877 to execute the read code twice, as it might have side-effects
1878 (e.g. if the property is in fact a setter/getter combination)
1880 So read the value, modify it, and write it again,
1881 using prefix only once and making sure (by using a temporary
1882 register) that the return value is what we just wrote */
1883 temp = gettempvar();
1884 c = code_append(c, prefix);
1885 c = code_append(c, r);
1888 c = abc_setlocal(c, temp);
1890 c = code_append(c, middlepart);
1893 c = abc_setlocal(c, temp);
1895 c = code_append(c, write);
1896 c = abc_getlocal(c, temp);
1897 c = abc_kill(c, temp);
1899 /* if we're allowed to execute the read code twice *and*
1900 the middlepart doesn't modify the code, things are easier.
1902 code_t* r2 = code_dup(r);
1903 //c = code_append(c, prefix);
1904 parserassert(!prefix);
1905 c = code_append(c, r);
1906 c = code_append(c, middlepart);
1907 c = code_append(c, write);
1908 c = code_append(c, r2);
1911 /* even smaller version: overwrite the value without reading
1915 c = code_append(c, prefix);
1918 c = code_append(c, middlepart);
1919 c = code_append(c, write);
1920 c = code_append(c, r);
1923 temp = gettempvar();
1925 c = code_append(c, prefix);
1927 c = code_append(c, middlepart);
1929 c = abc_setlocal(c, temp);
1930 c = code_append(c, write);
1931 c = abc_getlocal(c, temp);
1932 c = abc_kill(c, temp);
1938 char is_break_or_jump(code_t*c)
1942 if(c->opcode == OPCODE_JUMP ||
1943 c->opcode == OPCODE___BREAK__ ||
1944 c->opcode == OPCODE___CONTINUE__ ||
1945 c->opcode == OPCODE_THROW ||
1946 c->opcode == OPCODE_RETURNVOID ||
1947 c->opcode == OPCODE_RETURNVALUE) {
1953 void register_namespace(const char*name, const char*url)
1955 NEW(namespace_decl_t,n);
1958 if(!state->namespace2url) {
1959 state->namespace2url = dict_new();
1961 dict_put(state->namespace2url, name, url);
1962 list_append(state->new_namespaces, n);
1963 tokenizer_register_namespace(name);
1966 #define IS_FINALLY_TARGET(op) \
1967 ((op) == OPCODE___CONTINUE__ || \
1968 (op) == OPCODE___BREAK__ || \
1969 (op) == OPCODE_RETURNVOID || \
1970 (op) == OPCODE_RETURNVALUE || \
1971 (op) == OPCODE___RETHROW__)
1973 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1975 #define NEED_EXTRA_STACK_ARG
1976 code_t*finally_label = abc_nop(0);
1977 NEW(lookupswitch_t, l);
1983 code_t*prev = i->prev;
1984 if(IS_FINALLY_TARGET(i->opcode)) {
1987 if(i->opcode == OPCODE___RETHROW__ ||
1988 i->opcode == OPCODE_RETURNVALUE) {
1989 if(i->opcode == OPCODE___RETHROW__)
1990 i->opcode = OPCODE_THROW;
1992 p = abc_coerce_a(p);
1993 p = abc_setlocal(p, tempvar);
1995 p = abc_pushbyte(p, count++);
1996 p = abc_jump(p, finally_label);
1997 code_t*target = p = abc_label(p);
1998 #ifdef NEED_EXTRA_STACK_ARG
2002 p = abc_getlocal(p, tempvar);
2005 p->next = i;i->prev = p;
2006 list_append(l->targets, target);
2012 c = abc_pushbyte(c, -1);
2013 c = code_append(c, finally_label);
2014 c = code_append(c, finally);
2016 #ifdef NEED_EXTRA_STACK_ARG
2019 c = abc_lookupswitch(c, l);
2020 c = l->def = abc_label(c);
2021 #ifdef NEED_EXTRA_STACK_ARG
2028 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
2032 code_t*prev = i->prev;
2033 if(IS_FINALLY_TARGET(i->opcode)) {
2034 if(i->opcode == OPCODE___RETHROW__)
2035 i->opcode = OPCODE_THROW;
2036 code_t*end = code_dup(finally);
2037 code_t*start = code_start(end);
2038 if(prev) prev->next = start;
2045 return code_append(c, finally);
2048 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
2054 int num_insertion_points=0;
2056 if(IS_FINALLY_TARGET(i->opcode))
2057 num_insertion_points++;
2064 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
2069 int simple_version_cost = (1+num_insertion_points)*code_size;
2070 int lookup_version_cost = 4*num_insertion_points + 5;
2072 if(cantdup || simple_version_cost > lookup_version_cost) {
2073 //printf("(use lookup) simple=%d > lookup=%d\n", simple_version_cost, lookup_version_cost);
2074 return insert_finally_lookup(c, finally, tempvar);
2076 //printf("(use simple) simple=%d < lookup=%d\n", simple_version_cost, lookup_version_cost);
2077 return insert_finally_simple(c, finally, tempvar);
2081 #define PASS1 }} if(as3_pass == 1) {{
2082 #define PASS1END }} if(as3_pass == 2) {{
2083 #define PASS2 }} if(as3_pass == 2) {{
2084 #define PASS12 }} {{
2085 #define PASS12END }} if(as3_pass == 2) {{
2091 /* ------------ code blocks / statements ---------------- */
2093 PROGRAM: MAYBE_PROGRAM_CODE_LIST
2095 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
2096 PROGRAM_CODE_LIST: PROGRAM_CODE
2097 | PROGRAM_CODE_LIST PROGRAM_CODE
2099 PROGRAM_CODE: PACKAGE_DECLARATION
2100 | INTERFACE_DECLARATION
2102 | FUNCTION_DECLARATION
2105 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
2108 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
2109 INPACKAGE_CODE_LIST: INPACKAGE_CODE
2110 | INPACKAGE_CODE_LIST INPACKAGE_CODE
2112 INPACKAGE_CODE: INTERFACE_DECLARATION
2114 | FUNCTION_DECLARATION
2117 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
2120 MAYBECODE: CODE {$$=$1;}
2121 MAYBECODE: {$$=code_new();}
2123 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
2124 CODE: CODEPIECE {$$=$1;}
2126 // code which may appear outside of methods
2127 CODE_STATEMENT: IMPORT
2129 CODE_STATEMENT: FOR_IN
2130 CODE_STATEMENT: WHILE
2131 CODE_STATEMENT: DO_WHILE
2132 CODE_STATEMENT: SWITCH
2134 CODE_STATEMENT: WITH
2136 CODE_STATEMENT: VOIDEXPRESSION
2137 CODE_STATEMENT: USE_NAMESPACE
2138 CODE_STATEMENT: NAMESPACE_DECLARATION
2139 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
2140 CODE_STATEMENT: '{' '}' {$$=0;}
2142 // code which may appear in methods
2143 CODEPIECE: ';' {$$=0;}
2144 CODEPIECE: CODE_STATEMENT
2145 CODEPIECE: VARIABLE_DECLARATION
2150 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
2152 //CODEBLOCK : '{' CODE '}' {$$=$2;}
2153 //CODEBLOCK : '{' '}' {$$=0;}
2154 CODEBLOCK : CODEPIECE ';' {$$=$1;}
2155 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
2157 /* ------------ package init code ------------------- */
2159 PACKAGE_INITCODE: CODE_STATEMENT {
2160 code_t**cc = &global->init->method->body->code;
2161 *cc = code_append(*cc, $1);
2164 /* ------------ conditional compilation ------------- */
2166 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
2168 /* ------------ variables --------------------------- */
2170 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
2171 | {$$.c=abc_pushundefined(0);
2175 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
2176 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
2178 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
2179 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
2181 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2184 if(variable_exists($1))
2185 syntaxerror("Variable %s already defined", $1);
2187 new_variable($1, 0, 1, 0);
2190 if(!is_subtype_of($3.t, $2)) {
2191 syntaxerror("Can't convert %s to %s", $3.t->name,
2197 if(state->method->uses_slots) {
2198 variable_t* v = find_slot(state, $1);
2200 // this variable is stored in a slot
2208 index = new_variable($1, $2, 1, 0);
2211 $$ = slot?abc_getscopeobject(0, 1):0;
2214 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2215 $$ = code_append($$, $3.c);
2216 $$ = converttype($$, $3.t, $2);
2219 $$ = defaultvalue($$, $2);
2222 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2223 $$ = code_append($$, $3.c);
2224 $$ = abc_coerce_a($$);
2226 // don't do anything
2234 $$ = abc_setslot($$, index);
2236 $$ = abc_setlocal($$, index);
2240 /* ------------ control flow ------------------------- */
2242 MAYBEELSE: %prec below_else {$$ = code_new();}
2243 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2244 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2246 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2249 $$ = code_append($$, $4.c);
2250 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2252 $$ = code_append($$, $6);
2254 myjmp = $$ = abc_jump($$, 0);
2256 myif->branch = $$ = abc_nop($$);
2258 $$ = code_append($$, $7);
2259 myjmp->branch = $$ = abc_nop($$);
2265 FOR_INIT : {$$=code_new();}
2266 FOR_INIT : VARIABLE_DECLARATION
2267 FOR_INIT : VOIDEXPRESSION
2269 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2270 // (I don't see any easy way to revolve this conflict otherwise, as we
2271 // can't touch VAR_READ without upsetting the precedence about "return")
2272 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2273 PASS1 $$=$2;new_variable($2,0,1,0);
2274 PASS2 $$=$2;new_variable($2,$3,1,0);
2276 FOR_IN_INIT : T_IDENTIFIER {
2281 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2282 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2284 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2285 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2287 $$ = code_append($$, $2);
2288 code_t*loopstart = $$ = abc_label($$);
2289 $$ = code_append($$, $4.c);
2290 code_t*myif = $$ = abc_iffalse($$, 0);
2291 $$ = code_append($$, $8);
2292 code_t*cont = $$ = abc_nop($$);
2293 $$ = code_append($$, $6);
2294 $$ = abc_jump($$, loopstart);
2295 code_t*out = $$ = abc_nop($$);
2296 breakjumpsto($$, $1.name, out);
2297 continuejumpsto($$, $1.name, cont);
2304 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2305 variable_t*var = find_variable(state, $2);
2307 syntaxerror("variable %s not known in this scope", $2);
2310 char*tmp1name = concat2($2, "__tmp1__");
2311 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2312 char*tmp2name = concat2($2, "__array__");
2313 int array = new_variable(tmp1name, 0, 0, 0);
2316 $$ = code_append($$, $4.c);
2317 $$ = abc_coerce_a($$);
2318 $$ = abc_setlocal($$, array);
2319 $$ = abc_pushbyte($$, 0);
2320 $$ = abc_setlocal($$, it);
2322 code_t*loopstart = $$ = abc_label($$);
2324 $$ = abc_hasnext2($$, array, it);
2325 code_t*myif = $$ = abc_iffalse($$, 0);
2326 $$ = abc_getlocal($$, array);
2327 $$ = abc_getlocal($$, it);
2329 $$ = abc_nextname($$);
2331 $$ = abc_nextvalue($$);
2332 $$ = converttype($$, 0, var->type);
2333 $$ = abc_setlocal($$, var->index);
2335 $$ = code_append($$, $6);
2336 $$ = abc_jump($$, loopstart);
2338 code_t*out = $$ = abc_nop($$);
2339 breakjumpsto($$, $1.name, out);
2340 continuejumpsto($$, $1.name, loopstart);
2352 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2356 code_t*myjmp = $$ = abc_jump($$, 0);
2357 code_t*loopstart = $$ = abc_label($$);
2358 $$ = code_append($$, $6);
2359 code_t*cont = $$ = abc_nop($$);
2360 myjmp->branch = cont;
2361 $$ = code_append($$, $4.c);
2362 $$ = abc_iftrue($$, loopstart);
2363 code_t*out = $$ = abc_nop($$);
2364 breakjumpsto($$, $1, out);
2365 continuejumpsto($$, $1, cont);
2371 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2373 code_t*loopstart = $$ = abc_label($$);
2374 $$ = code_append($$, $3);
2375 code_t*cont = $$ = abc_nop($$);
2376 $$ = code_append($$, $6.c);
2377 $$ = abc_iftrue($$, loopstart);
2378 code_t*out = $$ = abc_nop($$);
2379 breakjumpsto($$, $1, out);
2380 continuejumpsto($$, $1, cont);
2386 BREAK : "break" %prec prec_none {
2387 $$ = abc___break__(0, "");
2389 BREAK : "break" T_IDENTIFIER {
2390 $$ = abc___break__(0, $2);
2392 CONTINUE : "continue" %prec prec_none {
2393 $$ = abc___continue__(0, "");
2395 CONTINUE : "continue" T_IDENTIFIER {
2396 $$ = abc___continue__(0, $2);
2399 MAYBE_CASE_LIST : {$$=0;}
2400 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2401 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2402 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2403 CASE_LIST: CASE {$$=$1;}
2404 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2406 CASE: "case" E ':' MAYBECODE {
2407 $$ = abc_getlocal(0, state->switch_var);
2408 $$ = code_append($$, $2.c);
2409 code_t*j = $$ = abc_ifne($$, 0);
2410 $$ = code_append($$, $4);
2411 if($$->opcode != OPCODE___BREAK__) {
2412 $$ = abc___fallthrough__($$, "");
2414 code_t*e = $$ = abc_nop($$);
2417 DEFAULT: "default" ':' MAYBECODE {
2420 SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
2422 $$ = abc_setlocal($$, state->switch_var);
2423 $$ = code_append($$, $7);
2425 code_t*out = $$ = abc_kill($$, state->switch_var);
2426 breakjumpsto($$, $1, out);
2428 code_t*c = $$,*lastblock=0;
2430 if(c->opcode == OPCODE_IFNE) {
2431 if(!c->next) syntaxerror("internal error in fallthrough handling");
2433 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2435 c->opcode = OPCODE_JUMP;
2436 c->branch = lastblock;
2438 /* fall through end of switch */
2439 c->opcode = OPCODE_NOP;
2449 /* ------------ try / catch /finally ---------------- */
2451 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2452 state->exception_name=$3;
2453 PASS1 new_variable($3, 0, 0, 0);
2454 PASS2 new_variable($3, $4, 0, 0);
2457 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2458 multiname_t name = {QNAME, &name_ns, 0, $3};
2460 NEW(abc_exception_t, e)
2461 e->exc_type = sig2mname($4);
2462 e->var_name = multiname_clone(&name);
2466 int i = find_variable_safe(state, $3)->index;
2467 e->target = c = abc_nop(0);
2468 c = abc_setlocal(c, i);
2469 c = code_append(c, code_dup(state->method->scope_code));
2470 c = code_append(c, $8);
2476 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2481 NEW(abc_exception_t, e)
2482 e->exc_type = 0; //all exceptions
2483 e->var_name = 0; //no name
2486 e->to = code_append(e->to, $4);
2492 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2493 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2494 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2495 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2499 list_append($$.l,$2);
2500 $$.finally = $2->to;$2->to=0;
2503 CATCH_FINALLY_LIST: FINALLY {
2507 list_append($$.l,$1);
2508 $$.finally = $1->to;$1->to=0;
2512 TRY : "try" '{' {PASS12 new_state();
2513 state->method->has_exceptions=1;
2514 state->method->late_binding=1;//for invariant scope_code
2515 } MAYBECODE '}' CATCH_FINALLY_LIST {
2516 code_t*out = abc_nop(0);
2518 code_t*start = abc_nop(0);
2519 $$ = code_append(start, $4);
2520 if(!is_break_or_jump($4)) {
2521 $$ = abc_jump($$, out);
2523 code_t*end = $$ = abc_nop($$);
2527 tmp = new_variable("__finally__", 0, 0, 0);
2529 abc_exception_list_t*l = $6.l;
2532 abc_exception_t*e = l->abc_exception;
2534 $$ = code_append($$, e->target);
2535 $$ = abc_jump($$, out);
2537 parserassert((ptroff_t)$6.finally);
2539 e->target = $$ = abc_nop($$);
2540 $$ = code_append($$, code_dup(state->method->scope_code));
2541 $$ = abc___rethrow__($$);
2549 $$ = code_append($$, out);
2551 $$ = insert_finally($$, $6.finally, tmp);
2553 list_concat(state->method->exceptions, $6.l);
2559 /* ------------ throw ------------------------------- */
2561 THROW : "throw" EXPRESSION {
2565 THROW : "throw" %prec prec_none {
2566 if(!state->exception_name)
2567 syntaxerror("re-throw only possible within a catch block");
2568 variable_t*v = find_variable(state, state->exception_name);
2570 $$=abc_getlocal($$, v->index);
2574 /* ------------ with -------------------------------- */
2576 WITH_HEAD : "with" '(' EXPRESSION ')' {
2578 if(state->method->has_exceptions) {
2579 int v = alloc_local();
2580 state->method->scope_code = abc_getlocal(state->method->scope_code, v);
2581 state->method->scope_code = abc_pushwith(state->method->scope_code);
2586 WITH : WITH_HEAD CODEBLOCK {
2587 /* remove getlocal;pushwith from scope code again */
2588 state->method->scope_code = code_cutlast(code_cutlast(state->method->scope_code));
2591 if(state->method->has_exceptions) {
2593 $$ = abc_setlocal($$, $1.number);
2595 $$ = abc_pushwith($$);
2596 $$ = code_append($$, $2);
2597 $$ = abc_popscope($$);
2601 /* ------------ packages and imports ---------------- */
2603 X_IDENTIFIER: T_IDENTIFIER
2604 | "package" {PASS12 $$="package";}
2605 | T_NAMESPACE {PASS12 $$=$1;}
2607 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2608 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2610 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2611 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2612 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2613 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2615 IMPORT : "import" PACKAGEANDCLASS {
2617 slotinfo_t*s = registry_find($2->package, $2->name);
2618 if(!s && as3_pass==1) {// || !(s->flags&FLAG_BUILTIN)) {
2619 as3_schedule_class($2->package, $2->name);
2625 syntaxerror("Couldn't import class\n");
2626 state_has_imports();
2627 dict_put(state->imports, c->name, c);
2628 import_toplevel(c->package);
2631 IMPORT : "import" PACKAGE '.' '*' {
2633 if(strncmp("flash.", $2, 6) && as3_pass==1) {
2634 as3_schedule_package($2);
2640 state_has_imports();
2641 list_append(state->wildcard_imports, i);
2642 import_toplevel(i->package);
2646 /* ------------ classes and interfaces (header) -------------- */
2648 MAYBE_MODIFIERS : %prec above_function {PASS12 $$.flags=0;$$.ns=0;}
2649 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2650 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2651 MODIFIER_LIST : MODIFIER_LIST MODIFIER {
2653 $$.flags=$1.flags|$2.flags;
2654 if($1.ns && $2.ns) syntaxerror("only one namespace allowed in one declaration");
2655 $$.ns=$1.ns?$1.ns:$2.ns;
2659 MODIFIER : KW_PUBLIC {PASS12 $$.flags=FLAG_PUBLIC;$$.ns=0;}
2660 | KW_PRIVATE {PASS12 $$.flags=FLAG_PRIVATE;$$.ns=0;}
2661 | KW_PROTECTED {PASS12 $$.flags=FLAG_PROTECTED;$$.ns=0;}
2662 | KW_STATIC {PASS12 $$.flags=FLAG_STATIC;$$.ns=0;}
2663 | KW_DYNAMIC {PASS12 $$.flags=FLAG_DYNAMIC;$$.ns=0;}
2664 | KW_FINAL {PASS12 $$.flags=FLAG_FINAL;$$.ns=0;}
2665 | KW_OVERRIDE {PASS12 $$.flags=FLAG_OVERRIDE;$$.ns=0;}
2666 | KW_NATIVE {PASS12 $$.flags=FLAG_NATIVE;$$.ns=0;}
2667 | KW_INTERNAL {PASS12 $$.flags=FLAG_PACKAGEINTERNAL;$$.ns=0;}
2668 | T_NAMESPACE {PASS12 $$.flags=FLAG_NAMESPACE;
2672 EXTENDS : {PASS12 $$=0;}
2673 EXTENDS : KW_EXTENDS CLASS_SPEC {PASS12 $$=$2;}
2675 EXTENDS_LIST : {PASS12 $$=list_new();}
2676 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2678 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2679 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2681 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2682 EXTENDS IMPLEMENTS_LIST
2683 '{' {PASS12 startclass(&$1,$3,$4,$5);}
2685 '}' {PASS12 endclass();$$=0;}
2687 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2689 '{' {PASS12 $1.flags|=FLAG_INTERFACE;
2690 startclass(&$1,$3,0,$4);}
2691 MAYBE_INTERFACE_BODY
2692 '}' {PASS12 endclass();$$=0;}
2694 /* ------------ classes and interfaces (body) -------------- */
2697 MAYBE_CLASS_BODY : CLASS_BODY
2698 CLASS_BODY : CLASS_BODY_ITEM
2699 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2700 CLASS_BODY_ITEM : ';'
2701 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2702 CLASS_BODY_ITEM : SLOT_DECLARATION
2703 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2705 CLASS_BODY_ITEM : CODE_STATEMENT {
2706 code_t*c = state->cls->static_init->header;
2707 c = code_append(c, $1);
2708 state->cls->static_init->header = c;
2711 MAYBE_INTERFACE_BODY :
2712 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2713 INTERFACE_BODY : IDECLARATION
2714 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2716 IDECLARATION : "var" T_IDENTIFIER {
2717 syntaxerror("variable declarations not allowed in interfaces");
2719 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2721 $1.flags |= FLAG_PUBLIC;
2722 if($1.flags&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2723 syntaxerror("invalid method modifiers: interface methods always need to be public");
2725 startfunction(&$1,$3,$4,&$6,$8);
2726 endfunction(&$1,$3,$4,&$6,$8, 0);
2727 list_deep_free($6.list);
2730 /* ------------ classes and interfaces (body, slots ) ------- */
2732 VARCONST: "var" | "const"
2734 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
2736 SLOT_LIST: ONE_SLOT {$$ = $1;}
2737 SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
2739 ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
2741 int flags = slotstate_flags->flags;
2742 namespace_t ns = modifiers2access(slotstate_flags);
2744 varinfo_t* info = 0;
2746 memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
2748 check_override(i, flags);
2750 info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
2752 slotinfo_t*i = registry_find(state->package, $1);
2754 syntaxerror("package %s already contains '%s'", state->package, $1);
2756 if(ns.name && ns.name[0]) {
2757 syntaxerror("namespaces not allowed on package-level variables");
2759 info = varinfo_register_global(ns.access, state->package, $1);
2763 info->flags = flags;
2766 multiname_t mname = {QNAME, &ns, 0, $1};
2768 trait_list_t**traits;
2772 ns.name = state->package;
2773 traits = &global->init->traits;
2774 code = &global->init->method->body->code;
2775 } else if(flags&FLAG_STATIC) {
2777 traits = &state->cls->abc->static_traits;
2778 code = &state->cls->static_init->header;
2780 // instance variable
2781 traits = &state->cls->abc->traits;
2782 code = &state->cls->init->header;
2788 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2790 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2792 info->slot = t->slot_id;
2794 /* initalization code (if needed) */
2796 if($3.c && !is_pushundefined($3.c)) {
2797 c = abc_getlocal_0(c);
2798 c = code_append(c, $3.c);
2799 c = converttype(c, $3.t, $2);
2800 c = abc_setslot(c, t->slot_id);
2803 *code = code_append(*code, c);
2805 if(slotstate_varconst==KW_CONST) {
2806 t->kind= TRAIT_CONST;
2812 /* ------------ constants -------------------------------------- */
2814 MAYBESTATICCONSTANT: {$$=0;}
2815 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2817 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2818 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2819 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2820 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2821 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2822 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2823 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2824 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2825 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2826 STATICCONSTANT : T_IDENTIFIER {
2827 if(!strcmp($1, "NaN")) {
2828 $$ = constant_new_float(__builtin_nan(""));
2830 as3_warning("Couldn't evaluate constant value of %s", $1);
2831 $$ = constant_new_null($1);
2835 /* ------------ classes and interfaces (body, functions) ------- */
2837 // non-vararg version
2840 memset(&$$,0,sizeof($$));
2842 MAYBE_PARAM_LIST: PARAM_LIST {
2848 MAYBE_PARAM_LIST: "..." PARAM {
2850 memset(&$$,0,sizeof($$));
2852 list_append($$.list, $2);
2854 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2858 list_append($$.list, $4);
2862 PARAM_LIST: PARAM_LIST ',' PARAM {
2865 list_append($$.list, $3);
2869 memset(&$$,0,sizeof($$));
2870 list_append($$.list, $1);
2873 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2875 $$ = rfx_calloc(sizeof(param_t));
2881 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2883 $$ = rfx_calloc(sizeof(param_t));
2885 $$->type = TYPE_ANY;
2893 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2894 MAYBETYPE '{' {PASS12 startfunction(&$1,$3,$4,&$6,$8);} MAYBECODE '}'
2897 endfunction(&$1,$3,$4,&$6,0,0);
2899 if(!state->method->info) syntaxerror("internal error");
2901 code_t*c = method_header(state->method);
2902 c = wrap_function(c, 0, $11);
2904 endfunction(&$1,$3,$4,&$6,$8,c);
2906 list_deep_free($6.list);
2910 MAYBE_IDENTIFIER: T_IDENTIFIER
2911 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2912 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2913 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2916 endfunction(0,0,$2,&$4,0,0);
2918 methodinfo_t*f = state->method->info;
2919 if(!f || !f->kind) syntaxerror("internal error");
2921 code_t*c = method_header(state->method);
2922 c = wrap_function(c, 0, $9);
2924 int index = state->method->var_index;
2925 endfunction(0,0,$2,&$4,$6,c);
2927 $$.c = abc_getlocal(0, index);
2928 $$.t = TYPE_FUNCTION(f);
2930 PASS12 list_deep_free($4.list);
2934 /* ------------- package + class ids --------------- */
2936 CLASS: X_IDENTIFIER {
2937 PASS1 NEW(unresolvedinfo_t,c);
2938 memset(c, 0, sizeof(*c));
2939 c->kind = INFOTYPE_UNRESOLVED;
2941 c->package = get_package_from_name($1);
2943 c->nsset = get_current_imports();
2944 /* make the compiler look for this class in the current directory,
2946 as3_schedule_class_noerror(state->package, $1);
2948 $$ = (classinfo_t*)c;
2950 slotinfo_t*s = find_class($1);
2951 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2952 $$ = (classinfo_t*)s;
2955 PACKAGEANDCLASS : PACKAGE '.' X_IDENTIFIER {
2956 PASS1 NEW(unresolvedinfo_t,c);
2957 memset(c, 0, sizeof(*c));
2958 c->kind = INFOTYPE_UNRESOLVED;
2961 $$ = (classinfo_t*)c;
2963 slotinfo_t*s = registry_find($1, $3);
2964 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2966 $$ = (classinfo_t*)s;
2969 CLASS_SPEC: PACKAGEANDCLASS
2972 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2973 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2975 TYPE : CLASS_SPEC {PASS12 $$=$1;}
2976 | '*' {PASS12 $$=registry_getanytype();}
2977 | "void" {PASS12 $$=registry_getanytype();}
2979 | "String" {$$=registry_getstringclass();}
2980 | "int" {$$=registry_getintclass();}
2981 | "uint" {$$=registry_getuintclass();}
2982 | "Boolean" {$$=registry_getbooleanclass();}
2983 | "Number" {$$=registry_getnumberclass();}
2986 MAYBETYPE: ':' TYPE {PASS12 $$=$2;}
2987 MAYBETYPE: {PASS12 $$=0;}
2989 /* ----------function calls, delete, constructor calls ------ */
2991 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.number=0;}
2992 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2994 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.number=0;}
2995 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2996 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2998 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.number=1;
3002 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
3003 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
3004 $$.number= $1.number+1;
3005 $$.cc = code_append($1.cc, $2.c);
3009 NEW : "new" E XX MAYBE_PARAM_VALUES {
3011 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
3013 code_t*paramcode = $4.cc;
3014 if($$.c->opcode == OPCODE_GETPROPERTY) {
3015 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3016 $$.c = code_cutlast($$.c);
3017 $$.c = code_append($$.c, paramcode);
3018 $$.c = abc_constructprop2($$.c, name, $4.number);
3019 multiname_destroy(name);
3020 } else if($$.c->opcode == OPCODE_GETSLOT) {
3021 int slot = (int)(ptroff_t)$$.c->data[0];
3022 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
3023 multiname_t*name = t->name;
3024 $$.c = code_cutlast($$.c);
3025 $$.c = code_append($$.c, paramcode);
3026 $$.c = abc_constructprop2($$.c, name, $4.number);
3028 $$.c = code_append($$.c, paramcode);
3029 $$.c = abc_construct($$.c, $4.number);
3033 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
3036 $$.c = abc_coerce_a($$.c);
3041 /* TODO: use abc_call (for calling local variables),
3042 abc_callstatic (for calling own methods)
3045 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
3048 if($$.c->opcode == OPCODE_COERCE_A) {
3049 $$.c = code_cutlast($$.c);
3051 code_t*paramcode = $3.cc;
3054 if($$.c->opcode == OPCODE_GETPROPERTY) {
3055 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3056 $$.c = code_cutlast($$.c);
3057 $$.c = code_append($$.c, paramcode);
3058 $$.c = abc_callproperty2($$.c, name, $3.number);
3059 multiname_destroy(name);
3060 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
3061 int slot = (int)(ptroff_t)$$.c->data[0];
3062 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
3063 if(t->kind!=TRAIT_METHOD) {
3064 //ok: flash allows to assign closures to members.
3066 multiname_t*name = t->name;
3067 $$.c = code_cutlast($$.c);
3068 $$.c = code_append($$.c, paramcode);
3069 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
3070 $$.c = abc_callproperty2($$.c, name, $3.number);
3071 } else if($$.c->opcode == OPCODE_GETSUPER) {
3072 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
3073 $$.c = code_cutlast($$.c);
3074 $$.c = code_append($$.c, paramcode);
3075 $$.c = abc_callsuper2($$.c, name, $3.number);
3076 multiname_destroy(name);
3078 $$.c = abc_getglobalscope($$.c);
3079 $$.c = code_append($$.c, paramcode);
3080 $$.c = abc_call($$.c, $3.number);
3083 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
3084 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
3086 $$.c = abc_coerce_a($$.c);
3091 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
3092 if(!state->cls) syntaxerror("super() not allowed outside of a class");
3093 if(!state->method) syntaxerror("super() not allowed outside of a function");
3094 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
3097 $$.c = abc_getlocal_0($$.c);
3099 $$.c = code_append($$.c, $3.cc);
3101 this is dependent on the control path, check this somewhere else
3102 if(state->method->has_super)
3103 syntaxerror("constructor may call super() only once");
3105 state->method->has_super = 1;
3107 $$.c = abc_constructsuper($$.c, $3.number);
3108 $$.c = abc_pushundefined($$.c);
3112 DELETE: "delete" E {
3114 if($$.c->opcode == OPCODE_COERCE_A) {
3115 $$.c = code_cutlast($$.c);
3117 multiname_t*name = 0;
3118 if($$.c->opcode == OPCODE_GETPROPERTY) {
3119 $$.c->opcode = OPCODE_DELETEPROPERTY;
3120 } else if($$.c->opcode == OPCODE_GETSLOT) {
3121 int slot = (int)(ptroff_t)$$.c->data[0];
3122 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
3123 $$.c = code_cutlast($$.c);
3124 $$.c = abc_deleteproperty2($$.c, name);
3126 $$.c = abc_getlocal_0($$.c);
3127 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
3128 $$.c = abc_deleteproperty2($$.c, &m);
3130 $$.t = TYPE_BOOLEAN;
3133 RETURN: "return" %prec prec_none {
3134 $$ = abc_returnvoid(0);
3136 RETURN: "return" EXPRESSION {
3138 $$ = abc_returnvalue($$);
3141 // ----------------------- expression types -------------------------------------
3143 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
3144 EXPRESSION : E %prec below_minus {$$ = $1;}
3145 EXPRESSION : EXPRESSION ',' E %prec below_minus {
3147 $$.c = cut_last_push($$.c);
3148 $$.c = code_append($$.c,$3.c);
3151 VOIDEXPRESSION : EXPRESSION %prec below_minus {
3152 $$=cut_last_push($1.c);
3155 // ----------------------- expression evaluation -------------------------------------
3157 E : INNERFUNCTION %prec prec_none {$$ = $1;}
3158 //V : CONSTANT {$$ = 0;}
3160 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
3161 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
3162 //V : NEW {$$ = $1.c;}
3164 //V : DELETE {$$ = $1.c;}
3165 E : DELETE {$$ = $1;}
3171 namespace_t ns = {ACCESS_PACKAGE, ""};
3172 multiname_t m = {QNAME, &ns, 0, "RegExp"};
3174 $$.c = abc_getlex2($$.c, &m);
3175 $$.c = abc_pushstring($$.c, $1.pattern);
3176 $$.c = abc_construct($$.c, 1);
3178 $$.c = abc_getlex2($$.c, &m);
3179 $$.c = abc_pushstring($$.c, $1.pattern);
3180 $$.c = abc_pushstring($$.c, $1.options);
3181 $$.c = abc_construct($$.c, 2);
3186 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
3187 //MULTINAME(m, registry_getintclass());
3188 //$$.c = abc_coerce2($$.c, &m); // FIXME
3191 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
3194 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
3197 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
3200 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
3203 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
3206 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
3209 CONSTANT : "true" {$$.c = abc_pushtrue(0);
3210 $$.t = TYPE_BOOLEAN;
3212 CONSTANT : "false" {$$.c = abc_pushfalse(0);
3213 $$.t = TYPE_BOOLEAN;
3215 CONSTANT : "null" {$$.c = abc_pushnull(0);
3219 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
3220 $$.t = TYPE_BOOLEAN;
3222 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
3223 $$.t = TYPE_BOOLEAN;
3225 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
3226 $$.t = TYPE_BOOLEAN;
3228 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
3229 $$.t = TYPE_BOOLEAN;
3231 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
3232 $$.t = TYPE_BOOLEAN;
3234 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
3235 $$.t = TYPE_BOOLEAN;
3237 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
3238 $$.t = TYPE_BOOLEAN;
3240 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
3241 $$.t = TYPE_BOOLEAN;
3244 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
3246 $$.c = converttype($$.c, $1.t, $$.t);
3247 $$.c = abc_dup($$.c);
3248 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
3249 $$.c = cut_last_push($$.c);
3250 $$.c = code_append($$.c,$3.c);
3251 $$.c = converttype($$.c, $3.t, $$.t);
3252 code_t*label = $$.c = abc_label($$.c);
3253 jmp->branch = label;
3256 $$.t = join_types($1.t, $3.t, 'A');
3257 /*printf("%08x:\n",$1.t);
3258 code_dump($1.c, 0, 0, "", stdout);
3259 printf("%08x:\n",$3.t);
3260 code_dump($3.c, 0, 0, "", stdout);
3261 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
3263 $$.c = converttype($$.c, $1.t, $$.t);
3264 $$.c = abc_dup($$.c);
3265 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
3266 $$.c = cut_last_push($$.c);
3267 $$.c = code_append($$.c,$3.c);
3268 $$.c = converttype($$.c, $3.t, $$.t);
3269 code_t*label = $$.c = abc_label($$.c);
3270 jmp->branch = label;
3273 E : '!' E {$$.c=$2.c;
3274 $$.c = abc_not($$.c);
3275 $$.t = TYPE_BOOLEAN;
3278 E : '~' E {$$.c=$2.c;
3279 $$.c = abc_bitnot($$.c);
3283 E : E '&' E {$$.c = code_append($1.c,$3.c);
3284 $$.c = abc_bitand($$.c);
3288 E : E '^' E {$$.c = code_append($1.c,$3.c);
3289 $$.c = abc_bitxor($$.c);
3293 E : E '|' E {$$.c = code_append($1.c,$3.c);
3294 $$.c = abc_bitor($$.c);
3298 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3299 $$.c = abc_rshift($$.c);
3302 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3303 $$.c = abc_urshift($$.c);
3306 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3307 $$.c = abc_lshift($$.c);
3311 E : E '/' E {$$.c = code_append($1.c,$3.c);
3312 $$.c = abc_divide($$.c);
3315 E : E '%' E {$$.c = code_append($1.c,$3.c);
3316 $$.c = abc_modulo($$.c);
3319 E : E '+' E {$$.c = code_append($1.c,$3.c);
3320 if(BOTH_INT($1.t, $3.t)) {
3321 $$.c = abc_add_i($$.c);
3324 $$.c = abc_add($$.c);
3325 $$.t = join_types($1.t,$3.t,'+');
3328 E : E '-' E {$$.c = code_append($1.c,$3.c);
3329 if(BOTH_INT($1.t,$3.t)) {
3330 $$.c = abc_subtract_i($$.c);
3333 $$.c = abc_subtract($$.c);
3337 E : E '*' E {$$.c = code_append($1.c,$3.c);
3338 if(BOTH_INT($1.t,$3.t)) {
3339 $$.c = abc_multiply_i($$.c);
3342 $$.c = abc_multiply($$.c);
3347 E : E "in" E {$$.c = code_append($1.c,$3.c);
3348 $$.c = abc_in($$.c);
3349 $$.t = TYPE_BOOLEAN;
3352 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3353 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3354 MULTINAME(m, (classinfo_t*)($3.t->data));
3355 $$.c = abc_astype2($1.c, &m);
3358 $$.c = code_append($1.c, $3.c);
3359 $$.c = abc_astypelate($$.c);
3364 E : E "instanceof" E
3365 {$$.c = code_append($1.c, $3.c);
3366 $$.c = abc_instanceof($$.c);
3367 $$.t = TYPE_BOOLEAN;
3370 E : E "is" E {$$.c = code_append($1.c, $3.c);
3371 $$.c = abc_istypelate($$.c);
3372 $$.t = TYPE_BOOLEAN;
3375 E : "typeof" '(' E ')' {
3377 $$.c = abc_typeof($$.c);
3382 $$.c = cut_last_push($2.c);
3383 $$.c = abc_pushundefined($$.c);
3387 E : "void" { $$.c = abc_pushundefined(0);
3391 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3396 $$.c=abc_negate_i($$.c);
3399 $$.c=abc_negate($$.c);
3406 $$.c = code_append($$.c, $3.c);
3408 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3409 $$.c = abc_getproperty2($$.c, &m);
3410 $$.t = 0; // array elements have unknown type
3413 E : '[' MAYBE_EXPRESSION_LIST ']' {
3415 $$.c = code_append($$.c, $2.cc);
3416 $$.c = abc_newarray($$.c, $2.number);
3417 $$.t = registry_getarrayclass();
3420 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
3421 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3423 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3425 $$.cc = code_append($$.cc, $1.c);
3426 $$.cc = code_append($$.cc, $3.c);
3429 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3431 $$.number = $1.number+2;
3432 $$.cc = code_append($$.cc, $3.c);
3433 $$.cc = code_append($$.cc, $5.c);
3438 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3440 $$.c = code_append($$.c, $2.cc);
3441 $$.c = abc_newobject($$.c, $2.number/2);
3442 $$.t = registry_getobjectclass();
3447 if(BOTH_INT($1.t,$3.t)) {
3448 c=abc_multiply_i(c);
3452 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3453 $$.c = toreadwrite($1.c, c, 0, 0);
3458 code_t*c = abc_modulo($3.c);
3459 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3460 $$.c = toreadwrite($1.c, c, 0, 0);
3464 code_t*c = abc_lshift($3.c);
3465 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3466 $$.c = toreadwrite($1.c, c, 0, 0);
3470 code_t*c = abc_rshift($3.c);
3471 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3472 $$.c = toreadwrite($1.c, c, 0, 0);
3476 code_t*c = abc_urshift($3.c);
3477 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3478 $$.c = toreadwrite($1.c, c, 0, 0);
3482 code_t*c = abc_divide($3.c);
3483 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3484 $$.c = toreadwrite($1.c, c, 0, 0);
3488 code_t*c = abc_bitor($3.c);
3489 c=converttype(c, TYPE_INT, $1.t);
3490 $$.c = toreadwrite($1.c, c, 0, 0);
3494 code_t*c = abc_bitxor($3.c);
3495 c=converttype(c, TYPE_INT, $1.t);
3496 $$.c = toreadwrite($1.c, c, 0, 0);
3502 if(TYPE_IS_INT($1.t)) {
3506 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3509 $$.c = toreadwrite($1.c, c, 0, 0);
3512 E : E "-=" E { code_t*c = $3.c;
3513 if(TYPE_IS_INT($1.t)) {
3514 c=abc_subtract_i(c);
3517 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3520 $$.c = toreadwrite($1.c, c, 0, 0);
3523 E : E '=' E { code_t*c = 0;
3524 c = code_append(c, $3.c);
3525 c = converttype(c, $3.t, $1.t);
3526 $$.c = toreadwrite($1.c, c, 1, 0);
3530 E : E '?' E ':' E %prec below_assignment {
3531 $$.t = join_types($3.t,$5.t,'?');
3533 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3534 $$.c = code_append($$.c, $3.c);
3535 $$.c = converttype($$.c, $3.t, $$.t);
3536 code_t*j2 = $$.c = abc_jump($$.c, 0);
3537 $$.c = j1->branch = abc_label($$.c);
3538 $$.c = code_append($$.c, $5.c);
3539 $$.c = converttype($$.c, $5.t, $$.t);
3540 $$.c = j2->branch = abc_label($$.c);
3543 E : E "++" { code_t*c = 0;
3544 classinfo_t*type = $1.t;
3545 if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
3546 int nr = getlocalnr($1.c);
3547 code_free($1.c);$1.c=0;
3548 if(TYPE_IS_INT($1.t)) {
3549 $$.c = abc_getlocal(0, nr);
3550 $$.c = abc_inclocal_i($$.c, nr);
3551 } else if(TYPE_IS_NUMBER($1.t)) {
3552 $$.c = abc_getlocal(0, nr);
3553 $$.c = abc_inclocal($$.c, nr);
3554 } else syntaxerror("internal error");
3556 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3557 c=abc_increment_i(c);
3563 c=converttype(c, type, $1.t);
3564 $$.c = toreadwrite($1.c, c, 0, 1);
3569 // TODO: use inclocal, like with ++
3570 E : E "--" { code_t*c = 0;
3571 classinfo_t*type = $1.t;
3572 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3573 c=abc_decrement_i(c);
3579 c=converttype(c, type, $1.t);
3580 $$.c = toreadwrite($1.c, c, 0, 1);
3584 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3585 classinfo_t*type = $2.t;
3586 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3587 c=abc_increment_i(c);
3593 c=converttype(c, type, $2.t);
3594 $$.c = toreadwrite($2.c, c, 0, 0);
3598 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3599 classinfo_t*type = $2.t;
3600 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3601 c=abc_decrement_i(c);
3607 c=converttype(c, type, $2.t);
3608 $$.c = toreadwrite($2.c, c, 0, 0);
3612 E : "super" '.' T_IDENTIFIER
3613 { if(!state->cls->info)
3614 syntaxerror("super keyword not allowed outside a class");
3615 classinfo_t*t = state->cls->info->superclass;
3616 if(!t) t = TYPE_OBJECT;
3618 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3620 MEMBER_MULTINAME(m, f, $3);
3622 $$.c = abc_getlocal_0($$.c);
3623 $$.c = abc_getsuper2($$.c, &m);
3624 $$.t = slotinfo_gettype((slotinfo_t*)f);
3627 E : '@' T_IDENTIFIER {
3629 $$.c = abc_pushundefined(0);
3631 as3_warning("ignored @ operator");
3634 E : E '.' '@' T_IDENTIFIER {
3635 // child attribute TODO
3636 $$.c = abc_pushundefined(0);
3638 as3_warning("ignored .@ operator");
3641 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3642 // namespace declaration TODO
3643 $$.c = abc_pushundefined(0);
3645 as3_warning("ignored :: operator");
3648 E : E ".." T_IDENTIFIER {
3650 $$.c = abc_pushundefined(0);
3652 as3_warning("ignored .. operator");
3655 E : E '.' '(' E ')' {
3657 $$.c = abc_pushundefined(0);
3659 as3_warning("ignored .() operator");
3662 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3666 E : E '.' T_IDENTIFIER {
3668 classinfo_t*t = $1.t;
3670 if(TYPE_IS_CLASS(t) && t->data) {
3675 if(t->subtype==INFOTYPE_UNRESOLVED) {
3676 syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
3678 memberinfo_t*f = registry_findmember_nsset(t, state->active_namespaces, $3, 1);
3680 if(f && !is_static != !(f->flags&FLAG_STATIC))
3682 if(f && f->slot && !noslot) {
3683 $$.c = abc_getslot($$.c, f->slot);
3685 MEMBER_MULTINAME(m, f, $3);
3686 $$.c = abc_getproperty2($$.c, &m);
3688 /* determine type */
3689 $$.t = slotinfo_gettype((slotinfo_t*)f);
3691 $$.c = abc_coerce_a($$.c);
3692 } else if($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
3693 string_t*package = $1.c->data[0];
3694 char*package2 = concat3(package->str, ".", $3);
3695 if(dict_contains(state->import_toplevel_packages, package2)) {
3697 $$.c->data[0] = string_new4(package2);
3700 slotinfo_t*a = registry_find(package->str, $3);
3702 syntaxerror("couldn't resolve %s", package2);
3706 /* when resolving a property on an unknown type, we do know the
3707 name of the property (and don't seem to need the package), but
3708 we need to make avm2 try out all access modes */
3709 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3710 $$.c = abc_getproperty2($$.c, &m);
3711 $$.c = abc_coerce_a($$.c);
3712 $$.t = registry_getanytype();
3716 VAR_READ : T_IDENTIFIER {
3718 /* Queue unresolved identifiers for checking against the parent
3719 function's variables.
3720 We consider everything which is not a local variable "unresolved".
3721 This encompasses class names, members of the surrounding class
3722 etc. which is *correct* because local variables of the parent function
3725 if(state->method->inner && !find_variable(state, $1)) {
3726 unknown_variable($1);
3729 /* let the compiler know that it might check the current directory/package
3730 for this identifier- maybe there's a file $1.as defining $1. */
3731 as3_schedule_class_noerror(state->package, $1);
3740 /* look at variables */
3741 if((v = find_variable(state, $1))) {
3742 // $1 is a local variable
3743 $$.c = abc_getlocal($$.c, v->index);
3747 if((v = find_slot(state, $1))) {
3748 $$.c = abc_getscopeobject($$.c, 1);
3749 $$.c = abc_getslot($$.c, v->index);
3754 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3756 /* look at current class' members */
3757 if(state->cls && (f = registry_findmember_nsset(state->cls->info, state->active_namespaces, $1, 1)) &&
3758 (f->flags&FLAG_STATIC) >= i_am_static) {
3759 // $1 is a function in this class
3760 int var_is_static = (f->flags&FLAG_STATIC);
3762 if(f->kind == INFOTYPE_METHOD) {
3763 $$.t = TYPE_FUNCTION(f);
3767 if(var_is_static && !i_am_static) {
3768 /* access to a static member from a non-static location.
3769 do this via findpropstrict:
3770 there doesn't seem to be any non-lookup way to access
3771 static properties of a class */
3772 state->method->late_binding = 1;
3774 namespace_t ns = {f->access, ""};
3775 multiname_t m = {QNAME, &ns, 0, $1};
3776 $$.c = abc_findpropstrict2($$.c, &m);
3777 $$.c = abc_getproperty2($$.c, &m);
3779 } else if(f->slot>0) {
3780 $$.c = abc_getlocal_0($$.c);
3781 $$.c = abc_getslot($$.c, f->slot);
3784 namespace_t ns = {f->access, ""};
3785 multiname_t m = {QNAME, &ns, 0, $1};
3786 $$.c = abc_getlocal_0($$.c);
3787 $$.c = abc_getproperty2($$.c, &m);
3792 /* look at actual classes, in the current package and imported */
3793 if((a = find_class($1))) {
3798 /* look through package prefixes */
3799 if(dict_contains(state->import_toplevel_packages, $1)) {
3800 $$.c = abc___pushpackage__($$.c, $1);
3805 /* unknown object, let the avm2 resolve it */
3807 //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3808 as3_warning("Couldn't resolve '%s', doing late binding", $1);
3809 state->method->late_binding = 1;
3811 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3814 $$.c = abc_findpropstrict2($$.c, &m);
3815 $$.c = abc_getproperty2($$.c, &m);
3819 // ----------------- namespaces -------------------------------------------------
3821 NAMESPACE_ID : "namespace" T_IDENTIFIER {
3823 NEW(namespace_decl_t,n);
3828 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_IDENTIFIER {
3830 NEW(namespace_decl_t,n);
3835 NAMESPACE_ID : "namespace" T_IDENTIFIER '=' T_STRING {
3837 NEW(namespace_decl_t,n);
3842 NAMESPACE_DECLARATION : MAYBE_MODIFIERS NAMESPACE_ID {
3844 register_namespace($2->name, $2->url);
3846 namespace_t access = modifiers2access(&$1);
3847 varinfo_t* var = varinfo_register_global(access.access, state->package, $2->name);
3848 var->type = TYPE_NAMESPACE;
3850 ns.access = ACCESS_NAMESPACE;
3852 var->value = constant_new_namespace(&ns);
3857 USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
3860 register_namespace($3->name, url);