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;
48 classinfo_t*classinfo;
49 classinfo_list_t*classinfo_list;
52 unsigned int number_uint;
56 //typedcode_list_t*value_list;
57 codeandnumber_t value_list;
63 for_start_t for_start;
64 abc_exception_t *exception;
67 abc_exception_list_t *l;
73 %token<id> T_IDENTIFIER T_NAMESPACE
75 %token<regexp> T_REGEXP
77 %token<number_int> T_INT
78 %token<number_uint> T_UINT
79 %token<number_uint> T_BYTE
80 %token<number_uint> T_SHORT
81 %token<number_float> T_FLOAT
83 %token<id> T_FOR "for"
84 %token<id> T_WHILE "while"
86 %token<id> T_SWITCH "switch"
88 %token<token> KW_IMPLEMENTS "implements"
89 %token<token> KW_NAMESPACE "namespace"
90 %token<token> KW_PACKAGE "package"
91 %token<token> KW_PROTECTED "protected"
92 %token<token> KW_PUBLIC "public"
93 %token<token> KW_PRIVATE "private"
94 %token<token> KW_USE "use"
95 %token<token> KW_INTERNAL "internal"
96 %token<token> KW_NEW "new"
97 %token<token> KW_NATIVE "native"
98 %token<token> KW_FUNCTION "function"
99 %token<token> KW_FINALLY "finally"
100 %token<token> KW_UNDEFINED "undefined"
101 %token<token> KW_CONTINUE "continue"
102 %token<token> KW_CLASS "class"
103 %token<token> KW_CONST "const"
104 %token<token> KW_CATCH "catch"
105 %token<token> KW_CASE "case"
106 %token<token> KW_SET "set"
107 %token<token> KW_VOID "void"
108 %token<token> KW_THROW "throw"
109 %token<token> KW_STATIC "static"
110 %token<token> KW_WITH "with"
111 %token<token> KW_INSTANCEOF "instanceof"
112 %token<token> KW_IMPORT "import"
113 %token<token> KW_RETURN "return"
114 %token<token> KW_TYPEOF "typeof"
115 %token<token> KW_INTERFACE "interface"
116 %token<token> KW_NULL "null"
117 %token<token> KW_VAR "var"
118 %token<token> KW_DYNAMIC "dynamic"
119 %token<token> KW_OVERRIDE "override"
120 %token<token> KW_FINAL "final"
121 %token<token> KW_EACH "each"
122 %token<token> KW_GET "get"
123 %token<token> KW_TRY "try"
124 %token<token> KW_SUPER "super"
125 %token<token> KW_EXTENDS "extends"
126 %token<token> KW_FALSE "false"
127 %token<token> KW_TRUE "true"
128 %token<token> KW_BOOLEAN "Boolean"
129 %token<token> KW_UINT "uint"
130 %token<token> KW_INT "int"
131 %token<token> KW_NUMBER "Number"
132 %token<token> KW_STRING "String"
133 %token<token> KW_DEFAULT "default"
134 %token<token> KW_DELETE "delete"
135 %token<token> KW_IF "if"
136 %token<token> KW_ELSE "else"
137 %token<token> KW_BREAK "break"
138 %token<token> KW_IS "is"
139 %token<token> KW_IN "in"
140 %token<token> KW_AS "as"
142 %token<token> T_DICTSTART "{ (dictionary)"
143 %token<token> T_EQEQ "=="
144 %token<token> T_EQEQEQ "==="
145 %token<token> T_NE "!="
146 %token<token> T_NEE "!=="
147 %token<token> T_LE "<="
148 %token<token> T_GE ">="
149 %token<token> T_ORBY "|="
150 %token<token> T_DIVBY "/="
151 %token<token> T_MODBY "%="
152 %token<token> T_MULBY "*="
153 %token<token> T_PLUSBY "+="
154 %token<token> T_MINUSBY "-="
155 %token<token> T_XORBY "^="
156 %token<token> T_SHRBY ">>="
157 %token<token> T_SHLBY "<<="
158 %token<token> T_USHRBY ">>>="
159 %token<token> T_OROR "||"
160 %token<token> T_ANDAND "&&"
161 %token<token> T_COLONCOLON "::"
162 %token<token> T_MINUSMINUS "--"
163 %token<token> T_PLUSPLUS "++"
164 %token<token> T_DOTDOT ".."
165 %token<token> T_DOTDOTDOT "..."
166 %token<token> T_SHL "<<"
167 %token<token> T_USHR ">>>"
168 %token<token> T_SHR ">>"
170 %type <for_start> FOR_START
171 %type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
172 %type <token> VARCONST
174 %type <code> CODEPIECE CODE_STATEMENT
175 %type <code> CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH
176 %type <code> PACKAGE_DECLARATION SLOT_DECLARATION
177 %type <code> FUNCTION_DECLARATION PACKAGE_INITCODE
178 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW
179 %type <exception> CATCH FINALLY
180 %type <catch_list> CATCH_LIST CATCH_FINALLY_LIST
181 %type <code> CLASS_DECLARATION
182 %type <code> NAMESPACE_DECLARATION
183 %type <code> INTERFACE_DECLARATION
184 %type <code> VOIDEXPRESSION
185 %type <value> EXPRESSION NONCOMMAEXPRESSION
186 %type <value> MAYBEEXPRESSION
187 %type <value> E DELETE
188 %type <value> CONSTANT
189 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
190 %type <value> INNERFUNCTION
191 %type <code> USE_NAMESPACE
192 %type <code> FOR_INIT
194 %type <classinfo> MAYBETYPE
197 %type <params> PARAM_LIST
198 %type <params> MAYBE_PARAM_LIST
199 %type <flags> MAYBE_MODIFIERS
200 %type <flags> MODIFIER_LIST
201 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
202 %type <classinfo_list> IMPLEMENTS_LIST
203 %type <classinfo> EXTENDS
204 %type <classinfo_list> EXTENDS_LIST
205 %type <classinfo> CLASS PACKAGEANDCLASS CLASS_SPEC
206 %type <classinfo_list> CLASS_SPEC_LIST
207 %type <classinfo> TYPE
208 //%type <token> VARIABLE
209 %type <value> VAR_READ
211 //%type <token> T_IDENTIFIER
212 %type <token> MODIFIER
213 %type <value> FUNCTIONCALL
214 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
216 // precedence: from low to high
220 %left below_semicolon
223 %nonassoc below_assignment // for ?:, contrary to spec
224 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
231 %nonassoc "==" "!=" "===" "!=="
232 %nonassoc "is" "as" "in"
233 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
234 %left "<<" ">>" ">>>"
238 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
240 %nonassoc below_curly
244 %left '[' ']' "new" '{' "{ (dictionary)" '.' ".." "::" '@'
247 %left above_identifier
251 // needed for "return" precedence:
252 %nonassoc T_STRING T_REGEXP
253 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
254 %nonassoc "false" "true" "null" "undefined" "super" "function"
261 static int a3_error(char*s)
263 syntaxerror("%s", s);
264 return 0; //make gcc happy
268 static char* concat2(const char* t1, const char* t2)
272 char*text = malloc(l1+l2+1);
273 memcpy(text , t1, l1);
274 memcpy(text+l1, t2, l2);
278 static char* concat3(const char* t1, const char* t2, const char* t3)
283 char*text = malloc(l1+l2+l3+1);
284 memcpy(text , t1, l1);
285 memcpy(text+l1, t2, l2);
286 memcpy(text+l1+l2, t3, l3);
291 typedef struct _import {
295 DECLARE_LIST(import);
297 DECLARE(methodstate);
298 DECLARE_LIST(methodstate);
300 typedef struct _classstate {
306 methodstate_t*static_init;
308 //code_t*static_init;
310 char has_constructor;
313 struct _methodstate {
322 dict_t*unresolved_variables;
325 char uses_parent_function;
330 int var_index; // for inner methods
331 int slot_index; // for inner methods
332 char is_a_slot; // for inner methods
335 abc_exception_list_t*exceptions;
337 methodstate_list_t*innerfunctions;
340 typedef struct _state {
345 import_list_t*wildcard_imports;
347 char has_own_imports;
348 char new_vars; // e.g. transition between two functions
351 methodstate_t*method;
358 typedef struct _global {
362 dict_t*file2token2info;
365 static global_t*global = 0;
366 static state_t* state = 0;
370 #define MULTINAME(m,x) \
374 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
376 #define MEMBER_MULTINAME(m,f,n) \
380 if((m##_ns.access = ((slotinfo_t*)(f))->access)==ACCESS_NAMESPACE) \
381 m##_ns.name = ((slotinfo_t*)(f))->package; \
386 m.namespace_set = 0; \
387 m.name = ((slotinfo_t*)(f))->name; \
389 m.type = MULTINAME; \
391 m.namespace_set = &nopackage_namespace_set; \
395 /* warning: list length of namespace set is undefined */
396 #define MULTINAME_LATE(m, access, package) \
397 namespace_t m##_ns = {access, package}; \
398 namespace_set_t m##_nsset; \
399 namespace_list_t m##_l;m##_l.next = 0; \
400 m##_nsset.namespaces = &m##_l; \
401 m##_nsset = m##_nsset; \
402 m##_l.namespace = &m##_ns; \
403 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
405 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
406 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
407 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
408 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
409 static namespace_list_t nl4 = {&ns4,0};
410 static namespace_list_t nl3 = {&ns3,&nl4};
411 static namespace_list_t nl2 = {&ns2,&nl3};
412 static namespace_list_t nl1 = {&ns1,&nl2};
413 static namespace_set_t nopackage_namespace_set = {&nl1};
415 static void new_state()
418 state_t*oldstate = state;
420 memcpy(s, state, sizeof(state_t)); //shallow copy
422 s->imports = dict_new();
426 state->has_own_imports = 0;
427 state->vars = dict_new();
428 state->old = oldstate;
431 static void state_has_imports()
433 state->wildcard_imports = list_clone(state->wildcard_imports);
434 state->imports = dict_clone(state->imports);
435 state->has_own_imports = 1;
438 static void state_destroy(state_t*state)
440 if(state->has_own_imports) {
441 list_free(state->wildcard_imports);
442 dict_destroy(state->imports);state->imports=0;
444 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
445 dict_destroy(state->imports);state->imports=0;
449 for(t=0;t<state->vars->hashsize;t++) {
450 dictentry_t*e =state->vars->slots[t];
452 free(e->data);e->data=0;
456 dict_destroy(state->vars);state->vars=0;
462 static void old_state()
464 if(!state || !state->old)
465 syntaxerror("invalid nesting");
466 state_t*leaving = state;
470 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
471 free(leaving->method);
474 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
479 state_destroy(leaving);
482 static code_t* method_header(methodstate_t*m);
483 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
484 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
487 static char* internal_filename_package = 0;
488 void initialize_file(char*filename)
491 syntaxerror("invalid call to initialize_file during parsing of another file");
494 state->package = internal_filename_package = strdup(filename);
496 global->token2info = dict_lookup(global->file2token2info,
497 current_filename // use long version
499 if(!global->token2info) {
500 global->token2info = dict_new2(&ptr_type);
501 dict_put(global->file2token2info, current_filename, global->token2info);
505 state->method = rfx_calloc(sizeof(methodstate_t));
506 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
508 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
509 function_initvars(state->method, 0, 0, 1);
510 global->init = abc_initscript(global->file);
511 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
517 if(!state || state->level!=1) {
518 syntaxerror("unexpected end of file in pass %d", as3_pass);
522 code_t*header = method_header(state->method);
523 code_t*c = wrap_function(header, 0, global->init->method->body->code);
524 global->init->method->body->code = c;
525 free(state->method);state->method=0;
528 //free(state->package);state->package=0; // used in registry
529 state_destroy(state);state=0;
532 void initialize_parser()
534 global = rfx_calloc(sizeof(global_t));
535 global->file = abc_file_new();
536 global->file->flags &= ~ABCFILE_LAZY;
537 global->file2token2info = dict_new();
538 global->token2info = 0;
541 void* finish_parser()
543 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
545 global->token2info=0;
551 static void xx_scopetest()
553 /* findpropstrict doesn't just return a scope object- it
554 also makes it "active" somehow. Push local_0 on the
555 scope stack and read it back with findpropstrict, it'll
556 contain properties like "trace". Trying to find the same
557 property on a "vanilla" local_0 yields only a "undefined" */
558 //c = abc_findpropstrict(c, "[package]::trace");
560 /*c = abc_getlocal_0(c);
561 c = abc_findpropstrict(c, "[package]::trace");
563 c = abc_setlocal_1(c);
565 c = abc_pushbyte(c, 0);
566 c = abc_setlocal_2(c);
568 code_t*xx = c = abc_label(c);
569 c = abc_findpropstrict(c, "[package]::trace");
570 c = abc_pushstring(c, "prop:");
571 c = abc_hasnext2(c, 1, 2);
573 c = abc_setlocal_3(c);
574 c = abc_callpropvoid(c, "[package]::trace", 2);
575 c = abc_getlocal_3(c);
577 c = abc_iftrue(c,xx);*/
580 typedef struct _variable {
584 methodstate_t*is_inner_method;
587 static variable_t* find_variable(state_t*s, char*name)
591 v = dict_lookup(s->vars, name);
593 if(s->new_vars) break;
598 static variable_t* find_slot(state_t*s, const char*name)
600 if(s->method && s->method->slots)
601 return dict_lookup(s->method->slots, name);
605 static variable_t* find_variable_safe(state_t*s, char*name)
607 variable_t* v = find_variable(s, name);
609 syntaxerror("undefined variable: %s", name);
612 static char variable_exists(char*name)
614 return dict_contains(state->vars, name);
616 code_t*defaultvalue(code_t*c, classinfo_t*type);
618 static variable_t* new_variable2(const char*name, classinfo_t*type, char init, char maybeslot)
621 variable_t*v = find_slot(state, name);
627 v->index = state->method->variable_count++;
631 dict_put(state->vars, name, v);
635 static int new_variable(const char*name, classinfo_t*type, char init, char maybeslot)
637 return new_variable2(name, type, init, maybeslot)->index;
640 #define TEMPVARNAME "__as3_temp__"
641 static int gettempvar()
643 variable_t*v = find_variable(state, TEMPVARNAME);
646 return new_variable(TEMPVARNAME, 0, 0, 0);
649 code_t* var_block(code_t*body)
655 for(t=0;t<state->vars->hashsize;t++) {
656 dictentry_t*e = state->vars->slots[t];
658 variable_t*v = (variable_t*)e->data;
659 if(v->type && v->init) {
660 c = defaultvalue(c, v->type);
661 c = abc_setlocal(c, v->index);
662 k = abc_kill(k, v->index);
672 if(x->opcode== OPCODE___BREAK__ ||
673 x->opcode== OPCODE___CONTINUE__) {
674 /* link kill code before break/continue */
675 code_t*e = code_dup(k);
676 code_t*s = code_start(e);
688 c = code_append(c, body);
689 c = code_append(c, k);
693 void unknown_variable(char*name)
695 if(!state->method->unresolved_variables)
696 state->method->unresolved_variables = dict_new();
697 if(!dict_contains(state->method->unresolved_variables, name))
698 dict_put(state->method->unresolved_variables, name, 0);
701 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
703 static void parsererror(const char*file, int line, const char*f)
705 syntaxerror("internal error in %s, %s:%d", f, file, line);
709 static code_t* method_header(methodstate_t*m)
712 if(m->uses_slots || (m->late_binding && !m->inner)) {
713 c = abc_getlocal_0(c);
714 c = abc_pushscope(c);
717 c = abc_newactivation(c);
718 c = abc_pushscope(c);
720 methodstate_list_t*l = m->innerfunctions;
722 parserassert(l->methodstate->abc);
723 if(m->uses_slots && l->methodstate->is_a_slot) {
724 c = abc_getscopeobject(c, 1);
725 c = abc_newfunction(c, l->methodstate->abc);
727 c = abc_setlocal(c, l->methodstate->var_index);
728 c = abc_setslot(c, l->methodstate->slot_index);
730 c = abc_newfunction(c, l->methodstate->abc);
731 c = abc_setlocal(c, l->methodstate->var_index);
733 free(l->methodstate);l->methodstate=0;
737 c = code_append(c, m->header);
740 if(m->is_constructor && !m->has_super) {
741 // call default constructor
742 c = abc_getlocal_0(c);
743 c = abc_constructsuper(c, 0);
745 list_free(m->innerfunctions);
746 m->innerfunctions = 0;
751 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
753 c = code_append(c, header);
754 c = code_append(c, var_block(body));
755 /* append return if necessary */
756 if(!c || (c->opcode != OPCODE_RETURNVOID &&
757 c->opcode != OPCODE_RETURNVALUE)) {
758 c = abc_returnvoid(c);
764 static void startpackage(char*name)
767 /*printf("entering package \"%s\"\n", name);*/
768 state->package = strdup(name);
770 static void endpackage()
772 /*printf("leaving package \"%s\"\n", state->package);*/
774 //used e.g. in classinfo_register:
775 //free(state->package);state->package=0;
780 #define FLAG_PUBLIC 256
781 #define FLAG_PROTECTED 512
782 #define FLAG_PRIVATE 1024
783 #define FLAG_PACKAGEINTERNAL 2048
784 #define FLAG_NAMESPACE 4096
786 static int flags2access(int flags)
789 if(flags&FLAG_PUBLIC) {
790 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
791 syntaxerror("invalid combination of access levels");
792 access = ACCESS_PACKAGE;
793 } else if(flags&FLAG_PRIVATE) {
794 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
795 syntaxerror("invalid combination of access levels");
796 access = ACCESS_PRIVATE;
797 } else if(flags&FLAG_PROTECTED) {
798 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
799 syntaxerror("invalid combination of access levels");
800 access = ACCESS_PROTECTED;
802 access = ACCESS_PACKAGEINTERNAL;
807 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
812 index = new_variable("this", 0, 0, 0);
813 else if(!m->is_global)
814 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
816 index = new_variable("globalscope", 0, 0, 0);
819 parserassert(!index);
822 /* as variables and slots share the same number, make sure
823 that those variable indices are reserved. It's up to the
824 optimizer to later shuffle the variables down to lower
826 m->variable_count = m->uses_slots;
831 for(p=params->list;p;p=p->next) {
832 new_variable(p->param->name, p->param->type, 0, 1);
836 methodstate_list_t*l = m->innerfunctions;
838 methodstate_t*m = l->methodstate;
839 variable_t* v = new_variable2(m->info->name, TYPE_FUNCTION(m->info), 0, 1);
840 m->var_index = v->index;
841 m->slot_index = v->index;
842 v->is_inner_method = m;
848 char*as3_globalclass=0;
849 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements)
852 syntaxerror("inner classes now allowed");
856 classinfo_list_t*mlist=0;
858 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
859 syntaxerror("invalid modifier(s)");
861 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
862 syntaxerror("public and internal not supported at the same time.");
864 /* create the class name, together with the proper attributes */
868 if(!(flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
869 access = ACCESS_PRIVATE; package = internal_filename_package;
870 } else if(!(flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
871 access = ACCESS_PACKAGEINTERNAL; package = state->package;
872 } else if(state->package!=internal_filename_package) {
873 access = ACCESS_PACKAGE; package = state->package;
875 syntaxerror("public classes only allowed inside a package");
879 state->cls = rfx_calloc(sizeof(classstate_t));
880 state->cls->init = rfx_calloc(sizeof(methodstate_t));
881 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
882 /* notice: we make no effort to initialize the top variable (local0) here,
883 even though it has special meaning. We just rely on the facat
884 that pass 1 won't do anything with variables */
886 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
888 /* set current method to constructor- all code within the class-level (except
889 static variable initializations) will be executed during construction time */
890 state->method = state->cls->init;
892 if(registry_find(package, classname)) {
893 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
895 /* build info struct */
896 int num_interfaces = (list_length(implements));
897 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
898 state->cls->info->flags |= flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
902 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
904 state->method = state->cls->init;
905 parserassert(state->cls && state->cls->info);
907 function_initvars(state->cls->init, 0, 0, 1);
908 function_initvars(state->cls->static_init, 0, 0, 0);
910 if(extends && (extends->flags & FLAG_FINAL))
911 syntaxerror("Can't extend final class '%s'", extends->name);
913 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
914 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
916 classinfo_list_t*l = implements;
917 for(l=implements;l;l=l->next) {
918 if(!(l->classinfo->flags & FLAG_INTERFACE))
919 syntaxerror("'%s' is not an interface", l->classinfo->name);
920 state->cls->info->interfaces[pos++] = l->classinfo;
923 /* generate the abc code for this class */
924 MULTINAME(classname2,state->cls->info);
925 multiname_t*extends2 = sig2mname(extends);
927 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
928 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
929 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
930 if(state->cls->info->flags&FLAG_INTERFACE) {
931 abc_class_interface(state->cls->abc);
934 abc_class_protectedNS(state->cls->abc, classname);
936 for(mlist=implements;mlist;mlist=mlist->next) {
937 MULTINAME(m, mlist->classinfo);
938 abc_class_add_interface(state->cls->abc, &m);
941 /* write the construction code for this class to the global init
943 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
945 abc_method_body_t*m = global->init->method->body;
946 __ getglobalscope(m);
947 classinfo_t*s = extends;
952 //TODO: take a look at the current scope stack, maybe
953 // we can re-use something
958 multiname_t*s2 = sig2mname(s);
960 multiname_destroy(s2);
962 __ pushscope(m); count++;
963 m->code = m->code->prev->prev; // invert
965 /* continue appending after last op end */
966 while(m->code && m->code->next) m->code = m->code->next;
968 /* TODO: if this is one of *our* classes, we can also
969 do a getglobalscope/getslot <nr> (which references
970 the init function's slots) */
972 __ getlex2(m, extends2);
974 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
975 stack is not the superclass */
976 __ pushscope(m);count++;
979 /* notice: we get a verify error #1107 if the top element on the scope
980 stack is not the global object */
982 __ pushscope(m);count++;
984 __ newclass(m,state->cls->abc);
988 __ setslot(m, slotindex);
989 multiname_destroy(extends2);
991 /* flash.display.MovieClip handling */
993 if(!as3_globalclass && (flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
994 if(state->package && state->package[0]) {
995 as3_globalclass = concat3(state->package, ".", classname);
997 as3_globalclass = strdup(classname);
1003 static void setstaticfunction(int x)
1007 state->method = state->cls->static_init;
1009 state->method = state->cls->init;
1012 parserassert(state->method);
1016 static void endclass()
1019 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
1021 c = abc_getlocal_0(c);
1022 c = abc_constructsuper(c, 0);
1023 state->cls->init->header = code_append(state->cls->init->header, c);
1024 state->cls->has_constructor=1;
1026 if(state->cls->init) {
1027 if(state->cls->info->flags&FLAG_INTERFACE) {
1028 if(state->cls->init->header)
1029 syntaxerror("interface can not have class-level code");
1031 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
1032 code_t*c = method_header(state->cls->init);
1033 m->body->code = wrap_function(c, 0, m->body->code);
1036 if(state->cls->static_init) {
1037 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
1038 code_t*c = method_header(state->cls->static_init);
1039 m->body->code = wrap_function(c, 0, m->body->code);
1046 void check_code_for_break(code_t*c)
1049 if(c->opcode == OPCODE___BREAK__) {
1050 char*name = string_cstr(c->data[0]);
1051 syntaxerror("Unresolved \"break %s\"", name);
1053 if(c->opcode == OPCODE___CONTINUE__) {
1054 char*name = string_cstr(c->data[0]);
1055 syntaxerror("Unresolved \"continue %s\"", name);
1062 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1064 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1065 if(TYPE_IS_NUMBER(t)) {
1066 xassert(c->type == CONSTANT_FLOAT
1067 || c->type == CONSTANT_INT
1068 || c->type == CONSTANT_UINT);
1069 } else if(TYPE_IS_UINT(t)) {
1070 xassert(c->type == CONSTANT_UINT ||
1071 (c->type == CONSTANT_INT && c->i>=0));
1072 } else if(TYPE_IS_INT(t)) {
1073 xassert(c->type == CONSTANT_INT);
1074 } else if(TYPE_IS_BOOLEAN(t)) {
1075 xassert(c->type == CONSTANT_TRUE
1076 || c->type == CONSTANT_FALSE);
1080 static void check_override(memberinfo_t*m, int flags)
1084 if(m->parent == state->cls->info)
1085 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1087 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1088 if(m->access==ACCESS_PRIVATE)
1090 if(m->flags & FLAG_FINAL)
1091 syntaxerror("can't override final member %s", m->name);
1092 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1093 syntaxerror("can't override static member %s", m->name);
1094 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1095 syntaxerror("can't override non-static member %s with static declaration", m->name);
1097 if(!(flags&FLAG_OVERRIDE)) {
1098 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1099 if(m->kind == INFOTYPE_METHOD)
1100 syntaxerror("can't override without explicit 'override' declaration");
1102 syntaxerror("can't override '%s'", m->name);
1107 static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
1109 methodinfo_t*minfo = 0;
1110 U8 access = flags2access(flags);
1113 minfo = methodinfo_register_global(access, state->package, name);
1114 minfo->return_type = return_type;
1115 } else if(getset != KW_GET && getset != KW_SET) {
1117 memberinfo_t* m = registry_findmember(state->cls->info, name, 0);
1119 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1121 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1122 minfo->return_type = return_type;
1123 // getslot on a member slot only returns "undefined", so no need
1124 // to actually store these
1125 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1127 //class getter/setter
1128 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1130 if(getset == KW_GET)
1132 else if(params->list && params->list->param)
1133 type = params->list->param->type;
1134 // not sure wether to look into superclasses here, too
1135 minfo = (methodinfo_t*)registry_findmember(state->cls->info, name, 1);
1137 if(minfo->kind!=INFOTYPE_SLOT)
1138 syntaxerror("class already contains a method called '%s'", name);
1139 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1140 syntaxerror("class already contains a field called '%s'", name);
1141 if(minfo->subtype & gs)
1142 syntaxerror("getter/setter for '%s' already defined", name);
1143 /* make a setter or getter into a getset */
1144 minfo->subtype |= gs;
1145 if(!minfo->return_type) {
1146 minfo->return_type = type;
1148 if(minfo && minfo->return_type != type)
1149 syntaxerror("different type in getter and setter");
1152 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1153 minfo->kind = INFOTYPE_SLOT; //hack
1154 minfo->subtype = gs;
1155 minfo->return_type = type;
1157 /* can't assign a slot as getter and setter might have different slots */
1158 //minfo->slot = slot;
1160 if(flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1161 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1162 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1166 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1168 //parserassert(state->method && state->method->info);
1170 methodstate_t*parent_method = state->method;
1173 return_type = 0; // not valid in pass 1
1177 state->new_vars = 1;
1180 state->method = rfx_calloc(sizeof(methodstate_t));
1181 state->method->inner = 1;
1182 state->method->variable_count = 0;
1183 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1185 NEW(methodinfo_t,minfo);
1186 minfo->kind = INFOTYPE_METHOD;
1187 minfo->access = ACCESS_PACKAGEINTERNAL;
1189 state->method->info = minfo;
1192 list_append(parent_method->innerfunctions, state->method);
1194 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1196 function_initvars(state->method, params, 0, 1);
1200 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1201 state->method->variable_count = 0;
1202 parserassert(state->method);
1204 state->method->info->return_type = return_type;
1205 function_initvars(state->method, params, 0, 1);
1209 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1210 params_t*params, classinfo_t*return_type)
1212 if(state->method && state->method->info) {
1213 syntaxerror("not able to start another method scope");
1216 state->new_vars = 1;
1219 state->method = rfx_calloc(sizeof(methodstate_t));
1220 state->method->has_super = 0;
1223 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1225 state->method->is_global = 1;
1226 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1228 if(state->method->is_constructor)
1229 name = "__as3_constructor__";
1232 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1234 function_initvars(state->method, params, flags, 1);
1236 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1240 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1241 state->method->variable_count = 0;
1242 parserassert(state->method);
1245 memberinfo_t*m = registry_findmember(state->cls->info, name, 2);
1246 check_override(m, flags);
1250 state->cls->has_constructor |= state->method->is_constructor;
1253 state->method->info->return_type = return_type;
1254 function_initvars(state->method, params, flags, 1);
1258 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1259 params_t*params, classinfo_t*return_type, code_t*body)
1262 // store inner methods in variables
1263 function_initvars(state->method, 0, 0, 0);
1265 methodstate_list_t*ml = state->method->innerfunctions;
1266 dict_t*xvars = dict_new();
1268 methodstate_t*m = ml->methodstate;
1269 parserassert(m->inner);
1270 if(m->unresolved_variables) {
1271 dict_t*d = m->unresolved_variables;
1273 for(t=0;t<d->hashsize;t++) {
1274 dictentry_t*l = d->slots[t];
1276 /* check parent method's variables */
1277 if(find_variable(state, l->key)) {
1278 m->uses_parent_function = 1;
1279 state->method->uses_slots = 1;
1280 dict_put(xvars, l->key, 0);
1287 dict_destroy(m->unresolved_variables);
1288 m->unresolved_variables = 0;
1292 if(state->method->uses_slots) {
1293 state->method->slots = dict_new();
1295 DICT_ITERATE_ITEMS(state->vars, char*, name, variable_t*, v) {
1296 if(v->index && dict_contains(xvars, name)) {
1299 if(v->is_inner_method) {
1300 v->is_inner_method->is_a_slot = 1;
1303 dict_put(state->method->slots, name, v);
1306 state->method->uses_slots = i;
1307 dict_destroy(state->vars);state->vars = 0;
1309 dict_destroy(xvars);
1316 /*if(state->method->uses_parent_function){
1317 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1322 multiname_t*type2 = sig2mname(return_type);
1324 if(state->method->inner) {
1325 f = state->method->abc;
1326 abc_method_init(f, global->file, type2, 1);
1327 } else if(state->method->is_constructor) {
1328 f = abc_class_getconstructor(state->cls->abc, type2);
1329 } else if(!state->method->is_global) {
1330 namespace_t mname_ns = {state->method->info->access, ""};
1331 multiname_t mname = {QNAME, &mname_ns, 0, name};
1333 if(flags&FLAG_STATIC)
1334 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1336 f = abc_class_method(state->cls->abc, type2, &mname);
1337 slot = f->trait->slot_id;
1339 namespace_t mname_ns = {state->method->info->access, state->package};
1340 multiname_t mname = {QNAME, &mname_ns, 0, name};
1342 f = abc_method_new(global->file, type2, 1);
1343 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1344 //abc_code_t*c = global->init->method->body->code;
1346 //flash doesn't seem to allow us to access function slots
1347 //state->method->info->slot = slot;
1349 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1350 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1351 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1352 if(params->varargs) f->flags |= METHOD_NEED_REST;
1356 for(p=params->list;p;p=p->next) {
1357 if(params->varargs && !p->next) {
1358 break; //varargs: omit last parameter in function signature
1360 multiname_t*m = sig2mname(p->param->type);
1361 list_append(f->parameters, m);
1362 if(p->param->value) {
1363 check_constant_against_type(p->param->type, p->param->value);
1364 opt=1;list_append(f->optional_parameters, p->param->value);
1366 syntaxerror("non-optional parameter not allowed after optional parameters");
1369 if(state->method->slots) {
1370 DICT_ITERATE_ITEMS(state->method->slots, char*, name, variable_t*, v) {
1372 multiname_t*mname = multiname_new(namespace_new(ACCESS_PACKAGE, ""), name);
1373 multiname_t*type = sig2mname(v->type);
1374 trait_t*t = trait_new_member(&f->body->traits, type, mname, 0);
1375 t->slot_id = v->index;
1380 check_code_for_break(body);
1383 f->body->code = body;
1384 f->body->exceptions = state->method->exceptions;
1385 } else { //interface
1387 syntaxerror("interface methods can't have a method body");
1397 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1402 void breakjumpsto(code_t*c, char*name, code_t*jump)
1405 if(c->opcode == OPCODE___BREAK__) {
1406 string_t*name2 = c->data[0];
1407 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1408 c->opcode = OPCODE_JUMP;
1415 void continuejumpsto(code_t*c, char*name, code_t*jump)
1418 if(c->opcode == OPCODE___CONTINUE__) {
1419 string_t*name2 = c->data[0];
1420 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1421 c->opcode = OPCODE_JUMP;
1429 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1430 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1431 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1433 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1435 if(!type1 || !type2)
1436 return registry_getanytype();
1437 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1438 return registry_getanytype();
1441 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1450 return registry_getanytype();
1452 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1457 return abc_coerce_a(c);
1461 // cast an "any" type to a specific type. subject to
1462 // runtime exceptions
1463 return abc_coerce2(c, &m);
1466 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1467 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1468 // allow conversion between number types
1469 return abc_coerce2(c, &m);
1471 //printf("%s.%s\n", from.package, from.name);
1472 //printf("%s.%s\n", to.package, to.name);
1474 classinfo_t*supertype = from;
1476 if(supertype == to) {
1477 // target type is one of from's superclasses
1478 return abc_coerce2(c, &m);
1481 while(supertype->interfaces[t]) {
1482 if(supertype->interfaces[t]==to) {
1483 // target type is one of from's interfaces
1484 return abc_coerce2(c, &m);
1488 supertype = supertype->superclass;
1490 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1492 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1494 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1497 as3_error("can't convert type %s%s%s to %s%s%s",
1498 from->package, from->package?".":"", from->name,
1499 to->package, to->package?".":"", to->name);
1503 code_t*defaultvalue(code_t*c, classinfo_t*type)
1505 if(TYPE_IS_INT(type)) {
1506 c = abc_pushbyte(c, 0);
1507 } else if(TYPE_IS_UINT(type)) {
1508 c = abc_pushuint(c, 0);
1509 } else if(TYPE_IS_FLOAT(type)) {
1511 } else if(TYPE_IS_BOOLEAN(type)) {
1512 c = abc_pushfalse(c);
1514 //c = abc_pushundefined(c);
1516 c = abc_pushnull(c);
1518 c = abc_coerce2(c, &m);
1523 char is_pushundefined(code_t*c)
1525 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1528 static slotinfo_t* find_class(char*name)
1532 c = registry_find(state->package, name);
1535 /* try explicit imports */
1536 dictentry_t* e = dict_get_slot(state->imports, name);
1539 if(!strcmp(e->key, name)) {
1540 c = (slotinfo_t*)e->data;
1546 /* try package.* imports */
1547 import_list_t*l = state->wildcard_imports;
1549 //printf("does package %s contain a class %s?\n", l->import->package, name);
1550 c = registry_find(l->import->package, name);
1555 /* try global package */
1556 c = registry_find("", name);
1559 /* try local "filename" package */
1560 c = registry_find(internal_filename_package, name);
1566 static char is_getlocal(code_t*c)
1568 if(!c || c->prev || c->next)
1570 return(c->opcode == OPCODE_GETLOCAL
1571 || c->opcode == OPCODE_GETLOCAL_0
1572 || c->opcode == OPCODE_GETLOCAL_1
1573 || c->opcode == OPCODE_GETLOCAL_2
1574 || c->opcode == OPCODE_GETLOCAL_3);
1576 static int getlocalnr(code_t*c)
1578 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1579 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1580 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1581 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1582 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1583 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1587 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1591 [prefix code] [read instruction]
1595 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1597 if(in && in->opcode == OPCODE_COERCE_A) {
1598 in = code_cutlast(in);
1601 syntaxerror("internal error");
1603 /* chop off read instruction */
1607 prefix = r->prev;r->prev = 0;
1613 char use_temp_var = readbefore;
1615 /* generate the write instruction, and maybe append a dup to the prefix code */
1616 code_t* write = abc_nop(0);
1617 if(r->opcode == OPCODE_GETPROPERTY) {
1618 write->opcode = OPCODE_SETPROPERTY;
1619 multiname_t*m = (multiname_t*)r->data[0];
1620 write->data[0] = multiname_clone(m);
1621 if(m->type == QNAME || m->type == MULTINAME) {
1623 prefix = abc_dup(prefix); // we need the object, too
1626 } else if(m->type == MULTINAMEL) {
1628 /* dupping two values on the stack requires 5 operations and one register-
1629 couldn't adobe just have given us a dup2? */
1630 int temp = gettempvar();
1631 prefix = abc_setlocal(prefix, temp);
1632 prefix = abc_dup(prefix);
1633 prefix = abc_getlocal(prefix, temp);
1634 prefix = abc_swap(prefix);
1635 prefix = abc_getlocal(prefix, temp);
1637 prefix = abc_kill(prefix, temp);
1641 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1643 } else if(r->opcode == OPCODE_GETSLOT) {
1644 write->opcode = OPCODE_SETSLOT;
1645 write->data[0] = r->data[0];
1647 prefix = abc_dup(prefix); // we need the object, too
1650 } else if(r->opcode == OPCODE_GETLOCAL) {
1651 write->opcode = OPCODE_SETLOCAL;
1652 write->data[0] = r->data[0];
1653 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1654 write->opcode = OPCODE_SETLOCAL_0;
1655 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1656 write->opcode = OPCODE_SETLOCAL_1;
1657 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1658 write->opcode = OPCODE_SETLOCAL_2;
1659 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1660 write->opcode = OPCODE_SETLOCAL_3;
1663 syntaxerror("illegal lvalue: can't assign a value to this expression");
1670 /* with getproperty/getslot, we have to be extra careful not
1671 to execute the read code twice, as it might have side-effects
1672 (e.g. if the property is in fact a setter/getter combination)
1674 So read the value, modify it, and write it again,
1675 using prefix only once and making sure (by using a temporary
1676 register) that the return value is what we just wrote */
1677 temp = gettempvar();
1678 c = code_append(c, prefix);
1679 c = code_append(c, r);
1682 c = abc_setlocal(c, temp);
1684 c = code_append(c, middlepart);
1687 c = abc_setlocal(c, temp);
1689 c = code_append(c, write);
1690 c = abc_getlocal(c, temp);
1691 c = abc_kill(c, temp);
1693 /* if we're allowed to execute the read code twice *and*
1694 the middlepart doesn't modify the code, things are easier.
1696 code_t* r2 = code_dup(r);
1697 //c = code_append(c, prefix);
1698 parserassert(!prefix);
1699 c = code_append(c, r);
1700 c = code_append(c, middlepart);
1701 c = code_append(c, write);
1702 c = code_append(c, r2);
1705 /* even smaller version: overwrite the value without reading
1709 c = code_append(c, prefix);
1712 c = code_append(c, middlepart);
1713 c = code_append(c, write);
1714 c = code_append(c, r);
1717 temp = gettempvar();
1719 c = code_append(c, prefix);
1721 c = code_append(c, middlepart);
1723 c = abc_setlocal(c, temp);
1724 c = code_append(c, write);
1725 c = abc_getlocal(c, temp);
1726 c = abc_kill(c, temp);
1732 char is_break_or_jump(code_t*c)
1736 if(c->opcode == OPCODE_JUMP ||
1737 c->opcode == OPCODE___BREAK__ ||
1738 c->opcode == OPCODE___CONTINUE__ ||
1739 c->opcode == OPCODE_THROW ||
1740 c->opcode == OPCODE_RETURNVOID ||
1741 c->opcode == OPCODE_RETURNVALUE) {
1748 #define IS_FINALLY_TARGET(op) \
1749 ((op) == OPCODE___CONTINUE__ || \
1750 (op) == OPCODE___BREAK__ || \
1751 (op) == OPCODE_RETURNVOID || \
1752 (op) == OPCODE_RETURNVALUE || \
1753 (op) == OPCODE___RETHROW__)
1755 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1757 #define NEED_EXTRA_STACK_ARG
1758 code_t*finally_label = abc_nop(0);
1759 NEW(lookupswitch_t, l);
1765 code_t*prev = i->prev;
1766 if(IS_FINALLY_TARGET(i->opcode)) {
1769 if(i->opcode == OPCODE___RETHROW__ ||
1770 i->opcode == OPCODE_RETURNVALUE) {
1771 if(i->opcode == OPCODE___RETHROW__)
1772 i->opcode = OPCODE_THROW;
1774 p = abc_coerce_a(p);
1775 p = abc_setlocal(p, tempvar);
1777 p = abc_pushbyte(p, count++);
1778 p = abc_jump(p, finally_label);
1779 code_t*target = p = abc_label(p);
1780 #ifdef NEED_EXTRA_STACK_ARG
1784 p = abc_getlocal(p, tempvar);
1787 p->next = i;i->prev = p;
1788 list_append(l->targets, target);
1794 c = abc_pushbyte(c, -1);
1795 c = code_append(c, finally_label);
1796 c = code_append(c, finally);
1798 #ifdef NEED_EXTRA_STACK_ARG
1801 c = abc_lookupswitch(c, l);
1802 c = l->def = abc_label(c);
1803 #ifdef NEED_EXTRA_STACK_ARG
1810 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1814 code_t*prev = i->prev;
1815 if(IS_FINALLY_TARGET(i->opcode)) {
1816 if(i->opcode == OPCODE___RETHROW__)
1817 i->opcode = OPCODE_THROW;
1818 code_t*end = code_dup(finally);
1819 code_t*start = code_start(end);
1820 if(prev) prev->next = start;
1827 return code_append(c, finally);
1830 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1836 int num_insertion_points=0;
1838 if(IS_FINALLY_TARGET(i->opcode))
1839 num_insertion_points++;
1846 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1851 int simple_version_cost = (1+num_insertion_points)*code_size;
1852 int lookup_version_cost = 4*num_insertion_points + 5;
1854 if(cantdup || simple_version_cost > lookup_version_cost) {
1855 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1856 return insert_finally_lookup(c, finally, tempvar);
1858 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1859 return insert_finally_simple(c, finally, tempvar);
1863 #define PASS1 }} if(as3_pass == 1) {{
1864 #define PASS1END }} if(as3_pass == 2) {{
1865 #define PASS2 }} if(as3_pass == 2) {{
1866 #define PASS12 }} {{
1867 #define PASS12END }} if(as3_pass == 2) {{
1873 /* ------------ code blocks / statements ---------------- */
1875 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1877 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1878 PROGRAM_CODE_LIST: PROGRAM_CODE
1879 | PROGRAM_CODE_LIST PROGRAM_CODE
1881 PROGRAM_CODE: PACKAGE_DECLARATION
1882 | INTERFACE_DECLARATION
1884 | FUNCTION_DECLARATION
1887 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
1890 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1891 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1892 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1894 INPACKAGE_CODE: INTERFACE_DECLARATION
1896 | FUNCTION_DECLARATION
1899 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
1902 MAYBECODE: CODE {$$=$1;}
1903 MAYBECODE: {$$=code_new();}
1905 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1906 CODE: CODEPIECE {$$=$1;}
1908 // code which may appear outside of methods
1909 CODE_STATEMENT: IMPORT
1911 CODE_STATEMENT: FOR_IN
1912 CODE_STATEMENT: WHILE
1913 CODE_STATEMENT: DO_WHILE
1914 CODE_STATEMENT: SWITCH
1916 CODE_STATEMENT: WITH
1918 CODE_STATEMENT: VOIDEXPRESSION
1919 CODE_STATEMENT: USE_NAMESPACE
1920 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1921 CODE_STATEMENT: '{' '}' {$$=0;}
1923 // code which may appear in methods
1924 CODEPIECE: ';' {$$=0;}
1925 CODEPIECE: CODE_STATEMENT
1926 CODEPIECE: VARIABLE_DECLARATION
1931 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
1933 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1935 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1936 //CODEBLOCK : '{' '}' {$$=0;}
1937 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1938 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1940 /* ------------ package init code ------------------- */
1942 PACKAGE_INITCODE: CODE_STATEMENT {
1943 code_t**cc = &global->init->method->body->code;
1944 *cc = code_append(*cc, $1);
1947 /* ------------ conditional compilation ------------- */
1949 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
1951 /* ------------ variables --------------------------- */
1953 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1954 | {$$.c=abc_pushundefined(0);
1958 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1959 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1961 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1962 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1964 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1967 if(variable_exists($1))
1968 syntaxerror("Variable %s already defined", $1);
1970 new_variable($1, 0, 1, 0);
1973 if(!is_subtype_of($3.t, $2)) {
1974 syntaxerror("Can't convert %s to %s", $3.t->name,
1980 if(state->method->uses_slots) {
1981 variable_t* v = find_slot(state, $1);
1983 // this variable is stored in a slot
1991 index = new_variable($1, $2, 1, 0);
1994 $$ = slot?abc_getscopeobject(0, 1):0;
1997 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1998 $$ = code_append($$, $3.c);
1999 $$ = converttype($$, $3.t, $2);
2002 $$ = defaultvalue($$, $2);
2005 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
2006 $$ = code_append($$, $3.c);
2007 $$ = abc_coerce_a($$);
2009 // don't do anything
2017 $$ = abc_setslot($$, index);
2019 $$ = abc_setlocal($$, index);
2023 /* ------------ control flow ------------------------- */
2025 MAYBEELSE: %prec below_else {$$ = code_new();}
2026 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
2027 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
2029 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
2032 $$ = code_append($$, $4.c);
2033 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
2035 $$ = code_append($$, $6);
2037 myjmp = $$ = abc_jump($$, 0);
2039 myif->branch = $$ = abc_nop($$);
2041 $$ = code_append($$, $7);
2042 myjmp->branch = $$ = abc_nop($$);
2048 FOR_INIT : {$$=code_new();}
2049 FOR_INIT : VARIABLE_DECLARATION
2050 FOR_INIT : VOIDEXPRESSION
2052 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
2053 // (I don't see any easy way to revolve this conflict otherwise, as we
2054 // can't touch VAR_READ without upsetting the precedence about "return")
2055 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
2056 PASS1 $$=$2;new_variable($2,0,1,0);
2057 PASS2 $$=$2;new_variable($2,$3,1,0);
2059 FOR_IN_INIT : T_IDENTIFIER {
2064 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
2065 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
2067 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
2068 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
2070 $$ = code_append($$, $2);
2071 code_t*loopstart = $$ = abc_label($$);
2072 $$ = code_append($$, $4.c);
2073 code_t*myif = $$ = abc_iffalse($$, 0);
2074 $$ = code_append($$, $8);
2075 code_t*cont = $$ = abc_nop($$);
2076 $$ = code_append($$, $6);
2077 $$ = abc_jump($$, loopstart);
2078 code_t*out = $$ = abc_nop($$);
2079 breakjumpsto($$, $1.name, out);
2080 continuejumpsto($$, $1.name, cont);
2087 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2088 variable_t*var = find_variable(state, $2);
2089 char*tmp1name = concat2($2, "__tmp1__");
2090 int it = new_variable(tmp1name, TYPE_INT, 0, 0);
2091 char*tmp2name = concat2($2, "__array__");
2092 int array = new_variable(tmp1name, 0, 0, 0);
2095 $$ = code_append($$, $4.c);
2096 $$ = abc_coerce_a($$);
2097 $$ = abc_setlocal($$, array);
2098 $$ = abc_pushbyte($$, 0);
2099 $$ = abc_setlocal($$, it);
2101 code_t*loopstart = $$ = abc_label($$);
2103 $$ = abc_hasnext2($$, array, it);
2104 code_t*myif = $$ = abc_iffalse($$, 0);
2105 $$ = abc_getlocal($$, array);
2106 $$ = abc_getlocal($$, it);
2108 $$ = abc_nextname($$);
2110 $$ = abc_nextvalue($$);
2111 $$ = converttype($$, 0, var->type);
2112 $$ = abc_setlocal($$, var->index);
2114 $$ = code_append($$, $6);
2115 $$ = abc_jump($$, loopstart);
2117 code_t*out = $$ = abc_nop($$);
2118 breakjumpsto($$, $1.name, out);
2119 continuejumpsto($$, $1.name, loopstart);
2131 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2135 code_t*myjmp = $$ = abc_jump($$, 0);
2136 code_t*loopstart = $$ = abc_label($$);
2137 $$ = code_append($$, $6);
2138 code_t*cont = $$ = abc_nop($$);
2139 myjmp->branch = cont;
2140 $$ = code_append($$, $4.c);
2141 $$ = abc_iftrue($$, loopstart);
2142 code_t*out = $$ = abc_nop($$);
2143 breakjumpsto($$, $1, out);
2144 continuejumpsto($$, $1, cont);
2150 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2152 code_t*loopstart = $$ = abc_label($$);
2153 $$ = code_append($$, $3);
2154 code_t*cont = $$ = abc_nop($$);
2155 $$ = code_append($$, $6.c);
2156 $$ = abc_iftrue($$, loopstart);
2157 code_t*out = $$ = abc_nop($$);
2158 breakjumpsto($$, $1, out);
2159 continuejumpsto($$, $1, cont);
2165 BREAK : "break" %prec prec_none {
2166 $$ = abc___break__(0, "");
2168 BREAK : "break" T_IDENTIFIER {
2169 $$ = abc___break__(0, $2);
2171 CONTINUE : "continue" %prec prec_none {
2172 $$ = abc___continue__(0, "");
2174 CONTINUE : "continue" T_IDENTIFIER {
2175 $$ = abc___continue__(0, $2);
2178 MAYBE_CASE_LIST : {$$=0;}
2179 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2180 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2181 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2182 CASE_LIST: CASE {$$=$1;}
2183 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2185 CASE: "case" E ':' MAYBECODE {
2187 $$ = code_append($$, $2.c);
2188 code_t*j = $$ = abc_ifne($$, 0);
2189 $$ = code_append($$, $4);
2190 if($$->opcode != OPCODE___BREAK__) {
2191 $$ = abc___fallthrough__($$, "");
2193 code_t*e = $$ = abc_nop($$);
2196 DEFAULT: "default" ':' MAYBECODE {
2199 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2201 $$ = code_append($$, $7);
2202 code_t*out = $$ = abc_pop($$);
2203 breakjumpsto($$, $1, out);
2205 code_t*c = $$,*lastblock=0;
2207 if(c->opcode == OPCODE_IFNE) {
2208 if(!c->next) syntaxerror("internal error in fallthrough handling");
2210 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2212 c->opcode = OPCODE_JUMP;
2213 c->branch = lastblock;
2215 /* fall through end of switch */
2216 c->opcode = OPCODE_NOP;
2226 /* ------------ try / catch /finally ---------------- */
2228 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();
2229 state->exception_name=$3;
2230 PASS1 new_variable($3, 0, 0, 0);
2231 PASS2 new_variable($3, $4, 0, 0);
2234 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2235 multiname_t name = {QNAME, &name_ns, 0, $3};
2237 NEW(abc_exception_t, e)
2238 e->exc_type = sig2mname($4);
2239 e->var_name = multiname_clone(&name);
2243 int i = find_variable_safe(state, $3)->index;
2244 e->target = c = abc_nop(0);
2245 c = abc_setlocal(c, i);
2246 c = code_append(c, $8);
2252 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2257 NEW(abc_exception_t, e)
2258 e->exc_type = 0; //all exceptions
2259 e->var_name = 0; //no name
2262 e->to = code_append(e->to, $4);
2268 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2269 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2270 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2271 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2275 list_append($$.l,$2);
2276 $$.finally = $2->to;$2->to=0;
2279 CATCH_FINALLY_LIST: FINALLY {
2283 list_append($$.l,$1);
2284 $$.finally = $1->to;$1->to=0;
2288 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2289 code_t*out = abc_nop(0);
2291 code_t*start = abc_nop(0);
2292 $$ = code_append(start, $4);
2293 if(!is_break_or_jump($4)) {
2294 $$ = abc_jump($$, out);
2296 code_t*end = $$ = abc_nop($$);
2300 tmp = new_variable("__finally__", 0, 0, 0);
2302 abc_exception_list_t*l = $6.l;
2305 abc_exception_t*e = l->abc_exception;
2307 $$ = code_append($$, e->target);
2308 $$ = abc_jump($$, out);
2310 parserassert((ptroff_t)$6.finally);
2312 e->target = $$ = abc_nop($$);
2313 $$ = abc___rethrow__($$);
2321 $$ = code_append($$, out);
2323 $$ = insert_finally($$, $6.finally, tmp);
2325 list_concat(state->method->exceptions, $6.l);
2331 /* ------------ throw ------------------------------- */
2333 THROW : "throw" EXPRESSION {
2337 THROW : "throw" %prec prec_none {
2338 if(!state->exception_name)
2339 syntaxerror("re-throw only possible within a catch block");
2340 variable_t*v = find_variable(state, state->exception_name);
2342 $$=abc_getlocal($$, v->index);
2346 /* ------------ with -------------------------------- */
2348 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2350 $$ = abc_pushscope($$);
2351 $$ = code_append($$, $5);
2352 $$ = abc_popscope($$);
2355 /* ------------ packages and imports ---------------- */
2357 X_IDENTIFIER: T_IDENTIFIER
2358 | "package" {PASS12 $$="package";}
2360 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2361 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2363 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2364 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2365 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2366 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2368 IMPORT : "import" PACKAGEANDCLASS {
2370 slotinfo_t*s = registry_find($2->package, $2->name);
2371 if(!s) {// || !(s->flags&FLAG_BUILTIN)) {
2372 as3_schedule_class($2->package, $2->name);
2378 syntaxerror("Couldn't import class\n");
2379 state_has_imports();
2380 dict_put(state->imports, c->name, c);
2383 IMPORT : "import" PACKAGE '.' '*' {
2385 if(strncmp("flash.", $2, 6)) {
2386 as3_schedule_package($2);
2392 state_has_imports();
2393 list_append(state->wildcard_imports, i);
2397 /* ------------ classes and interfaces (header) -------------- */
2399 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2400 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2401 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2402 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2404 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2405 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2406 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2407 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2408 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2409 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2410 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2411 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2412 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2413 | T_NAMESPACE {PASS12 $$=FLAG_NAMESPACE;}
2415 EXTENDS : {$$=registry_getobjectclass();}
2416 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2418 EXTENDS_LIST : {PASS12 $$=list_new();}
2419 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2421 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2422 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2424 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2425 EXTENDS IMPLEMENTS_LIST
2426 '{' {PASS12 startclass($1,$3,$4,$5);}
2428 '}' {PASS12 endclass();$$=0;}
2430 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2432 '{' {PASS12 startclass($1|FLAG_INTERFACE,$3,0,$4);}
2433 MAYBE_INTERFACE_BODY
2434 '}' {PASS12 endclass();$$=0;}
2436 /* ------------ classes and interfaces (body) -------------- */
2439 MAYBE_CLASS_BODY : CLASS_BODY
2440 CLASS_BODY : CLASS_BODY_ITEM
2441 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2442 CLASS_BODY_ITEM : ';'
2443 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2444 CLASS_BODY_ITEM : SLOT_DECLARATION
2445 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2447 CLASS_BODY_ITEM : CODE_STATEMENT {
2448 code_t*c = state->cls->static_init->header;
2449 c = code_append(c, $1);
2450 state->cls->static_init->header = c;
2453 MAYBE_INTERFACE_BODY :
2454 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2455 INTERFACE_BODY : IDECLARATION
2456 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2458 IDECLARATION : "var" T_IDENTIFIER {
2459 syntaxerror("variable declarations not allowed in interfaces");
2461 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2464 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2465 syntaxerror("invalid method modifiers: interface methods always need to be public");
2467 startfunction(0,$1,$3,$4,&$6,$8);
2468 endfunction(0,$1,$3,$4,&$6,$8, 0);
2469 list_deep_free($6.list);
2472 /* ------------ classes and interfaces (body, slots ) ------- */
2474 VARCONST: "var" | "const"
2476 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} MAYBETYPE MAYBEEXPRESSION {
2478 U8 access = flags2access($1);
2480 varinfo_t* info = 0;
2482 memberinfo_t*i = registry_findmember(state->cls->info, $3, 1);
2484 check_override(i, flags);
2486 info = varinfo_register_onclass(state->cls->info, access, $3);
2488 slotinfo_t*i = registry_find(state->package, $3);
2490 syntaxerror("package %s already contains '%s'", state->package, $3);
2492 info = varinfo_register_global(access, state->package, $3);
2496 info->flags = flags;
2499 namespace_t mname_ns = {access, ""};
2500 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2502 trait_list_t**traits;
2506 mname_ns.name = state->package;
2507 traits = &global->init->traits;
2508 code = &global->init->method->body->code;
2509 } else if(flags&FLAG_STATIC) {
2511 traits = &state->cls->abc->static_traits;
2512 code = &state->cls->static_init->header;
2514 // instance variable
2515 traits = &state->cls->abc->traits;
2516 code = &state->cls->init->header;
2522 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2524 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2526 info->slot = t->slot_id;
2528 /* initalization code (if needed) */
2530 if($6.c && !is_pushundefined($6.c)) {
2531 c = abc_getlocal_0(c);
2532 c = code_append(c, $6.c);
2533 c = converttype(c, $6.t, $5);
2534 c = abc_setslot(c, t->slot_id);
2537 *code = code_append(*code, c);
2540 t->kind= TRAIT_CONST;
2544 setstaticfunction(0);
2547 /* ------------ constants -------------------------------------- */
2549 MAYBESTATICCONSTANT: {$$=0;}
2550 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2552 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2553 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2554 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2555 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2556 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2557 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2558 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2559 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2560 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2561 STATICCONSTANT : T_IDENTIFIER {
2563 as3_warning("Couldn't resolve %s", $1);
2564 $$ = constant_new_null($1);
2567 /* ------------ classes and interfaces (body, functions) ------- */
2569 // non-vararg version
2572 memset(&$$,0,sizeof($$));
2574 MAYBE_PARAM_LIST: PARAM_LIST {
2580 MAYBE_PARAM_LIST: "..." PARAM {
2582 memset(&$$,0,sizeof($$));
2584 list_append($$.list, $2);
2586 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2590 list_append($$.list, $4);
2594 PARAM_LIST: PARAM_LIST ',' PARAM {
2597 list_append($$.list, $3);
2601 memset(&$$,0,sizeof($$));
2602 list_append($$.list, $1);
2605 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2607 $$ = rfx_calloc(sizeof(param_t));
2613 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2615 $$ = rfx_calloc(sizeof(param_t));
2617 $$->type = TYPE_ANY;
2625 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2626 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2629 endfunction(0,$1,$3,$4,&$6,0,0);
2631 if(!state->method->info) syntaxerror("internal error");
2633 code_t*c = method_header(state->method);
2634 c = wrap_function(c, 0, $11);
2636 endfunction(0,$1,$3,$4,&$6,$8,c);
2638 list_deep_free($6.list);
2642 MAYBE_IDENTIFIER: T_IDENTIFIER
2643 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2644 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2645 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2648 endfunction(0,0,0,$2,&$4,0,0);
2650 methodinfo_t*f = state->method->info;
2651 if(!f || !f->kind) syntaxerror("internal error");
2653 code_t*c = method_header(state->method);
2654 c = wrap_function(c, 0, $9);
2656 int index = state->method->var_index;
2657 endfunction(0,0,0,$2,&$4,$6,c);
2659 $$.c = abc_getlocal(0, index);
2660 $$.t = TYPE_FUNCTION(f);
2662 PASS12 list_deep_free($4.list);
2666 /* ------------- package + class ids --------------- */
2668 CLASS: T_IDENTIFIER {
2671 slotinfo_t*s = find_class($1);
2672 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2673 $$ = (classinfo_t*)s;
2676 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2677 PASS1 static classinfo_t c;
2678 memset(&c, 0, sizeof(c));
2683 slotinfo_t*s = registry_find($1, $3);
2684 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2686 $$ = (classinfo_t*)s;
2689 CLASS_SPEC: PACKAGEANDCLASS
2692 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2693 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2695 TYPE : CLASS_SPEC {$$=$1;}
2696 | '*' {$$=registry_getanytype();}
2697 | "void" {$$=registry_getanytype();}
2699 | "String" {$$=registry_getstringclass();}
2700 | "int" {$$=registry_getintclass();}
2701 | "uint" {$$=registry_getuintclass();}
2702 | "Boolean" {$$=registry_getbooleanclass();}
2703 | "Number" {$$=registry_getnumberclass();}
2706 MAYBETYPE: ':' TYPE {$$=$2;}
2709 /* ----------function calls, delete, constructor calls ------ */
2711 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2712 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2714 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2715 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2716 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2718 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2722 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2723 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2725 $$.cc = code_append($1.cc, $2.c);
2729 NEW : "new" E XX MAYBE_PARAM_VALUES {
2731 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2733 code_t*paramcode = $4.cc;
2734 if($$.c->opcode == OPCODE_GETPROPERTY) {
2735 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2736 $$.c = code_cutlast($$.c);
2737 $$.c = code_append($$.c, paramcode);
2738 $$.c = abc_constructprop2($$.c, name, $4.len);
2739 multiname_destroy(name);
2740 } else if($$.c->opcode == OPCODE_GETSLOT) {
2741 int slot = (int)(ptroff_t)$$.c->data[0];
2742 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);//FIXME
2743 multiname_t*name = t->name;
2744 $$.c = code_cutlast($$.c);
2745 $$.c = code_append($$.c, paramcode);
2746 $$.c = abc_constructprop2($$.c, name, $4.len);
2748 $$.c = code_append($$.c, paramcode);
2749 $$.c = abc_construct($$.c, $4.len);
2753 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2756 $$.c = abc_coerce_a($$.c);
2761 /* TODO: use abc_call (for calling local variables),
2762 abc_callstatic (for calling own methods)
2765 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2768 if($$.c->opcode == OPCODE_COERCE_A) {
2769 $$.c = code_cutlast($$.c);
2771 code_t*paramcode = $3.cc;
2774 if($$.c->opcode == OPCODE_GETPROPERTY) {
2775 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2776 $$.c = code_cutlast($$.c);
2777 $$.c = code_append($$.c, paramcode);
2778 $$.c = abc_callproperty2($$.c, name, $3.len);
2779 multiname_destroy(name);
2780 } else if($$.c->opcode == OPCODE_GETSLOT && $$.c->prev->opcode != OPCODE_GETSCOPEOBJECT) {
2781 int slot = (int)(ptroff_t)$$.c->data[0];
2782 trait_t*t = traits_find_slotid(state->cls->abc->traits,slot);
2783 if(t->kind!=TRAIT_METHOD) {
2784 //ok: flash allows to assign closures to members.
2786 multiname_t*name = t->name;
2787 $$.c = code_cutlast($$.c);
2788 $$.c = code_append($$.c, paramcode);
2789 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2790 $$.c = abc_callproperty2($$.c, name, $3.len);
2791 } else if($$.c->opcode == OPCODE_GETSUPER) {
2792 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2793 $$.c = code_cutlast($$.c);
2794 $$.c = code_append($$.c, paramcode);
2795 $$.c = abc_callsuper2($$.c, name, $3.len);
2796 multiname_destroy(name);
2798 $$.c = abc_getglobalscope($$.c);
2799 $$.c = code_append($$.c, paramcode);
2800 $$.c = abc_call($$.c, $3.len);
2803 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2804 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2806 $$.c = abc_coerce_a($$.c);
2811 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2812 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2813 if(!state->method) syntaxerror("super() not allowed outside of a function");
2814 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2817 $$.c = abc_getlocal_0($$.c);
2819 $$.c = code_append($$.c, $3.cc);
2821 this is dependent on the control path, check this somewhere else
2822 if(state->method->has_super)
2823 syntaxerror("constructor may call super() only once");
2825 state->method->has_super = 1;
2827 $$.c = abc_constructsuper($$.c, $3.len);
2828 $$.c = abc_pushundefined($$.c);
2832 DELETE: "delete" E {
2834 if($$.c->opcode == OPCODE_COERCE_A) {
2835 $$.c = code_cutlast($$.c);
2837 multiname_t*name = 0;
2838 if($$.c->opcode == OPCODE_GETPROPERTY) {
2839 $$.c->opcode = OPCODE_DELETEPROPERTY;
2840 } else if($$.c->opcode == OPCODE_GETSLOT) {
2841 int slot = (int)(ptroff_t)$$.c->data[0];
2842 multiname_t*name = traits_find_slotid(state->cls->abc->traits,slot)->name;
2843 $$.c = code_cutlast($$.c);
2844 $$.c = abc_deleteproperty2($$.c, name);
2846 $$.c = abc_getlocal_0($$.c);
2847 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2848 $$.c = abc_deleteproperty2($$.c, &m);
2850 $$.t = TYPE_BOOLEAN;
2853 RETURN: "return" %prec prec_none {
2854 $$ = abc_returnvoid(0);
2856 RETURN: "return" EXPRESSION {
2858 $$ = abc_returnvalue($$);
2861 // ----------------------- expression types -------------------------------------
2863 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2864 EXPRESSION : E %prec below_minus {$$ = $1;}
2865 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2867 $$.c = cut_last_push($$.c);
2868 $$.c = code_append($$.c,$3.c);
2871 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2872 $$=cut_last_push($1.c);
2875 // ----------------------- expression evaluation -------------------------------------
2877 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2878 //V : CONSTANT {$$ = 0;}
2880 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2881 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2882 //V : NEW {$$ = $1.c;}
2884 //V : DELETE {$$ = $1.c;}
2885 E : DELETE {$$ = $1;}
2891 namespace_t ns = {ACCESS_PACKAGE, ""};
2892 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2894 $$.c = abc_getlex2($$.c, &m);
2895 $$.c = abc_pushstring($$.c, $1.pattern);
2896 $$.c = abc_construct($$.c, 1);
2898 $$.c = abc_getlex2($$.c, &m);
2899 $$.c = abc_pushstring($$.c, $1.pattern);
2900 $$.c = abc_pushstring($$.c, $1.options);
2901 $$.c = abc_construct($$.c, 2);
2906 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2907 //MULTINAME(m, registry_getintclass());
2908 //$$.c = abc_coerce2($$.c, &m); // FIXME
2911 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2914 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2917 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2920 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2923 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
2926 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2929 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2930 $$.t = TYPE_BOOLEAN;
2932 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2933 $$.t = TYPE_BOOLEAN;
2935 CONSTANT : "null" {$$.c = abc_pushnull(0);
2939 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2940 $$.t = TYPE_BOOLEAN;
2942 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2943 $$.t = TYPE_BOOLEAN;
2945 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2946 $$.t = TYPE_BOOLEAN;
2948 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2949 $$.t = TYPE_BOOLEAN;
2951 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2952 $$.t = TYPE_BOOLEAN;
2954 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2955 $$.t = TYPE_BOOLEAN;
2957 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2958 $$.t = TYPE_BOOLEAN;
2960 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2961 $$.t = TYPE_BOOLEAN;
2964 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2966 $$.c = converttype($$.c, $1.t, $$.t);
2967 $$.c = abc_dup($$.c);
2968 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2969 $$.c = cut_last_push($$.c);
2970 $$.c = code_append($$.c,$3.c);
2971 $$.c = converttype($$.c, $3.t, $$.t);
2972 code_t*label = $$.c = abc_label($$.c);
2973 jmp->branch = label;
2976 $$.t = join_types($1.t, $3.t, 'A');
2977 /*printf("%08x:\n",$1.t);
2978 code_dump($1.c, 0, 0, "", stdout);
2979 printf("%08x:\n",$3.t);
2980 code_dump($3.c, 0, 0, "", stdout);
2981 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2983 $$.c = converttype($$.c, $1.t, $$.t);
2984 $$.c = abc_dup($$.c);
2985 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2986 $$.c = cut_last_push($$.c);
2987 $$.c = code_append($$.c,$3.c);
2988 $$.c = converttype($$.c, $3.t, $$.t);
2989 code_t*label = $$.c = abc_label($$.c);
2990 jmp->branch = label;
2993 E : '!' E {$$.c=$2.c;
2994 $$.c = abc_not($$.c);
2995 $$.t = TYPE_BOOLEAN;
2998 E : '~' E {$$.c=$2.c;
2999 $$.c = abc_bitnot($$.c);
3003 E : E '&' E {$$.c = code_append($1.c,$3.c);
3004 $$.c = abc_bitand($$.c);
3008 E : E '^' E {$$.c = code_append($1.c,$3.c);
3009 $$.c = abc_bitxor($$.c);
3013 E : E '|' E {$$.c = code_append($1.c,$3.c);
3014 $$.c = abc_bitor($$.c);
3018 E : E ">>" E {$$.c = code_append($1.c,$3.c);
3019 $$.c = abc_rshift($$.c);
3022 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
3023 $$.c = abc_urshift($$.c);
3026 E : E "<<" E {$$.c = code_append($1.c,$3.c);
3027 $$.c = abc_lshift($$.c);
3031 E : E '/' E {$$.c = code_append($1.c,$3.c);
3032 $$.c = abc_divide($$.c);
3035 E : E '%' E {$$.c = code_append($1.c,$3.c);
3036 $$.c = abc_modulo($$.c);
3039 E : E '+' E {$$.c = code_append($1.c,$3.c);
3040 if(BOTH_INT($1.t, $3.t)) {
3041 $$.c = abc_add_i($$.c);
3044 $$.c = abc_add($$.c);
3045 $$.t = join_types($1.t,$3.t,'+');
3048 E : E '-' E {$$.c = code_append($1.c,$3.c);
3049 if(BOTH_INT($1.t,$3.t)) {
3050 $$.c = abc_subtract_i($$.c);
3053 $$.c = abc_subtract($$.c);
3057 E : E '*' E {$$.c = code_append($1.c,$3.c);
3058 if(BOTH_INT($1.t,$3.t)) {
3059 $$.c = abc_multiply_i($$.c);
3062 $$.c = abc_multiply($$.c);
3067 E : E "in" E {$$.c = code_append($1.c,$3.c);
3068 $$.c = abc_in($$.c);
3069 $$.t = TYPE_BOOLEAN;
3072 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
3073 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
3074 MULTINAME(m, (classinfo_t*)($3.t->data));
3075 $$.c = abc_astype2($1.c, &m);
3078 $$.c = code_append($1.c, $3.c);
3079 $$.c = abc_astypelate($$.c);
3084 E : E "instanceof" E
3085 {$$.c = code_append($1.c, $3.c);
3086 $$.c = abc_instanceof($$.c);
3087 $$.t = TYPE_BOOLEAN;
3090 E : E "is" E {$$.c = code_append($1.c, $3.c);
3091 $$.c = abc_istypelate($$.c);
3092 $$.t = TYPE_BOOLEAN;
3095 E : "typeof" '(' E ')' {
3097 $$.c = abc_typeof($$.c);
3102 $$.c = cut_last_push($2.c);
3103 $$.c = abc_pushundefined($$.c);
3107 E : "void" { $$.c = abc_pushundefined(0);
3111 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3116 $$.c=abc_negate_i($$.c);
3119 $$.c=abc_negate($$.c);
3126 $$.c = code_append($$.c, $3.c);
3128 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3129 $$.c = abc_getproperty2($$.c, &m);
3130 $$.t = 0; // array elements have unknown type
3133 E : '[' MAYBE_EXPRESSION_LIST ']' {
3135 $$.c = code_append($$.c, $2.cc);
3136 $$.c = abc_newarray($$.c, $2.len);
3137 $$.t = registry_getarrayclass();
3140 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
3141 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3143 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3145 $$.cc = code_append($$.cc, $1.c);
3146 $$.cc = code_append($$.cc, $3.c);
3149 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3152 $$.cc = code_append($$.cc, $3.c);
3153 $$.cc = code_append($$.cc, $5.c);
3158 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3160 $$.c = code_append($$.c, $2.cc);
3161 $$.c = abc_newobject($$.c, $2.len/2);
3162 $$.t = registry_getobjectclass();
3167 if(BOTH_INT($1.t,$3.t)) {
3168 c=abc_multiply_i(c);
3172 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3173 $$.c = toreadwrite($1.c, c, 0, 0);
3178 code_t*c = abc_modulo($3.c);
3179 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3180 $$.c = toreadwrite($1.c, c, 0, 0);
3184 code_t*c = abc_lshift($3.c);
3185 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3186 $$.c = toreadwrite($1.c, c, 0, 0);
3190 code_t*c = abc_rshift($3.c);
3191 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3192 $$.c = toreadwrite($1.c, c, 0, 0);
3196 code_t*c = abc_urshift($3.c);
3197 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3198 $$.c = toreadwrite($1.c, c, 0, 0);
3202 code_t*c = abc_divide($3.c);
3203 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3204 $$.c = toreadwrite($1.c, c, 0, 0);
3208 code_t*c = abc_bitor($3.c);
3209 c=converttype(c, TYPE_INT, $1.t);
3210 $$.c = toreadwrite($1.c, c, 0, 0);
3214 code_t*c = abc_bitxor($3.c);
3215 c=converttype(c, TYPE_INT, $1.t);
3216 $$.c = toreadwrite($1.c, c, 0, 0);
3222 if(TYPE_IS_INT($1.t)) {
3226 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3229 $$.c = toreadwrite($1.c, c, 0, 0);
3232 E : E "-=" E { code_t*c = $3.c;
3233 if(TYPE_IS_INT($1.t)) {
3234 c=abc_subtract_i(c);
3237 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3240 $$.c = toreadwrite($1.c, c, 0, 0);
3243 E : E '=' E { code_t*c = 0;
3244 c = code_append(c, $3.c);
3245 c = converttype(c, $3.t, $1.t);
3246 $$.c = toreadwrite($1.c, c, 1, 0);
3250 E : E '?' E ':' E %prec below_assignment {
3251 $$.t = join_types($3.t,$5.t,'?');
3253 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3254 $$.c = code_append($$.c, $3.c);
3255 $$.c = converttype($$.c, $3.t, $$.t);
3256 code_t*j2 = $$.c = abc_jump($$.c, 0);
3257 $$.c = j1->branch = abc_label($$.c);
3258 $$.c = code_append($$.c, $5.c);
3259 $$.c = converttype($$.c, $5.t, $$.t);
3260 $$.c = j2->branch = abc_label($$.c);
3263 E : E "++" { code_t*c = 0;
3264 classinfo_t*type = $1.t;
3265 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
3266 int nr = getlocalnr($1.c);
3267 code_free($1.c);$1.c=0;
3268 if(TYPE_IS_INT($1.t)) {
3269 $$.c = abc_getlocal(0, nr);
3270 $$.c = abc_inclocal_i($$.c, nr);
3271 } else if(TYPE_IS_NUMBER($1.t)) {
3272 $$.c = abc_getlocal(0, nr);
3273 $$.c = abc_inclocal($$.c, nr);
3274 } else syntaxerror("internal error");
3276 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3277 c=abc_increment_i(c);
3283 c=converttype(c, type, $1.t);
3284 $$.c = toreadwrite($1.c, c, 0, 1);
3289 // TODO: use inclocal, like with ++
3290 E : E "--" { code_t*c = 0;
3291 classinfo_t*type = $1.t;
3292 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3293 c=abc_decrement_i(c);
3299 c=converttype(c, type, $1.t);
3300 $$.c = toreadwrite($1.c, c, 0, 1);
3304 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3305 classinfo_t*type = $2.t;
3306 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3307 c=abc_increment_i(c);
3313 c=converttype(c, type, $2.t);
3314 $$.c = toreadwrite($2.c, c, 0, 0);
3318 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3319 classinfo_t*type = $2.t;
3320 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3321 c=abc_decrement_i(c);
3327 c=converttype(c, type, $2.t);
3328 $$.c = toreadwrite($2.c, c, 0, 0);
3332 E : "super" '.' T_IDENTIFIER
3333 { if(!state->cls->info)
3334 syntaxerror("super keyword not allowed outside a class");
3335 classinfo_t*t = state->cls->info->superclass;
3336 if(!t) t = TYPE_OBJECT;
3338 memberinfo_t*f = registry_findmember(t, $3, 1);
3339 MEMBER_MULTINAME(m, f, $3);
3341 $$.c = abc_getlocal_0($$.c);
3342 $$.c = abc_getsuper2($$.c, &m);
3343 $$.t = slotinfo_gettype((slotinfo_t*)f);
3346 E : '@' T_IDENTIFIER {
3348 $$.c = abc_pushundefined(0);
3350 as3_warning("ignored @ operator");
3353 E : E '.' '@' T_IDENTIFIER {
3354 // child attribute TODO
3355 $$.c = abc_pushundefined(0);
3357 as3_warning("ignored .@ operator");
3360 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3361 // namespace declaration TODO
3362 $$.c = abc_pushundefined(0);
3364 as3_warning("ignored :: operator");
3367 E : E ".." T_IDENTIFIER {
3369 $$.c = abc_pushundefined(0);
3371 as3_warning("ignored .. operator");
3374 E : E '.' '(' E ')' {
3376 $$.c = abc_pushundefined(0);
3378 as3_warning("ignored .() operator");
3381 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3385 E : E '.' T_IDENTIFIER
3387 classinfo_t*t = $1.t;
3389 if(TYPE_IS_CLASS(t) && t->data) {
3394 memberinfo_t*f = registry_findmember(t, $3, 1);
3396 if(f && !is_static != !(f->flags&FLAG_STATIC))
3398 if(f && f->slot && !noslot) {
3399 $$.c = abc_getslot($$.c, f->slot);
3401 MEMBER_MULTINAME(m, f, $3);
3402 $$.c = abc_getproperty2($$.c, &m);
3404 /* determine type */
3405 $$.t = slotinfo_gettype((slotinfo_t*)f);
3407 $$.c = abc_coerce_a($$.c);
3409 /* when resolving a property on an unknown type, we do know the
3410 name of the property (and don't seem to need the package), but
3411 we need to make avm2 try out all access modes */
3412 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3413 $$.c = abc_getproperty2($$.c, &m);
3414 $$.c = abc_coerce_a($$.c);
3415 $$.t = registry_getanytype();
3419 VAR_READ : T_IDENTIFIER {
3421 /* Queue unresolved identifiers for checking against the parent
3422 function's variables.
3423 We consider everything which is not a local variable "unresolved".
3424 This encompasses class names, members of the surrounding class
3425 etc. which *correct* because local variables of the parent function
3428 if(state->method->inner && !find_variable(state, $1)) {
3429 unknown_variable($1);
3439 /* look at variables */
3440 if((v = find_variable(state, $1))) {
3441 // $1 is a local variable
3442 $$.c = abc_getlocal($$.c, v->index);
3446 if((v = find_slot(state, $1))) {
3447 $$.c = abc_getscopeobject($$.c, 1);
3448 $$.c = abc_getslot($$.c, v->index);
3453 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3455 /* look at current class' members */
3456 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
3457 (f->flags&FLAG_STATIC) >= i_am_static) {
3458 // $1 is a function in this class
3459 int var_is_static = (f->flags&FLAG_STATIC);
3461 if(f->kind == INFOTYPE_METHOD) {
3462 $$.t = TYPE_FUNCTION(f);
3466 if(var_is_static && !i_am_static) {
3467 /* access to a static member from a non-static location.
3468 do this via findpropstrict:
3469 there doesn't seem to be any non-lookup way to access
3470 static properties of a class */
3471 state->method->late_binding = 1;
3473 namespace_t ns = {f->access, ""};
3474 multiname_t m = {QNAME, &ns, 0, $1};
3475 $$.c = abc_findpropstrict2($$.c, &m);
3476 $$.c = abc_getproperty2($$.c, &m);
3478 } else if(f->slot>0) {
3479 $$.c = abc_getlocal_0($$.c);
3480 $$.c = abc_getslot($$.c, f->slot);
3483 namespace_t ns = {f->access, ""};
3484 multiname_t m = {QNAME, &ns, 0, $1};
3485 $$.c = abc_getlocal_0($$.c);
3486 $$.c = abc_getproperty2($$.c, &m);
3491 /* look at actual classes, in the current package and imported */
3492 if((a = find_class($1))) {
3493 if(a->access == ACCESS_PACKAGEINTERNAL &&
3494 strcmp(a->package, state->package) &&
3495 strcmp(a->package, internal_filename_package)
3497 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
3498 infotypename(a),$1, a->package, state->package);
3500 if(a->kind != INFOTYPE_CLASS) {
3502 $$.c = abc_findpropstrict2($$.c, &m);
3503 $$.c = abc_getproperty2($$.c, &m);
3504 if(a->kind == INFOTYPE_METHOD) {
3505 methodinfo_t*f = (methodinfo_t*)a;
3506 $$.t = TYPE_FUNCTION(f);
3508 varinfo_t*v = (varinfo_t*)a;
3512 classinfo_t*c = (classinfo_t*)a;
3514 $$.c = abc_getglobalscope($$.c);
3515 $$.c = abc_getslot($$.c, c->slot);
3518 $$.c = abc_getlex2($$.c, &m);
3520 $$.t = TYPE_CLASS(c);
3525 /* unknown object, let the avm2 resolve it */
3527 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3528 state->method->late_binding = 1;
3530 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3533 $$.c = abc_findpropstrict2($$.c, &m);
3534 $$.c = abc_getproperty2($$.c, &m);
3538 // ----------------- namespaces -------------------------------------------------
3540 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3541 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3542 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3544 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {
3546 tokenizer_register_namespace($3);