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"
42 enum yytokentype token;
45 classinfo_t*classinfo;
46 classinfo_list_t*classinfo_list;
49 unsigned int number_uint;
53 typedcode_list_t*value_list;
62 %token<id> T_IDENTIFIER
64 %token<token> T_REGEXP
66 %token<number_int> T_INT
67 %token<number_uint> T_UINT
68 %token<number_uint> T_BYTE
69 %token<number_uint> T_SHORT
70 %token<number_float> T_FLOAT
72 %token<token> KW_IMPLEMENTS
73 %token<token> KW_NAMESPACE "namespace"
74 %token<token> KW_PACKAGE "package"
75 %token<token> KW_PROTECTED
76 %token<token> KW_PUBLIC
77 %token<token> KW_PRIVATE
78 %token<token> KW_USE "use"
79 %token<token> KW_INTERNAL
80 %token<token> KW_NEW "new"
81 %token<token> KW_NATIVE
82 %token<token> KW_FUNCTION "function"
83 %token<token> KW_UNDEFINED "undefined"
84 %token<token> KW_FOR "for"
85 %token<token> KW_CLASS "class"
86 %token<token> KW_CONST "const"
87 %token<token> KW_SET "set"
88 %token<token> KW_VOID "void"
89 %token<token> KW_STATIC
90 %token<token> KW_IMPORT "import"
91 %token<token> KW_RETURN "return"
92 %token<token> KW_TYPEOF "typeof"
93 %token<token> KW_INTERFACE "interface"
94 %token<token> KW_NULL "null"
95 %token<token> KW_VAR "var"
96 %token<token> KW_DYNAMIC
97 %token<token> KW_OVERRIDE
98 %token<token> KW_FINAL
99 %token<token> KW_GET "get"
100 %token<token> KW_EXTENDS
101 %token<token> KW_FALSE "false"
102 %token<token> KW_TRUE "true"
103 %token<token> KW_BOOLEAN "Boolean"
104 %token<token> KW_UINT "uint"
105 %token<token> KW_INT "int"
106 %token<token> KW_WHILE "while"
107 %token<token> KW_NUMBER "Number"
108 %token<token> KW_STRING "String"
109 %token<token> KW_DELETE "delete"
110 %token<token> KW_IF "if"
111 %token<token> KW_ELSE "else"
112 %token<token> KW_BREAK "break"
113 %token<token> KW_IS "is"
114 %token<token> KW_AS "as"
116 %token<token> T_EQEQ "=="
117 %token<token> T_EQEQEQ "==="
118 %token<token> T_NE "!="
119 %token<token> T_NEE "!=="
120 %token<token> T_LE "<="
121 %token<token> T_GE ">="
122 %token<token> T_DIVBY "/="
123 %token<token> T_MODBY "%="
124 %token<token> T_MULBY "*="
125 %token<token> T_PLUSBY "+="
126 %token<token> T_MINUSBY "-="
127 %token<token> T_SHRBY ">>="
128 %token<token> T_SHLBY "<<="
129 %token<token> T_USHRBY ">>>="
130 %token<token> T_OROR "||"
131 %token<token> T_ANDAND "&&"
132 %token<token> T_COLONCOLON "::"
133 %token<token> T_MINUSMINUS "--"
134 %token<token> T_PLUSPLUS "++"
135 %token<token> T_DOTDOT ".."
136 %token<token> T_DOTDOTDOT "..."
137 %token<token> T_SHL "<<"
138 %token<token> T_USHR ">>>"
139 %token<token> T_SHR ">>"
141 %type <id> X_IDENTIFIER PACKAGE
142 %type <token> VARCONST
144 %type <code> CODEPIECE
145 %type <code> CODEBLOCK MAYBECODE
146 %type <token> PACKAGE_DECLARATION
147 %type <token> FUNCTION_DECLARATION
148 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST
149 %type <token> CLASS_DECLARATION
150 %type <token> NAMESPACE_DECLARATION
151 %type <token> INTERFACE_DECLARATION
152 %type <code> VOIDEXPRESSION
153 %type <value> EXPRESSION NONCOMMAEXPRESSION
154 %type <value> MAYBEEXPRESSION
155 %type <value> E DELETE
156 %type <value> CONSTANT
157 %type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
158 %type <token> USE_NAMESPACE
159 %type <code> FOR_INIT
161 %type <classinfo> MAYBETYPE
164 %type <params> PARAM_LIST
165 %type <params> MAYBE_PARAM_LIST
166 %type <flags> MAYBE_MODIFIERS
167 %type <flags> MODIFIER_LIST
168 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
169 %type <classinfo_list> IMPLEMENTS_LIST
170 %type <classinfo> EXTENDS
171 %type <classinfo_list> EXTENDS_LIST
172 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
173 %type <classinfo_list> QNAME_LIST
174 %type <classinfo> TYPE
176 //%type <token> VARIABLE
177 %type <value> VAR_READ
179 //%type <token> T_IDENTIFIER
180 %type <token> MODIFIER
181 %type <value> FUNCTIONCALL
182 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
184 // precedence: from low to high
188 %left below_semicolon
191 %nonassoc below_assignment // for ?:, contrary to spec
192 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
199 %nonassoc "==" "!=" "===" "!=="
201 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
202 %left "<<" ">>" ">>>"
206 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
208 %left '[' ']' '{' "new" '.' ".." "::"
209 %nonassoc T_IDENTIFIER
214 // needed for "return" precedence:
215 %nonassoc T_STRING T_REGEXP
216 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
217 %nonassoc "false" "true" "null" "undefined"
222 static int yyerror(char*s)
224 syntaxerror("%s", s);
226 static char* concat3str(const char* t1, const char* t2, const char* t3)
231 char*text = malloc(l1+l2+l3+1);
232 memcpy(text , t1, l1);
233 memcpy(text+l1, t2, l2);
234 memcpy(text+l1+l2, t3, l3);
239 typedef struct _import {
243 DECLARE_LIST(import);
245 typedef struct _state {
253 /* code that needs to be executed at the start of
254 a method (like initializing local registers) */
257 import_list_t*wildcard_imports;
259 char has_own_imports;
265 code_t*cls_static_init;
275 typedef struct _global {
279 static global_t*global = 0;
280 static state_t* state = 0;
284 #define MULTINAME(m,x) multiname_t m;namespace_t m##_ns;registry_fill_multiname(&m, &m##_ns, x);
286 /* warning: list length of namespace set is undefined */
287 #define MULTINAME_LATE(m, access, package) \
288 namespace_t m##_ns = {access, package}; \
289 namespace_set_t m##_nsset; \
290 namespace_list_t m##_l;m##_l.next = 0; \
291 m##_nsset.namespaces = &m##_l; \
292 m##_nsset = m##_nsset; \
293 m##_l.namespace = &m##_ns; \
294 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
296 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
297 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
298 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
299 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
300 static namespace_list_t nl4 = {&ns4,0};
301 static namespace_list_t nl3 = {&ns3,&nl4};
302 static namespace_list_t nl2 = {&ns2,&nl3};
303 static namespace_list_t nl1 = {&ns1,&nl2};
304 static namespace_set_t nopackage_namespace_set = {&nl1};
306 static state_list_t*state_stack=0;
308 static void init_globals()
310 global = rfx_calloc(sizeof(global_t));
313 static void new_state()
316 NEW(state_list_t, sl);
318 state_t*oldstate = state;
320 memcpy(s, state, sizeof(state_t)); //shallow copy
321 sl->next = state_stack;
324 s->imports = dict_new();
329 state->vars = dict_new();
331 state->has_own_imports = 0;
333 static void state_has_imports()
335 state->wildcard_imports = list_clone(state->wildcard_imports);
336 state->imports = dict_clone(state->imports);
337 state->has_own_imports = 1;
340 static void old_state()
342 if(!state_stack || !state_stack->next)
343 syntaxerror("invalid nesting");
344 state_t*oldstate = state;
345 state_list_t*old = state_stack;
346 state_stack = state_stack->next;
348 state = state_stack->state;
349 /*if(state->initcode) {
350 printf("residual initcode\n");
351 code_dump(state->initcode, 0, 0, "", stdout);
353 if(oldstate->has_own_imports) {
354 list_free(oldstate->wildcard_imports);
355 dict_destroy(oldstate->imports);oldstate->imports=0;
357 state->initcode = code_append(state->initcode, oldstate->initcode);
359 void initialize_state()
364 state->file = abc_file_new();
365 state->file->flags &= ~ABCFILE_LAZY;
367 state->init = abc_initscript(state->file, 0);
368 code_t*c = state->init->method->body->code;
370 c = abc_getlocal_0(c);
371 c = abc_pushscope(c);
373 /* findpropstrict doesn't just return a scope object- it
374 also makes it "active" somehow. Push local_0 on the
375 scope stack and read it back with findpropstrict, it'll
376 contain properties like "trace". Trying to find the same
377 property on a "vanilla" local_0 yields only a "undefined" */
378 //c = abc_findpropstrict(c, "[package]::trace");
380 /*c = abc_getlocal_0(c);
381 c = abc_findpropstrict(c, "[package]::trace");
383 c = abc_setlocal_1(c);
385 c = abc_pushbyte(c, 0);
386 c = abc_setlocal_2(c);
388 code_t*xx = c = abc_label(c);
389 c = abc_findpropstrict(c, "[package]::trace");
390 c = abc_pushstring(c, "prop:");
391 c = abc_hasnext2(c, 1, 2);
393 c = abc_setlocal_3(c);
394 c = abc_callpropvoid(c, "[package]::trace", 2);
395 c = abc_getlocal_3(c);
397 c = abc_iftrue(c,xx);*/
399 c = abc_findpropstrict(c, "[package]::trace");
400 c = abc_pushstring(c, "[entering global init function]");
401 c = abc_callpropvoid(c, "[package]::trace", 1);
403 state->init->method->body->code = c;
405 void* finalize_state()
407 if(state->level!=1) {
408 syntaxerror("unexpected end of file");
410 abc_method_body_t*m = state->init->method->body;
413 __ findpropstrict(m, "[package]::trace");
414 __ pushstring(m, "[leaving global init function]");
415 __ callpropvoid(m, "[package]::trace", 1);
421 static void startpackage(char*name)
424 syntaxerror("Packages can not be nested.");
427 /*printf("entering package \"%s\"\n", name);*/
428 state->package = name;
430 static void endpackage()
432 /*printf("leaving package \"%s\"\n", state->package);*/
437 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
440 syntaxerror("inner classes now allowed");
445 classinfo_list_t*mlist=0;
446 /*printf("entering class %s\n", name);
447 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n");
449 printf(" extends: %s.%s\n", extends->package, extends->name);
450 printf(" implements (%d): ", list_length(implements));
451 for(mlist=implements;mlist;mlist=mlist->next) {
452 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
457 if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL))
458 syntaxerror("invalid modifier(s)");
460 if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
461 syntaxerror("public and internal not supported at the same time.");
463 /* create the class name, together with the proper attributes */
467 if(!(flags&FLAG_PUBLIC) && !state->package) {
468 access = ACCESS_PRIVATE; package = current_filename;
469 } else if(!(flags&FLAG_PUBLIC) && state->package) {
470 access = ACCESS_PACKAGEINTERNAL; package = state->package;
471 } else if(state->package) {
472 access = ACCESS_PACKAGE; package = state->package;
474 syntaxerror("public classes only allowed inside a package");
477 if(registry_findclass(package, classname)) {
478 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
481 /* build info struct */
482 int num_interfaces = (list_length(implements));
483 state->clsinfo = classinfo_register(access, package, classname, num_interfaces);
484 state->clsinfo->superclass = extends;
486 classinfo_list_t*l = implements;
487 for(l=implements;l;l=l->next) {
488 state->clsinfo->interfaces[pos++] = l->classinfo;
491 MULTINAME(classname2,state->clsinfo);
493 multiname_t*extends2 = sig2mname(extends);
496 state->cls_init = abc_getlocal_0(state->cls_init);
497 state->cls_init = abc_constructsuper(state->cls_init, 0);
500 state->cls = abc_class_new(state->file, &classname2, extends2);
501 if(flags&FLAG_FINAL) abc_class_final(state->cls);
502 if(flags&FLAG_DYNAMIC) abc_class_sealed(state->cls);
503 if(interface) abc_class_interface(state->cls);
505 for(mlist=implements;mlist;mlist=mlist->next) {
506 MULTINAME(m, mlist->classinfo);
507 abc_class_add_interface(state->cls, &m);
510 /* now write the construction code for this class */
511 int slotindex = abc_initscript_addClassTrait(state->init, &classname2, state->cls);
513 abc_method_body_t*m = state->init->method->body;
514 __ getglobalscope(m);
515 classinfo_t*s = extends;
520 //TODO: take a look at the current scope stack, maybe
521 // we can re-use something
526 multiname_t*s2 = sig2mname(s);
528 multiname_destroy(s2);
530 __ pushscope(m); count++;
531 m->code = m->code->prev->prev; // invert
533 /* continue appending after last op end */
534 while(m->code && m->code->next) m->code = m->code->next;
536 /* TODO: if this is one of *our* classes, we can also
537 do a getglobalscope/getslot <nr> (which references
538 the init function's slots) */
540 __ getlex2(m, extends2);
542 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
543 stack is not the superclass */
544 __ pushscope(m);count++;
547 /* notice: we get a verify error #1107 if the top element on the scope
548 stack is not the global object */
550 __ pushscope(m);count++;
552 __ newclass(m,state->cls);
556 __ setslot(m, slotindex);
558 /* flash.display.MovieClip handling */
559 if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
560 if(state->package && state->package[0]) {
561 globalclass = concat3str(state->package, ".", classname);
563 globalclass = strdup(classname);
566 multiname_destroy(extends2);
569 static void endclass()
571 if(state->cls_init) {
572 if(!state->cls->constructor) {
573 abc_method_t*m = abc_class_constructor(state->cls, 0);
574 m->body->code = code_append(m->body->code, state->cls_init);
575 m->body->code = abc_returnvoid(m->body->code);
577 code_t*c = state->cls->constructor->body->code;
578 c = code_append(state->cls_init, c);
579 state->cls->constructor->body->code = c;
583 if(state->cls_static_init) {
584 if(!state->cls->static_constructor) {
585 abc_method_t*m = abc_class_staticconstructor(state->cls, 0);
586 m->body->code = code_append(m->body->code, state->cls_static_init);
587 m->body->code = abc_returnvoid(m->body->code);
589 state->cls->static_constructor->body->code =
590 code_append(state->cls_static_init, state->cls->static_constructor->body->code);
597 typedef struct _variable {
602 static int find_variable(char*name, classinfo_t**m)
604 state_list_t* s = state_stack;
606 variable_t*v = dict_lookup(s->state->vars, name);
617 static int find_variable_safe(char*name, classinfo_t**m)
619 int i = find_variable(name, m);
621 syntaxerror("undefined variable: %s", name);
624 static char variable_exists(char*name)
626 return dict_lookup(state->vars, name)!=0;
628 static int new_variable(char*name, classinfo_t*type)
631 v->index = global->variable_count;
633 dict_put(state->vars, name, v);
634 return global->variable_count++;
636 #define TEMPVARNAME "__as3_temp__"
637 static int gettempvar()
639 int i = find_variable(TEMPVARNAME, 0);
641 return new_variable(TEMPVARNAME, 0);
647 code_t* killvars(code_t*c)
650 for(t=0;t<state->vars->hashsize;t++) {
651 dictentry_t*e =state->vars->slots[t];
653 variable_t*v = (variable_t*)e->data;
654 //do this always, otherwise register types don't match
655 //in the verifier when doing nested loops
656 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
657 c = abc_kill(c, v->index);
665 static void check_constant_against_type(classinfo_t*t, constant_t*c)
667 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
668 if(TYPE_IS_NUMBER(t)) {
669 xassert(c->type == CONSTANT_FLOAT
670 || c->type == CONSTANT_INT
671 || c->type == CONSTANT_UINT);
672 } else if(TYPE_IS_UINT(t)) {
673 xassert(c->type == CONSTANT_UINT ||
674 (c->type == CONSTANT_INT && c->i>0));
675 } else if(TYPE_IS_INT(t)) {
676 xassert(c->type == CONSTANT_INT);
677 } else if(TYPE_IS_BOOLEAN(t)) {
678 xassert(c->type == CONSTANT_TRUE
679 || c->type == CONSTANT_FALSE);
683 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
685 memberinfo_t*minfo = 0;
686 if(getset != KW_GET && getset != KW_SET) {
687 if(registry_findmember(state->clsinfo, name)) {
688 syntaxerror("class already contains a member/method called '%s'", name);
690 minfo = memberinfo_register(state->clsinfo, name, MEMBER_METHOD);
691 minfo->return_type = return_type;
692 // getslot on a member slot only returns "undefined", so no need
693 // to actually store these
694 //state->minfo->slot = state->m->method->trait->slot_id;
696 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
700 else if(params->list)
701 type = params->list->param->type;
702 if((minfo=registry_findmember(state->clsinfo, name))) {
703 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
704 syntaxerror("class already contains a member or method called '%s'", name);
706 syntaxerror("getter/setter for '%s' already defined", name);
707 /* make a setter or getter into a getset */
712 if(type && minfo->type != type)
713 syntaxerror("different type in getter and setter");
715 minfo = memberinfo_register(state->clsinfo, name, gs);
718 /* can't assign a slot as getter and setter might have different slots */
719 //minfo->slot = slot;
721 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
722 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
723 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
724 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
725 if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
729 static int flags2access(int flags)
732 if(flags&FLAG_PUBLIC) {
733 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
734 access = ACCESS_PACKAGE;
735 } else if(flags&FLAG_PRIVATE) {
736 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
737 access = ACCESS_PRIVATE;
738 } else if(flags&FLAG_PROTECTED) {
739 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
740 access = ACCESS_PROTECTED;
742 access = ACCESS_PACKAGEINTERNAL;
747 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
748 params_t*params, classinfo_t*return_type)
752 global->variable_count = 0;
753 state->function = name;
756 syntaxerror("not able to start another method scope");
759 namespace_t mname_ns = {flags2access(flags), ""};
760 multiname_t mname = {QNAME, &mname_ns, 0, name};
762 multiname_t*type2 = sig2mname(return_type);
764 if(!strcmp(state->clsinfo->name,name)) {
765 state->m = abc_class_constructor(state->cls, type2);
766 name = "__as3_constructor__";
768 if(flags&FLAG_STATIC)
769 state->m = abc_class_staticmethod(state->cls, type2, &mname);
771 state->m = abc_class_method(state->cls, type2, &mname);
772 slot = state->m->trait->slot_id;
774 state->minfo = registerfunction(getset, flags, name, params, return_type, slot);
776 if(getset == KW_GET) state->m->trait->kind = TRAIT_GETTER;
777 if(getset == KW_SET) state->m->trait->kind = TRAIT_SETTER;
778 if(params->varargs) state->m->flags |= METHOD_NEED_REST;
782 for(p=params->list;p;p=p->next) {
783 if(params->varargs && !p->next) {
784 break; //varargs: omit last parameter in function signature
786 multiname_t*m = sig2mname(p->param->type);
787 list_append(state->m->parameters, m);
788 if(p->param->value) {
789 check_constant_against_type(p->param->type, p->param->value);
790 opt=1;list_append(state->m->optional_parameters, p->param->value);
792 syntaxerror("non-optional parameter not allowed after optional parameters");
796 /* state->vars is initialized by state_new */
797 if(new_variable((flags&FLAG_STATIC)?"class":"this", state->clsinfo)!=0) syntaxerror("Internal error");
799 for(p=params->list;p;p=p->next) {
800 new_variable(p->param->name, p->param->type);
803 static void endfunction(code_t*body)
806 if(!(state->cls->flags & CLASS_INTERFACE)) {
808 if(state->late_binding) {
809 c = abc_getlocal_0(c);
810 c = abc_pushscope(c);
812 c = code_append(c, state->initcode);
813 c = code_append(c, body);
815 /* append return if necessary */
816 if(!c || c->opcode != OPCODE_RETURNVOID &&
817 c->opcode != OPCODE_RETURNVALUE) {
818 c = abc_returnvoid(c);
820 if(state->m->body->code) syntaxerror("internal error");
821 state->m->body->code = c;
828 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
833 void breakjumpsto(code_t*c, code_t*jump)
838 if(c->opcode == OPCODE___BREAK__) {
839 c->opcode = OPCODE_JUMP;
846 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
849 return registry_getanytype();
850 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
851 return registry_getanytype();
854 return registry_getanytype();
856 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
861 return abc_coerce_a(c);
865 // cast an "any" type to a specific type. subject to
866 // runtime exceptions
867 return abc_coerce2(c, &m);
870 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
871 return abc_coerce2(c, &m);
873 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
874 return abc_coerce2(c, &m);
876 /* these are subject to overflow */
877 if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) {
878 return abc_coerce2(c, &m);
880 if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) {
881 return abc_coerce2(c, &m);
884 classinfo_t*supertype = from;
886 if(supertype == to) {
887 // target type is one of from's superclasses
888 return abc_coerce2(c, &m);
891 while(supertype->interfaces[t]) {
892 if(supertype->interfaces[t]==to) {
893 // to type is one of from's interfaces
894 return abc_coerce2(c, &m);
898 supertype = supertype->superclass;
900 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
902 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
904 syntaxerror("can't convert type %s to %s", from->name, to->name);
907 code_t*defaultvalue(code_t*c, classinfo_t*type)
909 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) {
910 c = abc_pushbyte(c, 0);
911 } else if(TYPE_IS_BOOLEAN(type)) {
912 c = abc_pushfalse(c);
919 char is_pushundefined(code_t*c)
921 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
924 void parserassert(int b)
926 if(!b) syntaxerror("internal error: assertion failed");
929 static classinfo_t* find_class(char*name)
933 c = registry_findclass(state->package, name);
935 /* try explicit imports */
936 dictentry_t* e = dict_get_slot(state->imports, name);
940 if(!strcmp(e->key, name)) {
941 c = (classinfo_t*)e->data;
946 /* try package.* imports */
947 import_list_t*l = state->wildcard_imports;
951 //printf("does package %s contain a class %s?\n", l->import->package, name);
952 c = registry_findclass(l->import->package, name);
956 /* try global package */
958 c = registry_findclass("", name);
963 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
967 [prefix code] [read instruction]
971 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
974 if(in && in->opcode == OPCODE_COERCE_A) {
975 in = code_cutlast(in);
978 syntaxerror("internal error");
980 /* chop off read instruction */
984 prefix = r->prev;r->prev = 0;
990 char use_temp_var = readbefore;
992 /* generate the write instruction, and maybe append a dup to the prefix code */
993 code_t* write = abc_nop(0);
994 if(r->opcode == OPCODE_GETPROPERTY) {
995 write->opcode = OPCODE_SETPROPERTY;
996 multiname_t*m = (multiname_t*)r->data[0];
997 write->data[0] = multiname_clone(m);
998 if(m->type == QNAME || m->type == MULTINAME) {
1000 prefix = abc_dup(prefix); // we need the object, too
1003 } else if(m->type == MULTINAMEL) {
1005 /* dupping two values on the stack requires 5 operations and one register-
1006 couldn't adobe just have given us a dup2? */
1007 int temp = gettempvar();
1008 prefix = abc_setlocal(prefix, temp);
1009 prefix = abc_dup(prefix);
1010 prefix = abc_getlocal(prefix, temp);
1011 prefix = abc_swap(prefix);
1012 prefix = abc_getlocal(prefix, temp);
1016 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1018 } else if(r->opcode == OPCODE_GETSLOT) {
1019 write->opcode = OPCODE_SETSLOT;
1020 write->data[0] = r->data[0];
1022 prefix = abc_dup(prefix); // we need the object, too
1025 } else if(r->opcode == OPCODE_GETLOCAL) {
1026 write->opcode = OPCODE_SETLOCAL;
1027 write->data[0] = r->data[0];
1028 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1029 write->opcode = OPCODE_SETLOCAL_0;
1030 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1031 write->opcode = OPCODE_SETLOCAL_1;
1032 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1033 write->opcode = OPCODE_SETLOCAL_2;
1034 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1035 write->opcode = OPCODE_SETLOCAL_3;
1037 code_dump(r, 0, 0, "", stdout);
1038 syntaxerror("illegal lvalue: can't assign a value to this expression");
1045 /* with getproperty/getslot, we have to be extra careful not
1046 to execute the read code twice, as it might have side-effects
1047 (e.g. if the property is in fact a setter/getter combination)
1049 So read the value, modify it, and write it again,
1050 using prefix only once and making sure (by using a temporary
1051 register) that the return value is what we just wrote */
1052 temp = gettempvar();
1053 c = code_append(c, prefix);
1054 c = code_append(c, r);
1057 c = abc_setlocal(c, temp);
1059 c = code_append(c, middlepart);
1062 c = abc_setlocal(c, temp);
1064 c = code_append(c, write);
1065 c = abc_getlocal(c, temp);
1066 c = abc_kill(c, temp);
1068 /* if we're allowed to execute the read code twice *and*
1069 the middlepart doesn't modify the code, things are easier.
1071 code_t* r2 = code_dup(r);
1072 //c = code_append(c, prefix);
1073 parserassert(!prefix);
1074 c = code_append(c, r);
1075 c = code_append(c, middlepart);
1076 c = code_append(c, write);
1077 c = code_append(c, r2);
1080 /* even smaller version: overwrite the value without reading
1084 c = code_append(c, prefix);
1087 c = code_append(c, middlepart);
1088 c = code_append(c, write);
1089 c = code_append(c, r);
1091 temp = gettempvar();
1093 c = code_append(c, prefix);
1096 c = code_append(c, middlepart);
1098 c = abc_setlocal(c, temp);
1099 c = code_append(c, write);
1100 c = abc_getlocal(c, temp);
1107 #define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
1108 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1115 /* ------------ code blocks / statements ---------------- */
1119 MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/}
1120 MAYBECODE: {$$=code_new();}
1122 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1123 CODE: CODEPIECE {$$=$1;}
1125 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
1126 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
1127 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
1128 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
1129 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
1130 CODEPIECE: ';' {$$=code_new();}
1131 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
1132 CODEPIECE: VOIDEXPRESSION {$$=$1}
1133 CODEPIECE: FOR {$$=$1}
1134 CODEPIECE: WHILE {$$=$1}
1135 CODEPIECE: BREAK {$$=$1}
1136 CODEPIECE: RETURN {$$=$1}
1137 CODEPIECE: IF {$$=$1}
1138 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
1139 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
1141 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
1142 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1143 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1145 /* ------------ variables --------------------------- */
1147 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1148 | {$$.c=abc_pushundefined(0);
1152 VAR : "const" | "var"
1153 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
1155 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1156 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1158 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1160 if(variable_exists($2))
1161 syntaxerror("Variable %s already defined", $2);
1163 if(!is_subtype_of($4.t, $3)) {
1164 syntaxerror("Can't convert %s to %s", $4.t->name,
1168 int index = new_variable($2, $3);
1171 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1173 $$ = converttype($$, $4.t, $3);
1174 $$ = abc_setlocal($$, index);
1176 $$ = defaultvalue(0, $3);
1177 $$ = abc_setlocal($$, index);
1180 /* if this is a typed variable:
1181 push default value for type on stack */
1183 state->initcode = defaultvalue(state->initcode, $3);
1184 state->initcode = abc_setlocal(state->initcode, index);
1187 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1189 $$ = abc_coerce_a($$);
1190 $$ = abc_setlocal($$, index);
1196 /* that's the default for a local register, anyway
1198 state->initcode = abc_pushundefined(state->initcode);
1199 state->initcode = abc_setlocal(state->initcode, index);
1201 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1204 /* ------------ control flow ------------------------- */
1206 MAYBEELSE: %prec below_else {$$ = code_new();}
1207 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1208 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1210 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1211 $$ = state->initcode;state->initcode=0;
1213 $$ = code_append($$, $4.c);
1214 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1216 $$ = code_append($$, $6);
1218 myjmp = $$ = abc_jump($$, 0);
1220 myif->branch = $$ = abc_label($$);
1222 $$ = code_append($$, $7);
1223 myjmp->branch = $$ = abc_label($$);
1226 $$ = killvars($$);old_state();
1229 FOR_INIT : {$$=code_new();}
1230 FOR_INIT : VARIABLE_DECLARATION
1231 FOR_INIT : VOIDEXPRESSION
1233 FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1234 $$ = state->initcode;state->initcode=0;
1236 $$ = code_append($$, $4);
1237 code_t*loopstart = $$ = abc_label($$);
1238 $$ = code_append($$, $6.c);
1239 code_t*myif = $$ = abc_iffalse($$, 0);
1240 $$ = code_append($$, $10);
1241 $$ = code_append($$, $8);
1242 $$ = abc_jump($$, loopstart);
1243 code_t*out = $$ = abc_label($$);
1244 breakjumpsto($$, out);
1247 $$ = killvars($$);old_state();
1250 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1251 $$ = state->initcode;state->initcode=0;
1253 code_t*myjmp = $$ = abc_jump($$, 0);
1254 code_t*loopstart = $$ = abc_label($$);
1255 $$ = code_append($$, $6);
1256 myjmp->branch = $$ = abc_label($$);
1257 $$ = code_append($$, $4.c);
1258 $$ = abc_iftrue($$, loopstart);
1259 code_t*out = $$ = abc_label($$);
1260 breakjumpsto($$, out);
1262 $$ = killvars($$);old_state();
1266 $$ = abc___break__(0);
1269 /* ------------ packages and imports ---------------- */
1271 X_IDENTIFIER: T_IDENTIFIER
1272 | "package" {$$="package";}
1274 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3str($1,".",$3);}
1275 PACKAGE: X_IDENTIFIER {$$=$1;}
1277 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1278 PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()}
1280 IMPORT : "import" QNAME {
1283 syntaxerror("Couldn't import class\n");
1284 state_has_imports();
1285 dict_put(state->imports, c->name, c);
1288 IMPORT : "import" PACKAGE '.' '*' {
1291 state_has_imports();
1292 list_append(state->wildcard_imports, i);
1296 /* ------------ classes and interfaces (header) -------------- */
1298 MAYBE_MODIFIERS : {$$=0;}
1299 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
1300 MODIFIER_LIST : MODIFIER {$$=$1;}
1301 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
1303 MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
1304 | KW_PRIVATE {$$=FLAG_PRIVATE;}
1305 | KW_PROTECTED {$$=FLAG_PROTECTED;}
1306 | KW_STATIC {$$=FLAG_STATIC;}
1307 | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
1308 | KW_FINAL {$$=FLAG_FINAL;}
1309 | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
1310 | KW_NATIVE {$$=FLAG_NATIVE;}
1311 | KW_INTERNAL {$$=FLAG_INTERNAL;}
1313 EXTENDS : {$$=registry_getobjectclass();}
1314 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1316 EXTENDS_LIST : {$$=list_new();}
1317 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1319 IMPLEMENTS_LIST : {$$=list_new();}
1320 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1322 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
1323 EXTENDS IMPLEMENTS_LIST
1324 '{' {startclass($1,$3,$4,$5, 0);}
1325 MAYBE_DECLARATION_LIST
1328 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
1330 '{' {startclass($1,$3,0,$4,1);}
1331 MAYBE_IDECLARATION_LIST
1334 /* ------------ classes and interfaces (body) -------------- */
1336 MAYBE_DECLARATION_LIST :
1337 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1338 DECLARATION_LIST : DECLARATION
1339 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1341 DECLARATION : SLOT_DECLARATION
1342 DECLARATION : FUNCTION_DECLARATION
1344 MAYBE_IDECLARATION_LIST :
1345 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1346 IDECLARATION_LIST : IDECLARATION
1347 IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
1349 IDECLARATION : "var" T_IDENTIFIER {
1350 syntaxerror("variable declarations not allowed in interfaces");
1352 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
1354 if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
1355 syntaxerror("invalid method modifiers: interface methods always need to be public");
1357 startfunction(0,$1,$3,$4,&$6,$8);
1361 /* ------------ classes and interfaces (body, slots ) ------- */
1363 VARCONST: "var" | "const"
1365 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1367 memberinfo_t* info = memberinfo_register(state->clsinfo, $3, MEMBER_SLOT);
1369 info->flags = flags;
1372 namespace_t mname_ns = {flags2access(flags), ""};
1373 multiname_t mname = {QNAME, &mname_ns, 0, $3};
1375 if(!(flags&FLAG_STATIC)) {
1378 t=abc_class_slot(state->cls, &mname, &m);
1380 t=abc_class_slot(state->cls, &mname, 0);
1382 info->slot = t->slot_id;
1386 t=abc_class_staticslot(state->cls, &mname, &m);
1388 t=abc_class_staticslot(state->cls, &mname, 0);
1390 info->slot = t->slot_id;
1392 if($5.c && !is_pushundefined($5.c)) {
1394 c = abc_getlocal_0(c);
1395 c = code_append(c, $5.c);
1396 c = converttype(c, $5.t, $4);
1397 c = abc_setslot(c, t->slot_id);
1398 if(!(flags&FLAG_STATIC))
1399 state->cls_init = code_append(state->cls_init, c);
1401 state->cls_static_init = code_append(state->cls_static_init, c);
1404 t->kind= TRAIT_CONST;
1408 /* ------------ constants -------------------------------------- */
1410 MAYBESTATICCONSTANT: {$$=0;}
1411 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1413 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1414 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1415 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1416 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1417 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
1418 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1419 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
1420 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
1421 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
1423 /* ------------ classes and interfaces (body, functions) ------- */
1425 // non-vararg version
1427 memset(&$$,0,sizeof($$));
1429 MAYBE_PARAM_LIST: PARAM_LIST {
1434 MAYBE_PARAM_LIST: "..." PARAM {
1435 memset(&$$,0,sizeof($$));
1437 list_append($$.list, $2);
1439 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1442 list_append($$.list, $4);
1446 PARAM_LIST: PARAM_LIST ',' PARAM {
1448 list_append($$.list, $3);
1451 memset(&$$,0,sizeof($$));
1452 list_append($$.list, $1);
1455 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1456 $$ = malloc(sizeof(param_t));
1461 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1462 $$ = malloc(sizeof(param_t));
1464 $$->type = TYPE_ANY;
1467 GETSET : "get" {$$=$1;}
1471 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1472 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1474 if(!state->m) syntaxerror("internal error: undefined function");
1478 /* ------------- package + class ids --------------- */
1480 CLASS: T_IDENTIFIER {
1482 /* try current package */
1483 $$ = find_class($1);
1484 if(!$$) syntaxerror("Could not find class %s\n", $1);
1487 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1488 $$ = registry_findclass($1, $3);
1489 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
1492 QNAME: PACKAGEANDCLASS
1495 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1496 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1498 TYPE : QNAME {$$=$1;}
1499 | '*' {$$=registry_getanytype();}
1501 | "String" {$$=registry_getstringclass();}
1502 | "int" {$$=registry_getintclass();}
1503 | "uint" {$$=registry_getuintclass();}
1504 | "Boolean" {$$=registry_getbooleanclass();}
1505 | "Number" {$$=registry_getnumberclass();}
1508 MAYBETYPE: ':' TYPE {$$=$2;}
1511 /* ----------function calls, delete, constructor calls ------ */
1513 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1514 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1516 MAYBE_EXPRESSION_LIST : {$$=0;}
1517 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1518 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1519 typedcode_t*t = malloc(sizeof(typedcode_t));
1521 list_append($$, t);}
1522 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1523 typedcode_t*t = malloc(sizeof(typedcode_t));
1525 list_append($$, t);}
1527 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1532 $$.c = abc_getglobalscope($$.c);
1533 $$.c = abc_getslot($$.c, $2->slot);
1535 $$.c = abc_findpropstrict2($$.c, &m);
1538 typedcode_list_t*l = $3;
1541 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1546 $$.c = abc_construct($$.c, len);
1548 $$.c = abc_constructprop2($$.c, &m, len);
1552 /* TODO: use abc_call (for calling local variables),
1553 abc_callstatic (for calling own methods)
1556 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1557 typedcode_list_t*l = $3;
1559 code_t*paramcode = 0;
1561 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1567 if($$.c->opcode == OPCODE_COERCE_A) {
1568 $$.c = code_cutlast($$.c);
1572 multiname_t*name = 0;
1573 if($$.c->opcode == OPCODE_GETPROPERTY) {
1574 name = multiname_clone($$.c->data[0]);
1575 $$.c = code_cutlast($$.c);
1576 $$.c = code_append($$.c, paramcode);
1577 $$.c = abc_callproperty2($$.c, name, len);
1578 } else if($$.c->opcode == OPCODE_GETSLOT) {
1579 int slot = (int)(ptroff_t)$$.c->data[0];
1580 trait_t*t = abc_class_find_slotid(state->cls,slot);//FIXME
1581 if(t->kind!=TRAIT_METHOD) {
1582 //flash allows to assign closures to members.
1583 //syntaxerror("not a function");
1586 $$.c = code_cutlast($$.c);
1587 $$.c = code_append($$.c, paramcode);
1588 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1589 $$.c = abc_callproperty2($$.c, name, len);
1591 $$.c = abc_getlocal_0($$.c);
1592 $$.c = code_append($$.c, paramcode);
1593 $$.c = abc_call($$.c, len);
1598 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
1599 $$.t = $1.t->function->return_type;
1601 $$.c = abc_coerce_a($$.c);
1606 DELETE: "delete" E {
1608 if($$.c->opcode == OPCODE_COERCE_A) {
1609 $$.c = code_cutlast($$.c);
1611 multiname_t*name = 0;
1612 if($$.c->opcode == OPCODE_GETPROPERTY) {
1613 $$.c->opcode = OPCODE_DELETEPROPERTY;
1614 } else if($$.c->opcode == OPCODE_GETSLOT) {
1615 int slot = (int)(ptroff_t)$$.c->data[0];
1616 multiname_t*name = abc_class_find_slotid(state->cls,slot)->name;
1617 $$.c = code_cutlast($$.c);
1618 $$.c = abc_deleteproperty2($$.c, name);
1620 $$.c = abc_getlocal_0($$.c);
1621 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
1622 $$.c = abc_deleteproperty2($$.c, &m);
1624 $$.t = TYPE_BOOLEAN;
1627 RETURN: "return" %prec prec_none {
1628 $$ = abc_returnvoid(0);
1630 RETURN: "return" EXPRESSION {
1632 $$ = abc_returnvalue($$);
1635 // ----------------------- expression types -------------------------------------
1637 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
1638 EXPRESSION : E %prec below_minus {$$ = $1;}
1639 EXPRESSION : EXPRESSION ',' E %prec below_minus {
1641 $$.c = cut_last_push($$.c);
1642 $$.c = code_append($$.c,$3.c);
1645 VOIDEXPRESSION : EXPRESSION %prec below_minus {$$=cut_last_push($1.c);}
1647 // ----------------------- expression evaluation -------------------------------------
1650 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1652 E : DELETE {$$ = $1;}
1653 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1657 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1658 //MULTINAME(m, registry_getintclass());
1659 //$$.c = abc_coerce2($$.c, &m); // FIXME
1662 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1665 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1668 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1671 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1674 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
1677 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
1680 CONSTANT : "true" {$$.c = abc_pushtrue(0);
1681 $$.t = TYPE_BOOLEAN;
1683 CONSTANT : "false" {$$.c = abc_pushfalse(0);
1684 $$.t = TYPE_BOOLEAN;
1686 CONSTANT : "null" {$$.c = abc_pushnull(0);
1691 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1692 $$.t = TYPE_BOOLEAN;
1694 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1695 $$.t = TYPE_BOOLEAN;
1697 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1698 $$.t = TYPE_BOOLEAN;
1700 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1701 $$.t = TYPE_BOOLEAN;
1703 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1704 $$.t = TYPE_BOOLEAN;
1706 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1707 $$.t = TYPE_BOOLEAN;
1709 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
1710 $$.t = TYPE_BOOLEAN;
1712 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1713 $$.t = TYPE_BOOLEAN;
1716 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1718 $$.c = converttype($$.c, $1.t, $$.t);
1719 $$.c = abc_dup($$.c);
1720 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1721 $$.c = cut_last_push($$.c);
1722 $$.c = code_append($$.c,$3.c);
1723 $$.c = converttype($$.c, $3.t, $$.t);
1724 code_t*label = $$.c = abc_label($$.c);
1725 jmp->branch = label;
1728 $$.t = join_types($1.t, $3.t, 'A');
1729 /*printf("%08x:\n",$1.t);
1730 code_dump($1.c, 0, 0, "", stdout);
1731 printf("%08x:\n",$3.t);
1732 code_dump($3.c, 0, 0, "", stdout);
1733 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
1735 $$.c = converttype($$.c, $1.t, $$.t);
1736 $$.c = abc_dup($$.c);
1737 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1738 $$.c = cut_last_push($$.c);
1739 $$.c = code_append($$.c,$3.c);
1740 $$.c = converttype($$.c, $3.t, $$.t);
1741 code_t*label = $$.c = abc_label($$.c);
1742 jmp->branch = label;
1745 E : '!' E {$$.c=$2.c;
1746 $$.c = abc_not($$.c);
1747 $$.t = TYPE_BOOLEAN;
1750 E : '~' E {$$.c=$2.c;
1751 $$.c = abc_bitnot($$.c);
1755 E : E '&' E {$$.c = code_append($1.c,$3.c);
1756 $$.c = abc_bitand($$.c);
1760 E : E '^' E {$$.c = code_append($1.c,$3.c);
1761 $$.c = abc_bitxor($$.c);
1765 E : E '|' E {$$.c = code_append($1.c,$3.c);
1766 $$.c = abc_bitor($$.c);
1770 E : E '-' E {$$.c = code_append($1.c,$3.c);
1771 if(BOTH_INT($1,$3)) {
1772 $$.c = abc_subtract_i($$.c);
1775 $$.c = abc_subtract($$.c);
1779 E : E ">>" E {$$.c = code_append($1.c,$3.c);
1780 $$.c = abc_rshift($$.c);
1783 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
1784 $$.c = abc_urshift($$.c);
1787 E : E "<<" E {$$.c = code_append($1.c,$3.c);
1788 $$.c = abc_lshift($$.c);
1792 E : E '/' E {$$.c = code_append($1.c,$3.c);
1793 $$.c = abc_divide($$.c);
1796 E : E '+' E {$$.c = code_append($1.c,$3.c);
1797 $$.c = abc_add($$.c);
1800 E : E '%' E {$$.c = code_append($1.c,$3.c);
1801 $$.c = abc_modulo($$.c);
1804 E : E '*' E {$$.c = code_append($1.c,$3.c);
1805 if(BOTH_INT($1,$3)) {
1806 $$.c = abc_multiply_i($$.c);
1809 $$.c = abc_multiply($$.c);
1814 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
1815 if(use_astype && TYPE_IS_CLASS($3.t)) {
1816 MULTINAME(m,$3.t->cls);
1817 $$.c = abc_astype2($1.c, &m);
1820 $$.c = code_append($1.c, $3.c);
1821 $$.c = abc_astypelate($$.c);
1826 E : E "is" E {$$.c = code_append($1.c, $3.c);
1827 $$.c = abc_istypelate($$.c);
1828 $$.t = TYPE_BOOLEAN;
1831 E : "typeof" '(' E ')' {
1833 $$.c = abc_typeof($$.c);
1838 $$.c = cut_last_push($2.c);
1839 $$.c = abc_pushundefined($$.c);
1843 E : "void" { $$.c = abc_pushundefined(0);
1847 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
1852 $$.c=abc_negate_i($$.c);
1855 $$.c=abc_negate($$.c);
1862 $$.c = code_append($$.c, $3.c);
1864 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
1865 $$.c = abc_getproperty2($$.c, &m);
1866 $$.t = 0; // array elements have unknown type
1871 if(BOTH_INT($1,$3)) {
1872 c=abc_multiply_i(c);
1876 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
1877 $$.c = toreadwrite($1.c, c, 0, 0);
1882 code_t*c = abc_modulo($3.c);
1883 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
1884 $$.c = toreadwrite($1.c, c, 0, 0);
1888 code_t*c = abc_lshift($3.c);
1889 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
1890 $$.c = toreadwrite($1.c, c, 0, 0);
1894 code_t*c = abc_rshift($3.c);
1895 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
1896 $$.c = toreadwrite($1.c, c, 0, 0);
1900 code_t*c = abc_urshift($3.c);
1901 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
1902 $$.c = toreadwrite($1.c, c, 0, 0);
1906 code_t*c = abc_divide($3.c);
1907 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
1908 $$.c = toreadwrite($1.c, c, 0, 0);
1913 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1918 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
1920 $$.c = toreadwrite($1.c, c, 0, 0);
1923 E : E "-=" E { code_t*c = $3.c;
1924 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1925 c=abc_subtract_i(c);
1929 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
1931 $$.c = toreadwrite($1.c, c, 0, 0);
1934 E : E '=' E { code_t*c = 0;
1935 c = code_append(c, $3.c);
1936 c = converttype(c, $3.t, $1.t);
1937 $$.c = toreadwrite($1.c, c, 1, 0);
1941 E : E '?' E ':' E %prec below_assignment {
1943 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
1944 $$.c = code_append($$.c, $3.c);
1945 code_t*j2 = $$.c = abc_jump($$.c, 0);
1946 $$.c = j1->branch = abc_label($$.c);
1947 $$.c = code_append($$.c, $5.c);
1948 $$.c = j2->branch = abc_label($$.c);
1949 $$.t = join_types($3.t,$5.t,'?');
1952 // TODO: use inclocal where appropriate
1953 E : E "++" { code_t*c = 0;
1954 classinfo_t*type = $1.t;
1955 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1956 c=abc_increment_i(c);
1962 c=converttype(c, type, $1.t);
1963 $$.c = toreadwrite($1.c, c, 0, 1);
1966 E : E "--" { code_t*c = 0;
1967 classinfo_t*type = $1.t;
1968 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1969 c=abc_decrement_i(c);
1975 c=converttype(c, type, $1.t);
1976 $$.c = toreadwrite($1.c, c, 0, 1);
1980 E : "++" %prec plusplus_prefix E { code_t*c = 0;
1981 classinfo_t*type = $2.t;
1982 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1983 c=abc_increment_i(c);
1989 c=converttype(c, type, $2.t);
1990 $$.c = toreadwrite($2.c, c, 0, 0);
1994 E : "--" %prec minusminus_prefix E { code_t*c = 0;
1995 classinfo_t*type = $2.t;
1996 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1997 c=abc_decrement_i(c);
2003 c=converttype(c, type, $2.t);
2004 $$.c = toreadwrite($2.c, c, 0, 0);
2008 E : E '.' T_IDENTIFIER
2010 classinfo_t*t = $1.t;
2012 if(TYPE_IS_CLASS(t)) {
2013 memberinfo_t*m = registry_findmember($1.t, "prototype");
2014 if(!m) syntaxerror("identifier '%s' not found in anonymous class", $3);
2019 memberinfo_t*f = registry_findmember(t, $3);
2021 if(f && !is_static != !(f->flags&FLAG_STATIC))
2024 if(f && f->slot && !noslot) {
2025 $$.c = abc_getslot($$.c, f->slot);
2028 namespace_t ns = {flags2access(f->flags), ""}; // needs to be "", not $$.t->package (!)
2029 multiname_t m = {QNAME, &ns, 0, $3};
2030 $$.c = abc_getproperty2($$.c, &m);
2032 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2033 $$.c = abc_getproperty2($$.c, &m);
2036 /* determine type */
2038 if(f->kind == MEMBER_METHOD) {
2039 $$.t = TYPE_FUNCTION(f);
2044 $$.c = abc_coerce_a($$.c);
2045 $$.t = registry_getanytype();
2048 /* when resolving a property on an unknown type, we do know the
2049 name of the property (and don't seem to need the package), but
2050 we do need to make avm2 try out all access modes */
2051 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2052 $$.c = abc_getproperty2($$.c, &m);
2053 $$.c = abc_coerce_a($$.c);
2054 $$.t = registry_getanytype();
2058 VAR_READ : T_IDENTIFIER {
2065 /* look at variables */
2066 if((i = find_variable($1, &$$.t)) >= 0) {
2067 // $1 is a local variable
2068 $$.c = abc_getlocal($$.c, i);
2070 /* look at current class' members */
2071 } else if((f = registry_findmember(state->clsinfo, $1))) {
2072 // $1 is a function in this class
2073 int var_is_static = (f->flags&FLAG_STATIC);
2074 int i_am_static = (state->minfo?(state->minfo->flags&FLAG_STATIC):FLAG_STATIC);
2075 if(var_is_static != i_am_static) {
2076 /* there doesn't seem to be any "static" way to access
2077 static properties of a class */
2078 state->late_binding = 1;
2080 namespace_t ns = {flags2access(f->flags), ""};
2081 multiname_t m = {QNAME, &ns, 0, $1};
2082 $$.c = abc_findpropstrict2($$.c, &m);
2083 $$.c = abc_getproperty2($$.c, &m);
2086 $$.c = abc_getlocal_0($$.c);
2087 $$.c = abc_getslot($$.c, f->slot);
2089 namespace_t ns = {flags2access(f->flags), ""};
2090 multiname_t m = {QNAME, &ns, 0, $1};
2091 $$.c = abc_getlocal_0($$.c);
2092 $$.c = abc_getproperty2($$.c, &m);
2095 if(f->kind == MEMBER_METHOD) {
2096 $$.t = TYPE_FUNCTION(f);
2101 /* look at classes in the current package and imported classes */
2102 } else if((a = find_class($1))) {
2104 $$.c = abc_getglobalscope($$.c);
2105 $$.c = abc_getslot($$.c, a->slot);
2108 $$.c = abc_getlex2($$.c, &m);
2110 $$.t = TYPE_CLASS(a);
2112 /* unknown object, let the avm2 resolve it */
2114 if(strcmp($1,"trace"))
2115 warning("Couldn't resolve '%s', doing late binding", $1);
2116 state->late_binding = 1;
2118 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
2121 $$.c = abc_findpropstrict2($$.c, &m);
2122 $$.c = abc_getproperty2($$.c, &m);
2127 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
2128 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
2129 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
2131 // ----------------- namespaces -------------------------------------------------
2133 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
2134 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
2135 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}
2137 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER