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;
327 int var_index; // for inner methods
330 abc_exception_list_t*exceptions;
332 methodstate_list_t*innerfunctions;
335 typedef struct _state {
340 import_list_t*wildcard_imports;
342 char has_own_imports;
343 char new_vars; // e.g. transition between two functions
346 methodstate_t*method;
353 typedef struct _global {
357 dict_t*file2token2info;
360 static global_t*global = 0;
361 static state_t* state = 0;
365 #define MULTINAME(m,x) \
369 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
371 #define MEMBER_MULTINAME(m,f,n) \
375 m##_ns.access = (f)->access; \
379 m.namespace_set = 0; \
382 m.type = MULTINAME; \
384 m.namespace_set = &nopackage_namespace_set; \
388 /* warning: list length of namespace set is undefined */
389 #define MULTINAME_LATE(m, access, package) \
390 namespace_t m##_ns = {access, package}; \
391 namespace_set_t m##_nsset; \
392 namespace_list_t m##_l;m##_l.next = 0; \
393 m##_nsset.namespaces = &m##_l; \
394 m##_nsset = m##_nsset; \
395 m##_l.namespace = &m##_ns; \
396 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
398 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
399 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
400 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
401 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
402 static namespace_list_t nl4 = {&ns4,0};
403 static namespace_list_t nl3 = {&ns3,&nl4};
404 static namespace_list_t nl2 = {&ns2,&nl3};
405 static namespace_list_t nl1 = {&ns1,&nl2};
406 static namespace_set_t nopackage_namespace_set = {&nl1};
408 static void new_state()
411 state_t*oldstate = state;
413 memcpy(s, state, sizeof(state_t)); //shallow copy
415 s->imports = dict_new();
419 state->has_own_imports = 0;
420 state->vars = dict_new();
421 state->old = oldstate;
423 static void state_has_imports()
425 state->wildcard_imports = list_clone(state->wildcard_imports);
426 state->imports = dict_clone(state->imports);
427 state->has_own_imports = 1;
430 static void state_destroy(state_t*state)
432 if(state->has_own_imports) {
433 list_free(state->wildcard_imports);
434 dict_destroy(state->imports);state->imports=0;
436 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
437 dict_destroy(state->imports);state->imports=0;
441 for(t=0;t<state->vars->hashsize;t++) {
442 dictentry_t*e =state->vars->slots[t];
444 free(e->data);e->data=0;
448 dict_destroy(state->vars);state->vars=0;
454 static void old_state()
456 if(!state || !state->old)
457 syntaxerror("invalid nesting");
458 state_t*leaving = state;
462 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
463 free(leaving->method);
466 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
471 state_destroy(leaving);
474 static code_t* method_header(methodstate_t*m);
475 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
476 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
479 static char* internal_filename_package = 0;
480 void initialize_file(char*filename)
483 syntaxerror("invalid call to initialize_file during parsing of another file");
486 state->package = internal_filename_package = strdup(filename);
488 global->token2info = dict_lookup(global->file2token2info,
489 current_filename // use long version
491 if(!global->token2info) {
492 global->token2info = dict_new2(&ptr_type);
493 dict_put(global->file2token2info, current_filename, global->token2info);
497 state->method = rfx_calloc(sizeof(methodstate_t));
498 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
500 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
501 function_initvars(state->method, 0, 0, 1);
502 global->init = abc_initscript(global->file);
503 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
509 if(!state || state->level!=1) {
510 syntaxerror("unexpected end of file in pass %d", as3_pass);
514 code_t*header = method_header(state->method);
515 code_t*c = wrap_function(header, 0, global->init->method->body->code);
516 global->init->method->body->code = c;
517 free(state->method);state->method=0;
520 //free(state->package);state->package=0; // used in registry
521 state_destroy(state);state=0;
524 void initialize_parser()
526 global = rfx_calloc(sizeof(global_t));
527 global->file = abc_file_new();
528 global->file->flags &= ~ABCFILE_LAZY;
529 global->file2token2info = dict_new();
530 global->token2info = 0;
533 void* finish_parser()
535 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
537 global->token2info=0;
543 static void xx_scopetest()
545 /* findpropstrict doesn't just return a scope object- it
546 also makes it "active" somehow. Push local_0 on the
547 scope stack and read it back with findpropstrict, it'll
548 contain properties like "trace". Trying to find the same
549 property on a "vanilla" local_0 yields only a "undefined" */
550 //c = abc_findpropstrict(c, "[package]::trace");
552 /*c = abc_getlocal_0(c);
553 c = abc_findpropstrict(c, "[package]::trace");
555 c = abc_setlocal_1(c);
557 c = abc_pushbyte(c, 0);
558 c = abc_setlocal_2(c);
560 code_t*xx = c = abc_label(c);
561 c = abc_findpropstrict(c, "[package]::trace");
562 c = abc_pushstring(c, "prop:");
563 c = abc_hasnext2(c, 1, 2);
565 c = abc_setlocal_3(c);
566 c = abc_callpropvoid(c, "[package]::trace", 2);
567 c = abc_getlocal_3(c);
569 c = abc_iftrue(c,xx);*/
572 typedef struct _variable {
576 methodstate_t*method;
579 static variable_t* find_variable(state_t*s, char*name)
584 v = dict_lookup(s->vars, name);
594 static variable_t* find_variable_safe(state_t*s, char*name)
596 variable_t* v = find_variable(s, name);
598 syntaxerror("undefined variable: %s", name);
601 static char variable_exists(char*name)
603 return dict_contains(state->vars, name);
605 code_t*defaultvalue(code_t*c, classinfo_t*type);
606 static int new_variable(const char*name, classinfo_t*type, char init)
609 v->index = state->method->variable_count;
612 v->method = state->method;
614 dict_put(state->vars, name, v);
616 return state->method->variable_count++;
618 #define TEMPVARNAME "__as3_temp__"
619 static int gettempvar()
621 variable_t*v = find_variable(state, TEMPVARNAME);
624 return new_variable(TEMPVARNAME, 0, 0);
627 code_t* var_block(code_t*body)
633 for(t=0;t<state->vars->hashsize;t++) {
634 dictentry_t*e = state->vars->slots[t];
636 variable_t*v = (variable_t*)e->data;
637 if(v->type && v->init) {
638 c = defaultvalue(c, v->type);
639 c = abc_setlocal(c, v->index);
640 k = abc_kill(k, v->index);
650 if(x->opcode== OPCODE___BREAK__ ||
651 x->opcode== OPCODE___CONTINUE__) {
652 /* link kill code before break/continue */
653 code_t*e = code_dup(k);
654 code_t*s = code_start(e);
666 c = code_append(c, body);
667 c = code_append(c, k);
671 void unknown_variable(char*name)
673 if(!state->method->unresolved_variables)
674 state->method->unresolved_variables = dict_new();
675 if(!dict_contains(state->method->unresolved_variables, name))
676 dict_put(state->method->unresolved_variables, name, 0);
679 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
681 static void parsererror(const char*file, int line, const char*f)
683 syntaxerror("internal error in %s, %s:%d", f, file, line);
687 static code_t* method_header(methodstate_t*m)
690 if(m->late_binding && !m->inner) {
691 c = abc_getlocal_0(c);
692 c = abc_pushscope(c);
694 /*if(m->innerfunctions) {
695 c = abc_newactivation(c);
696 c = abc_pushscope(c);
698 methodstate_list_t*l = m->innerfunctions;
700 parserassert(l->methodstate->abc);
701 c = abc_newfunction(c, l->methodstate->abc);
702 c = abc_setlocal(c, l->methodstate->var_index);
703 free(l->methodstate);l->methodstate=0;
707 c = code_append(c, m->header);
710 if(m->is_constructor && !m->has_super) {
711 // call default constructor
712 c = abc_getlocal_0(c);
713 c = abc_constructsuper(c, 0);
715 list_free(m->innerfunctions);
716 m->innerfunctions = 0;
721 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
723 c = code_append(c, header);
724 c = code_append(c, var_block(body));
725 /* append return if necessary */
726 if(!c || (c->opcode != OPCODE_RETURNVOID &&
727 c->opcode != OPCODE_RETURNVALUE)) {
728 c = abc_returnvoid(c);
734 static void startpackage(char*name)
737 /*printf("entering package \"%s\"\n", name);*/
738 state->package = strdup(name);
740 static void endpackage()
742 /*printf("leaving package \"%s\"\n", state->package);*/
744 //used e.g. in classinfo_register:
745 //free(state->package);state->package=0;
750 #define FLAG_PUBLIC 256
751 #define FLAG_PROTECTED 512
752 #define FLAG_PRIVATE 1024
753 #define FLAG_PACKAGEINTERNAL 2048
754 #define FLAG_NAMESPACE 4096
756 static int flags2access(int flags)
759 if(flags&FLAG_PUBLIC) {
760 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
761 syntaxerror("invalid combination of access levels");
762 access = ACCESS_PACKAGE;
763 } else if(flags&FLAG_PRIVATE) {
764 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
765 syntaxerror("invalid combination of access levels");
766 access = ACCESS_PRIVATE;
767 } else if(flags&FLAG_PROTECTED) {
768 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
769 syntaxerror("invalid combination of access levels");
770 access = ACCESS_PROTECTED;
772 access = ACCESS_PACKAGEINTERNAL;
777 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
782 index = new_variable("this", 0, 0);
783 else if(!m->is_global)
784 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0);
786 index = new_variable("globalscope", 0, 0);
789 parserassert(!index);
794 for(p=params->list;p;p=p->next) {
795 new_variable(p->param->name, p->param->type, 0);
799 methodstate_list_t*l = m->innerfunctions;
801 methodstate_t*m = l->methodstate;
802 m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0);
808 char*as3_globalclass=0;
809 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements)
812 syntaxerror("inner classes now allowed");
816 classinfo_list_t*mlist=0;
818 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
819 syntaxerror("invalid modifier(s)");
821 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
822 syntaxerror("public and internal not supported at the same time.");
824 /* create the class name, together with the proper attributes */
828 if(!(flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
829 access = ACCESS_PRIVATE; package = internal_filename_package;
830 } else if(!(flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
831 access = ACCESS_PACKAGEINTERNAL; package = state->package;
832 } else if(state->package!=internal_filename_package) {
833 access = ACCESS_PACKAGE; package = state->package;
835 syntaxerror("public classes only allowed inside a package");
839 state->cls = rfx_calloc(sizeof(classstate_t));
840 state->cls->init = rfx_calloc(sizeof(methodstate_t));
841 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
842 /* notice: we make no effort to initialize the top variable (local0) here,
843 even though it has special meaning. We just rely on the facat
844 that pass 1 won't do anything with variables */
846 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
848 /* set current method to constructor- all code within the class-level (except
849 static variable initializations) will be executed during construction time */
850 state->method = state->cls->init;
852 if(registry_find(package, classname)) {
853 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
855 /* build info struct */
856 int num_interfaces = (list_length(implements));
857 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
858 state->cls->info->flags |= flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
862 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
864 state->method = state->cls->init;
865 parserassert(state->cls && state->cls->info);
867 function_initvars(state->cls->init, 0, 0, 1);
868 function_initvars(state->cls->static_init, 0, 0, 0);
870 if(extends && (extends->flags & FLAG_FINAL))
871 syntaxerror("Can't extend final class '%s'", extends->name);
873 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
874 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
876 classinfo_list_t*l = implements;
877 for(l=implements;l;l=l->next) {
878 if(!(l->classinfo->flags & FLAG_INTERFACE))
879 syntaxerror("'%s' is not an interface", l->classinfo->name);
880 state->cls->info->interfaces[pos++] = l->classinfo;
883 /* generate the abc code for this class */
884 MULTINAME(classname2,state->cls->info);
885 multiname_t*extends2 = sig2mname(extends);
887 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
888 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
889 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
890 if(state->cls->info->flags&FLAG_INTERFACE) {
891 abc_class_interface(state->cls->abc);
894 abc_class_protectedNS(state->cls->abc, classname);
896 for(mlist=implements;mlist;mlist=mlist->next) {
897 MULTINAME(m, mlist->classinfo);
898 abc_class_add_interface(state->cls->abc, &m);
901 /* write the construction code for this class to the global init
903 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
905 abc_method_body_t*m = global->init->method->body;
906 __ getglobalscope(m);
907 classinfo_t*s = extends;
912 //TODO: take a look at the current scope stack, maybe
913 // we can re-use something
918 multiname_t*s2 = sig2mname(s);
920 multiname_destroy(s2);
922 __ pushscope(m); count++;
923 m->code = m->code->prev->prev; // invert
925 /* continue appending after last op end */
926 while(m->code && m->code->next) m->code = m->code->next;
928 /* TODO: if this is one of *our* classes, we can also
929 do a getglobalscope/getslot <nr> (which references
930 the init function's slots) */
932 __ getlex2(m, extends2);
934 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
935 stack is not the superclass */
936 __ pushscope(m);count++;
939 /* notice: we get a verify error #1107 if the top element on the scope
940 stack is not the global object */
942 __ pushscope(m);count++;
944 __ newclass(m,state->cls->abc);
948 __ setslot(m, slotindex);
949 multiname_destroy(extends2);
951 /* flash.display.MovieClip handling */
953 if(!as3_globalclass && (flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
954 if(state->package && state->package[0]) {
955 as3_globalclass = concat3(state->package, ".", classname);
957 as3_globalclass = strdup(classname);
963 static void setstaticfunction(int x)
967 state->method = state->cls->static_init;
969 state->method = state->cls->init;
972 parserassert(state->method);
976 static void endclass()
979 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
981 c = abc_getlocal_0(c);
982 c = abc_constructsuper(c, 0);
983 state->cls->init->header = code_append(state->cls->init->header, c);
984 state->cls->has_constructor=1;
986 if(state->cls->init) {
987 if(state->cls->info->flags&FLAG_INTERFACE) {
988 if(state->cls->init->header)
989 syntaxerror("interface can not have class-level code");
991 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
992 code_t*c = method_header(state->cls->init);
993 m->body->code = wrap_function(c, 0, m->body->code);
996 if(state->cls->static_init) {
997 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
998 code_t*c = method_header(state->cls->static_init);
999 m->body->code = wrap_function(c, 0, m->body->code);
1006 void check_code_for_break(code_t*c)
1009 if(c->opcode == OPCODE___BREAK__) {
1010 char*name = string_cstr(c->data[0]);
1011 syntaxerror("Unresolved \"break %s\"", name);
1013 if(c->opcode == OPCODE___CONTINUE__) {
1014 char*name = string_cstr(c->data[0]);
1015 syntaxerror("Unresolved \"continue %s\"", name);
1022 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1024 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1025 if(TYPE_IS_NUMBER(t)) {
1026 xassert(c->type == CONSTANT_FLOAT
1027 || c->type == CONSTANT_INT
1028 || c->type == CONSTANT_UINT);
1029 } else if(TYPE_IS_UINT(t)) {
1030 xassert(c->type == CONSTANT_UINT ||
1031 (c->type == CONSTANT_INT && c->i>=0));
1032 } else if(TYPE_IS_INT(t)) {
1033 xassert(c->type == CONSTANT_INT);
1034 } else if(TYPE_IS_BOOLEAN(t)) {
1035 xassert(c->type == CONSTANT_TRUE
1036 || c->type == CONSTANT_FALSE);
1040 static void check_override(memberinfo_t*m, int flags)
1044 if(m->parent == state->cls->info)
1045 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1047 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1048 if(m->access==ACCESS_PRIVATE)
1050 if(m->flags & FLAG_FINAL)
1051 syntaxerror("can't override final member %s", m->name);
1052 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1053 syntaxerror("can't override static member %s", m->name);
1054 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1055 syntaxerror("can't override non-static member %s with static declaration", m->name);
1057 if(!(flags&FLAG_OVERRIDE)) {
1058 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1059 if(m->kind == INFOTYPE_METHOD)
1060 syntaxerror("can't override without explicit 'override' declaration");
1062 syntaxerror("can't override '%s'", m->name);
1067 static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
1069 methodinfo_t*minfo = 0;
1070 U8 access = flags2access(flags);
1073 minfo = methodinfo_register_global(access, state->package, name);
1074 minfo->return_type = return_type;
1075 } else if(getset != KW_GET && getset != KW_SET) {
1077 memberinfo_t* m = registry_findmember(state->cls->info, name, 0);
1079 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1081 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1082 minfo->return_type = return_type;
1083 // getslot on a member slot only returns "undefined", so no need
1084 // to actually store these
1085 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1087 //class getter/setter
1088 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1090 if(getset == KW_GET)
1092 else if(params->list && params->list->param)
1093 type = params->list->param->type;
1094 // not sure wether to look into superclasses here, too
1095 minfo = (methodinfo_t*)registry_findmember(state->cls->info, name, 1);
1097 if(minfo->kind!=INFOTYPE_SLOT)
1098 syntaxerror("class already contains a method called '%s'", name);
1099 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1100 syntaxerror("class already contains a field called '%s'", name);
1101 if(minfo->subtype & gs)
1102 syntaxerror("getter/setter for '%s' already defined", name);
1103 /* make a setter or getter into a getset */
1104 minfo->subtype |= gs;
1105 if(!minfo->return_type) {
1106 minfo->return_type = type;
1108 if(minfo && minfo->return_type != type)
1109 syntaxerror("different type in getter and setter");
1112 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1113 minfo->kind = INFOTYPE_SLOT; //hack
1114 minfo->subtype = gs;
1115 minfo->return_type = type;
1117 /* can't assign a slot as getter and setter might have different slots */
1118 //minfo->slot = slot;
1120 if(flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1121 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1122 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1126 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1128 //parserassert(state->method && state->method->info);
1130 methodstate_t*parent_method = state->method;
1133 return_type = 0; // not valid in pass 1
1137 state->new_vars = 1;
1140 state->method = rfx_calloc(sizeof(methodstate_t));
1141 state->method->inner = 1;
1142 state->method->variable_count = 0;
1143 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1145 NEW(methodinfo_t,minfo);
1146 minfo->kind = INFOTYPE_METHOD;
1147 minfo->access = ACCESS_PACKAGEINTERNAL;
1149 state->method->info = minfo;
1152 list_append(parent_method->innerfunctions, state->method);
1154 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1156 function_initvars(state->method, params, 0, 1);
1160 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1161 state->method->variable_count = 0;
1162 parserassert(state->method);
1164 state->method->info->return_type = return_type;
1165 function_initvars(state->method, params, 0, 1);
1169 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1170 params_t*params, classinfo_t*return_type)
1172 if(state->method && state->method->info) {
1173 syntaxerror("not able to start another method scope");
1178 state->method = rfx_calloc(sizeof(methodstate_t));
1179 state->method->has_super = 0;
1182 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1184 state->method->is_global = 1;
1185 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1187 if(state->method->is_constructor)
1188 name = "__as3_constructor__";
1191 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1193 function_initvars(state->method, params, flags, 1);
1195 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1199 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1200 state->method->variable_count = 0;
1201 parserassert(state->method);
1204 memberinfo_t*m = registry_findmember(state->cls->info, name, 2);
1205 check_override(m, flags);
1209 state->cls->has_constructor |= state->method->is_constructor;
1212 state->method->info->return_type = return_type;
1213 function_initvars(state->method, params, flags, 1);
1217 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1218 params_t*params, classinfo_t*return_type, code_t*body)
1221 // store inner methods in variables
1222 function_initvars(state->method, 0, 0, 0);
1224 methodstate_list_t*ml = state->method->innerfunctions;
1226 methodstate_t*m = ml->methodstate;
1227 parserassert(m->inner);
1228 if(m->unresolved_variables) {
1229 dict_t*d = m->unresolved_variables;
1231 for(t=0;t<d->hashsize;t++) {
1232 dictentry_t*l = d->slots[t];
1234 /* check parent method's variables */
1235 if(find_variable(state, l->key)) {
1236 m->uses_parent_function = 1;
1244 dict_destroy(m->unresolved_variables);
1245 m->unresolved_variables = 0;
1254 if(state->method->uses_parent_function){
1255 syntaxerror("accessing variables of parent function from inner functions not supported yet");
1260 multiname_t*type2 = sig2mname(return_type);
1262 if(state->method->inner) {
1263 f = state->method->abc;
1264 abc_method_init(f, global->file, type2, 1);
1265 } else if(state->method->is_constructor) {
1266 f = abc_class_getconstructor(state->cls->abc, type2);
1267 } else if(!state->method->is_global) {
1268 namespace_t mname_ns = {state->method->info->access, ""};
1269 multiname_t mname = {QNAME, &mname_ns, 0, name};
1271 if(flags&FLAG_STATIC)
1272 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1274 f = abc_class_method(state->cls->abc, type2, &mname);
1275 slot = f->trait->slot_id;
1277 namespace_t mname_ns = {state->method->info->access, state->package};
1278 multiname_t mname = {QNAME, &mname_ns, 0, name};
1280 f = abc_method_new(global->file, type2, 1);
1281 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1282 //abc_code_t*c = global->init->method->body->code;
1284 //flash doesn't seem to allow us to access function slots
1285 //state->method->info->slot = slot;
1287 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1288 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1289 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1290 if(params->varargs) f->flags |= METHOD_NEED_REST;
1294 for(p=params->list;p;p=p->next) {
1295 if(params->varargs && !p->next) {
1296 break; //varargs: omit last parameter in function signature
1298 multiname_t*m = sig2mname(p->param->type);
1299 list_append(f->parameters, m);
1300 if(p->param->value) {
1301 check_constant_against_type(p->param->type, p->param->value);
1302 opt=1;list_append(f->optional_parameters, p->param->value);
1304 syntaxerror("non-optional parameter not allowed after optional parameters");
1307 check_code_for_break(body);
1310 f->body->code = body;
1311 f->body->exceptions = state->method->exceptions;
1312 } else { //interface
1314 syntaxerror("interface methods can't have a method body");
1324 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1329 void breakjumpsto(code_t*c, char*name, code_t*jump)
1332 if(c->opcode == OPCODE___BREAK__) {
1333 string_t*name2 = c->data[0];
1334 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1335 c->opcode = OPCODE_JUMP;
1342 void continuejumpsto(code_t*c, char*name, code_t*jump)
1345 if(c->opcode == OPCODE___CONTINUE__) {
1346 string_t*name2 = c->data[0];
1347 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1348 c->opcode = OPCODE_JUMP;
1356 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1357 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1358 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1360 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1362 if(!type1 || !type2)
1363 return registry_getanytype();
1364 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1365 return registry_getanytype();
1368 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1377 return registry_getanytype();
1379 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1384 return abc_coerce_a(c);
1388 // cast an "any" type to a specific type. subject to
1389 // runtime exceptions
1390 return abc_coerce2(c, &m);
1393 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1394 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1395 // allow conversion between number types
1396 return abc_coerce2(c, &m);
1398 //printf("%s.%s\n", from.package, from.name);
1399 //printf("%s.%s\n", to.package, to.name);
1401 classinfo_t*supertype = from;
1403 if(supertype == to) {
1404 // target type is one of from's superclasses
1405 return abc_coerce2(c, &m);
1408 while(supertype->interfaces[t]) {
1409 if(supertype->interfaces[t]==to) {
1410 // target type is one of from's interfaces
1411 return abc_coerce2(c, &m);
1415 supertype = supertype->superclass;
1417 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1419 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1421 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1424 as3_error("can't convert type %s%s%s to %s%s%s",
1425 from->package, from->package?".":"", from->name,
1426 to->package, to->package?".":"", to->name);
1430 code_t*defaultvalue(code_t*c, classinfo_t*type)
1432 if(TYPE_IS_INT(type)) {
1433 c = abc_pushbyte(c, 0);
1434 } else if(TYPE_IS_UINT(type)) {
1435 c = abc_pushuint(c, 0);
1436 } else if(TYPE_IS_FLOAT(type)) {
1438 } else if(TYPE_IS_BOOLEAN(type)) {
1439 c = abc_pushfalse(c);
1441 //c = abc_pushundefined(c);
1443 c = abc_pushnull(c);
1445 c = abc_coerce2(c, &m);
1450 char is_pushundefined(code_t*c)
1452 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1455 static slotinfo_t* find_class(char*name)
1459 c = registry_find(state->package, name);
1462 /* try explicit imports */
1463 dictentry_t* e = dict_get_slot(state->imports, name);
1466 if(!strcmp(e->key, name)) {
1467 c = (slotinfo_t*)e->data;
1473 /* try package.* imports */
1474 import_list_t*l = state->wildcard_imports;
1476 //printf("does package %s contain a class %s?\n", l->import->package, name);
1477 c = registry_find(l->import->package, name);
1482 /* try global package */
1483 c = registry_find("", name);
1486 /* try local "filename" package */
1487 c = registry_find(internal_filename_package, name);
1493 static char is_getlocal(code_t*c)
1495 if(!c || c->prev || c->next)
1497 return(c->opcode == OPCODE_GETLOCAL
1498 || c->opcode == OPCODE_GETLOCAL_0
1499 || c->opcode == OPCODE_GETLOCAL_1
1500 || c->opcode == OPCODE_GETLOCAL_2
1501 || c->opcode == OPCODE_GETLOCAL_3);
1503 static int getlocalnr(code_t*c)
1505 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1506 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1507 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1508 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1509 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1510 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1514 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1518 [prefix code] [read instruction]
1522 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1524 if(in && in->opcode == OPCODE_COERCE_A) {
1525 in = code_cutlast(in);
1528 syntaxerror("internal error");
1530 /* chop off read instruction */
1534 prefix = r->prev;r->prev = 0;
1540 char use_temp_var = readbefore;
1542 /* generate the write instruction, and maybe append a dup to the prefix code */
1543 code_t* write = abc_nop(0);
1544 if(r->opcode == OPCODE_GETPROPERTY) {
1545 write->opcode = OPCODE_SETPROPERTY;
1546 multiname_t*m = (multiname_t*)r->data[0];
1547 write->data[0] = multiname_clone(m);
1548 if(m->type == QNAME || m->type == MULTINAME) {
1550 prefix = abc_dup(prefix); // we need the object, too
1553 } else if(m->type == MULTINAMEL) {
1555 /* dupping two values on the stack requires 5 operations and one register-
1556 couldn't adobe just have given us a dup2? */
1557 int temp = gettempvar();
1558 prefix = abc_setlocal(prefix, temp);
1559 prefix = abc_dup(prefix);
1560 prefix = abc_getlocal(prefix, temp);
1561 prefix = abc_swap(prefix);
1562 prefix = abc_getlocal(prefix, temp);
1564 prefix = abc_kill(prefix, temp);
1568 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1570 } else if(r->opcode == OPCODE_GETSLOT) {
1571 write->opcode = OPCODE_SETSLOT;
1572 write->data[0] = r->data[0];
1574 prefix = abc_dup(prefix); // we need the object, too
1577 } else if(r->opcode == OPCODE_GETLOCAL) {
1578 write->opcode = OPCODE_SETLOCAL;
1579 write->data[0] = r->data[0];
1580 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1581 write->opcode = OPCODE_SETLOCAL_0;
1582 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1583 write->opcode = OPCODE_SETLOCAL_1;
1584 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1585 write->opcode = OPCODE_SETLOCAL_2;
1586 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1587 write->opcode = OPCODE_SETLOCAL_3;
1590 syntaxerror("illegal lvalue: can't assign a value to this expression");
1597 /* with getproperty/getslot, we have to be extra careful not
1598 to execute the read code twice, as it might have side-effects
1599 (e.g. if the property is in fact a setter/getter combination)
1601 So read the value, modify it, and write it again,
1602 using prefix only once and making sure (by using a temporary
1603 register) that the return value is what we just wrote */
1604 temp = gettempvar();
1605 c = code_append(c, prefix);
1606 c = code_append(c, r);
1609 c = abc_setlocal(c, temp);
1611 c = code_append(c, middlepart);
1614 c = abc_setlocal(c, temp);
1616 c = code_append(c, write);
1617 c = abc_getlocal(c, temp);
1618 c = abc_kill(c, temp);
1620 /* if we're allowed to execute the read code twice *and*
1621 the middlepart doesn't modify the code, things are easier.
1623 code_t* r2 = code_dup(r);
1624 //c = code_append(c, prefix);
1625 parserassert(!prefix);
1626 c = code_append(c, r);
1627 c = code_append(c, middlepart);
1628 c = code_append(c, write);
1629 c = code_append(c, r2);
1632 /* even smaller version: overwrite the value without reading
1636 c = code_append(c, prefix);
1639 c = code_append(c, middlepart);
1640 c = code_append(c, write);
1641 c = code_append(c, r);
1644 temp = gettempvar();
1646 c = code_append(c, prefix);
1648 c = code_append(c, middlepart);
1650 c = abc_setlocal(c, temp);
1651 c = code_append(c, write);
1652 c = abc_getlocal(c, temp);
1653 c = abc_kill(c, temp);
1659 char is_break_or_jump(code_t*c)
1663 if(c->opcode == OPCODE_JUMP ||
1664 c->opcode == OPCODE___BREAK__ ||
1665 c->opcode == OPCODE___CONTINUE__ ||
1666 c->opcode == OPCODE_THROW ||
1667 c->opcode == OPCODE_RETURNVOID ||
1668 c->opcode == OPCODE_RETURNVALUE) {
1675 #define IS_FINALLY_TARGET(op) \
1676 ((op) == OPCODE___CONTINUE__ || \
1677 (op) == OPCODE___BREAK__ || \
1678 (op) == OPCODE_RETURNVOID || \
1679 (op) == OPCODE_RETURNVALUE || \
1680 (op) == OPCODE___RETHROW__)
1682 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1684 #define NEED_EXTRA_STACK_ARG
1685 code_t*finally_label = abc_nop(0);
1686 NEW(lookupswitch_t, l);
1692 code_t*prev = i->prev;
1693 if(IS_FINALLY_TARGET(i->opcode)) {
1696 if(i->opcode == OPCODE___RETHROW__ ||
1697 i->opcode == OPCODE_RETURNVALUE) {
1698 if(i->opcode == OPCODE___RETHROW__)
1699 i->opcode = OPCODE_THROW;
1701 p = abc_coerce_a(p);
1702 p = abc_setlocal(p, tempvar);
1704 p = abc_pushbyte(p, count++);
1705 p = abc_jump(p, finally_label);
1706 code_t*target = p = abc_label(p);
1707 #ifdef NEED_EXTRA_STACK_ARG
1711 p = abc_getlocal(p, tempvar);
1714 p->next = i;i->prev = p;
1715 list_append(l->targets, target);
1721 c = abc_pushbyte(c, -1);
1722 c = code_append(c, finally_label);
1723 c = code_append(c, finally);
1725 #ifdef NEED_EXTRA_STACK_ARG
1728 c = abc_lookupswitch(c, l);
1729 c = l->def = abc_label(c);
1730 #ifdef NEED_EXTRA_STACK_ARG
1737 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1741 code_t*prev = i->prev;
1742 if(IS_FINALLY_TARGET(i->opcode)) {
1743 if(i->opcode == OPCODE___RETHROW__)
1744 i->opcode = OPCODE_THROW;
1745 code_t*end = code_dup(finally);
1746 code_t*start = code_start(end);
1747 if(prev) prev->next = start;
1754 return code_append(c, finally);
1757 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1763 int num_insertion_points=0;
1765 if(IS_FINALLY_TARGET(i->opcode))
1766 num_insertion_points++;
1773 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1778 int simple_version_cost = (1+num_insertion_points)*code_size;
1779 int lookup_version_cost = 4*num_insertion_points + 5;
1781 if(cantdup || simple_version_cost > lookup_version_cost) {
1782 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1783 return insert_finally_lookup(c, finally, tempvar);
1785 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1786 return insert_finally_simple(c, finally, tempvar);
1790 #define PASS1 }} if(as3_pass == 1) {{
1791 #define PASS1END }} if(as3_pass == 2) {{
1792 #define PASS2 }} if(as3_pass == 2) {{
1793 #define PASS12 }} {{
1794 #define PASS12END }} if(as3_pass == 2) {{
1800 /* ------------ code blocks / statements ---------------- */
1802 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1804 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1805 PROGRAM_CODE_LIST: PROGRAM_CODE
1806 | PROGRAM_CODE_LIST PROGRAM_CODE
1808 PROGRAM_CODE: PACKAGE_DECLARATION
1809 | INTERFACE_DECLARATION
1811 | FUNCTION_DECLARATION
1814 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
1817 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1818 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1819 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1821 INPACKAGE_CODE: INTERFACE_DECLARATION
1823 | FUNCTION_DECLARATION
1826 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
1829 MAYBECODE: CODE {$$=$1;}
1830 MAYBECODE: {$$=code_new();}
1832 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1833 CODE: CODEPIECE {$$=$1;}
1835 // code which also may appear outside a method
1836 CODE_STATEMENT: IMPORT
1838 CODE_STATEMENT: FOR_IN
1839 CODE_STATEMENT: WHILE
1840 CODE_STATEMENT: DO_WHILE
1841 CODE_STATEMENT: SWITCH
1843 CODE_STATEMENT: WITH
1845 CODE_STATEMENT: VOIDEXPRESSION
1846 CODE_STATEMENT: USE_NAMESPACE
1847 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1848 CODE_STATEMENT: '{' '}' {$$=0;}
1850 // code which may appear anywhere
1851 CODEPIECE: ';' {$$=0;}
1852 CODEPIECE: CODE_STATEMENT
1853 CODEPIECE: VARIABLE_DECLARATION
1858 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
1860 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1862 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1863 //CODEBLOCK : '{' '}' {$$=0;}
1864 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1865 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1867 /* ------------ package init code ------------------- */
1869 PACKAGE_INITCODE: CODE_STATEMENT {
1870 code_t**cc = &global->init->method->body->code;
1871 *cc = code_append(*cc, $1);
1874 /* ------------ conditional compilation ------------- */
1876 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
1878 /* ------------ variables --------------------------- */
1880 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1881 | {$$.c=abc_pushundefined(0);
1885 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1886 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1888 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1889 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1891 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1894 if(variable_exists($1))
1895 syntaxerror("Variable %s already defined", $1);
1897 new_variable($1, $2, 1);
1900 if(!is_subtype_of($3.t, $2)) {
1901 syntaxerror("Can't convert %s to %s", $3.t->name,
1904 int index = new_variable($1, $2, 1);
1907 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1909 $$ = converttype($$, $3.t, $2);
1910 $$ = abc_setlocal($$, index);
1913 $$ = defaultvalue(0, $2);
1914 $$ = abc_setlocal($$, index);
1917 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1919 $$ = abc_coerce_a($$);
1920 $$ = abc_setlocal($$, index);
1927 /* that's the default for a local register, anyway
1929 state->method->initcode = abc_pushundefined(state->method->initcode);
1930 state->method->initcode = abc_setlocal(state->method->initcode, index);
1932 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1935 /* ------------ control flow ------------------------- */
1937 MAYBEELSE: %prec below_else {$$ = code_new();}
1938 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1939 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1941 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1944 $$ = code_append($$, $4.c);
1945 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1947 $$ = code_append($$, $6);
1949 myjmp = $$ = abc_jump($$, 0);
1951 myif->branch = $$ = abc_nop($$);
1953 $$ = code_append($$, $7);
1954 myjmp->branch = $$ = abc_nop($$);
1960 FOR_INIT : {$$=code_new();}
1961 FOR_INIT : VARIABLE_DECLARATION
1962 FOR_INIT : VOIDEXPRESSION
1964 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
1965 // (I don't see any easy way to revolve this conflict otherwise, as we
1966 // can't touch VAR_READ without upsetting the precedence about "return")
1967 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1969 $$=$2;new_variable($2,$3,1);
1971 FOR_IN_INIT : T_IDENTIFIER {
1976 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
1977 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
1979 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1980 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1982 $$ = code_append($$, $2);
1983 code_t*loopstart = $$ = abc_label($$);
1984 $$ = code_append($$, $4.c);
1985 code_t*myif = $$ = abc_iffalse($$, 0);
1986 $$ = code_append($$, $8);
1987 code_t*cont = $$ = abc_nop($$);
1988 $$ = code_append($$, $6);
1989 $$ = abc_jump($$, loopstart);
1990 code_t*out = $$ = abc_nop($$);
1991 breakjumpsto($$, $1.name, out);
1992 continuejumpsto($$, $1.name, cont);
1999 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
2000 variable_t*var = find_variable(state, $2);
2001 char*tmp1name = concat2($2, "__tmp1__");
2002 int it = new_variable(tmp1name, TYPE_INT, 0);
2003 char*tmp2name = concat2($2, "__array__");
2004 int array = new_variable(tmp1name, 0, 0);
2007 $$ = code_append($$, $4.c);
2008 $$ = abc_coerce_a($$);
2009 $$ = abc_setlocal($$, array);
2010 $$ = abc_pushbyte($$, 0);
2011 $$ = abc_setlocal($$, it);
2013 code_t*loopstart = $$ = abc_label($$);
2015 $$ = abc_hasnext2($$, array, it);
2016 code_t*myif = $$ = abc_iffalse($$, 0);
2017 $$ = abc_getlocal($$, array);
2018 $$ = abc_getlocal($$, it);
2020 $$ = abc_nextname($$);
2022 $$ = abc_nextvalue($$);
2023 $$ = converttype($$, 0, var->type);
2024 $$ = abc_setlocal($$, var->index);
2026 $$ = code_append($$, $6);
2027 $$ = abc_jump($$, loopstart);
2029 code_t*out = $$ = abc_nop($$);
2030 breakjumpsto($$, $1.name, out);
2031 continuejumpsto($$, $1.name, loopstart);
2043 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2047 code_t*myjmp = $$ = abc_jump($$, 0);
2048 code_t*loopstart = $$ = abc_label($$);
2049 $$ = code_append($$, $6);
2050 code_t*cont = $$ = abc_nop($$);
2051 myjmp->branch = cont;
2052 $$ = code_append($$, $4.c);
2053 $$ = abc_iftrue($$, loopstart);
2054 code_t*out = $$ = abc_nop($$);
2055 breakjumpsto($$, $1, out);
2056 continuejumpsto($$, $1, cont);
2062 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2064 code_t*loopstart = $$ = abc_label($$);
2065 $$ = code_append($$, $3);
2066 code_t*cont = $$ = abc_nop($$);
2067 $$ = code_append($$, $6.c);
2068 $$ = abc_iftrue($$, loopstart);
2069 code_t*out = $$ = abc_nop($$);
2070 breakjumpsto($$, $1, out);
2071 continuejumpsto($$, $1, cont);
2077 BREAK : "break" %prec prec_none {
2078 $$ = abc___break__(0, "");
2080 BREAK : "break" T_IDENTIFIER {
2081 $$ = abc___break__(0, $2);
2083 CONTINUE : "continue" %prec prec_none {
2084 $$ = abc___continue__(0, "");
2086 CONTINUE : "continue" T_IDENTIFIER {
2087 $$ = abc___continue__(0, $2);
2090 MAYBE_CASE_LIST : {$$=0;}
2091 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2092 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2093 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2094 CASE_LIST: CASE {$$=$1;}
2095 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2097 CASE: "case" E ':' MAYBECODE {
2099 $$ = code_append($$, $2.c);
2100 code_t*j = $$ = abc_ifne($$, 0);
2101 $$ = code_append($$, $4);
2102 if($$->opcode != OPCODE___BREAK__) {
2103 $$ = abc___fallthrough__($$, "");
2105 code_t*e = $$ = abc_nop($$);
2108 DEFAULT: "default" ':' MAYBECODE {
2111 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2113 $$ = code_append($$, $7);
2114 code_t*out = $$ = abc_pop($$);
2115 breakjumpsto($$, $1, out);
2117 code_t*c = $$,*lastblock=0;
2119 if(c->opcode == OPCODE_IFNE) {
2120 if(!c->next) syntaxerror("internal error in fallthrough handling");
2122 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2124 c->opcode = OPCODE_JUMP;
2125 c->branch = lastblock;
2127 /* fall through end of switch */
2128 c->opcode = OPCODE_NOP;
2138 /* ------------ try / catch /finally ---------------- */
2140 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();state->exception_name=$3;new_variable($3, $4, 0);}
2142 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2143 multiname_t name = {QNAME, &name_ns, 0, $3};
2145 NEW(abc_exception_t, e)
2146 e->exc_type = sig2mname($4);
2147 e->var_name = multiname_clone(&name);
2151 int i = find_variable_safe(state, $3)->index;
2152 e->target = c = abc_nop(0);
2153 c = abc_setlocal(c, i);
2154 c = code_append(c, $8);
2160 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2165 NEW(abc_exception_t, e)
2166 e->exc_type = 0; //all exceptions
2167 e->var_name = 0; //no name
2170 e->to = code_append(e->to, $4);
2176 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2177 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2178 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2179 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2183 list_append($$.l,$2);
2184 $$.finally = $2->to;$2->to=0;
2187 CATCH_FINALLY_LIST: FINALLY {
2191 list_append($$.l,$1);
2192 $$.finally = $1->to;$1->to=0;
2196 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2197 code_t*out = abc_nop(0);
2199 code_t*start = abc_nop(0);
2200 $$ = code_append(start, $4);
2201 if(!is_break_or_jump($4)) {
2202 $$ = abc_jump($$, out);
2204 code_t*end = $$ = abc_nop($$);
2208 tmp = new_variable("__finally__", 0, 0);
2210 abc_exception_list_t*l = $6.l;
2213 abc_exception_t*e = l->abc_exception;
2215 $$ = code_append($$, e->target);
2216 $$ = abc_jump($$, out);
2218 parserassert((ptroff_t)$6.finally);
2220 e->target = $$ = abc_nop($$);
2221 $$ = abc___rethrow__($$);
2229 $$ = code_append($$, out);
2231 $$ = insert_finally($$, $6.finally, tmp);
2233 list_concat(state->method->exceptions, $6.l);
2239 /* ------------ throw ------------------------------- */
2241 THROW : "throw" EXPRESSION {
2245 THROW : "throw" %prec prec_none {
2246 if(!state->exception_name)
2247 syntaxerror("re-throw only possible within a catch block");
2248 variable_t*v = find_variable(state, state->exception_name);
2250 $$=abc_getlocal($$, v->index);
2254 /* ------------ with -------------------------------- */
2256 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2258 $$ = abc_pushscope($$);
2259 $$ = code_append($$, $5);
2260 $$ = abc_popscope($$);
2263 /* ------------ packages and imports ---------------- */
2265 X_IDENTIFIER: T_IDENTIFIER
2266 | "package" {PASS12 $$="package";}
2268 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2269 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2271 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2272 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2273 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2274 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2276 IMPORT : "import" PACKAGEANDCLASS {
2278 slotinfo_t*s = registry_find($2->package, $2->name);
2279 if(!s) {// || !(s->flags&FLAG_BUILTIN)) {
2280 as3_schedule_class($2->package, $2->name);
2286 syntaxerror("Couldn't import class\n");
2287 state_has_imports();
2288 dict_put(state->imports, c->name, c);
2291 IMPORT : "import" PACKAGE '.' '*' {
2293 if(strncmp("flash.", $2, 6)) {
2294 as3_schedule_package($2);
2300 state_has_imports();
2301 list_append(state->wildcard_imports, i);
2305 /* ------------ classes and interfaces (header) -------------- */
2307 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2308 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2309 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2310 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2312 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2313 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2314 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2315 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2316 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2317 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2318 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2319 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2320 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2321 | T_NAMESPACE {PASS12 $$=FLAG_NAMESPACE;}
2323 EXTENDS : {$$=registry_getobjectclass();}
2324 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2326 EXTENDS_LIST : {PASS12 $$=list_new();}
2327 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2329 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2330 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2332 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2333 EXTENDS IMPLEMENTS_LIST
2334 '{' {PASS12 startclass($1,$3,$4,$5);}
2336 '}' {PASS12 endclass();$$=0;}
2338 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2340 '{' {PASS12 startclass($1|FLAG_INTERFACE,$3,0,$4);}
2341 MAYBE_INTERFACE_BODY
2342 '}' {PASS12 endclass();$$=0;}
2344 /* ------------ classes and interfaces (body) -------------- */
2347 MAYBE_CLASS_BODY : CLASS_BODY
2348 CLASS_BODY : CLASS_BODY_ITEM
2349 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2350 CLASS_BODY_ITEM : ';'
2351 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2352 CLASS_BODY_ITEM : SLOT_DECLARATION
2353 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2355 CLASS_BODY_ITEM : CODE_STATEMENT {
2356 code_t*c = state->cls->static_init->header;
2357 c = code_append(c, $1);
2358 state->cls->static_init->header = c;
2361 MAYBE_INTERFACE_BODY :
2362 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2363 INTERFACE_BODY : IDECLARATION
2364 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2366 IDECLARATION : "var" T_IDENTIFIER {
2367 syntaxerror("variable declarations not allowed in interfaces");
2369 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2372 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2373 syntaxerror("invalid method modifiers: interface methods always need to be public");
2375 startfunction(0,$1,$3,$4,&$6,$8);
2376 endfunction(0,$1,$3,$4,&$6,$8, 0);
2377 list_deep_free($6.list);
2380 /* ------------ classes and interfaces (body, slots ) ------- */
2382 VARCONST: "var" | "const"
2384 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} MAYBETYPE MAYBEEXPRESSION {
2386 U8 access = flags2access($1);
2388 varinfo_t* info = 0;
2390 memberinfo_t*i = registry_findmember(state->cls->info, $3, 1);
2392 check_override(i, flags);
2394 info = varinfo_register_onclass(state->cls->info, access, $3);
2396 slotinfo_t*i = registry_find(state->package, $3);
2398 syntaxerror("package %s already contains '%s'", state->package, $3);
2400 info = varinfo_register_global(access, state->package, $3);
2404 info->flags = flags;
2407 namespace_t mname_ns = {access, ""};
2408 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2410 trait_list_t**traits;
2414 mname_ns.name = state->package;
2415 traits = &global->init->traits;
2416 code = &global->init->method->body->code;
2417 } else if(flags&FLAG_STATIC) {
2419 traits = &state->cls->abc->static_traits;
2420 code = &state->cls->static_init->header;
2422 // instance variable
2423 traits = &state->cls->abc->traits;
2424 code = &state->cls->init->header;
2430 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2432 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2434 info->slot = t->slot_id;
2436 /* initalization code (if needed) */
2438 if($6.c && !is_pushundefined($6.c)) {
2439 c = abc_getlocal_0(c);
2440 c = code_append(c, $6.c);
2441 c = converttype(c, $6.t, $5);
2442 c = abc_setslot(c, t->slot_id);
2445 *code = code_append(*code, c);
2448 t->kind= TRAIT_CONST;
2452 setstaticfunction(0);
2455 /* ------------ constants -------------------------------------- */
2457 MAYBESTATICCONSTANT: {$$=0;}
2458 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2460 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2461 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2462 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2463 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2464 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2465 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2466 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2467 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2468 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2469 STATICCONSTANT : T_IDENTIFIER {
2471 as3_warning("Couldn't resolve %s", $1);
2472 $$ = constant_new_null($1);
2475 /* ------------ classes and interfaces (body, functions) ------- */
2477 // non-vararg version
2480 memset(&$$,0,sizeof($$));
2482 MAYBE_PARAM_LIST: PARAM_LIST {
2488 MAYBE_PARAM_LIST: "..." PARAM {
2490 memset(&$$,0,sizeof($$));
2492 list_append($$.list, $2);
2494 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2498 list_append($$.list, $4);
2502 PARAM_LIST: PARAM_LIST ',' PARAM {
2505 list_append($$.list, $3);
2509 memset(&$$,0,sizeof($$));
2510 list_append($$.list, $1);
2513 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2515 $$ = rfx_calloc(sizeof(param_t));
2521 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2523 $$ = rfx_calloc(sizeof(param_t));
2525 $$->type = TYPE_ANY;
2529 GETSET : "get" {$$=$1;}
2533 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2534 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2537 endfunction(0,$1,$3,$4,&$6,0,0);
2539 if(!state->method->info) syntaxerror("internal error");
2541 code_t*c = method_header(state->method);
2542 c = wrap_function(c, 0, $11);
2544 endfunction(0,$1,$3,$4,&$6,$8,c);
2546 list_deep_free($6.list);
2550 MAYBE_IDENTIFIER: T_IDENTIFIER
2551 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2552 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2553 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2556 endfunction(0,0,0,$2,&$4,0,0);
2558 methodinfo_t*f = state->method->info;
2559 if(!f || !f->kind) syntaxerror("internal error");
2561 code_t*c = method_header(state->method);
2562 c = wrap_function(c, 0, $9);
2564 int index = state->method->var_index;
2565 endfunction(0,0,0,$2,&$4,$6,c);
2567 $$.c = abc_getlocal(0, index);
2568 $$.t = TYPE_FUNCTION(f);
2570 PASS12 list_deep_free($4.list);
2574 /* ------------- package + class ids --------------- */
2576 CLASS: T_IDENTIFIER {
2579 slotinfo_t*s = find_class($1);
2580 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2581 $$ = (classinfo_t*)s;
2584 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2585 PASS1 static classinfo_t c;
2586 memset(&c, 0, sizeof(c));
2591 slotinfo_t*s = registry_find($1, $3);
2592 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2594 $$ = (classinfo_t*)s;
2597 CLASS_SPEC: PACKAGEANDCLASS
2600 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2601 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2603 TYPE : CLASS_SPEC {$$=$1;}
2604 | '*' {$$=registry_getanytype();}
2605 | "void" {$$=registry_getanytype();}
2607 | "String" {$$=registry_getstringclass();}
2608 | "int" {$$=registry_getintclass();}
2609 | "uint" {$$=registry_getuintclass();}
2610 | "Boolean" {$$=registry_getbooleanclass();}
2611 | "Number" {$$=registry_getnumberclass();}
2614 MAYBETYPE: ':' TYPE {$$=$2;}
2617 /* ----------function calls, delete, constructor calls ------ */
2619 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2620 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2622 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2623 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2624 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2626 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2630 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2631 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2633 $$.cc = code_append($1.cc, $2.c);
2637 NEW : "new" E XX MAYBE_PARAM_VALUES {
2639 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2641 code_t*paramcode = $4.cc;
2642 if($$.c->opcode == OPCODE_GETPROPERTY) {
2643 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2644 $$.c = code_cutlast($$.c);
2645 $$.c = code_append($$.c, paramcode);
2646 $$.c = abc_constructprop2($$.c, name, $4.len);
2647 multiname_destroy(name);
2648 } else if($$.c->opcode == OPCODE_GETSLOT) {
2649 int slot = (int)(ptroff_t)$$.c->data[0];
2650 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2651 multiname_t*name = t->name;
2652 $$.c = code_cutlast($$.c);
2653 $$.c = code_append($$.c, paramcode);
2654 $$.c = abc_constructprop2($$.c, name, $4.len);
2656 $$.c = code_append($$.c, paramcode);
2657 $$.c = abc_construct($$.c, $4.len);
2661 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2664 $$.c = abc_coerce_a($$.c);
2669 /* TODO: use abc_call (for calling local variables),
2670 abc_callstatic (for calling own methods)
2673 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2676 if($$.c->opcode == OPCODE_COERCE_A) {
2677 $$.c = code_cutlast($$.c);
2679 code_t*paramcode = $3.cc;
2682 if($$.c->opcode == OPCODE_GETPROPERTY) {
2683 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2684 $$.c = code_cutlast($$.c);
2685 $$.c = code_append($$.c, paramcode);
2686 $$.c = abc_callproperty2($$.c, name, $3.len);
2687 multiname_destroy(name);
2688 } else if($$.c->opcode == OPCODE_GETSLOT) {
2689 int slot = (int)(ptroff_t)$$.c->data[0];
2690 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2691 if(t->kind!=TRAIT_METHOD) {
2692 //ok: flash allows to assign closures to members.
2694 multiname_t*name = t->name;
2695 $$.c = code_cutlast($$.c);
2696 $$.c = code_append($$.c, paramcode);
2697 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2698 $$.c = abc_callproperty2($$.c, name, $3.len);
2699 } else if($$.c->opcode == OPCODE_GETSUPER) {
2700 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2701 $$.c = code_cutlast($$.c);
2702 $$.c = code_append($$.c, paramcode);
2703 $$.c = abc_callsuper2($$.c, name, $3.len);
2704 multiname_destroy(name);
2706 $$.c = abc_getglobalscope($$.c);
2707 $$.c = code_append($$.c, paramcode);
2708 $$.c = abc_call($$.c, $3.len);
2711 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2712 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2714 $$.c = abc_coerce_a($$.c);
2719 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2720 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2721 if(!state->method) syntaxerror("super() not allowed outside of a function");
2722 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2725 $$.c = abc_getlocal_0($$.c);
2727 $$.c = code_append($$.c, $3.cc);
2729 this is dependent on the control path, check this somewhere else
2730 if(state->method->has_super)
2731 syntaxerror("constructor may call super() only once");
2733 state->method->has_super = 1;
2735 $$.c = abc_constructsuper($$.c, $3.len);
2736 $$.c = abc_pushundefined($$.c);
2740 DELETE: "delete" E {
2742 if($$.c->opcode == OPCODE_COERCE_A) {
2743 $$.c = code_cutlast($$.c);
2745 multiname_t*name = 0;
2746 if($$.c->opcode == OPCODE_GETPROPERTY) {
2747 $$.c->opcode = OPCODE_DELETEPROPERTY;
2748 } else if($$.c->opcode == OPCODE_GETSLOT) {
2749 int slot = (int)(ptroff_t)$$.c->data[0];
2750 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
2751 $$.c = code_cutlast($$.c);
2752 $$.c = abc_deleteproperty2($$.c, name);
2754 $$.c = abc_getlocal_0($$.c);
2755 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2756 $$.c = abc_deleteproperty2($$.c, &m);
2758 $$.t = TYPE_BOOLEAN;
2761 RETURN: "return" %prec prec_none {
2762 $$ = abc_returnvoid(0);
2764 RETURN: "return" EXPRESSION {
2766 $$ = abc_returnvalue($$);
2769 // ----------------------- expression types -------------------------------------
2771 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2772 EXPRESSION : E %prec below_minus {$$ = $1;}
2773 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2775 $$.c = cut_last_push($$.c);
2776 $$.c = code_append($$.c,$3.c);
2779 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2780 $$=cut_last_push($1.c);
2783 // ----------------------- expression evaluation -------------------------------------
2785 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2786 //V : CONSTANT {$$ = 0;}
2788 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2789 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2790 //V : NEW {$$ = $1.c;}
2792 //V : DELETE {$$ = $1.c;}
2793 E : DELETE {$$ = $1;}
2799 namespace_t ns = {ACCESS_PACKAGE, ""};
2800 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2802 $$.c = abc_getlex2($$.c, &m);
2803 $$.c = abc_pushstring($$.c, $1.pattern);
2804 $$.c = abc_construct($$.c, 1);
2806 $$.c = abc_getlex2($$.c, &m);
2807 $$.c = abc_pushstring($$.c, $1.pattern);
2808 $$.c = abc_pushstring($$.c, $1.options);
2809 $$.c = abc_construct($$.c, 2);
2814 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2815 //MULTINAME(m, registry_getintclass());
2816 //$$.c = abc_coerce2($$.c, &m); // FIXME
2819 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2822 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2825 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2828 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2831 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
2834 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2837 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2838 $$.t = TYPE_BOOLEAN;
2840 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2841 $$.t = TYPE_BOOLEAN;
2843 CONSTANT : "null" {$$.c = abc_pushnull(0);
2847 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2848 $$.t = TYPE_BOOLEAN;
2850 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2851 $$.t = TYPE_BOOLEAN;
2853 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2854 $$.t = TYPE_BOOLEAN;
2856 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2857 $$.t = TYPE_BOOLEAN;
2859 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2860 $$.t = TYPE_BOOLEAN;
2862 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2863 $$.t = TYPE_BOOLEAN;
2865 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2866 $$.t = TYPE_BOOLEAN;
2868 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2869 $$.t = TYPE_BOOLEAN;
2872 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2874 $$.c = converttype($$.c, $1.t, $$.t);
2875 $$.c = abc_dup($$.c);
2876 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2877 $$.c = cut_last_push($$.c);
2878 $$.c = code_append($$.c,$3.c);
2879 $$.c = converttype($$.c, $3.t, $$.t);
2880 code_t*label = $$.c = abc_label($$.c);
2881 jmp->branch = label;
2884 $$.t = join_types($1.t, $3.t, 'A');
2885 /*printf("%08x:\n",$1.t);
2886 code_dump($1.c, 0, 0, "", stdout);
2887 printf("%08x:\n",$3.t);
2888 code_dump($3.c, 0, 0, "", stdout);
2889 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2891 $$.c = converttype($$.c, $1.t, $$.t);
2892 $$.c = abc_dup($$.c);
2893 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2894 $$.c = cut_last_push($$.c);
2895 $$.c = code_append($$.c,$3.c);
2896 $$.c = converttype($$.c, $3.t, $$.t);
2897 code_t*label = $$.c = abc_label($$.c);
2898 jmp->branch = label;
2901 E : '!' E {$$.c=$2.c;
2902 $$.c = abc_not($$.c);
2903 $$.t = TYPE_BOOLEAN;
2906 E : '~' E {$$.c=$2.c;
2907 $$.c = abc_bitnot($$.c);
2911 E : E '&' E {$$.c = code_append($1.c,$3.c);
2912 $$.c = abc_bitand($$.c);
2916 E : E '^' E {$$.c = code_append($1.c,$3.c);
2917 $$.c = abc_bitxor($$.c);
2921 E : E '|' E {$$.c = code_append($1.c,$3.c);
2922 $$.c = abc_bitor($$.c);
2926 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2927 $$.c = abc_rshift($$.c);
2930 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2931 $$.c = abc_urshift($$.c);
2934 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2935 $$.c = abc_lshift($$.c);
2939 E : E '/' E {$$.c = code_append($1.c,$3.c);
2940 $$.c = abc_divide($$.c);
2943 E : E '%' E {$$.c = code_append($1.c,$3.c);
2944 $$.c = abc_modulo($$.c);
2947 E : E '+' E {$$.c = code_append($1.c,$3.c);
2948 if(BOTH_INT($1.t, $3.t)) {
2949 $$.c = abc_add_i($$.c);
2952 $$.c = abc_add($$.c);
2953 $$.t = join_types($1.t,$3.t,'+');
2956 E : E '-' E {$$.c = code_append($1.c,$3.c);
2957 if(BOTH_INT($1.t,$3.t)) {
2958 $$.c = abc_subtract_i($$.c);
2961 $$.c = abc_subtract($$.c);
2965 E : E '*' E {$$.c = code_append($1.c,$3.c);
2966 if(BOTH_INT($1.t,$3.t)) {
2967 $$.c = abc_multiply_i($$.c);
2970 $$.c = abc_multiply($$.c);
2975 E : E "in" E {$$.c = code_append($1.c,$3.c);
2976 $$.c = abc_in($$.c);
2977 $$.t = TYPE_BOOLEAN;
2980 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2981 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
2982 MULTINAME(m, (classinfo_t*)($3.t->data));
2983 $$.c = abc_astype2($1.c, &m);
2986 $$.c = code_append($1.c, $3.c);
2987 $$.c = abc_astypelate($$.c);
2992 E : E "instanceof" E
2993 {$$.c = code_append($1.c, $3.c);
2994 $$.c = abc_instanceof($$.c);
2995 $$.t = TYPE_BOOLEAN;
2998 E : E "is" E {$$.c = code_append($1.c, $3.c);
2999 $$.c = abc_istypelate($$.c);
3000 $$.t = TYPE_BOOLEAN;
3003 E : "typeof" '(' E ')' {
3005 $$.c = abc_typeof($$.c);
3010 $$.c = cut_last_push($2.c);
3011 $$.c = abc_pushundefined($$.c);
3015 E : "void" { $$.c = abc_pushundefined(0);
3019 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
3024 $$.c=abc_negate_i($$.c);
3027 $$.c=abc_negate($$.c);
3034 $$.c = code_append($$.c, $3.c);
3036 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
3037 $$.c = abc_getproperty2($$.c, &m);
3038 $$.t = 0; // array elements have unknown type
3041 E : '[' MAYBE_EXPRESSION_LIST ']' {
3043 $$.c = code_append($$.c, $2.cc);
3044 $$.c = abc_newarray($$.c, $2.len);
3045 $$.t = registry_getarrayclass();
3048 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
3049 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
3051 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3053 $$.cc = code_append($$.cc, $1.c);
3054 $$.cc = code_append($$.cc, $3.c);
3057 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3060 $$.cc = code_append($$.cc, $3.c);
3061 $$.cc = code_append($$.cc, $5.c);
3066 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3068 $$.c = code_append($$.c, $2.cc);
3069 $$.c = abc_newobject($$.c, $2.len/2);
3070 $$.t = registry_getobjectclass();
3075 if(BOTH_INT($1.t,$3.t)) {
3076 c=abc_multiply_i(c);
3080 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3081 $$.c = toreadwrite($1.c, c, 0, 0);
3086 code_t*c = abc_modulo($3.c);
3087 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3088 $$.c = toreadwrite($1.c, c, 0, 0);
3092 code_t*c = abc_lshift($3.c);
3093 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3094 $$.c = toreadwrite($1.c, c, 0, 0);
3098 code_t*c = abc_rshift($3.c);
3099 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3100 $$.c = toreadwrite($1.c, c, 0, 0);
3104 code_t*c = abc_urshift($3.c);
3105 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3106 $$.c = toreadwrite($1.c, c, 0, 0);
3110 code_t*c = abc_divide($3.c);
3111 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3112 $$.c = toreadwrite($1.c, c, 0, 0);
3116 code_t*c = abc_bitor($3.c);
3117 c=converttype(c, TYPE_INT, $1.t);
3118 $$.c = toreadwrite($1.c, c, 0, 0);
3122 code_t*c = abc_bitxor($3.c);
3123 c=converttype(c, TYPE_INT, $1.t);
3124 $$.c = toreadwrite($1.c, c, 0, 0);
3130 if(TYPE_IS_INT($1.t)) {
3134 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3137 $$.c = toreadwrite($1.c, c, 0, 0);
3140 E : E "-=" E { code_t*c = $3.c;
3141 if(TYPE_IS_INT($1.t)) {
3142 c=abc_subtract_i(c);
3145 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3148 $$.c = toreadwrite($1.c, c, 0, 0);
3151 E : E '=' E { code_t*c = 0;
3152 c = code_append(c, $3.c);
3153 c = converttype(c, $3.t, $1.t);
3154 $$.c = toreadwrite($1.c, c, 1, 0);
3158 E : E '?' E ':' E %prec below_assignment {
3159 $$.t = join_types($3.t,$5.t,'?');
3161 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3162 $$.c = code_append($$.c, $3.c);
3163 $$.c = converttype($$.c, $3.t, $$.t);
3164 code_t*j2 = $$.c = abc_jump($$.c, 0);
3165 $$.c = j1->branch = abc_label($$.c);
3166 $$.c = code_append($$.c, $5.c);
3167 $$.c = converttype($$.c, $3.t, $$.t);
3168 $$.c = j2->branch = abc_label($$.c);
3171 E : E "++" { code_t*c = 0;
3172 classinfo_t*type = $1.t;
3173 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
3174 int nr = getlocalnr($1.c);
3175 code_free($1.c);$1.c=0;
3176 if(TYPE_IS_INT($1.t)) {
3177 $$.c = abc_getlocal(0, nr);
3178 $$.c = abc_inclocal_i($$.c, nr);
3179 } else if(TYPE_IS_NUMBER($1.t)) {
3180 $$.c = abc_getlocal(0, nr);
3181 $$.c = abc_inclocal($$.c, nr);
3182 } else syntaxerror("internal error");
3184 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3185 c=abc_increment_i(c);
3191 c=converttype(c, type, $1.t);
3192 $$.c = toreadwrite($1.c, c, 0, 1);
3197 // TODO: use inclocal, like with ++
3198 E : E "--" { code_t*c = 0;
3199 classinfo_t*type = $1.t;
3200 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3201 c=abc_decrement_i(c);
3207 c=converttype(c, type, $1.t);
3208 $$.c = toreadwrite($1.c, c, 0, 1);
3212 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3213 classinfo_t*type = $2.t;
3214 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3215 c=abc_increment_i(c);
3221 c=converttype(c, type, $2.t);
3222 $$.c = toreadwrite($2.c, c, 0, 0);
3226 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3227 classinfo_t*type = $2.t;
3228 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3229 c=abc_decrement_i(c);
3235 c=converttype(c, type, $2.t);
3236 $$.c = toreadwrite($2.c, c, 0, 0);
3240 E : "super" '.' T_IDENTIFIER
3241 { if(!state->cls->info)
3242 syntaxerror("super keyword not allowed outside a class");
3243 classinfo_t*t = state->cls->info->superclass;
3244 if(!t) t = TYPE_OBJECT;
3246 memberinfo_t*f = registry_findmember(t, $3, 1);
3247 namespace_t ns = {f->access, ""};
3248 MEMBER_MULTINAME(m, f, $3);
3250 $$.c = abc_getlocal_0($$.c);
3251 $$.c = abc_getsuper2($$.c, &m);
3252 $$.t = slotinfo_gettype((slotinfo_t*)f);
3255 E : '@' T_IDENTIFIER {
3257 $$.c = abc_pushundefined(0);
3259 as3_warning("ignored @ operator");
3262 E : E '.' '@' T_IDENTIFIER {
3263 // child attribute TODO
3264 $$.c = abc_pushundefined(0);
3266 as3_warning("ignored .@ operator");
3269 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3270 // namespace declaration TODO
3271 $$.c = abc_pushundefined(0);
3273 as3_warning("ignored :: operator");
3276 E : E ".." T_IDENTIFIER {
3278 $$.c = abc_pushundefined(0);
3280 as3_warning("ignored .. operator");
3283 E : E '.' '(' E ')' {
3285 $$.c = abc_pushundefined(0);
3287 as3_warning("ignored .() operator");
3290 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3294 E : E '.' T_IDENTIFIER
3296 classinfo_t*t = $1.t;
3298 if(TYPE_IS_CLASS(t) && t->data) {
3303 memberinfo_t*f = registry_findmember(t, $3, 1);
3305 if(f && !is_static != !(f->flags&FLAG_STATIC))
3307 if(f && f->slot && !noslot) {
3308 $$.c = abc_getslot($$.c, f->slot);
3310 MEMBER_MULTINAME(m, f, $3);
3311 $$.c = abc_getproperty2($$.c, &m);
3313 /* determine type */
3314 $$.t = slotinfo_gettype((slotinfo_t*)f);
3316 $$.c = abc_coerce_a($$.c);
3318 /* when resolving a property on an unknown type, we do know the
3319 name of the property (and don't seem to need the package), but
3320 we need to make avm2 try out all access modes */
3321 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3322 $$.c = abc_getproperty2($$.c, &m);
3323 $$.c = abc_coerce_a($$.c);
3324 $$.t = registry_getanytype();
3328 VAR_READ : T_IDENTIFIER {
3330 /* Queue unresolved identifiers for checking against the parent
3331 function's variables.
3332 We consider everything which is not a local variable "unresolved".
3333 This encompasses class names, members of the surrounding class
3334 etc. which *correct* because local variables of the parent function
3337 if(state->method->inner && !find_variable(state, $1)) {
3338 unknown_variable($1);
3348 /* look at variables */
3349 if((v = find_variable(state, $1))) {
3350 // $1 is a local variable
3351 $$.c = abc_getlocal($$.c, v->index);
3356 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3358 /* look at current class' members */
3359 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
3360 (f->flags&FLAG_STATIC) >= i_am_static) {
3361 // $1 is a function in this class
3362 int var_is_static = (f->flags&FLAG_STATIC);
3364 if(f->kind == INFOTYPE_METHOD) {
3365 $$.t = TYPE_FUNCTION(f);
3369 if(var_is_static && !i_am_static) {
3370 /* access to a static member from a non-static location.
3371 do this via findpropstrict:
3372 there doesn't seem to be any non-lookup way to access
3373 static properties of a class */
3374 state->method->late_binding = 1;
3376 namespace_t ns = {f->access, ""};
3377 multiname_t m = {QNAME, &ns, 0, $1};
3378 $$.c = abc_findpropstrict2($$.c, &m);
3379 $$.c = abc_getproperty2($$.c, &m);
3381 } else if(f->slot>0) {
3382 $$.c = abc_getlocal_0($$.c);
3383 $$.c = abc_getslot($$.c, f->slot);
3386 namespace_t ns = {f->access, ""};
3387 multiname_t m = {QNAME, &ns, 0, $1};
3388 $$.c = abc_getlocal_0($$.c);
3389 $$.c = abc_getproperty2($$.c, &m);
3394 /* look at actual classes, in the current package and imported */
3395 if((a = find_class($1))) {
3396 if(a->access == ACCESS_PACKAGEINTERNAL &&
3397 strcmp(a->package, state->package) &&
3398 strcmp(a->package, internal_filename_package)
3400 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
3401 infotypename(a),$1, a->package, state->package);
3403 if(a->kind != INFOTYPE_CLASS) {
3405 $$.c = abc_findpropstrict2($$.c, &m);
3406 $$.c = abc_getproperty2($$.c, &m);
3407 if(a->kind == INFOTYPE_METHOD) {
3408 methodinfo_t*f = (methodinfo_t*)a;
3409 $$.t = TYPE_FUNCTION(f);
3411 varinfo_t*v = (varinfo_t*)a;
3415 classinfo_t*c = (classinfo_t*)a;
3417 $$.c = abc_getglobalscope($$.c);
3418 $$.c = abc_getslot($$.c, c->slot);
3421 $$.c = abc_getlex2($$.c, &m);
3423 $$.t = TYPE_CLASS(c);
3428 /* unknown object, let the avm2 resolve it */
3430 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3431 state->method->late_binding = 1;
3433 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3436 $$.c = abc_findpropstrict2($$.c, &m);
3437 $$.c = abc_getproperty2($$.c, &m);
3441 // ----------------- namespaces -------------------------------------------------
3443 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3444 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3445 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3447 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {
3449 tokenizer_register_namespace($3);