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_INSTANCEOF "instanceof"
91 %token<token> KW_IMPORT "import"
92 %token<token> KW_RETURN "return"
93 %token<token> KW_TYPEOF "typeof"
94 %token<token> KW_INTERFACE "interface"
95 %token<token> KW_NULL "null"
96 %token<token> KW_VAR "var"
97 %token<token> KW_DYNAMIC
98 %token<token> KW_OVERRIDE
99 %token<token> KW_FINAL
100 %token<token> KW_GET "get"
101 %token<token> KW_SUPER "super"
102 %token<token> KW_EXTENDS
103 %token<token> KW_FALSE "false"
104 %token<token> KW_TRUE "true"
105 %token<token> KW_BOOLEAN "Boolean"
106 %token<token> KW_UINT "uint"
107 %token<token> KW_INT "int"
108 %token<token> KW_WHILE "while"
109 %token<token> KW_NUMBER "Number"
110 %token<token> KW_STRING "String"
111 %token<token> KW_DELETE "delete"
112 %token<token> KW_IF "if"
113 %token<token> KW_ELSE "else"
114 %token<token> KW_BREAK "break"
115 %token<token> KW_IS "is"
116 %token<token> KW_AS "as"
118 %token<token> T_EQEQ "=="
119 %token<token> T_EQEQEQ "==="
120 %token<token> T_NE "!="
121 %token<token> T_NEE "!=="
122 %token<token> T_LE "<="
123 %token<token> T_GE ">="
124 %token<token> T_DIVBY "/="
125 %token<token> T_MODBY "%="
126 %token<token> T_MULBY "*="
127 %token<token> T_PLUSBY "+="
128 %token<token> T_MINUSBY "-="
129 %token<token> T_SHRBY ">>="
130 %token<token> T_SHLBY "<<="
131 %token<token> T_USHRBY ">>>="
132 %token<token> T_OROR "||"
133 %token<token> T_ANDAND "&&"
134 %token<token> T_COLONCOLON "::"
135 %token<token> T_MINUSMINUS "--"
136 %token<token> T_PLUSPLUS "++"
137 %token<token> T_DOTDOT ".."
138 %token<token> T_DOTDOTDOT "..."
139 %token<token> T_SHL "<<"
140 %token<token> T_USHR ">>>"
141 %token<token> T_SHR ">>"
143 %type <id> X_IDENTIFIER PACKAGE
144 %type <token> VARCONST
146 %type <code> CODEPIECE
147 %type <code> CODEBLOCK MAYBECODE
148 %type <token> PACKAGE_DECLARATION
149 %type <token> FUNCTION_DECLARATION
150 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST
151 %type <token> CLASS_DECLARATION
152 %type <token> NAMESPACE_DECLARATION
153 %type <token> INTERFACE_DECLARATION
154 %type <code> VOIDEXPRESSION
155 %type <value> EXPRESSION NONCOMMAEXPRESSION
156 %type <value> MAYBEEXPRESSION
157 %type <value> E DELETE
158 %type <value> CONSTANT
159 %type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
160 %type <token> USE_NAMESPACE
161 %type <code> FOR_INIT
163 %type <classinfo> MAYBETYPE
166 %type <params> PARAM_LIST
167 %type <params> MAYBE_PARAM_LIST
168 %type <flags> MAYBE_MODIFIERS
169 %type <flags> MODIFIER_LIST
170 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
171 %type <classinfo_list> IMPLEMENTS_LIST
172 %type <classinfo> EXTENDS
173 %type <classinfo_list> EXTENDS_LIST
174 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
175 %type <classinfo_list> QNAME_LIST
176 %type <classinfo> TYPE
178 //%type <token> VARIABLE
179 %type <value> VAR_READ
181 //%type <token> T_IDENTIFIER
182 %type <token> MODIFIER
183 %type <value> FUNCTIONCALL
184 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
186 // precedence: from low to high
190 %left below_semicolon
193 %nonassoc below_assignment // for ?:, contrary to spec
194 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
201 %nonassoc "==" "!=" "===" "!=="
203 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
204 %left "<<" ">>" ">>>"
208 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
210 %left '[' ']' '{' "new" '.' ".." "::"
211 %nonassoc T_IDENTIFIER
216 // needed for "return" precedence:
217 %nonassoc T_STRING T_REGEXP
218 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
219 %nonassoc "false" "true" "null" "undefined" "super"
224 static int yyerror(char*s)
226 syntaxerror("%s", s);
228 static char* concat3str(const char* t1, const char* t2, const char* t3)
233 char*text = malloc(l1+l2+l3+1);
234 memcpy(text , t1, l1);
235 memcpy(text+l1, t2, l2);
236 memcpy(text+l1+l2, t3, l3);
241 typedef struct _import {
245 DECLARE_LIST(import);
247 typedef struct _classstate {
255 typedef struct _methodstate {
259 /* code that needs to be executed at the start of
260 a method (like initializing local registers) */
266 typedef struct _state {
270 import_list_t*wildcard_imports;
272 char has_own_imports;
275 methodstate_t*method;
280 typedef struct _global {
287 static global_t*global = 0;
288 static state_t* state = 0;
292 #define MULTINAME(m,x) \
295 registry_fill_multiname(&m, &m##_ns, x);
297 #define MEMBER_MULTINAME(m,f,n) \
301 m##_ns.access = flags2access(f->flags); \
305 m.namespace_set = 0; \
308 m.type = MULTINAME; \
310 m.namespace_set = &nopackage_namespace_set; \
314 /* warning: list length of namespace set is undefined */
315 #define MULTINAME_LATE(m, access, package) \
316 namespace_t m##_ns = {access, package}; \
317 namespace_set_t m##_nsset; \
318 namespace_list_t m##_l;m##_l.next = 0; \
319 m##_nsset.namespaces = &m##_l; \
320 m##_nsset = m##_nsset; \
321 m##_l.namespace = &m##_ns; \
322 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
324 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
325 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
326 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
327 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
328 static namespace_list_t nl4 = {&ns4,0};
329 static namespace_list_t nl3 = {&ns3,&nl4};
330 static namespace_list_t nl2 = {&ns2,&nl3};
331 static namespace_list_t nl1 = {&ns1,&nl2};
332 static namespace_set_t nopackage_namespace_set = {&nl1};
334 static state_list_t*state_stack=0;
336 static void init_globals()
338 global = rfx_calloc(sizeof(global_t));
341 static void new_state()
344 NEW(state_list_t, sl);
346 state_t*oldstate = state;
348 memcpy(s, state, sizeof(state_t)); //shallow copy
349 sl->next = state_stack;
352 s->imports = dict_new();
357 state->has_own_imports = 0;
358 state->vars = dict_new();
360 static void state_has_imports()
362 state->wildcard_imports = list_clone(state->wildcard_imports);
363 state->imports = dict_clone(state->imports);
364 state->has_own_imports = 1;
367 static void old_state()
369 if(!state_stack || !state_stack->next)
370 syntaxerror("invalid nesting");
371 state_t*oldstate = state;
372 state_list_t*old = state_stack;
373 state_stack = state_stack->next;
375 state = state_stack->state;
376 /*if(state->method->initcode) {
377 printf("residual initcode\n");
378 code_dump(state->method->initcode, 0, 0, "", stdout);
380 if(oldstate->has_own_imports) {
381 list_free(oldstate->wildcard_imports);
382 dict_destroy(oldstate->imports);oldstate->imports=0;
385 void initialize_state()
390 global->file = abc_file_new();
391 global->file->flags &= ~ABCFILE_LAZY;
393 global->init = abc_initscript(global->file, 0);
394 code_t*c = global->init->method->body->code;
396 c = abc_getlocal_0(c);
397 c = abc_pushscope(c);
399 /* findpropstrict doesn't just return a scope object- it
400 also makes it "active" somehow. Push local_0 on the
401 scope stack and read it back with findpropstrict, it'll
402 contain properties like "trace". Trying to find the same
403 property on a "vanilla" local_0 yields only a "undefined" */
404 //c = abc_findpropstrict(c, "[package]::trace");
406 /*c = abc_getlocal_0(c);
407 c = abc_findpropstrict(c, "[package]::trace");
409 c = abc_setlocal_1(c);
411 c = abc_pushbyte(c, 0);
412 c = abc_setlocal_2(c);
414 code_t*xx = c = abc_label(c);
415 c = abc_findpropstrict(c, "[package]::trace");
416 c = abc_pushstring(c, "prop:");
417 c = abc_hasnext2(c, 1, 2);
419 c = abc_setlocal_3(c);
420 c = abc_callpropvoid(c, "[package]::trace", 2);
421 c = abc_getlocal_3(c);
423 c = abc_iftrue(c,xx);*/
425 c = abc_findpropstrict(c, "[package]::trace");
426 c = abc_pushstring(c, "[entering global init function]");
427 c = abc_callpropvoid(c, "[package]::trace", 1);
429 global->init->method->body->code = c;
431 void* finalize_state()
433 if(state->level!=1) {
434 syntaxerror("unexpected end of file");
436 abc_method_body_t*m = global->init->method->body;
439 __ findpropstrict(m, "[package]::trace");
440 __ pushstring(m, "[leaving global init function]");
441 __ callpropvoid(m, "[package]::trace", 1);
447 static void startpackage(char*name)
450 syntaxerror("Packages can not be nested.");
453 /*printf("entering package \"%s\"\n", name);*/
454 state->package = name;
456 static void endpackage()
458 /*printf("leaving package \"%s\"\n", state->package);*/
463 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
466 syntaxerror("inner classes now allowed");
469 state->cls = rfx_calloc(sizeof(classstate_t));
472 classinfo_list_t*mlist=0;
473 /*printf("entering class %s\n", name);
474 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n");
476 printf(" extends: %s.%s\n", extends->package, extends->name);
477 printf(" implements (%d): ", list_length(implements));
478 for(mlist=implements;mlist;mlist=mlist->next) {
479 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
484 if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL))
485 syntaxerror("invalid modifier(s)");
487 if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
488 syntaxerror("public and internal not supported at the same time.");
490 /* create the class name, together with the proper attributes */
494 if(!(flags&FLAG_PUBLIC) && !state->package) {
495 access = ACCESS_PRIVATE; package = current_filename;
496 } else if(!(flags&FLAG_PUBLIC) && state->package) {
497 access = ACCESS_PACKAGEINTERNAL; package = state->package;
498 } else if(state->package) {
499 access = ACCESS_PACKAGE; package = state->package;
501 syntaxerror("public classes only allowed inside a package");
504 if(registry_findclass(package, classname)) {
505 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
509 /* build info struct */
510 int num_interfaces = (list_length(implements));
511 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
512 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
514 classinfo_list_t*l = implements;
515 for(l=implements;l;l=l->next) {
516 state->cls->info->interfaces[pos++] = l->classinfo;
519 multiname_t*extends2 = sig2mname(extends);
521 MULTINAME(classname2,state->cls->info);
524 state->cls_init = abc_getlocal_0(state->cls_init);
525 state->cls_init = abc_constructsuper(state->cls_init, 0);
528 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
529 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
530 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
531 if(interface) abc_class_interface(state->cls->abc);
532 abc_class_protectedNS(state->cls->abc, classname);
534 for(mlist=implements;mlist;mlist=mlist->next) {
535 MULTINAME(m, mlist->classinfo);
536 abc_class_add_interface(state->cls->abc, &m);
539 /* now write the construction code for this class */
540 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
542 abc_method_body_t*m = global->init->method->body;
543 __ getglobalscope(m);
544 classinfo_t*s = extends;
549 //TODO: take a look at the current scope stack, maybe
550 // we can re-use something
555 multiname_t*s2 = sig2mname(s);
557 multiname_destroy(s2);
559 __ pushscope(m); count++;
560 m->code = m->code->prev->prev; // invert
562 /* continue appending after last op end */
563 while(m->code && m->code->next) m->code = m->code->next;
565 /* TODO: if this is one of *our* classes, we can also
566 do a getglobalscope/getslot <nr> (which references
567 the init function's slots) */
569 __ getlex2(m, extends2);
571 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
572 stack is not the superclass */
573 __ pushscope(m);count++;
576 /* notice: we get a verify error #1107 if the top element on the scope
577 stack is not the global object */
579 __ pushscope(m);count++;
581 __ newclass(m,state->cls->abc);
585 __ setslot(m, slotindex);
587 /* flash.display.MovieClip handling */
588 if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
589 if(state->package && state->package[0]) {
590 globalclass = concat3str(state->package, ".", classname);
592 globalclass = strdup(classname);
595 multiname_destroy(extends2);
598 static void endclass()
600 if(state->cls->init) {
601 if(!state->cls->abc->constructor) {
602 abc_method_t*m = abc_class_constructor(state->cls->abc, 0);
603 m->body->code = code_append(m->body->code, state->cls->init);
604 m->body->code = abc_returnvoid(m->body->code);
606 code_t*c = state->cls->abc->constructor->body->code;
607 c = code_append(state->cls->init, c);
608 state->cls->abc->constructor->body->code = c;
612 if(state->cls->static_init) {
613 if(!state->cls->abc->static_constructor) {
614 abc_method_t*m = abc_class_staticconstructor(state->cls->abc, 0);
615 m->body->code = code_append(m->body->code, state->cls->static_init);
616 m->body->code = abc_returnvoid(m->body->code);
618 state->cls->abc->static_constructor->body->code =
619 code_append(state->cls->static_init, state->cls->abc->static_constructor->body->code);
626 typedef struct _variable {
631 static int find_variable(char*name, classinfo_t**m)
633 state_list_t* s = state_stack;
637 v = dict_lookup(s->state->vars, name);
648 static int find_variable_safe(char*name, classinfo_t**m)
650 int i = find_variable(name, m);
652 syntaxerror("undefined variable: %s", name);
655 static char variable_exists(char*name)
657 return dict_lookup(state->vars, name)!=0;
659 static int new_variable(char*name, classinfo_t*type)
662 v->index = global->variable_count;
664 dict_put(state->vars, name, v);
665 return global->variable_count++;
667 #define TEMPVARNAME "__as3_temp__"
668 static int gettempvar()
670 int i = find_variable(TEMPVARNAME, 0);
672 i = new_variable(TEMPVARNAME, 0);
677 code_t* killvars(code_t*c)
680 for(t=0;t<state->vars->hashsize;t++) {
681 dictentry_t*e =state->vars->slots[t];
683 variable_t*v = (variable_t*)e->data;
684 //do this always, otherwise register types don't match
685 //in the verifier when doing nested loops
686 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
687 c = abc_kill(c, v->index);
695 static void check_constant_against_type(classinfo_t*t, constant_t*c)
697 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
698 if(TYPE_IS_NUMBER(t)) {
699 xassert(c->type == CONSTANT_FLOAT
700 || c->type == CONSTANT_INT
701 || c->type == CONSTANT_UINT);
702 } else if(TYPE_IS_UINT(t)) {
703 xassert(c->type == CONSTANT_UINT ||
704 (c->type == CONSTANT_INT && c->i>0));
705 } else if(TYPE_IS_INT(t)) {
706 xassert(c->type == CONSTANT_INT);
707 } else if(TYPE_IS_BOOLEAN(t)) {
708 xassert(c->type == CONSTANT_TRUE
709 || c->type == CONSTANT_FALSE);
713 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
715 memberinfo_t*minfo = 0;
716 if(getset != KW_GET && getset != KW_SET) {
717 if(registry_findmember(state->cls->info, name)) {
718 syntaxerror("class already contains a member/method called '%s'", name);
720 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
721 minfo->return_type = return_type;
722 // getslot on a member slot only returns "undefined", so no need
723 // to actually store these
724 //state->minfo->slot = state->method->abc->method->trait->slot_id;
726 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
730 else if(params->list)
731 type = params->list->param->type;
732 if((minfo=registry_findmember(state->cls->info, name))) {
733 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
734 syntaxerror("class already contains a member or method called '%s'", name);
736 syntaxerror("getter/setter for '%s' already defined", name);
737 /* make a setter or getter into a getset */
742 if(type && minfo->type != type)
743 syntaxerror("different type in getter and setter");
745 minfo = memberinfo_register(state->cls->info, name, gs);
748 /* can't assign a slot as getter and setter might have different slots */
749 //minfo->slot = slot;
751 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
752 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
753 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
754 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
755 if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
759 static int flags2access(int flags)
762 if(flags&FLAG_PUBLIC) {
763 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
764 access = ACCESS_PACKAGE;
765 } else if(flags&FLAG_PRIVATE) {
766 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
767 access = ACCESS_PRIVATE;
768 } else if(flags&FLAG_PROTECTED) {
769 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
770 access = ACCESS_PROTECTED;
772 access = ACCESS_PACKAGEINTERNAL;
777 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
778 params_t*params, classinfo_t*return_type)
781 syntaxerror("not able to start another method scope");
784 state->method = rfx_calloc(sizeof(methodstate_t));
785 state->method->initcode = 0;
786 state->method->is_constructor = !strcmp(state->cls->info->name,name);
787 state->method->has_super = 0;
789 global->variable_count = 0;
791 /* state->vars is initialized by state_new */
792 if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info)!=0) syntaxerror("Internal error");
794 for(p=params->list;p;p=p->next) {
795 new_variable(p->param->name, p->param->type);
797 if(state->method->is_constructor)
798 name = "__as3_constructor__";
799 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
802 static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
803 params_t*params, classinfo_t*return_type, code_t*body)
805 namespace_t mname_ns = {flags2access(flags), ""};
806 multiname_t mname = {QNAME, &mname_ns, 0, name};
810 multiname_t*type2 = sig2mname(return_type);
812 if(state->method->is_constructor) {
813 f = abc_class_constructor(state->cls->abc, type2);
815 if(flags&FLAG_STATIC)
816 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
818 f = abc_class_method(state->cls->abc, type2, &mname);
819 slot = f->trait->slot_id;
821 //flash doesn't seem to allow us to access function slots
822 //state->method->info->slot = slot;
824 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
825 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
826 if(params->varargs) f->flags |= METHOD_NEED_REST;
830 for(p=params->list;p;p=p->next) {
831 if(params->varargs && !p->next) {
832 break; //varargs: omit last parameter in function signature
834 multiname_t*m = sig2mname(p->param->type);
835 list_append(f->parameters, m);
836 if(p->param->value) {
837 check_constant_against_type(p->param->type, p->param->value);
838 opt=1;list_append(f->optional_parameters, p->param->value);
840 syntaxerror("non-optional parameter not allowed after optional parameters");
843 f->body->code = body;
850 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
855 void breakjumpsto(code_t*c, code_t*jump)
860 if(c->opcode == OPCODE___BREAK__) {
861 c->opcode = OPCODE_JUMP;
868 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
871 return registry_getanytype();
872 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
873 return registry_getanytype();
876 return registry_getanytype();
878 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
883 return abc_coerce_a(c);
887 // cast an "any" type to a specific type. subject to
888 // runtime exceptions
889 return abc_coerce2(c, &m);
892 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
893 return abc_coerce2(c, &m);
895 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
896 return abc_coerce2(c, &m);
898 /* these are subject to overflow */
899 if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) {
900 return abc_coerce2(c, &m);
902 if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) {
903 return abc_coerce2(c, &m);
906 classinfo_t*supertype = from;
908 if(supertype == to) {
909 // target type is one of from's superclasses
910 return abc_coerce2(c, &m);
913 while(supertype->interfaces[t]) {
914 if(supertype->interfaces[t]==to) {
915 // to type is one of from's interfaces
916 return abc_coerce2(c, &m);
920 supertype = supertype->superclass;
922 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
924 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
926 syntaxerror("can't convert type %s to %s", from->name, to->name);
929 code_t*defaultvalue(code_t*c, classinfo_t*type)
931 if(TYPE_IS_INT(type)) {
932 c = abc_pushbyte(c, 0);
933 } else if(TYPE_IS_UINT(type)) {
934 c = abc_pushuint(c, 0);
935 } else if(TYPE_IS_FLOAT(type)) {
937 } else if(TYPE_IS_BOOLEAN(type)) {
938 c = abc_pushfalse(c);
945 char is_pushundefined(code_t*c)
947 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
950 void parserassert(int b)
952 if(!b) syntaxerror("internal error: assertion failed");
955 static classinfo_t* find_class(char*name)
959 c = registry_findclass(state->package, name);
961 /* try explicit imports */
962 dictentry_t* e = dict_get_slot(state->imports, name);
966 if(!strcmp(e->key, name)) {
967 c = (classinfo_t*)e->data;
972 /* try package.* imports */
973 import_list_t*l = state->wildcard_imports;
977 //printf("does package %s contain a class %s?\n", l->import->package, name);
978 c = registry_findclass(l->import->package, name);
982 /* try global package */
984 c = registry_findclass("", name);
989 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
993 [prefix code] [read instruction]
997 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1000 if(in && in->opcode == OPCODE_COERCE_A) {
1001 in = code_cutlast(in);
1004 syntaxerror("internal error");
1006 /* chop off read instruction */
1010 prefix = r->prev;r->prev = 0;
1016 char use_temp_var = readbefore;
1018 /* generate the write instruction, and maybe append a dup to the prefix code */
1019 code_t* write = abc_nop(0);
1020 if(r->opcode == OPCODE_GETPROPERTY) {
1021 write->opcode = OPCODE_SETPROPERTY;
1022 multiname_t*m = (multiname_t*)r->data[0];
1023 write->data[0] = multiname_clone(m);
1024 if(m->type == QNAME || m->type == MULTINAME) {
1026 prefix = abc_dup(prefix); // we need the object, too
1029 } else if(m->type == MULTINAMEL) {
1031 /* dupping two values on the stack requires 5 operations and one register-
1032 couldn't adobe just have given us a dup2? */
1033 int temp = gettempvar();
1034 prefix = abc_setlocal(prefix, temp);
1035 prefix = abc_dup(prefix);
1036 prefix = abc_getlocal(prefix, temp);
1037 prefix = abc_swap(prefix);
1038 prefix = abc_getlocal(prefix, temp);
1042 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1044 } else if(r->opcode == OPCODE_GETSLOT) {
1045 write->opcode = OPCODE_SETSLOT;
1046 write->data[0] = r->data[0];
1048 prefix = abc_dup(prefix); // we need the object, too
1051 } else if(r->opcode == OPCODE_GETLOCAL) {
1052 write->opcode = OPCODE_SETLOCAL;
1053 write->data[0] = r->data[0];
1054 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1055 write->opcode = OPCODE_SETLOCAL_0;
1056 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1057 write->opcode = OPCODE_SETLOCAL_1;
1058 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1059 write->opcode = OPCODE_SETLOCAL_2;
1060 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1061 write->opcode = OPCODE_SETLOCAL_3;
1063 code_dump(r, 0, 0, "", stdout);
1064 syntaxerror("illegal lvalue: can't assign a value to this expression");
1071 /* with getproperty/getslot, we have to be extra careful not
1072 to execute the read code twice, as it might have side-effects
1073 (e.g. if the property is in fact a setter/getter combination)
1075 So read the value, modify it, and write it again,
1076 using prefix only once and making sure (by using a temporary
1077 register) that the return value is what we just wrote */
1078 temp = gettempvar();
1079 c = code_append(c, prefix);
1080 c = code_append(c, r);
1083 c = abc_setlocal(c, temp);
1085 c = code_append(c, middlepart);
1088 c = abc_setlocal(c, temp);
1090 c = code_append(c, write);
1091 c = abc_getlocal(c, temp);
1092 c = abc_kill(c, temp);
1094 /* if we're allowed to execute the read code twice *and*
1095 the middlepart doesn't modify the code, things are easier.
1097 code_t* r2 = code_dup(r);
1098 //c = code_append(c, prefix);
1099 parserassert(!prefix);
1100 c = code_append(c, r);
1101 c = code_append(c, middlepart);
1102 c = code_append(c, write);
1103 c = code_append(c, r2);
1106 /* even smaller version: overwrite the value without reading
1110 c = code_append(c, prefix);
1113 c = code_append(c, middlepart);
1114 c = code_append(c, write);
1115 c = code_append(c, r);
1117 temp = gettempvar();
1119 c = code_append(c, prefix);
1122 c = code_append(c, middlepart);
1124 c = abc_setlocal(c, temp);
1125 c = code_append(c, write);
1126 c = abc_getlocal(c, temp);
1133 #define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
1134 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1141 /* ------------ code blocks / statements ---------------- */
1145 MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/}
1146 MAYBECODE: {$$=code_new();}
1148 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1149 CODE: CODEPIECE {$$=$1;}
1151 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
1152 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
1153 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
1154 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
1155 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
1156 CODEPIECE: ';' {$$=code_new();}
1157 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
1158 CODEPIECE: VOIDEXPRESSION {$$=$1}
1159 CODEPIECE: FOR {$$=$1}
1160 CODEPIECE: WHILE {$$=$1}
1161 CODEPIECE: BREAK {$$=$1}
1162 CODEPIECE: RETURN {$$=$1}
1163 CODEPIECE: IF {$$=$1}
1164 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
1165 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
1167 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
1168 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1169 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1171 /* ------------ variables --------------------------- */
1173 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1174 | {$$.c=abc_pushundefined(0);
1178 VAR : "const" | "var"
1179 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
1181 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1182 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1184 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1186 if(variable_exists($2))
1187 syntaxerror("Variable %s already defined", $2);
1189 if(!is_subtype_of($4.t, $3)) {
1190 syntaxerror("Can't convert %s to %s", $4.t->name,
1194 int index = new_variable($2, $3);
1197 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1199 $$ = converttype($$, $4.t, $3);
1200 $$ = abc_setlocal($$, index);
1202 $$ = defaultvalue(0, $3);
1203 $$ = abc_setlocal($$, index);
1206 /* if this is a typed variable:
1207 push default value for type on stack */
1209 state->method->initcode = defaultvalue(state->method->initcode, $3);
1210 state->method->initcode = abc_setlocal(state->method->initcode, index);
1213 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1215 $$ = abc_coerce_a($$);
1216 $$ = abc_setlocal($$, index);
1222 /* that's the default for a local register, anyway
1224 state->method->initcode = abc_pushundefined(state->method->initcode);
1225 state->method->initcode = abc_setlocal(state->method->initcode, index);
1227 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1230 /* ------------ control flow ------------------------- */
1232 MAYBEELSE: %prec below_else {$$ = code_new();}
1233 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1234 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1236 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1238 $$ = code_append($$, $4.c);
1239 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1241 $$ = code_append($$, $6);
1243 myjmp = $$ = abc_jump($$, 0);
1245 myif->branch = $$ = abc_label($$);
1247 $$ = code_append($$, $7);
1248 // might use a nop here too, depending on whether
1249 // the code $7 reaches the end or not
1250 myjmp->branch = $$ = abc_label($$);
1253 $$ = killvars($$);old_state();
1256 FOR_INIT : {$$=code_new();}
1257 FOR_INIT : VARIABLE_DECLARATION
1258 FOR_INIT : VOIDEXPRESSION
1260 FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1262 $$ = code_append($$, $4);
1263 code_t*loopstart = $$ = abc_nop($$);
1264 $$ = code_append($$, $6.c);
1265 code_t*myif = $$ = abc_iffalse($$, 0);
1266 $$ = code_append($$, $10);
1267 $$ = code_append($$, $8);
1268 $$ = abc_jump($$, loopstart);
1269 code_t*out = $$ = abc_label($$);
1270 breakjumpsto($$, out);
1273 $$ = killvars($$);old_state();
1276 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1279 code_t*myjmp = $$ = abc_jump($$, 0);
1280 code_t*loopstart = $$ = abc_label($$);
1281 $$ = code_append($$, $6);
1282 myjmp->branch = $$ = abc_nop($$);
1283 $$ = code_append($$, $4.c);
1284 $$ = abc_iftrue($$, loopstart);
1285 code_t*out = $$ = abc_nop($$);
1286 breakjumpsto($$, out);
1288 $$ = killvars($$);old_state();
1292 $$ = abc___break__(0);
1295 /* ------------ packages and imports ---------------- */
1297 X_IDENTIFIER: T_IDENTIFIER
1298 | "package" {$$="package";}
1300 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3str($1,".",$3);}
1301 PACKAGE: X_IDENTIFIER {$$=$1;}
1303 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1304 PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()}
1306 IMPORT : "import" QNAME {
1309 syntaxerror("Couldn't import class\n");
1310 state_has_imports();
1311 dict_put(state->imports, c->name, c);
1314 IMPORT : "import" PACKAGE '.' '*' {
1317 state_has_imports();
1318 list_append(state->wildcard_imports, i);
1322 /* ------------ classes and interfaces (header) -------------- */
1324 MAYBE_MODIFIERS : {$$=0;}
1325 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
1326 MODIFIER_LIST : MODIFIER {$$=$1;}
1327 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
1329 MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
1330 | KW_PRIVATE {$$=FLAG_PRIVATE;}
1331 | KW_PROTECTED {$$=FLAG_PROTECTED;}
1332 | KW_STATIC {$$=FLAG_STATIC;}
1333 | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
1334 | KW_FINAL {$$=FLAG_FINAL;}
1335 | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
1336 | KW_NATIVE {$$=FLAG_NATIVE;}
1337 | KW_INTERNAL {$$=FLAG_INTERNAL;}
1339 EXTENDS : {$$=registry_getobjectclass();}
1340 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1342 EXTENDS_LIST : {$$=list_new();}
1343 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1345 IMPLEMENTS_LIST : {$$=list_new();}
1346 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1348 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
1349 EXTENDS IMPLEMENTS_LIST
1350 '{' {startclass($1,$3,$4,$5, 0);}
1351 MAYBE_DECLARATION_LIST
1354 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
1356 '{' {startclass($1,$3,0,$4,1);}
1357 MAYBE_IDECLARATION_LIST
1360 /* ------------ classes and interfaces (body) -------------- */
1362 MAYBE_DECLARATION_LIST :
1363 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1364 DECLARATION_LIST : DECLARATION
1365 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1367 DECLARATION : SLOT_DECLARATION
1368 DECLARATION : FUNCTION_DECLARATION
1370 MAYBE_IDECLARATION_LIST :
1371 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1372 IDECLARATION_LIST : IDECLARATION
1373 IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
1375 IDECLARATION : "var" T_IDENTIFIER {
1376 syntaxerror("variable declarations not allowed in interfaces");
1378 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
1380 if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
1381 syntaxerror("invalid method modifiers: interface methods always need to be public");
1383 startfunction(0,$1,$3,$4,&$6,$8);
1384 endfunction(0,$1,$3,$4,&$6,$8, 0);
1387 /* ------------ classes and interfaces (body, slots ) ------- */
1389 VARCONST: "var" | "const"
1391 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1393 memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
1395 info->flags = flags;
1398 namespace_t mname_ns = {flags2access(flags), ""};
1399 multiname_t mname = {QNAME, &mname_ns, 0, $3};
1401 if(!(flags&FLAG_STATIC)) {
1404 t=abc_class_slot(state->cls->abc, &mname, &m);
1406 t=abc_class_slot(state->cls->abc, &mname, 0);
1408 info->slot = t->slot_id;
1412 t=abc_class_staticslot(state->cls->abc, &mname, &m);
1414 t=abc_class_staticslot(state->cls->abc, &mname, 0);
1416 info->slot = t->slot_id;
1418 if($5.c && !is_pushundefined($5.c)) {
1420 c = abc_getlocal_0(c);
1421 c = code_append(c, $5.c);
1422 c = converttype(c, $5.t, $4);
1423 c = abc_setslot(c, t->slot_id);
1424 if(!(flags&FLAG_STATIC))
1425 state->cls->init = code_append(state->cls->init, c);
1427 state->cls->static_init = code_append(state->cls->static_init, c);
1430 t->kind= TRAIT_CONST;
1434 /* ------------ constants -------------------------------------- */
1436 MAYBESTATICCONSTANT: {$$=0;}
1437 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1439 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1440 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1441 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1442 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1443 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
1444 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1445 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
1446 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
1447 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
1449 /* ------------ classes and interfaces (body, functions) ------- */
1451 // non-vararg version
1453 memset(&$$,0,sizeof($$));
1455 MAYBE_PARAM_LIST: PARAM_LIST {
1460 MAYBE_PARAM_LIST: "..." PARAM {
1461 memset(&$$,0,sizeof($$));
1463 list_append($$.list, $2);
1465 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1468 list_append($$.list, $4);
1472 PARAM_LIST: PARAM_LIST ',' PARAM {
1474 list_append($$.list, $3);
1477 memset(&$$,0,sizeof($$));
1478 list_append($$.list, $1);
1481 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1482 $$ = malloc(sizeof(param_t));
1487 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1488 $$ = malloc(sizeof(param_t));
1490 $$->type = TYPE_ANY;
1493 GETSET : "get" {$$=$1;}
1497 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1498 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1501 if(state->method->late_binding) {
1502 c = abc_getlocal_0(c);
1503 c = abc_pushscope(c);
1505 if(state->method->is_constructor && !state->method->has_super) {
1506 // generate default constructor
1507 c = abc_getlocal_0(c);
1508 c = abc_constructsuper(c, 0);
1511 c = code_append(c, state->method->initcode);
1512 c = code_append(c, $11);
1514 /* append return if necessary */
1515 if(!c || c->opcode != OPCODE_RETURNVOID &&
1516 c->opcode != OPCODE_RETURNVALUE) {
1517 c = abc_returnvoid(c);
1519 endfunction(0,$1,$3,$4,&$6,$8,c);
1522 /* ------------- package + class ids --------------- */
1524 CLASS: T_IDENTIFIER {
1526 /* try current package */
1527 $$ = find_class($1);
1528 if(!$$) syntaxerror("Could not find class %s\n", $1);
1531 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1532 $$ = registry_findclass($1, $3);
1533 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
1536 QNAME: PACKAGEANDCLASS
1539 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1540 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1542 TYPE : QNAME {$$=$1;}
1543 | '*' {$$=registry_getanytype();}
1545 | "String" {$$=registry_getstringclass();}
1546 | "int" {$$=registry_getintclass();}
1547 | "uint" {$$=registry_getuintclass();}
1548 | "Boolean" {$$=registry_getbooleanclass();}
1549 | "Number" {$$=registry_getnumberclass();}
1552 MAYBETYPE: ':' TYPE {$$=$2;}
1555 /* ----------function calls, delete, constructor calls ------ */
1557 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1558 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1560 MAYBE_EXPRESSION_LIST : {$$=0;}
1561 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1562 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1563 typedcode_t*t = malloc(sizeof(typedcode_t));
1565 list_append($$, t);}
1566 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1567 typedcode_t*t = malloc(sizeof(typedcode_t));
1569 list_append($$, t);}
1571 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1576 $$.c = abc_getglobalscope($$.c);
1577 $$.c = abc_getslot($$.c, $2->slot);
1579 $$.c = abc_findpropstrict2($$.c, &m);
1582 typedcode_list_t*l = $3;
1585 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1590 $$.c = abc_construct($$.c, len);
1592 $$.c = abc_constructprop2($$.c, &m, len);
1596 /* TODO: use abc_call (for calling local variables),
1597 abc_callstatic (for calling own methods)
1600 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1601 typedcode_list_t*l = $3;
1603 code_t*paramcode = 0;
1605 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1611 if($$.c->opcode == OPCODE_COERCE_A) {
1612 $$.c = code_cutlast($$.c);
1616 multiname_t*name = 0;
1617 if($$.c->opcode == OPCODE_GETPROPERTY) {
1618 name = multiname_clone($$.c->data[0]);
1619 $$.c = code_cutlast($$.c);
1620 $$.c = code_append($$.c, paramcode);
1621 $$.c = abc_callproperty2($$.c, name, len);
1622 } else if($$.c->opcode == OPCODE_GETSLOT) {
1623 int slot = (int)(ptroff_t)$$.c->data[0];
1624 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
1625 if(t->kind!=TRAIT_METHOD) {
1626 //flash allows to assign closures to members.
1627 //syntaxerror("not a function");
1630 $$.c = code_cutlast($$.c);
1631 $$.c = code_append($$.c, paramcode);
1632 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1633 $$.c = abc_callproperty2($$.c, name, len);
1634 } else if($$.c->opcode == OPCODE_GETSUPER) {
1635 name = multiname_clone($$.c->data[0]);
1636 $$.c = code_cutlast($$.c);
1637 $$.c = code_append($$.c, paramcode);
1638 $$.c = abc_callsuper2($$.c, name, len);
1640 $$.c = abc_getlocal_0($$.c);
1641 $$.c = code_append($$.c, paramcode);
1642 $$.c = abc_call($$.c, len);
1647 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
1648 $$.t = $1.t->function->return_type;
1650 $$.c = abc_coerce_a($$.c);
1654 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
1655 if(!state->cls) syntaxerror("super() not allowed outside of a class");
1656 if(!state->method) syntaxerror("super() not allowed outside of a function");
1657 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
1660 $$.c = abc_getlocal_0($$.c);
1661 typedcode_list_t*l = 0;
1663 for(l=$3;l;l=l->next) {
1664 $$.c = code_append($$.c, l->typedcode->c);len++;
1667 this is dependent on the control path, check this somewhere else
1668 if(state->method->has_super)
1669 syntaxerror("constructor may call super() only once");
1671 state->method->has_super = 1;
1672 $$.c = abc_constructsuper($$.c, len);
1673 $$.c = abc_pushundefined($$.c);
1677 DELETE: "delete" E {
1679 if($$.c->opcode == OPCODE_COERCE_A) {
1680 $$.c = code_cutlast($$.c);
1682 multiname_t*name = 0;
1683 if($$.c->opcode == OPCODE_GETPROPERTY) {
1684 $$.c->opcode = OPCODE_DELETEPROPERTY;
1685 } else if($$.c->opcode == OPCODE_GETSLOT) {
1686 int slot = (int)(ptroff_t)$$.c->data[0];
1687 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
1688 $$.c = code_cutlast($$.c);
1689 $$.c = abc_deleteproperty2($$.c, name);
1691 $$.c = abc_getlocal_0($$.c);
1692 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
1693 $$.c = abc_deleteproperty2($$.c, &m);
1695 $$.t = TYPE_BOOLEAN;
1698 RETURN: "return" %prec prec_none {
1699 $$ = abc_returnvoid(0);
1701 RETURN: "return" EXPRESSION {
1703 $$ = abc_returnvalue($$);
1706 // ----------------------- expression types -------------------------------------
1708 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
1709 EXPRESSION : E %prec below_minus {$$ = $1;}
1710 EXPRESSION : EXPRESSION ',' E %prec below_minus {
1712 $$.c = cut_last_push($$.c);
1713 $$.c = code_append($$.c,$3.c);
1716 VOIDEXPRESSION : EXPRESSION %prec below_minus {
1717 $$=cut_last_push($1.c);
1720 // ----------------------- expression evaluation -------------------------------------
1723 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1725 E : DELETE {$$ = $1;}
1726 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1730 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1731 //MULTINAME(m, registry_getintclass());
1732 //$$.c = abc_coerce2($$.c, &m); // FIXME
1735 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1738 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1741 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1744 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1747 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
1750 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
1753 CONSTANT : "true" {$$.c = abc_pushtrue(0);
1754 $$.t = TYPE_BOOLEAN;
1756 CONSTANT : "false" {$$.c = abc_pushfalse(0);
1757 $$.t = TYPE_BOOLEAN;
1759 CONSTANT : "null" {$$.c = abc_pushnull(0);
1764 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1765 $$.t = TYPE_BOOLEAN;
1767 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1768 $$.t = TYPE_BOOLEAN;
1770 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1771 $$.t = TYPE_BOOLEAN;
1773 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1774 $$.t = TYPE_BOOLEAN;
1776 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1777 $$.t = TYPE_BOOLEAN;
1779 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1780 $$.t = TYPE_BOOLEAN;
1782 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
1783 $$.t = TYPE_BOOLEAN;
1785 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1786 $$.t = TYPE_BOOLEAN;
1789 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1791 $$.c = converttype($$.c, $1.t, $$.t);
1792 $$.c = abc_dup($$.c);
1793 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1794 $$.c = cut_last_push($$.c);
1795 $$.c = code_append($$.c,$3.c);
1796 $$.c = converttype($$.c, $3.t, $$.t);
1797 code_t*label = $$.c = abc_label($$.c);
1798 jmp->branch = label;
1801 $$.t = join_types($1.t, $3.t, 'A');
1802 /*printf("%08x:\n",$1.t);
1803 code_dump($1.c, 0, 0, "", stdout);
1804 printf("%08x:\n",$3.t);
1805 code_dump($3.c, 0, 0, "", stdout);
1806 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
1808 $$.c = converttype($$.c, $1.t, $$.t);
1809 $$.c = abc_dup($$.c);
1810 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1811 $$.c = cut_last_push($$.c);
1812 $$.c = code_append($$.c,$3.c);
1813 $$.c = converttype($$.c, $3.t, $$.t);
1814 code_t*label = $$.c = abc_label($$.c);
1815 jmp->branch = label;
1818 E : '!' E {$$.c=$2.c;
1819 $$.c = abc_not($$.c);
1820 $$.t = TYPE_BOOLEAN;
1823 E : '~' E {$$.c=$2.c;
1824 $$.c = abc_bitnot($$.c);
1828 E : E '&' E {$$.c = code_append($1.c,$3.c);
1829 $$.c = abc_bitand($$.c);
1833 E : E '^' E {$$.c = code_append($1.c,$3.c);
1834 $$.c = abc_bitxor($$.c);
1838 E : E '|' E {$$.c = code_append($1.c,$3.c);
1839 $$.c = abc_bitor($$.c);
1843 E : E '-' E {$$.c = code_append($1.c,$3.c);
1844 if(BOTH_INT($1,$3)) {
1845 $$.c = abc_subtract_i($$.c);
1848 $$.c = abc_subtract($$.c);
1852 E : E ">>" E {$$.c = code_append($1.c,$3.c);
1853 $$.c = abc_rshift($$.c);
1856 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
1857 $$.c = abc_urshift($$.c);
1860 E : E "<<" E {$$.c = code_append($1.c,$3.c);
1861 $$.c = abc_lshift($$.c);
1865 E : E '/' E {$$.c = code_append($1.c,$3.c);
1866 $$.c = abc_divide($$.c);
1869 E : E '+' E {$$.c = code_append($1.c,$3.c);
1870 $$.c = abc_add($$.c);
1873 E : E '%' E {$$.c = code_append($1.c,$3.c);
1874 $$.c = abc_modulo($$.c);
1877 E : E '*' E {$$.c = code_append($1.c,$3.c);
1878 if(BOTH_INT($1,$3)) {
1879 $$.c = abc_multiply_i($$.c);
1882 $$.c = abc_multiply($$.c);
1887 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
1888 if(use_astype && TYPE_IS_CLASS($3.t)) {
1889 MULTINAME(m,$3.t->cls);
1890 $$.c = abc_astype2($1.c, &m);
1893 $$.c = code_append($1.c, $3.c);
1894 $$.c = abc_astypelate($$.c);
1899 E : E "instanceof" E
1900 {$$.c = code_append($1.c, $3.c);
1901 $$.c = abc_instanceof($$.c);
1902 $$.t = TYPE_BOOLEAN;
1905 E : E "is" E {$$.c = code_append($1.c, $3.c);
1906 $$.c = abc_istypelate($$.c);
1907 $$.t = TYPE_BOOLEAN;
1910 E : "typeof" '(' E ')' {
1912 $$.c = abc_typeof($$.c);
1917 $$.c = cut_last_push($2.c);
1918 $$.c = abc_pushundefined($$.c);
1922 E : "void" { $$.c = abc_pushundefined(0);
1926 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
1931 $$.c=abc_negate_i($$.c);
1934 $$.c=abc_negate($$.c);
1941 $$.c = code_append($$.c, $3.c);
1943 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
1944 $$.c = abc_getproperty2($$.c, &m);
1945 $$.t = 0; // array elements have unknown type
1950 if(BOTH_INT($1,$3)) {
1951 c=abc_multiply_i(c);
1955 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
1956 $$.c = toreadwrite($1.c, c, 0, 0);
1961 code_t*c = abc_modulo($3.c);
1962 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
1963 $$.c = toreadwrite($1.c, c, 0, 0);
1967 code_t*c = abc_lshift($3.c);
1968 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
1969 $$.c = toreadwrite($1.c, c, 0, 0);
1973 code_t*c = abc_rshift($3.c);
1974 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
1975 $$.c = toreadwrite($1.c, c, 0, 0);
1979 code_t*c = abc_urshift($3.c);
1980 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
1981 $$.c = toreadwrite($1.c, c, 0, 0);
1985 code_t*c = abc_divide($3.c);
1986 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
1987 $$.c = toreadwrite($1.c, c, 0, 0);
1992 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1997 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
1999 $$.c = toreadwrite($1.c, c, 0, 0);
2002 E : E "-=" E { code_t*c = $3.c;
2003 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2004 c=abc_subtract_i(c);
2008 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2010 $$.c = toreadwrite($1.c, c, 0, 0);
2013 E : E '=' E { code_t*c = 0;
2014 c = code_append(c, $3.c);
2015 c = converttype(c, $3.t, $1.t);
2016 $$.c = toreadwrite($1.c, c, 1, 0);
2020 E : E '?' E ':' E %prec below_assignment {
2022 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2023 $$.c = code_append($$.c, $3.c);
2024 code_t*j2 = $$.c = abc_jump($$.c, 0);
2025 $$.c = j1->branch = abc_label($$.c);
2026 $$.c = code_append($$.c, $5.c);
2027 $$.c = j2->branch = abc_label($$.c);
2028 $$.t = join_types($3.t,$5.t,'?');
2031 // TODO: use inclocal where appropriate
2032 E : E "++" { code_t*c = 0;
2033 classinfo_t*type = $1.t;
2034 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2035 c=abc_increment_i(c);
2041 c=converttype(c, type, $1.t);
2042 $$.c = toreadwrite($1.c, c, 0, 1);
2045 E : E "--" { code_t*c = 0;
2046 classinfo_t*type = $1.t;
2047 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2048 c=abc_decrement_i(c);
2054 c=converttype(c, type, $1.t);
2055 $$.c = toreadwrite($1.c, c, 0, 1);
2059 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2060 classinfo_t*type = $2.t;
2061 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2062 c=abc_increment_i(c);
2068 c=converttype(c, type, $2.t);
2069 $$.c = toreadwrite($2.c, c, 0, 0);
2073 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2074 classinfo_t*type = $2.t;
2075 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2076 c=abc_decrement_i(c);
2082 c=converttype(c, type, $2.t);
2083 $$.c = toreadwrite($2.c, c, 0, 0);
2087 E : "super" '.' T_IDENTIFIER
2088 { if(!state->cls->info)
2089 syntaxerror("super keyword not allowed outside a class");
2090 classinfo_t*t = state->cls->info->superclass;
2091 if(!t) t = TYPE_OBJECT;
2093 memberinfo_t*f = registry_findmember(t, $3);
2094 namespace_t ns = {flags2access(f->flags), ""};
2095 MEMBER_MULTINAME(m, f, $3);
2097 $$.c = abc_getlocal_0($$.c);
2098 $$.c = abc_getsuper2($$.c, &m);
2099 $$.t = memberinfo_gettype(f);
2102 E : E '.' T_IDENTIFIER
2104 classinfo_t*t = $1.t;
2106 if(TYPE_IS_CLASS(t) && t->cls) {
2111 memberinfo_t*f = registry_findmember(t, $3);
2113 if(f && !is_static != !(f->flags&FLAG_STATIC))
2115 if(f && f->slot && !noslot) {
2116 $$.c = abc_getslot($$.c, f->slot);
2118 MEMBER_MULTINAME(m, f, $3);
2119 $$.c = abc_getproperty2($$.c, &m);
2121 /* determine type */
2122 $$.t = memberinfo_gettype(f);
2124 $$.c = abc_coerce_a($$.c);
2126 /* when resolving a property on an unknown type, we do know the
2127 name of the property (and don't seem to need the package), but
2128 we need to make avm2 try out all access modes */
2129 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2130 $$.c = abc_getproperty2($$.c, &m);
2131 $$.c = abc_coerce_a($$.c);
2132 $$.t = registry_getanytype();
2136 VAR_READ : T_IDENTIFIER {
2143 /* look at variables */
2144 if((i = find_variable($1, &$$.t)) >= 0) {
2145 // $1 is a local variable
2146 $$.c = abc_getlocal($$.c, i);
2148 /* look at current class' members */
2149 } else if((f = registry_findmember(state->cls->info, $1))) {
2150 // $1 is a function in this class
2151 int var_is_static = (f->flags&FLAG_STATIC);
2152 int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
2153 if(var_is_static != i_am_static) {
2154 /* there doesn't seem to be any "static" way to access
2155 static properties of a class */
2156 state->method->late_binding = 1;
2158 namespace_t ns = {flags2access(f->flags), ""};
2159 multiname_t m = {QNAME, &ns, 0, $1};
2160 $$.c = abc_findpropstrict2($$.c, &m);
2161 $$.c = abc_getproperty2($$.c, &m);
2164 $$.c = abc_getlocal_0($$.c);
2165 $$.c = abc_getslot($$.c, f->slot);
2167 namespace_t ns = {flags2access(f->flags), ""};
2168 multiname_t m = {QNAME, &ns, 0, $1};
2169 $$.c = abc_getlocal_0($$.c);
2170 $$.c = abc_getproperty2($$.c, &m);
2173 if(f->kind == MEMBER_METHOD) {
2174 $$.t = TYPE_FUNCTION(f);
2179 /* look at classes in the current package and imported classes */
2180 } else if((a = find_class($1))) {
2182 $$.c = abc_getglobalscope($$.c);
2183 $$.c = abc_getslot($$.c, a->slot);
2186 $$.c = abc_getlex2($$.c, &m);
2188 $$.t = TYPE_CLASS(a);
2190 /* unknown object, let the avm2 resolve it */
2192 if(strcmp($1,"trace"))
2193 warning("Couldn't resolve '%s', doing late binding", $1);
2194 state->method->late_binding = 1;
2196 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
2199 $$.c = abc_findpropstrict2($$.c, &m);
2200 $$.c = abc_getproperty2($$.c, &m);
2205 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
2206 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
2207 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
2209 // ----------------- namespaces -------------------------------------------------
2211 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
2212 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
2213 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}
2215 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER