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 {
324 int var_index; // for inner methods
327 abc_exception_list_t*exceptions;
329 methodstate_list_t*innerfunctions;
332 typedef struct _state {
337 import_list_t*wildcard_imports;
339 char has_own_imports;
340 char new_vars; // e.g. transition between two functions
343 methodstate_t*method;
350 typedef struct _global {
354 dict_t*file2token2info;
357 static global_t*global = 0;
358 static state_t* state = 0;
362 #define MULTINAME(m,x) \
366 registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
368 #define MEMBER_MULTINAME(m,f,n) \
372 m##_ns.access = (f)->access; \
376 m.namespace_set = 0; \
379 m.type = MULTINAME; \
381 m.namespace_set = &nopackage_namespace_set; \
385 /* warning: list length of namespace set is undefined */
386 #define MULTINAME_LATE(m, access, package) \
387 namespace_t m##_ns = {access, package}; \
388 namespace_set_t m##_nsset; \
389 namespace_list_t m##_l;m##_l.next = 0; \
390 m##_nsset.namespaces = &m##_l; \
391 m##_nsset = m##_nsset; \
392 m##_l.namespace = &m##_ns; \
393 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
395 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
396 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
397 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
398 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
399 static namespace_list_t nl4 = {&ns4,0};
400 static namespace_list_t nl3 = {&ns3,&nl4};
401 static namespace_list_t nl2 = {&ns2,&nl3};
402 static namespace_list_t nl1 = {&ns1,&nl2};
403 static namespace_set_t nopackage_namespace_set = {&nl1};
405 static void new_state()
408 state_t*oldstate = state;
410 memcpy(s, state, sizeof(state_t)); //shallow copy
412 s->imports = dict_new();
416 state->has_own_imports = 0;
417 state->vars = dict_new();
418 state->old = oldstate;
420 static void state_has_imports()
422 state->wildcard_imports = list_clone(state->wildcard_imports);
423 state->imports = dict_clone(state->imports);
424 state->has_own_imports = 1;
427 static void state_destroy(state_t*state)
429 if(state->has_own_imports) {
430 list_free(state->wildcard_imports);
431 dict_destroy(state->imports);state->imports=0;
433 if(state->imports && (!state->old || state->old->imports!=state->imports)) {
434 dict_destroy(state->imports);state->imports=0;
438 for(t=0;t<state->vars->hashsize;t++) {
439 dictentry_t*e =state->vars->slots[t];
441 free(e->data);e->data=0;
445 dict_destroy(state->vars);state->vars=0;
451 static void old_state()
453 if(!state || !state->old)
454 syntaxerror("invalid nesting");
455 state_t*leaving = state;
459 if(as3_pass>1 && leaving->method && leaving->method != state->method && !leaving->method->inner) {
460 free(leaving->method);
463 if(as3_pass>1 && leaving->cls && leaving->cls != state->cls) {
468 state_destroy(leaving);
471 static code_t* method_header(methodstate_t*m);
472 static code_t* wrap_function(code_t*c,code_t*header, code_t*body);
473 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0);
476 static char* internal_filename_package = 0;
477 void initialize_file(char*filename)
480 syntaxerror("invalid call to initialize_file during parsing of another file");
483 state->package = internal_filename_package = strdup(filename);
485 global->token2info = dict_lookup(global->file2token2info,
486 current_filename // use long version
488 if(!global->token2info) {
489 global->token2info = dict_new2(&ptr_type);
490 dict_put(global->file2token2info, current_filename, global->token2info);
494 state->method = rfx_calloc(sizeof(methodstate_t));
495 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
497 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
498 function_initvars(state->method, 0, 0, 1);
499 global->init = abc_initscript(global->file);
500 state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
506 if(!state || state->level!=1) {
507 syntaxerror("unexpected end of file in pass %d", as3_pass);
511 code_t*header = method_header(state->method);
512 code_t*c = wrap_function(header, 0, global->init->method->body->code);
513 global->init->method->body->code = c;
514 free(state->method);state->method=0;
517 //free(state->package);state->package=0; // used in registry
518 state_destroy(state);state=0;
521 void initialize_parser()
523 global = rfx_calloc(sizeof(global_t));
524 global->file = abc_file_new();
525 global->file->flags &= ~ABCFILE_LAZY;
526 global->file2token2info = dict_new();
527 global->token2info = 0;
530 void* finish_parser()
532 dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
534 global->token2info=0;
540 static void xx_scopetest()
542 /* findpropstrict doesn't just return a scope object- it
543 also makes it "active" somehow. Push local_0 on the
544 scope stack and read it back with findpropstrict, it'll
545 contain properties like "trace". Trying to find the same
546 property on a "vanilla" local_0 yields only a "undefined" */
547 //c = abc_findpropstrict(c, "[package]::trace");
549 /*c = abc_getlocal_0(c);
550 c = abc_findpropstrict(c, "[package]::trace");
552 c = abc_setlocal_1(c);
554 c = abc_pushbyte(c, 0);
555 c = abc_setlocal_2(c);
557 code_t*xx = c = abc_label(c);
558 c = abc_findpropstrict(c, "[package]::trace");
559 c = abc_pushstring(c, "prop:");
560 c = abc_hasnext2(c, 1, 2);
562 c = abc_setlocal_3(c);
563 c = abc_callpropvoid(c, "[package]::trace", 2);
564 c = abc_getlocal_3(c);
566 c = abc_iftrue(c,xx);*/
569 typedef struct _variable {
573 methodstate_t*method;
576 static variable_t* find_variable(char*name)
582 v = dict_lookup(s->vars, name);
592 static variable_t* find_variable_safe(char*name)
594 variable_t* v = find_variable(name);
596 syntaxerror("undefined variable: %s", name);
599 static char variable_exists(char*name)
601 return dict_lookup(state->vars, name)!=0;
603 code_t*defaultvalue(code_t*c, classinfo_t*type);
604 static int new_variable(const char*name, classinfo_t*type, char init)
607 v->index = state->method->variable_count;
610 v->method = state->method;
612 dict_put(state->vars, name, v);
614 return state->method->variable_count++;
616 #define TEMPVARNAME "__as3_temp__"
617 static int gettempvar()
619 variable_t*v = find_variable(TEMPVARNAME);
622 return new_variable(TEMPVARNAME, 0, 0);
625 code_t* var_block(code_t*body)
631 for(t=0;t<state->vars->hashsize;t++) {
632 dictentry_t*e = state->vars->slots[t];
634 variable_t*v = (variable_t*)e->data;
635 if(v->type && v->init) {
636 c = defaultvalue(c, v->type);
637 c = abc_setlocal(c, v->index);
638 k = abc_kill(k, v->index);
648 if(x->opcode== OPCODE___BREAK__ ||
649 x->opcode== OPCODE___CONTINUE__) {
650 /* link kill code before break/continue */
651 code_t*e = code_dup(k);
652 code_t*s = code_start(e);
664 c = code_append(c, body);
665 c = code_append(c, k);
669 #define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
671 static void parsererror(const char*file, int line, const char*f)
673 syntaxerror("internal error in %s, %s:%d", f, file, line);
677 static code_t* method_header(methodstate_t*m)
680 if(m->late_binding && !m->inner) {
681 c = abc_getlocal_0(c);
682 c = abc_pushscope(c);
684 /*if(m->innerfunctions) {
685 c = abc_newactivation(c);
686 c = abc_pushscope(c);
688 methodstate_list_t*l = m->innerfunctions;
690 parserassert(l->methodstate->abc);
691 c = abc_newfunction(c, l->methodstate->abc);
692 c = abc_setlocal(c, l->methodstate->var_index);
693 free(l->methodstate);l->methodstate=0;
697 c = code_append(c, m->header);
700 if(m->is_constructor && !m->has_super) {
701 // call default constructor
702 c = abc_getlocal_0(c);
703 c = abc_constructsuper(c, 0);
705 list_free(m->innerfunctions);
706 m->innerfunctions = 0;
711 static code_t* wrap_function(code_t*c,code_t*header, code_t*body)
713 c = code_append(c, header);
714 c = code_append(c, var_block(body));
715 /* append return if necessary */
716 if(!c || (c->opcode != OPCODE_RETURNVOID &&
717 c->opcode != OPCODE_RETURNVALUE)) {
718 c = abc_returnvoid(c);
724 static void startpackage(char*name)
727 /*printf("entering package \"%s\"\n", name);*/
728 state->package = strdup(name);
730 static void endpackage()
732 /*printf("leaving package \"%s\"\n", state->package);*/
734 //used e.g. in classinfo_register:
735 //free(state->package);state->package=0;
740 #define FLAG_PUBLIC 256
741 #define FLAG_PROTECTED 512
742 #define FLAG_PRIVATE 1024
743 #define FLAG_PACKAGEINTERNAL 2048
744 #define FLAG_NAMESPACE 4096
746 static int flags2access(int flags)
749 if(flags&FLAG_PUBLIC) {
750 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
751 syntaxerror("invalid combination of access levels");
752 access = ACCESS_PACKAGE;
753 } else if(flags&FLAG_PRIVATE) {
754 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL))
755 syntaxerror("invalid combination of access levels");
756 access = ACCESS_PRIVATE;
757 } else if(flags&FLAG_PROTECTED) {
758 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL))
759 syntaxerror("invalid combination of access levels");
760 access = ACCESS_PROTECTED;
762 access = ACCESS_PACKAGEINTERNAL;
767 static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
772 index = new_variable("this", 0, 0);
773 else if(!m->is_global)
774 index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0);
776 index = new_variable("globalscope", 0, 0);
779 parserassert(!index);
784 for(p=params->list;p;p=p->next) {
785 new_variable(p->param->name, p->param->type, 0);
789 methodstate_list_t*l = m->innerfunctions;
791 methodstate_t*m = l->methodstate;
792 m->var_index = new_variable(m->info->name, TYPE_FUNCTION(m->info), 0);
798 char*as3_globalclass=0;
799 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements)
802 syntaxerror("inner classes now allowed");
806 classinfo_list_t*mlist=0;
808 if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC|FLAG_INTERFACE))
809 syntaxerror("invalid modifier(s)");
811 if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL))
812 syntaxerror("public and internal not supported at the same time.");
814 /* create the class name, together with the proper attributes */
818 if(!(flags&FLAG_PUBLIC) && state->package==internal_filename_package) {
819 access = ACCESS_PRIVATE; package = internal_filename_package;
820 } else if(!(flags&FLAG_PUBLIC) && state->package!=internal_filename_package) {
821 access = ACCESS_PACKAGEINTERNAL; package = state->package;
822 } else if(state->package!=internal_filename_package) {
823 access = ACCESS_PACKAGE; package = state->package;
825 syntaxerror("public classes only allowed inside a package");
829 state->cls = rfx_calloc(sizeof(classstate_t));
830 state->cls->init = rfx_calloc(sizeof(methodstate_t));
831 state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
832 /* notice: we make no effort to initialize the top variable (local0) here,
833 even though it has special meaning. We just rely on the facat
834 that pass 1 won't do anything with variables */
836 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->cls);
838 /* set current method to constructor- all code within the class-level (except
839 static variable initializations) will be executed during construction time */
840 state->method = state->cls->init;
842 if(registry_find(package, classname)) {
843 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
845 /* build info struct */
846 int num_interfaces = (list_length(implements));
847 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
848 state->cls->info->flags |= flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
852 state->cls = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
854 state->method = state->cls->init;
855 parserassert(state->cls && state->cls->info);
857 function_initvars(state->cls->init, 0, 0, 1);
858 function_initvars(state->cls->static_init, 0, 0, 0);
860 if(extends && (extends->flags & FLAG_FINAL))
861 syntaxerror("Can't extend final class '%s'", extends->name);
863 /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
864 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
866 classinfo_list_t*l = implements;
867 for(l=implements;l;l=l->next) {
868 if(!(l->classinfo->flags & FLAG_INTERFACE))
869 syntaxerror("'%s' is not an interface", l->classinfo->name);
870 state->cls->info->interfaces[pos++] = l->classinfo;
873 /* generate the abc code for this class */
874 MULTINAME(classname2,state->cls->info);
875 multiname_t*extends2 = sig2mname(extends);
877 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
878 if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
879 if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
880 if(state->cls->info->flags&FLAG_INTERFACE) {
881 abc_class_interface(state->cls->abc);
884 abc_class_protectedNS(state->cls->abc, classname);
886 for(mlist=implements;mlist;mlist=mlist->next) {
887 MULTINAME(m, mlist->classinfo);
888 abc_class_add_interface(state->cls->abc, &m);
891 /* write the construction code for this class to the global init
893 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
895 abc_method_body_t*m = global->init->method->body;
896 __ getglobalscope(m);
897 classinfo_t*s = extends;
902 //TODO: take a look at the current scope stack, maybe
903 // we can re-use something
908 multiname_t*s2 = sig2mname(s);
910 multiname_destroy(s2);
912 __ pushscope(m); count++;
913 m->code = m->code->prev->prev; // invert
915 /* continue appending after last op end */
916 while(m->code && m->code->next) m->code = m->code->next;
918 /* TODO: if this is one of *our* classes, we can also
919 do a getglobalscope/getslot <nr> (which references
920 the init function's slots) */
922 __ getlex2(m, extends2);
924 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
925 stack is not the superclass */
926 __ pushscope(m);count++;
929 /* notice: we get a verify error #1107 if the top element on the scope
930 stack is not the global object */
932 __ pushscope(m);count++;
934 __ newclass(m,state->cls->abc);
938 __ setslot(m, slotindex);
939 multiname_destroy(extends2);
941 /* flash.display.MovieClip handling */
943 if(!as3_globalclass && (flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
944 if(state->package && state->package[0]) {
945 as3_globalclass = concat3(state->package, ".", classname);
947 as3_globalclass = strdup(classname);
953 static void setstaticfunction(int x)
957 state->method = state->cls->static_init;
959 state->method = state->cls->init;
962 parserassert(state->method);
966 static void endclass()
969 if(!state->cls->has_constructor && !(state->cls->info->flags&FLAG_INTERFACE)) {
971 c = abc_getlocal_0(c);
972 c = abc_constructsuper(c, 0);
973 state->cls->init->header = code_append(state->cls->init->header, c);
974 state->cls->has_constructor=1;
976 if(state->cls->init) {
977 if(state->cls->info->flags&FLAG_INTERFACE) {
978 if(state->cls->init->header)
979 syntaxerror("interface can not have class-level code");
981 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
982 code_t*c = method_header(state->cls->init);
983 m->body->code = wrap_function(c, 0, m->body->code);
986 if(state->cls->static_init) {
987 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
988 code_t*c = method_header(state->cls->static_init);
989 m->body->code = wrap_function(c, 0, m->body->code);
996 void check_code_for_break(code_t*c)
999 if(c->opcode == OPCODE___BREAK__) {
1000 char*name = string_cstr(c->data[0]);
1001 syntaxerror("Unresolved \"break %s\"", name);
1003 if(c->opcode == OPCODE___CONTINUE__) {
1004 char*name = string_cstr(c->data[0]);
1005 syntaxerror("Unresolved \"continue %s\"", name);
1012 static void check_constant_against_type(classinfo_t*t, constant_t*c)
1014 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
1015 if(TYPE_IS_NUMBER(t)) {
1016 xassert(c->type == CONSTANT_FLOAT
1017 || c->type == CONSTANT_INT
1018 || c->type == CONSTANT_UINT);
1019 } else if(TYPE_IS_UINT(t)) {
1020 xassert(c->type == CONSTANT_UINT ||
1021 (c->type == CONSTANT_INT && c->i>=0));
1022 } else if(TYPE_IS_INT(t)) {
1023 xassert(c->type == CONSTANT_INT);
1024 } else if(TYPE_IS_BOOLEAN(t)) {
1025 xassert(c->type == CONSTANT_TRUE
1026 || c->type == CONSTANT_FALSE);
1030 static void check_override(memberinfo_t*m, int flags)
1034 if(m->parent == state->cls->info)
1035 syntaxerror("class '%s' already contains a method/slot '%s'", m->parent->name, m->name);
1037 syntaxerror("internal error: overriding method %s, which doesn't have parent", m->name);
1038 if(m->access==ACCESS_PRIVATE)
1040 if(m->flags & FLAG_FINAL)
1041 syntaxerror("can't override final member %s", m->name);
1042 if((m->flags & FLAG_STATIC) && !(flags&FLAG_STATIC))
1043 syntaxerror("can't override static member %s", m->name);
1044 if(!(m->flags & FLAG_STATIC) && (flags&FLAG_STATIC))
1045 syntaxerror("can't override non-static member %s with static declaration", m->name);
1047 if(!(flags&FLAG_OVERRIDE)) {
1048 if(m->parent && !(m->parent->flags&FLAG_INTERFACE)) {
1049 if(m->kind == INFOTYPE_METHOD)
1050 syntaxerror("can't override without explicit 'override' declaration");
1052 syntaxerror("can't override '%s'", m->name);
1057 static methodinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
1059 methodinfo_t*minfo = 0;
1060 U8 access = flags2access(flags);
1063 minfo = methodinfo_register_global(access, state->package, name);
1064 minfo->return_type = return_type;
1065 } else if(getset != KW_GET && getset != KW_SET) {
1067 memberinfo_t* m = registry_findmember(state->cls->info, name, 0);
1069 syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
1071 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1072 minfo->return_type = return_type;
1073 // getslot on a member slot only returns "undefined", so no need
1074 // to actually store these
1075 //state->minfo->slot = state->method->abc->method->trait->slot_id;
1077 //class getter/setter
1078 int gs = getset==KW_GET?SUBTYPE_GET:SUBTYPE_SET;
1080 if(getset == KW_GET)
1082 else if(params->list && params->list->param)
1083 type = params->list->param->type;
1084 // not sure wether to look into superclasses here, too
1085 minfo = (methodinfo_t*)registry_findmember(state->cls->info, name, 1);
1087 if(minfo->kind!=INFOTYPE_SLOT)
1088 syntaxerror("class already contains a method called '%s'", name);
1089 if(!(minfo->subtype & (SUBTYPE_GETSET)))
1090 syntaxerror("class already contains a field called '%s'", name);
1091 if(minfo->subtype & gs)
1092 syntaxerror("getter/setter for '%s' already defined", name);
1093 /* make a setter or getter into a getset */
1094 minfo->subtype |= gs;
1095 if(!minfo->return_type) {
1096 minfo->return_type = type;
1098 if(minfo && minfo->return_type != type)
1099 syntaxerror("different type in getter and setter");
1102 minfo = methodinfo_register_onclass(state->cls->info, access, name);
1103 minfo->kind = INFOTYPE_SLOT; //hack
1104 minfo->subtype = gs;
1105 minfo->return_type = type;
1107 /* can't assign a slot as getter and setter might have different slots */
1108 //minfo->slot = slot;
1110 if(flags&FLAG_FINAL) minfo->flags |= FLAG_FINAL;
1111 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
1112 if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE;
1116 static void innerfunction(char*name, params_t*params, classinfo_t*return_type)
1118 //parserassert(state->method && state->method->info);
1120 methodstate_t*parent_method = state->method;
1129 state->new_vars = 1;
1132 state->method = rfx_calloc(sizeof(methodstate_t));
1133 state->method->inner = 1;
1134 state->method->variable_count = 0;
1135 state->method->abc = rfx_calloc(sizeof(abc_method_t));
1137 NEW(methodinfo_t,minfo);
1138 minfo->kind = INFOTYPE_METHOD;
1139 minfo->access = ACCESS_PACKAGEINTERNAL;
1141 state->method->info = minfo;
1144 list_append(parent_method->innerfunctions, state->method);
1146 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1148 function_initvars(state->method, params, 0, 1);
1152 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1153 state->method->variable_count = 0;
1154 parserassert(state->method);
1156 state->method->info->return_type = return_type;
1157 function_initvars(state->method, params, 0, 1);
1161 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1162 params_t*params, classinfo_t*return_type)
1164 if(state->method && state->method->info) {
1165 syntaxerror("not able to start another method scope");
1170 state->method = rfx_calloc(sizeof(methodstate_t));
1171 state->method->has_super = 0;
1174 state->method->is_constructor = !strcmp(state->cls->info->name,name);
1176 state->method->is_global = 1;
1177 state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack
1179 if(state->method->is_constructor)
1180 name = "__as3_constructor__";
1183 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
1185 function_initvars(state->method, params, flags, 1);
1187 dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, state->method);
1191 state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
1192 state->method->variable_count = 0;
1193 parserassert(state->method);
1196 memberinfo_t*m = registry_findmember(state->cls->info, name, 2);
1197 check_override(m, flags);
1201 state->cls->has_constructor |= state->method->is_constructor;
1204 state->method->info->return_type = return_type;
1205 function_initvars(state->method, params, flags, 1);
1209 static abc_method_t* endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
1210 params_t*params, classinfo_t*return_type, code_t*body)
1218 multiname_t*type2 = sig2mname(return_type);
1220 if(state->method->inner) {
1221 f = state->method->abc;
1222 abc_method_init(f, global->file, type2, 1);
1223 } else if(state->method->is_constructor) {
1224 f = abc_class_getconstructor(state->cls->abc, type2);
1225 } else if(!state->method->is_global) {
1226 namespace_t mname_ns = {state->method->info->access, ""};
1227 multiname_t mname = {QNAME, &mname_ns, 0, name};
1229 if(flags&FLAG_STATIC)
1230 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
1232 f = abc_class_method(state->cls->abc, type2, &mname);
1233 slot = f->trait->slot_id;
1235 namespace_t mname_ns = {state->method->info->access, state->package};
1236 multiname_t mname = {QNAME, &mname_ns, 0, name};
1238 f = abc_method_new(global->file, type2, 1);
1239 trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f);
1240 //abc_code_t*c = global->init->method->body->code;
1242 //flash doesn't seem to allow us to access function slots
1243 //state->method->info->slot = slot;
1245 if(flags&FLAG_OVERRIDE) f->trait->attributes |= TRAIT_ATTR_OVERRIDE;
1246 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
1247 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
1248 if(params->varargs) f->flags |= METHOD_NEED_REST;
1252 for(p=params->list;p;p=p->next) {
1253 if(params->varargs && !p->next) {
1254 break; //varargs: omit last parameter in function signature
1256 multiname_t*m = sig2mname(p->param->type);
1257 list_append(f->parameters, m);
1258 if(p->param->value) {
1259 check_constant_against_type(p->param->type, p->param->value);
1260 opt=1;list_append(f->optional_parameters, p->param->value);
1262 syntaxerror("non-optional parameter not allowed after optional parameters");
1265 check_code_for_break(body);
1268 f->body->code = body;
1269 f->body->exceptions = state->method->exceptions;
1270 } else { //interface
1272 syntaxerror("interface methods can't have a method body");
1278 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
1283 void breakjumpsto(code_t*c, char*name, code_t*jump)
1286 if(c->opcode == OPCODE___BREAK__) {
1287 string_t*name2 = c->data[0];
1288 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1289 c->opcode = OPCODE_JUMP;
1296 void continuejumpsto(code_t*c, char*name, code_t*jump)
1299 if(c->opcode == OPCODE___CONTINUE__) {
1300 string_t*name2 = c->data[0];
1301 if(!name2->len || !strncmp(name2->str, name, name2->len)) {
1302 c->opcode = OPCODE_JUMP;
1310 #define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
1311 #define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
1312 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1314 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
1316 if(!type1 || !type2)
1317 return registry_getanytype();
1318 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
1319 return registry_getanytype();
1322 if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
1331 return registry_getanytype();
1333 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
1338 return abc_coerce_a(c);
1342 // cast an "any" type to a specific type. subject to
1343 // runtime exceptions
1344 return abc_coerce2(c, &m);
1347 if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
1348 (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
1349 // allow conversion between number types
1350 return abc_coerce2(c, &m);
1352 //printf("%s.%s\n", from.package, from.name);
1353 //printf("%s.%s\n", to.package, to.name);
1355 classinfo_t*supertype = from;
1357 if(supertype == to) {
1358 // target type is one of from's superclasses
1359 return abc_coerce2(c, &m);
1362 while(supertype->interfaces[t]) {
1363 if(supertype->interfaces[t]==to) {
1364 // target type is one of from's interfaces
1365 return abc_coerce2(c, &m);
1369 supertype = supertype->superclass;
1371 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
1373 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
1375 if(TYPE_IS_NULL(from) && !IS_NUMBER_OR_INT(to))
1378 as3_error("can't convert type %s%s%s to %s%s%s",
1379 from->package, from->package?".":"", from->name,
1380 to->package, to->package?".":"", to->name);
1384 code_t*defaultvalue(code_t*c, classinfo_t*type)
1386 if(TYPE_IS_INT(type)) {
1387 c = abc_pushbyte(c, 0);
1388 } else if(TYPE_IS_UINT(type)) {
1389 c = abc_pushuint(c, 0);
1390 } else if(TYPE_IS_FLOAT(type)) {
1392 } else if(TYPE_IS_BOOLEAN(type)) {
1393 c = abc_pushfalse(c);
1395 //c = abc_pushundefined(c);
1397 c = abc_pushnull(c);
1399 c = abc_coerce2(c, &m);
1404 char is_pushundefined(code_t*c)
1406 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
1409 static slotinfo_t* find_class(char*name)
1413 c = registry_find(state->package, name);
1416 /* try explicit imports */
1417 dictentry_t* e = dict_get_slot(state->imports, name);
1420 if(!strcmp(e->key, name)) {
1421 c = (slotinfo_t*)e->data;
1427 /* try package.* imports */
1428 import_list_t*l = state->wildcard_imports;
1430 //printf("does package %s contain a class %s?\n", l->import->package, name);
1431 c = registry_find(l->import->package, name);
1436 /* try global package */
1437 c = registry_find("", name);
1440 /* try local "filename" package */
1441 c = registry_find(internal_filename_package, name);
1447 static char is_getlocal(code_t*c)
1449 if(!c || c->prev || c->next)
1451 return(c->opcode == OPCODE_GETLOCAL
1452 || c->opcode == OPCODE_GETLOCAL_0
1453 || c->opcode == OPCODE_GETLOCAL_1
1454 || c->opcode == OPCODE_GETLOCAL_2
1455 || c->opcode == OPCODE_GETLOCAL_3);
1457 static int getlocalnr(code_t*c)
1459 if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
1460 else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
1461 else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
1462 else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
1463 else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
1464 else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
1468 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1472 [prefix code] [read instruction]
1476 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1478 if(in && in->opcode == OPCODE_COERCE_A) {
1479 in = code_cutlast(in);
1482 syntaxerror("internal error");
1484 /* chop off read instruction */
1488 prefix = r->prev;r->prev = 0;
1494 char use_temp_var = readbefore;
1496 /* generate the write instruction, and maybe append a dup to the prefix code */
1497 code_t* write = abc_nop(0);
1498 if(r->opcode == OPCODE_GETPROPERTY) {
1499 write->opcode = OPCODE_SETPROPERTY;
1500 multiname_t*m = (multiname_t*)r->data[0];
1501 write->data[0] = multiname_clone(m);
1502 if(m->type == QNAME || m->type == MULTINAME) {
1504 prefix = abc_dup(prefix); // we need the object, too
1507 } else if(m->type == MULTINAMEL) {
1509 /* dupping two values on the stack requires 5 operations and one register-
1510 couldn't adobe just have given us a dup2? */
1511 int temp = gettempvar();
1512 prefix = abc_setlocal(prefix, temp);
1513 prefix = abc_dup(prefix);
1514 prefix = abc_getlocal(prefix, temp);
1515 prefix = abc_swap(prefix);
1516 prefix = abc_getlocal(prefix, temp);
1518 prefix = abc_kill(prefix, temp);
1522 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1524 } else if(r->opcode == OPCODE_GETSLOT) {
1525 write->opcode = OPCODE_SETSLOT;
1526 write->data[0] = r->data[0];
1528 prefix = abc_dup(prefix); // we need the object, too
1531 } else if(r->opcode == OPCODE_GETLOCAL) {
1532 write->opcode = OPCODE_SETLOCAL;
1533 write->data[0] = r->data[0];
1534 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1535 write->opcode = OPCODE_SETLOCAL_0;
1536 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1537 write->opcode = OPCODE_SETLOCAL_1;
1538 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1539 write->opcode = OPCODE_SETLOCAL_2;
1540 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1541 write->opcode = OPCODE_SETLOCAL_3;
1544 syntaxerror("illegal lvalue: can't assign a value to this expression");
1551 /* with getproperty/getslot, we have to be extra careful not
1552 to execute the read code twice, as it might have side-effects
1553 (e.g. if the property is in fact a setter/getter combination)
1555 So read the value, modify it, and write it again,
1556 using prefix only once and making sure (by using a temporary
1557 register) that the return value is what we just wrote */
1558 temp = gettempvar();
1559 c = code_append(c, prefix);
1560 c = code_append(c, r);
1563 c = abc_setlocal(c, temp);
1565 c = code_append(c, middlepart);
1568 c = abc_setlocal(c, temp);
1570 c = code_append(c, write);
1571 c = abc_getlocal(c, temp);
1572 c = abc_kill(c, temp);
1574 /* if we're allowed to execute the read code twice *and*
1575 the middlepart doesn't modify the code, things are easier.
1577 code_t* r2 = code_dup(r);
1578 //c = code_append(c, prefix);
1579 parserassert(!prefix);
1580 c = code_append(c, r);
1581 c = code_append(c, middlepart);
1582 c = code_append(c, write);
1583 c = code_append(c, r2);
1586 /* even smaller version: overwrite the value without reading
1590 c = code_append(c, prefix);
1593 c = code_append(c, middlepart);
1594 c = code_append(c, write);
1595 c = code_append(c, r);
1598 temp = gettempvar();
1600 c = code_append(c, prefix);
1602 c = code_append(c, middlepart);
1604 c = abc_setlocal(c, temp);
1605 c = code_append(c, write);
1606 c = abc_getlocal(c, temp);
1607 c = abc_kill(c, temp);
1613 char is_break_or_jump(code_t*c)
1617 if(c->opcode == OPCODE_JUMP ||
1618 c->opcode == OPCODE___BREAK__ ||
1619 c->opcode == OPCODE___CONTINUE__ ||
1620 c->opcode == OPCODE_THROW ||
1621 c->opcode == OPCODE_RETURNVOID ||
1622 c->opcode == OPCODE_RETURNVALUE) {
1629 #define IS_FINALLY_TARGET(op) \
1630 ((op) == OPCODE___CONTINUE__ || \
1631 (op) == OPCODE___BREAK__ || \
1632 (op) == OPCODE_RETURNVOID || \
1633 (op) == OPCODE_RETURNVALUE || \
1634 (op) == OPCODE___RETHROW__)
1636 static code_t* insert_finally_lookup(code_t*c, code_t*finally, int tempvar)
1638 #define NEED_EXTRA_STACK_ARG
1639 code_t*finally_label = abc_nop(0);
1640 NEW(lookupswitch_t, l);
1646 code_t*prev = i->prev;
1647 if(IS_FINALLY_TARGET(i->opcode)) {
1650 if(i->opcode == OPCODE___RETHROW__ ||
1651 i->opcode == OPCODE_RETURNVALUE) {
1652 if(i->opcode == OPCODE___RETHROW__)
1653 i->opcode = OPCODE_THROW;
1655 p = abc_coerce_a(p);
1656 p = abc_setlocal(p, tempvar);
1658 p = abc_pushbyte(p, count++);
1659 p = abc_jump(p, finally_label);
1660 code_t*target = p = abc_label(p);
1661 #ifdef NEED_EXTRA_STACK_ARG
1665 p = abc_getlocal(p, tempvar);
1668 p->next = i;i->prev = p;
1669 list_append(l->targets, target);
1675 c = abc_pushbyte(c, -1);
1676 c = code_append(c, finally_label);
1677 c = code_append(c, finally);
1679 #ifdef NEED_EXTRA_STACK_ARG
1682 c = abc_lookupswitch(c, l);
1683 c = l->def = abc_label(c);
1684 #ifdef NEED_EXTRA_STACK_ARG
1691 static code_t* insert_finally_simple(code_t*c, code_t*finally, int tempvar)
1695 code_t*prev = i->prev;
1696 if(IS_FINALLY_TARGET(i->opcode)) {
1697 if(i->opcode == OPCODE___RETHROW__)
1698 i->opcode = OPCODE_THROW;
1699 code_t*end = code_dup(finally);
1700 code_t*start = code_start(end);
1701 if(prev) prev->next = start;
1708 return code_append(c, finally);
1711 code_t* insert_finally(code_t*c, code_t*finally, int tempvar)
1717 int num_insertion_points=0;
1719 if(IS_FINALLY_TARGET(i->opcode))
1720 num_insertion_points++;
1727 if(i->branch || i->opcode == OPCODE_LOOKUPSWITCH) {
1732 int simple_version_cost = (1+num_insertion_points)*code_size;
1733 int lookup_version_cost = 4*num_insertion_points + 5;
1735 if(cantdup || simple_version_cost > lookup_version_cost) {
1736 printf("lookup %d > *%d*\n", simple_version_cost, lookup_version_cost);
1737 return insert_finally_lookup(c, finally, tempvar);
1739 printf("simple *%d* < %d\n", simple_version_cost, lookup_version_cost);
1740 return insert_finally_simple(c, finally, tempvar);
1744 #define PASS1 }} if(as3_pass == 1) {{
1745 #define PASS1END }} if(as3_pass == 2) {{
1746 #define PASS2 }} if(as3_pass == 2) {{
1747 #define PASS12 }} {{
1748 #define PASS12END }} if(as3_pass == 2) {{
1754 /* ------------ code blocks / statements ---------------- */
1756 PROGRAM: MAYBE_PROGRAM_CODE_LIST
1758 MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST
1759 PROGRAM_CODE_LIST: PROGRAM_CODE
1760 | PROGRAM_CODE_LIST PROGRAM_CODE
1762 PROGRAM_CODE: PACKAGE_DECLARATION
1763 | INTERFACE_DECLARATION
1765 | FUNCTION_DECLARATION
1768 | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
1771 MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
1772 INPACKAGE_CODE_LIST: INPACKAGE_CODE
1773 | INPACKAGE_CODE_LIST INPACKAGE_CODE
1775 INPACKAGE_CODE: INTERFACE_DECLARATION
1777 | FUNCTION_DECLARATION
1780 | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
1783 MAYBECODE: CODE {$$=$1;}
1784 MAYBECODE: {$$=code_new();}
1786 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1787 CODE: CODEPIECE {$$=$1;}
1789 // code which also may appear outside a method
1790 CODE_STATEMENT: IMPORT
1792 CODE_STATEMENT: FOR_IN
1793 CODE_STATEMENT: WHILE
1794 CODE_STATEMENT: DO_WHILE
1795 CODE_STATEMENT: SWITCH
1797 CODE_STATEMENT: WITH
1799 CODE_STATEMENT: VOIDEXPRESSION
1800 CODE_STATEMENT: USE_NAMESPACE
1801 CODE_STATEMENT: '{' CODE '}' {$$=$2;}
1802 CODE_STATEMENT: '{' '}' {$$=0;}
1804 // code which may appear anywhere
1805 CODEPIECE: ';' {$$=0;}
1806 CODEPIECE: CODE_STATEMENT
1807 CODEPIECE: VARIABLE_DECLARATION
1812 CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {$$=$3;}
1814 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;}
1816 //CODEBLOCK : '{' CODE '}' {$$=$2;}
1817 //CODEBLOCK : '{' '}' {$$=0;}
1818 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1819 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1821 /* ------------ package init code ------------------- */
1823 PACKAGE_INITCODE: CODE_STATEMENT {
1824 code_t**cc = &global->init->method->body->code;
1825 *cc = code_append(*cc, $1);
1828 /* ------------ conditional compilation ------------- */
1830 CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
1832 /* ------------ variables --------------------------- */
1834 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1835 | {$$.c=abc_pushundefined(0);
1839 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
1840 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
1842 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1843 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1845 ONE_VARIABLE: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1848 if(variable_exists($1))
1849 syntaxerror("Variable %s already defined", $1);
1851 new_variable($1, $2, 1);
1854 if(!is_subtype_of($3.t, $2)) {
1855 syntaxerror("Can't convert %s to %s", $3.t->name,
1858 int index = new_variable($1, $2, 1);
1861 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1863 $$ = converttype($$, $3.t, $2);
1864 $$ = abc_setlocal($$, index);
1867 $$ = defaultvalue(0, $2);
1868 $$ = abc_setlocal($$, index);
1871 if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
1873 $$ = abc_coerce_a($$);
1874 $$ = abc_setlocal($$, index);
1881 /* that's the default for a local register, anyway
1883 state->method->initcode = abc_pushundefined(state->method->initcode);
1884 state->method->initcode = abc_setlocal(state->method->initcode, index);
1886 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1889 /* ------------ control flow ------------------------- */
1891 MAYBEELSE: %prec below_else {$$ = code_new();}
1892 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1893 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1895 IF : "if" '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1898 $$ = code_append($$, $4.c);
1899 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1901 $$ = code_append($$, $6);
1903 myjmp = $$ = abc_jump($$, 0);
1905 myif->branch = $$ = abc_nop($$);
1907 $$ = code_append($$, $7);
1908 myjmp->branch = $$ = abc_nop($$);
1914 FOR_INIT : {$$=code_new();}
1915 FOR_INIT : VARIABLE_DECLARATION
1916 FOR_INIT : VOIDEXPRESSION
1918 // TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
1919 // (I don't see any easy way to revolve this conflict otherwise, as we
1920 // can't touch VAR_READ without upsetting the precedence about "return")
1921 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
1923 $$=$2;new_variable($2,$3,1);
1925 FOR_IN_INIT : T_IDENTIFIER {
1930 FOR_START : T_FOR '(' {PASS12 new_state();$$.name=$1;$$.each=0;}
1931 FOR_START : T_FOR "each" '(' {PASS12 new_state();$$.name=$1;$$.each=1;}
1933 FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1934 if($1.each) syntaxerror("invalid syntax: ; not allowed in for each statement");
1936 $$ = code_append($$, $2);
1937 code_t*loopstart = $$ = abc_label($$);
1938 $$ = code_append($$, $4.c);
1939 code_t*myif = $$ = abc_iffalse($$, 0);
1940 $$ = code_append($$, $8);
1941 code_t*cont = $$ = abc_nop($$);
1942 $$ = code_append($$, $6);
1943 $$ = abc_jump($$, loopstart);
1944 code_t*out = $$ = abc_nop($$);
1945 breakjumpsto($$, $1.name, out);
1946 continuejumpsto($$, $1.name, cont);
1953 FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK {
1954 variable_t*var = find_variable($2);
1955 char*tmp1name = concat2($2, "__tmp1__");
1956 int it = new_variable(tmp1name, TYPE_INT, 0);
1957 char*tmp2name = concat2($2, "__array__");
1958 int array = new_variable(tmp1name, 0, 0);
1961 $$ = code_append($$, $4.c);
1962 $$ = abc_coerce_a($$);
1963 $$ = abc_setlocal($$, array);
1964 $$ = abc_pushbyte($$, 0);
1965 $$ = abc_setlocal($$, it);
1967 code_t*loopstart = $$ = abc_label($$);
1969 $$ = abc_hasnext2($$, array, it);
1970 code_t*myif = $$ = abc_iffalse($$, 0);
1971 $$ = abc_getlocal($$, array);
1972 $$ = abc_getlocal($$, it);
1974 $$ = abc_nextname($$);
1976 $$ = abc_nextvalue($$);
1977 $$ = converttype($$, 0, var->type);
1978 $$ = abc_setlocal($$, var->index);
1980 $$ = code_append($$, $6);
1981 $$ = abc_jump($$, loopstart);
1983 code_t*out = $$ = abc_nop($$);
1984 breakjumpsto($$, $1.name, out);
1985 continuejumpsto($$, $1.name, loopstart);
1997 WHILE : T_WHILE '(' {PASS12 new_state();} EXPRESSION ')' CODEBLOCK {
2001 code_t*myjmp = $$ = abc_jump($$, 0);
2002 code_t*loopstart = $$ = abc_label($$);
2003 $$ = code_append($$, $6);
2004 code_t*cont = $$ = abc_nop($$);
2005 myjmp->branch = cont;
2006 $$ = code_append($$, $4.c);
2007 $$ = abc_iftrue($$, loopstart);
2008 code_t*out = $$ = abc_nop($$);
2009 breakjumpsto($$, $1, out);
2010 continuejumpsto($$, $1, cont);
2016 DO_WHILE : T_DO {PASS12 new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
2018 code_t*loopstart = $$ = abc_label($$);
2019 $$ = code_append($$, $3);
2020 code_t*cont = $$ = abc_nop($$);
2021 $$ = code_append($$, $6.c);
2022 $$ = abc_iftrue($$, loopstart);
2023 code_t*out = $$ = abc_nop($$);
2024 breakjumpsto($$, $1, out);
2025 continuejumpsto($$, $1, cont);
2031 BREAK : "break" %prec prec_none {
2032 $$ = abc___break__(0, "");
2034 BREAK : "break" T_IDENTIFIER {
2035 $$ = abc___break__(0, $2);
2037 CONTINUE : "continue" %prec prec_none {
2038 $$ = abc___continue__(0, "");
2040 CONTINUE : "continue" T_IDENTIFIER {
2041 $$ = abc___continue__(0, $2);
2044 MAYBE_CASE_LIST : {$$=0;}
2045 MAYBE_CASE_LIST : CASE_LIST {$$=$1;}
2046 MAYBE_CASE_LIST : DEFAULT {$$=$1;}
2047 MAYBE_CASE_LIST : CASE_LIST DEFAULT {$$=code_append($1,$2);}
2048 CASE_LIST: CASE {$$=$1;}
2049 CASE_LIST: CASE_LIST CASE {$$=code_append($$,$2);}
2051 CASE: "case" E ':' MAYBECODE {
2053 $$ = code_append($$, $2.c);
2054 code_t*j = $$ = abc_ifne($$, 0);
2055 $$ = code_append($$, $4);
2056 if($$->opcode != OPCODE___BREAK__) {
2057 $$ = abc___fallthrough__($$, "");
2059 code_t*e = $$ = abc_nop($$);
2062 DEFAULT: "default" ':' MAYBECODE {
2065 SWITCH : T_SWITCH '(' {PASS12 new_state();} E ')' '{' MAYBE_CASE_LIST '}' {
2067 $$ = code_append($$, $7);
2068 code_t*out = $$ = abc_pop($$);
2069 breakjumpsto($$, $1, out);
2071 code_t*c = $$,*lastblock=0;
2073 if(c->opcode == OPCODE_IFNE) {
2074 if(!c->next) syntaxerror("internal error in fallthrough handling");
2076 } else if(c->opcode == OPCODE___FALLTHROUGH__) {
2078 c->opcode = OPCODE_JUMP;
2079 c->branch = lastblock;
2081 /* fall through end of switch */
2082 c->opcode = OPCODE_NOP;
2092 /* ------------ try / catch /finally ---------------- */
2094 CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {PASS12 new_state();state->exception_name=$3;new_variable($3, $4, 0);}
2096 namespace_t name_ns = {ACCESS_PACKAGE, ""};
2097 multiname_t name = {QNAME, &name_ns, 0, $3};
2099 NEW(abc_exception_t, e)
2100 e->exc_type = sig2mname($4);
2101 e->var_name = multiname_clone(&name);
2105 int i = find_variable_safe($3)->index;
2106 e->target = c = abc_nop(0);
2107 c = abc_setlocal(c, i);
2108 c = code_append(c, $8);
2114 FINALLY: "finally" '{' {PASS12 new_state();state->exception_name=0;} MAYBECODE '}' {
2119 NEW(abc_exception_t, e)
2120 e->exc_type = 0; //all exceptions
2121 e->var_name = 0; //no name
2124 e->to = code_append(e->to, $4);
2130 CATCH_LIST: CATCH {$$.l=list_new();$$.finally=0;list_append($$.l,$1);}
2131 CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$.l,$2);}
2132 CATCH_FINALLY_LIST: CATCH_LIST {$$=$1;}
2133 CATCH_FINALLY_LIST: CATCH_LIST FINALLY {
2137 list_append($$.l,$2);
2138 $$.finally = $2->to;$2->to=0;
2141 CATCH_FINALLY_LIST: FINALLY {
2145 list_append($$.l,$1);
2146 $$.finally = $1->to;$1->to=0;
2150 TRY : "try" '{' {PASS12 new_state();} MAYBECODE '}' CATCH_FINALLY_LIST {
2151 code_t*out = abc_nop(0);
2153 code_t*start = abc_nop(0);
2154 $$ = code_append(start, $4);
2155 if(!is_break_or_jump($4)) {
2156 $$ = abc_jump($$, out);
2158 code_t*end = $$ = abc_nop($$);
2162 tmp = new_variable("__finally__", 0, 0);
2164 abc_exception_list_t*l = $6.l;
2167 abc_exception_t*e = l->abc_exception;
2169 $$ = code_append($$, e->target);
2170 $$ = abc_jump($$, out);
2172 parserassert((ptroff_t)$6.finally);
2174 e->target = $$ = abc_nop($$);
2175 $$ = abc___rethrow__($$);
2183 $$ = code_append($$, out);
2185 $$ = insert_finally($$, $6.finally, tmp);
2187 list_concat(state->method->exceptions, $6.l);
2193 /* ------------ throw ------------------------------- */
2195 THROW : "throw" EXPRESSION {
2199 THROW : "throw" %prec prec_none {
2200 if(!state->exception_name)
2201 syntaxerror("re-throw only possible within a catch block");
2202 variable_t*v = find_variable(state->exception_name);
2204 $$=abc_getlocal($$, v->index);
2208 /* ------------ with -------------------------------- */
2210 WITH : "with" '(' EXPRESSION ')' CODEBLOCK {
2212 $$ = abc_pushscope($$);
2213 $$ = code_append($$, $5);
2214 $$ = abc_popscope($$);
2217 /* ------------ packages and imports ---------------- */
2219 X_IDENTIFIER: T_IDENTIFIER
2220 | "package" {PASS12 $$="package";}
2222 PACKAGE: PACKAGE '.' X_IDENTIFIER {PASS12 $$ = concat3($1,".",$3);free($1);$1=0;}
2223 PACKAGE: X_IDENTIFIER {PASS12 $$=strdup($1);}
2225 PACKAGE_DECLARATION : "package" PACKAGE '{' {PASS12 startpackage($2);free($2);$2=0;}
2226 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2227 PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
2228 MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
2230 IMPORT : "import" PACKAGEANDCLASS {
2232 slotinfo_t*s = registry_find($2->package, $2->name);
2233 if(!s) {// || !(s->flags&FLAG_BUILTIN)) {
2234 as3_schedule_class($2->package, $2->name);
2240 syntaxerror("Couldn't import class\n");
2241 state_has_imports();
2242 dict_put(state->imports, c->name, c);
2245 IMPORT : "import" PACKAGE '.' '*' {
2247 if(strncmp("flash.", $2, 6)) {
2248 as3_schedule_package($2);
2254 state_has_imports();
2255 list_append(state->wildcard_imports, i);
2259 /* ------------ classes and interfaces (header) -------------- */
2261 MAYBE_MODIFIERS : %prec above_function {PASS12 $$=0;}
2262 MAYBE_MODIFIERS : MODIFIER_LIST {PASS12 $$=$1;}
2263 MODIFIER_LIST : MODIFIER {PASS12 $$=$1;}
2264 MODIFIER_LIST : MODIFIER_LIST MODIFIER {PASS12 $$=$1|$2;}
2266 MODIFIER : KW_PUBLIC {PASS12 $$=FLAG_PUBLIC;}
2267 | KW_PRIVATE {PASS12 $$=FLAG_PRIVATE;}
2268 | KW_PROTECTED {PASS12 $$=FLAG_PROTECTED;}
2269 | KW_STATIC {PASS12 $$=FLAG_STATIC;}
2270 | KW_DYNAMIC {PASS12 $$=FLAG_DYNAMIC;}
2271 | KW_FINAL {PASS12 $$=FLAG_FINAL;}
2272 | KW_OVERRIDE {PASS12 $$=FLAG_OVERRIDE;}
2273 | KW_NATIVE {PASS12 $$=FLAG_NATIVE;}
2274 | KW_INTERNAL {PASS12 $$=FLAG_PACKAGEINTERNAL;}
2275 | T_NAMESPACE {PASS12 $$=FLAG_NAMESPACE;}
2277 EXTENDS : {$$=registry_getobjectclass();}
2278 EXTENDS : KW_EXTENDS CLASS_SPEC {$$=$2;}
2280 EXTENDS_LIST : {PASS12 $$=list_new();}
2281 EXTENDS_LIST : KW_EXTENDS CLASS_SPEC_LIST {PASS12 $$=$2;}
2283 IMPLEMENTS_LIST : {PASS12 $$=list_new();}
2284 IMPLEMENTS_LIST : KW_IMPLEMENTS CLASS_SPEC_LIST {PASS12 $$=$2;}
2286 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
2287 EXTENDS IMPLEMENTS_LIST
2288 '{' {PASS12 startclass($1,$3,$4,$5);}
2290 '}' {PASS12 endclass();$$=0;}
2292 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
2294 '{' {PASS12 startclass($1|FLAG_INTERFACE,$3,0,$4);}
2295 MAYBE_INTERFACE_BODY
2296 '}' {PASS12 endclass();$$=0;}
2298 /* ------------ classes and interfaces (body) -------------- */
2301 MAYBE_CLASS_BODY : CLASS_BODY
2302 CLASS_BODY : CLASS_BODY_ITEM
2303 CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
2304 CLASS_BODY_ITEM : ';'
2305 CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
2306 CLASS_BODY_ITEM : SLOT_DECLARATION
2307 CLASS_BODY_ITEM : FUNCTION_DECLARATION
2309 CLASS_BODY_ITEM : CODE_STATEMENT {
2310 code_t*c = state->cls->static_init->header;
2311 c = code_append(c, $1);
2312 state->cls->static_init->header = c;
2315 MAYBE_INTERFACE_BODY :
2316 MAYBE_INTERFACE_BODY : INTERFACE_BODY
2317 INTERFACE_BODY : IDECLARATION
2318 INTERFACE_BODY : INTERFACE_BODY IDECLARATION
2320 IDECLARATION : "var" T_IDENTIFIER {
2321 syntaxerror("variable declarations not allowed in interfaces");
2323 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
2326 if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) {
2327 syntaxerror("invalid method modifiers: interface methods always need to be public");
2329 startfunction(0,$1,$3,$4,&$6,$8);
2330 endfunction(0,$1,$3,$4,&$6,$8, 0);
2332 old_state();list_deep_free($6.list);
2335 /* ------------ classes and interfaces (body, slots ) ------- */
2337 VARCONST: "var" | "const"
2339 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER {setstaticfunction($1);} MAYBETYPE MAYBEEXPRESSION {
2341 U8 access = flags2access($1);
2343 varinfo_t* info = 0;
2345 memberinfo_t*i = registry_findmember(state->cls->info, $3, 1);
2347 check_override(i, flags);
2349 info = varinfo_register_onclass(state->cls->info, access, $3);
2351 slotinfo_t*i = registry_find(state->package, $3);
2353 syntaxerror("package %s already contains '%s'", state->package, $3);
2355 info = varinfo_register_global(access, state->package, $3);
2359 info->flags = flags;
2362 namespace_t mname_ns = {access, ""};
2363 multiname_t mname = {QNAME, &mname_ns, 0, $3};
2365 trait_list_t**traits;
2369 mname_ns.name = state->package;
2370 traits = &global->init->traits;
2371 code = &global->init->method->body->code;
2372 } else if(flags&FLAG_STATIC) {
2374 traits = &state->cls->abc->static_traits;
2375 code = &state->cls->static_init->header;
2377 // instance variable
2378 traits = &state->cls->abc->traits;
2379 code = &state->cls->init->header;
2385 t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
2387 t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
2389 info->slot = t->slot_id;
2391 /* initalization code (if needed) */
2393 if($6.c && !is_pushundefined($6.c)) {
2394 c = abc_getlocal_0(c);
2395 c = code_append(c, $6.c);
2396 c = converttype(c, $6.t, $5);
2397 c = abc_setslot(c, t->slot_id);
2400 *code = code_append(*code, c);
2403 t->kind= TRAIT_CONST;
2407 setstaticfunction(0);
2410 /* ------------ constants -------------------------------------- */
2412 MAYBESTATICCONSTANT: {$$=0;}
2413 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
2415 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
2416 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
2417 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
2418 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
2419 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
2420 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
2421 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
2422 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
2423 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
2424 STATICCONSTANT : T_IDENTIFIER {
2426 as3_warning("Couldn't resolve %s", $1);
2427 $$ = constant_new_null($1);
2430 /* ------------ classes and interfaces (body, functions) ------- */
2432 // non-vararg version
2435 memset(&$$,0,sizeof($$));
2437 MAYBE_PARAM_LIST: PARAM_LIST {
2443 MAYBE_PARAM_LIST: "..." PARAM {
2445 memset(&$$,0,sizeof($$));
2447 list_append($$.list, $2);
2449 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
2453 list_append($$.list, $4);
2457 PARAM_LIST: PARAM_LIST ',' PARAM {
2460 list_append($$.list, $3);
2464 memset(&$$,0,sizeof($$));
2465 list_append($$.list, $1);
2468 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
2470 $$ = rfx_calloc(sizeof(param_t));
2476 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
2478 $$ = rfx_calloc(sizeof(param_t));
2480 $$->type = TYPE_ANY;
2484 GETSET : "get" {$$=$1;}
2488 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
2489 MAYBETYPE '{' {PASS12 startfunction(0,$1,$3,$4,&$6,$8);} MAYBECODE '}'
2491 if(!state->method->info) syntaxerror("internal error");
2493 code_t*c = method_header(state->method);
2494 c = wrap_function(c, 0, $11);
2496 endfunction(0,$1,$3,$4,&$6,$8,c);
2498 PASS12 old_state();list_deep_free($6.list);
2501 MAYBE_IDENTIFIER: T_IDENTIFIER
2502 MAYBE_IDENTIFIER: {PASS12 $$=0;}
2503 INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE
2504 '{' {PASS12 innerfunction($2,&$4,$6);} MAYBECODE '}'
2506 methodinfo_t*f = state->method->info;
2507 if(!f || !f->kind) syntaxerror("internal error");
2509 code_t*c = method_header(state->method);
2510 c = wrap_function(c, 0, $9);
2512 int index = state->method->var_index;
2513 endfunction(0,0,0,$2,&$4,$6,c);
2515 $$.c = abc_getlocal(0, index);
2516 $$.t = TYPE_FUNCTION(f);
2517 PASS12 old_state();list_deep_free($4.list);
2521 /* ------------- package + class ids --------------- */
2523 CLASS: T_IDENTIFIER {
2526 slotinfo_t*s = find_class($1);
2527 if(!s) syntaxerror("Could not find class/method %s (current package: %s)\n", $1, state->package);
2528 $$ = (classinfo_t*)s;
2531 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
2532 PASS1 static classinfo_t c;
2533 memset(&c, 0, sizeof(c));
2538 slotinfo_t*s = registry_find($1, $3);
2539 if(!s) syntaxerror("Couldn't find class/method %s.%s\n", $1, $3);
2541 $$ = (classinfo_t*)s;
2544 CLASS_SPEC: PACKAGEANDCLASS
2547 CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
2548 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
2550 TYPE : CLASS_SPEC {$$=$1;}
2551 | '*' {$$=registry_getanytype();}
2552 | "void" {$$=registry_getanytype();}
2554 | "String" {$$=registry_getstringclass();}
2555 | "int" {$$=registry_getintclass();}
2556 | "uint" {$$=registry_getuintclass();}
2557 | "Boolean" {$$=registry_getbooleanclass();}
2558 | "Number" {$$=registry_getnumberclass();}
2561 MAYBETYPE: ':' TYPE {$$=$2;}
2564 /* ----------function calls, delete, constructor calls ------ */
2566 MAYBE_PARAM_VALUES : %prec prec_none {$$.cc=0;$$.len=0;}
2567 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2;}
2569 MAYBE_EXPRESSION_LIST : {$$.cc=0;$$.len=0;}
2570 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
2571 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA
2573 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$.len=1;
2577 EXPRESSION_LIST_AND_COMMA: EXPRESSION_LIST ',' {$$ = $1;}
2578 EXPRESSION_LIST : EXPRESSION_LIST_AND_COMMA NONCOMMAEXPRESSION {
2580 $$.cc = code_append($1.cc, $2.c);
2584 NEW : "new" E XX MAYBE_PARAM_VALUES {
2586 if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
2588 code_t*paramcode = $4.cc;
2589 if($$.c->opcode == OPCODE_GETPROPERTY) {
2590 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2591 $$.c = code_cutlast($$.c);
2592 $$.c = code_append($$.c, paramcode);
2593 $$.c = abc_constructprop2($$.c, name, $4.len);
2594 multiname_destroy(name);
2595 } else if($$.c->opcode == OPCODE_GETSLOT) {
2596 int slot = (int)(ptroff_t)$$.c->data[0];
2597 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2598 multiname_t*name = t->name;
2599 $$.c = code_cutlast($$.c);
2600 $$.c = code_append($$.c, paramcode);
2601 $$.c = abc_constructprop2($$.c, name, $4.len);
2603 $$.c = code_append($$.c, paramcode);
2604 $$.c = abc_construct($$.c, $4.len);
2608 if(TYPE_IS_CLASS($2.t) && $2.t->data) {
2611 $$.c = abc_coerce_a($$.c);
2616 /* TODO: use abc_call (for calling local variables),
2617 abc_callstatic (for calling own methods)
2620 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
2623 if($$.c->opcode == OPCODE_COERCE_A) {
2624 $$.c = code_cutlast($$.c);
2626 code_t*paramcode = $3.cc;
2629 if($$.c->opcode == OPCODE_GETPROPERTY) {
2630 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2631 $$.c = code_cutlast($$.c);
2632 $$.c = code_append($$.c, paramcode);
2633 $$.c = abc_callproperty2($$.c, name, $3.len);
2634 multiname_destroy(name);
2635 } else if($$.c->opcode == OPCODE_GETSLOT) {
2636 int slot = (int)(ptroff_t)$$.c->data[0];
2637 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
2638 if(t->kind!=TRAIT_METHOD) {
2639 //ok: flash allows to assign closures to members.
2641 multiname_t*name = t->name;
2642 $$.c = code_cutlast($$.c);
2643 $$.c = code_append($$.c, paramcode);
2644 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
2645 $$.c = abc_callproperty2($$.c, name, $3.len);
2646 } else if($$.c->opcode == OPCODE_GETSUPER) {
2647 multiname_t*name = $$.c->data[0];$$.c->data[0]=0;
2648 $$.c = code_cutlast($$.c);
2649 $$.c = code_append($$.c, paramcode);
2650 $$.c = abc_callsuper2($$.c, name, $3.len);
2651 multiname_destroy(name);
2653 $$.c = abc_getglobalscope($$.c);
2654 $$.c = code_append($$.c, paramcode);
2655 $$.c = abc_call($$.c, $3.len);
2658 if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
2659 $$.t = ((methodinfo_t*)($1.t->data))->return_type;
2661 $$.c = abc_coerce_a($$.c);
2666 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
2667 if(!state->cls) syntaxerror("super() not allowed outside of a class");
2668 if(!state->method) syntaxerror("super() not allowed outside of a function");
2669 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
2672 $$.c = abc_getlocal_0($$.c);
2674 $$.c = code_append($$.c, $3.cc);
2676 this is dependent on the control path, check this somewhere else
2677 if(state->method->has_super)
2678 syntaxerror("constructor may call super() only once");
2680 state->method->has_super = 1;
2682 $$.c = abc_constructsuper($$.c, $3.len);
2683 $$.c = abc_pushundefined($$.c);
2687 DELETE: "delete" E {
2689 if($$.c->opcode == OPCODE_COERCE_A) {
2690 $$.c = code_cutlast($$.c);
2692 multiname_t*name = 0;
2693 if($$.c->opcode == OPCODE_GETPROPERTY) {
2694 $$.c->opcode = OPCODE_DELETEPROPERTY;
2695 } else if($$.c->opcode == OPCODE_GETSLOT) {
2696 int slot = (int)(ptroff_t)$$.c->data[0];
2697 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
2698 $$.c = code_cutlast($$.c);
2699 $$.c = abc_deleteproperty2($$.c, name);
2701 $$.c = abc_getlocal_0($$.c);
2702 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
2703 $$.c = abc_deleteproperty2($$.c, &m);
2705 $$.t = TYPE_BOOLEAN;
2708 RETURN: "return" %prec prec_none {
2709 $$ = abc_returnvoid(0);
2711 RETURN: "return" EXPRESSION {
2713 $$ = abc_returnvalue($$);
2716 // ----------------------- expression types -------------------------------------
2718 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
2719 EXPRESSION : E %prec below_minus {$$ = $1;}
2720 EXPRESSION : EXPRESSION ',' E %prec below_minus {
2722 $$.c = cut_last_push($$.c);
2723 $$.c = code_append($$.c,$3.c);
2726 VOIDEXPRESSION : EXPRESSION %prec below_minus {
2727 $$=cut_last_push($1.c);
2730 // ----------------------- expression evaluation -------------------------------------
2732 E : INNERFUNCTION %prec prec_none {$$ = $1;}
2733 //V : CONSTANT {$$ = 0;}
2735 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
2736 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
2737 //V : NEW {$$ = $1.c;}
2739 //V : DELETE {$$ = $1.c;}
2740 E : DELETE {$$ = $1;}
2746 namespace_t ns = {ACCESS_PACKAGE, ""};
2747 multiname_t m = {QNAME, &ns, 0, "RegExp"};
2749 $$.c = abc_getlex2($$.c, &m);
2750 $$.c = abc_pushstring($$.c, $1.pattern);
2751 $$.c = abc_construct($$.c, 1);
2753 $$.c = abc_getlex2($$.c, &m);
2754 $$.c = abc_pushstring($$.c, $1.pattern);
2755 $$.c = abc_pushstring($$.c, $1.options);
2756 $$.c = abc_construct($$.c, 2);
2761 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
2762 //MULTINAME(m, registry_getintclass());
2763 //$$.c = abc_coerce2($$.c, &m); // FIXME
2766 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
2769 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
2772 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
2775 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
2778 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
2781 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
2784 CONSTANT : "true" {$$.c = abc_pushtrue(0);
2785 $$.t = TYPE_BOOLEAN;
2787 CONSTANT : "false" {$$.c = abc_pushfalse(0);
2788 $$.t = TYPE_BOOLEAN;
2790 CONSTANT : "null" {$$.c = abc_pushnull(0);
2794 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
2795 $$.t = TYPE_BOOLEAN;
2797 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
2798 $$.t = TYPE_BOOLEAN;
2800 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
2801 $$.t = TYPE_BOOLEAN;
2803 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
2804 $$.t = TYPE_BOOLEAN;
2806 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
2807 $$.t = TYPE_BOOLEAN;
2809 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
2810 $$.t = TYPE_BOOLEAN;
2812 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
2813 $$.t = TYPE_BOOLEAN;
2815 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
2816 $$.t = TYPE_BOOLEAN;
2819 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
2821 $$.c = converttype($$.c, $1.t, $$.t);
2822 $$.c = abc_dup($$.c);
2823 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
2824 $$.c = cut_last_push($$.c);
2825 $$.c = code_append($$.c,$3.c);
2826 $$.c = converttype($$.c, $3.t, $$.t);
2827 code_t*label = $$.c = abc_label($$.c);
2828 jmp->branch = label;
2831 $$.t = join_types($1.t, $3.t, 'A');
2832 /*printf("%08x:\n",$1.t);
2833 code_dump($1.c, 0, 0, "", stdout);
2834 printf("%08x:\n",$3.t);
2835 code_dump($3.c, 0, 0, "", stdout);
2836 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
2838 $$.c = converttype($$.c, $1.t, $$.t);
2839 $$.c = abc_dup($$.c);
2840 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
2841 $$.c = cut_last_push($$.c);
2842 $$.c = code_append($$.c,$3.c);
2843 $$.c = converttype($$.c, $3.t, $$.t);
2844 code_t*label = $$.c = abc_label($$.c);
2845 jmp->branch = label;
2848 E : '!' E {$$.c=$2.c;
2849 $$.c = abc_not($$.c);
2850 $$.t = TYPE_BOOLEAN;
2853 E : '~' E {$$.c=$2.c;
2854 $$.c = abc_bitnot($$.c);
2858 E : E '&' E {$$.c = code_append($1.c,$3.c);
2859 $$.c = abc_bitand($$.c);
2863 E : E '^' E {$$.c = code_append($1.c,$3.c);
2864 $$.c = abc_bitxor($$.c);
2868 E : E '|' E {$$.c = code_append($1.c,$3.c);
2869 $$.c = abc_bitor($$.c);
2873 E : E ">>" E {$$.c = code_append($1.c,$3.c);
2874 $$.c = abc_rshift($$.c);
2877 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
2878 $$.c = abc_urshift($$.c);
2881 E : E "<<" E {$$.c = code_append($1.c,$3.c);
2882 $$.c = abc_lshift($$.c);
2886 E : E '/' E {$$.c = code_append($1.c,$3.c);
2887 $$.c = abc_divide($$.c);
2890 E : E '%' E {$$.c = code_append($1.c,$3.c);
2891 $$.c = abc_modulo($$.c);
2894 E : E '+' E {$$.c = code_append($1.c,$3.c);
2895 if(BOTH_INT($1.t, $3.t)) {
2896 $$.c = abc_add_i($$.c);
2899 $$.c = abc_add($$.c);
2900 $$.t = join_types($1.t,$3.t,'+');
2903 E : E '-' E {$$.c = code_append($1.c,$3.c);
2904 if(BOTH_INT($1.t,$3.t)) {
2905 $$.c = abc_subtract_i($$.c);
2908 $$.c = abc_subtract($$.c);
2912 E : E '*' E {$$.c = code_append($1.c,$3.c);
2913 if(BOTH_INT($1.t,$3.t)) {
2914 $$.c = abc_multiply_i($$.c);
2917 $$.c = abc_multiply($$.c);
2922 E : E "in" E {$$.c = code_append($1.c,$3.c);
2923 $$.c = abc_in($$.c);
2924 $$.t = TYPE_BOOLEAN;
2927 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
2928 if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
2929 MULTINAME(m, (classinfo_t*)($3.t->data));
2930 $$.c = abc_astype2($1.c, &m);
2933 $$.c = code_append($1.c, $3.c);
2934 $$.c = abc_astypelate($$.c);
2939 E : E "instanceof" E
2940 {$$.c = code_append($1.c, $3.c);
2941 $$.c = abc_instanceof($$.c);
2942 $$.t = TYPE_BOOLEAN;
2945 E : E "is" E {$$.c = code_append($1.c, $3.c);
2946 $$.c = abc_istypelate($$.c);
2947 $$.t = TYPE_BOOLEAN;
2950 E : "typeof" '(' E ')' {
2952 $$.c = abc_typeof($$.c);
2957 $$.c = cut_last_push($2.c);
2958 $$.c = abc_pushundefined($$.c);
2962 E : "void" { $$.c = abc_pushundefined(0);
2966 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
2971 $$.c=abc_negate_i($$.c);
2974 $$.c=abc_negate($$.c);
2981 $$.c = code_append($$.c, $3.c);
2983 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
2984 $$.c = abc_getproperty2($$.c, &m);
2985 $$.t = 0; // array elements have unknown type
2988 E : '[' MAYBE_EXPRESSION_LIST ']' {
2990 $$.c = code_append($$.c, $2.cc);
2991 $$.c = abc_newarray($$.c, $2.len);
2992 $$.t = registry_getarrayclass();
2995 MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.len=0;}
2996 MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
2998 EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3000 $$.cc = code_append($$.cc, $1.c);
3001 $$.cc = code_append($$.cc, $3.c);
3004 EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
3007 $$.cc = code_append($$.cc, $3.c);
3008 $$.cc = code_append($$.cc, $5.c);
3013 E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
3015 $$.c = code_append($$.c, $2.cc);
3016 $$.c = abc_newobject($$.c, $2.len/2);
3017 $$.t = registry_getobjectclass();
3022 if(BOTH_INT($1.t,$3.t)) {
3023 c=abc_multiply_i(c);
3027 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
3028 $$.c = toreadwrite($1.c, c, 0, 0);
3033 code_t*c = abc_modulo($3.c);
3034 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
3035 $$.c = toreadwrite($1.c, c, 0, 0);
3039 code_t*c = abc_lshift($3.c);
3040 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
3041 $$.c = toreadwrite($1.c, c, 0, 0);
3045 code_t*c = abc_rshift($3.c);
3046 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
3047 $$.c = toreadwrite($1.c, c, 0, 0);
3051 code_t*c = abc_urshift($3.c);
3052 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
3053 $$.c = toreadwrite($1.c, c, 0, 0);
3057 code_t*c = abc_divide($3.c);
3058 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
3059 $$.c = toreadwrite($1.c, c, 0, 0);
3063 code_t*c = abc_bitor($3.c);
3064 c=converttype(c, TYPE_INT, $1.t);
3065 $$.c = toreadwrite($1.c, c, 0, 0);
3069 code_t*c = abc_bitxor($3.c);
3070 c=converttype(c, TYPE_INT, $1.t);
3071 $$.c = toreadwrite($1.c, c, 0, 0);
3077 if(TYPE_IS_INT($1.t)) {
3081 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
3084 $$.c = toreadwrite($1.c, c, 0, 0);
3087 E : E "-=" E { code_t*c = $3.c;
3088 if(TYPE_IS_INT($1.t)) {
3089 c=abc_subtract_i(c);
3092 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
3095 $$.c = toreadwrite($1.c, c, 0, 0);
3098 E : E '=' E { code_t*c = 0;
3099 c = code_append(c, $3.c);
3100 c = converttype(c, $3.t, $1.t);
3101 $$.c = toreadwrite($1.c, c, 1, 0);
3105 E : E '?' E ':' E %prec below_assignment {
3106 $$.t = join_types($3.t,$5.t,'?');
3108 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
3109 $$.c = code_append($$.c, $3.c);
3110 $$.c = converttype($$.c, $3.t, $$.t);
3111 code_t*j2 = $$.c = abc_jump($$.c, 0);
3112 $$.c = j1->branch = abc_label($$.c);
3113 $$.c = code_append($$.c, $5.c);
3114 $$.c = converttype($$.c, $3.t, $$.t);
3115 $$.c = j2->branch = abc_label($$.c);
3118 E : E "++" { code_t*c = 0;
3119 classinfo_t*type = $1.t;
3120 if((is_getlocal($1.c) && TYPE_IS_INT($1.t)) || TYPE_IS_NUMBER($1.t)) {
3121 int nr = getlocalnr($1.c);
3122 code_free($1.c);$1.c=0;
3123 if(TYPE_IS_INT($1.t)) {
3124 $$.c = abc_getlocal(0, nr);
3125 $$.c = abc_inclocal_i($$.c, nr);
3126 } else if(TYPE_IS_NUMBER($1.t)) {
3127 $$.c = abc_getlocal(0, nr);
3128 $$.c = abc_inclocal($$.c, nr);
3129 } else syntaxerror("internal error");
3131 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3132 c=abc_increment_i(c);
3138 c=converttype(c, type, $1.t);
3139 $$.c = toreadwrite($1.c, c, 0, 1);
3144 // TODO: use inclocal, like with ++
3145 E : E "--" { code_t*c = 0;
3146 classinfo_t*type = $1.t;
3147 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3148 c=abc_decrement_i(c);
3154 c=converttype(c, type, $1.t);
3155 $$.c = toreadwrite($1.c, c, 0, 1);
3159 E : "++" %prec plusplus_prefix E { code_t*c = 0;
3160 classinfo_t*type = $2.t;
3161 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3162 c=abc_increment_i(c);
3168 c=converttype(c, type, $2.t);
3169 $$.c = toreadwrite($2.c, c, 0, 0);
3173 E : "--" %prec minusminus_prefix E { code_t*c = 0;
3174 classinfo_t*type = $2.t;
3175 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
3176 c=abc_decrement_i(c);
3182 c=converttype(c, type, $2.t);
3183 $$.c = toreadwrite($2.c, c, 0, 0);
3187 E : "super" '.' T_IDENTIFIER
3188 { if(!state->cls->info)
3189 syntaxerror("super keyword not allowed outside a class");
3190 classinfo_t*t = state->cls->info->superclass;
3191 if(!t) t = TYPE_OBJECT;
3193 memberinfo_t*f = registry_findmember(t, $3, 1);
3194 namespace_t ns = {f->access, ""};
3195 MEMBER_MULTINAME(m, f, $3);
3197 $$.c = abc_getlocal_0($$.c);
3198 $$.c = abc_getsuper2($$.c, &m);
3199 $$.t = slotinfo_gettype((slotinfo_t*)f);
3202 E : '@' T_IDENTIFIER {
3204 $$.c = abc_pushundefined(0);
3206 as3_warning("ignored @ operator");
3209 E : E '.' '@' T_IDENTIFIER {
3210 // child attribute TODO
3211 $$.c = abc_pushundefined(0);
3213 as3_warning("ignored .@ operator");
3216 E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
3217 // namespace declaration TODO
3218 $$.c = abc_pushundefined(0);
3220 as3_warning("ignored :: operator");
3223 E : E ".." T_IDENTIFIER {
3225 $$.c = abc_pushundefined(0);
3227 as3_warning("ignored .. operator");
3230 E : E '.' '(' E ')' {
3232 $$.c = abc_pushundefined(0);
3234 as3_warning("ignored .() operator");
3237 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
3241 E : E '.' T_IDENTIFIER
3243 classinfo_t*t = $1.t;
3245 if(TYPE_IS_CLASS(t) && t->data) {
3250 memberinfo_t*f = registry_findmember(t, $3, 1);
3252 if(f && !is_static != !(f->flags&FLAG_STATIC))
3254 if(f && f->slot && !noslot) {
3255 $$.c = abc_getslot($$.c, f->slot);
3257 MEMBER_MULTINAME(m, f, $3);
3258 $$.c = abc_getproperty2($$.c, &m);
3260 /* determine type */
3261 $$.t = slotinfo_gettype((slotinfo_t*)f);
3263 $$.c = abc_coerce_a($$.c);
3265 /* when resolving a property on an unknown type, we do know the
3266 name of the property (and don't seem to need the package), but
3267 we need to make avm2 try out all access modes */
3268 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
3269 $$.c = abc_getproperty2($$.c, &m);
3270 $$.c = abc_coerce_a($$.c);
3271 $$.t = registry_getanytype();
3275 VAR_READ : T_IDENTIFIER {
3282 /* look at variables */
3283 if((v = find_variable($1))) {
3284 // $1 is a local variable
3285 $$.c = abc_getlocal($$.c, v->index);
3290 int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
3292 /* look at current class' members */
3293 if(state->cls && (f = registry_findmember(state->cls->info, $1, 1)) &&
3294 (f->flags&FLAG_STATIC) >= i_am_static) {
3295 // $1 is a function in this class
3296 int var_is_static = (f->flags&FLAG_STATIC);
3298 if(f->kind == INFOTYPE_METHOD) {
3299 $$.t = TYPE_FUNCTION(f);
3303 if(var_is_static && !i_am_static) {
3304 /* access to a static member from a non-static location.
3305 do this via findpropstrict:
3306 there doesn't seem to be any non-lookup way to access
3307 static properties of a class */
3308 state->method->late_binding = 1;
3310 namespace_t ns = {f->access, ""};
3311 multiname_t m = {QNAME, &ns, 0, $1};
3312 $$.c = abc_findpropstrict2($$.c, &m);
3313 $$.c = abc_getproperty2($$.c, &m);
3315 } else if(f->slot>0) {
3316 $$.c = abc_getlocal_0($$.c);
3317 $$.c = abc_getslot($$.c, f->slot);
3320 namespace_t ns = {f->access, ""};
3321 multiname_t m = {QNAME, &ns, 0, $1};
3322 $$.c = abc_getlocal_0($$.c);
3323 $$.c = abc_getproperty2($$.c, &m);
3328 /* look at actual classes, in the current package and imported */
3329 if((a = find_class($1))) {
3330 if(a->access == ACCESS_PACKAGEINTERNAL &&
3331 strcmp(a->package, state->package) &&
3332 strcmp(a->package, internal_filename_package)
3334 syntaxerror("Can't access internal %s %s in package '%s' from package '%s'",
3335 infotypename(a),$1, a->package, state->package);
3337 if(a->kind != INFOTYPE_CLASS) {
3339 $$.c = abc_findpropstrict2($$.c, &m);
3340 $$.c = abc_getproperty2($$.c, &m);
3341 if(a->kind == INFOTYPE_METHOD) {
3342 methodinfo_t*f = (methodinfo_t*)a;
3343 $$.t = TYPE_FUNCTION(f);
3345 varinfo_t*v = (varinfo_t*)a;
3349 classinfo_t*c = (classinfo_t*)a;
3351 $$.c = abc_getglobalscope($$.c);
3352 $$.c = abc_getslot($$.c, c->slot);
3355 $$.c = abc_getlex2($$.c, &m);
3357 $$.t = TYPE_CLASS(c);
3362 /* unknown object, let the avm2 resolve it */
3364 as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
3365 state->method->late_binding = 1;
3367 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
3370 $$.c = abc_findpropstrict2($$.c, &m);
3371 $$.c = abc_getproperty2($$.c, &m);
3375 // ----------------- namespaces -------------------------------------------------
3377 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;}
3378 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;}
3379 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;}
3381 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {
3383 tokenizer_register_namespace($3);