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_SUPER "super"
101 %token<token> KW_EXTENDS
102 %token<token> KW_FALSE "false"
103 %token<token> KW_TRUE "true"
104 %token<token> KW_BOOLEAN "Boolean"
105 %token<token> KW_UINT "uint"
106 %token<token> KW_INT "int"
107 %token<token> KW_WHILE "while"
108 %token<token> KW_NUMBER "Number"
109 %token<token> KW_STRING "String"
110 %token<token> KW_DELETE "delete"
111 %token<token> KW_IF "if"
112 %token<token> KW_ELSE "else"
113 %token<token> KW_BREAK "break"
114 %token<token> KW_IS "is"
115 %token<token> KW_AS "as"
117 %token<token> T_EQEQ "=="
118 %token<token> T_EQEQEQ "==="
119 %token<token> T_NE "!="
120 %token<token> T_NEE "!=="
121 %token<token> T_LE "<="
122 %token<token> T_GE ">="
123 %token<token> T_DIVBY "/="
124 %token<token> T_MODBY "%="
125 %token<token> T_MULBY "*="
126 %token<token> T_PLUSBY "+="
127 %token<token> T_MINUSBY "-="
128 %token<token> T_SHRBY ">>="
129 %token<token> T_SHLBY "<<="
130 %token<token> T_USHRBY ">>>="
131 %token<token> T_OROR "||"
132 %token<token> T_ANDAND "&&"
133 %token<token> T_COLONCOLON "::"
134 %token<token> T_MINUSMINUS "--"
135 %token<token> T_PLUSPLUS "++"
136 %token<token> T_DOTDOT ".."
137 %token<token> T_DOTDOTDOT "..."
138 %token<token> T_SHL "<<"
139 %token<token> T_USHR ">>>"
140 %token<token> T_SHR ">>"
142 %type <id> X_IDENTIFIER PACKAGE
143 %type <token> VARCONST
145 %type <code> CODEPIECE
146 %type <code> CODEBLOCK MAYBECODE
147 %type <token> PACKAGE_DECLARATION
148 %type <token> FUNCTION_DECLARATION
149 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST
150 %type <token> CLASS_DECLARATION
151 %type <token> NAMESPACE_DECLARATION
152 %type <token> INTERFACE_DECLARATION
153 %type <code> VOIDEXPRESSION
154 %type <value> EXPRESSION NONCOMMAEXPRESSION
155 %type <value> MAYBEEXPRESSION
156 %type <value> E DELETE
157 %type <value> CONSTANT
158 %type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
159 %type <token> USE_NAMESPACE
160 %type <code> FOR_INIT
162 %type <classinfo> MAYBETYPE
165 %type <params> PARAM_LIST
166 %type <params> MAYBE_PARAM_LIST
167 %type <flags> MAYBE_MODIFIERS
168 %type <flags> MODIFIER_LIST
169 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
170 %type <classinfo_list> IMPLEMENTS_LIST
171 %type <classinfo> EXTENDS
172 %type <classinfo_list> EXTENDS_LIST
173 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
174 %type <classinfo_list> QNAME_LIST
175 %type <classinfo> TYPE
177 //%type <token> VARIABLE
178 %type <value> VAR_READ
180 //%type <token> T_IDENTIFIER
181 %type <token> MODIFIER
182 %type <value> FUNCTIONCALL
183 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
185 // precedence: from low to high
189 %left below_semicolon
192 %nonassoc below_assignment // for ?:, contrary to spec
193 %right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|="
200 %nonassoc "==" "!=" "===" "!=="
202 %nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
203 %left "<<" ">>" ">>>"
207 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
209 %left '[' ']' '{' "new" '.' ".." "::"
210 %nonassoc T_IDENTIFIER
215 // needed for "return" precedence:
216 %nonassoc T_STRING T_REGEXP
217 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
218 %nonassoc "false" "true" "null" "undefined" "super"
223 static int yyerror(char*s)
225 syntaxerror("%s", s);
227 static char* concat3str(const char* t1, const char* t2, const char* t3)
232 char*text = malloc(l1+l2+l3+1);
233 memcpy(text , t1, l1);
234 memcpy(text+l1, t2, l2);
235 memcpy(text+l1+l2, t3, l3);
240 typedef struct _import {
244 DECLARE_LIST(import);
246 typedef struct _classstate {
254 typedef struct _methodstate {
258 /* code that needs to be executed at the start of
259 a method (like initializing local registers) */
265 typedef struct _state {
269 import_list_t*wildcard_imports;
271 char has_own_imports;
274 methodstate_t*method;
279 typedef struct _global {
286 static global_t*global = 0;
287 static state_t* state = 0;
291 #define MULTINAME(m,x) \
294 registry_fill_multiname(&m, &m##_ns, x);
296 #define MEMBER_MULTINAME(m,f) \
300 m##_ns.access = flags2access(f->flags); \
304 m.namespace_set = 0; \
307 m.type = MULTINAME; \
309 m.namespace_set = &nopackage_namespace_set; \
313 /* warning: list length of namespace set is undefined */
314 #define MULTINAME_LATE(m, access, package) \
315 namespace_t m##_ns = {access, package}; \
316 namespace_set_t m##_nsset; \
317 namespace_list_t m##_l;m##_l.next = 0; \
318 m##_nsset.namespaces = &m##_l; \
319 m##_nsset = m##_nsset; \
320 m##_l.namespace = &m##_ns; \
321 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
323 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
324 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
325 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
326 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
327 static namespace_list_t nl4 = {&ns4,0};
328 static namespace_list_t nl3 = {&ns3,&nl4};
329 static namespace_list_t nl2 = {&ns2,&nl3};
330 static namespace_list_t nl1 = {&ns1,&nl2};
331 static namespace_set_t nopackage_namespace_set = {&nl1};
333 static state_list_t*state_stack=0;
335 static void init_globals()
337 global = rfx_calloc(sizeof(global_t));
340 static void new_state()
343 NEW(state_list_t, sl);
345 state_t*oldstate = state;
347 memcpy(s, state, sizeof(state_t)); //shallow copy
348 sl->next = state_stack;
351 s->imports = dict_new();
356 state->has_own_imports = 0;
357 state->vars = dict_new();
359 static void state_has_imports()
361 state->wildcard_imports = list_clone(state->wildcard_imports);
362 state->imports = dict_clone(state->imports);
363 state->has_own_imports = 1;
366 static void old_state()
368 if(!state_stack || !state_stack->next)
369 syntaxerror("invalid nesting");
370 state_t*oldstate = state;
371 state_list_t*old = state_stack;
372 state_stack = state_stack->next;
374 state = state_stack->state;
375 /*if(state->method->initcode) {
376 printf("residual initcode\n");
377 code_dump(state->method->initcode, 0, 0, "", stdout);
379 if(oldstate->has_own_imports) {
380 list_free(oldstate->wildcard_imports);
381 dict_destroy(oldstate->imports);oldstate->imports=0;
384 state->method->initcode =
385 code_append(state->method->initcode,
386 oldstate->method->initcode);
388 void initialize_state()
393 global->file = abc_file_new();
394 global->file->flags &= ~ABCFILE_LAZY;
396 global->init = abc_initscript(global->file, 0);
397 code_t*c = global->init->method->body->code;
399 c = abc_getlocal_0(c);
400 c = abc_pushscope(c);
402 /* findpropstrict doesn't just return a scope object- it
403 also makes it "active" somehow. Push local_0 on the
404 scope stack and read it back with findpropstrict, it'll
405 contain properties like "trace". Trying to find the same
406 property on a "vanilla" local_0 yields only a "undefined" */
407 //c = abc_findpropstrict(c, "[package]::trace");
409 /*c = abc_getlocal_0(c);
410 c = abc_findpropstrict(c, "[package]::trace");
412 c = abc_setlocal_1(c);
414 c = abc_pushbyte(c, 0);
415 c = abc_setlocal_2(c);
417 code_t*xx = c = abc_label(c);
418 c = abc_findpropstrict(c, "[package]::trace");
419 c = abc_pushstring(c, "prop:");
420 c = abc_hasnext2(c, 1, 2);
422 c = abc_setlocal_3(c);
423 c = abc_callpropvoid(c, "[package]::trace", 2);
424 c = abc_getlocal_3(c);
426 c = abc_iftrue(c,xx);*/
428 c = abc_findpropstrict(c, "[package]::trace");
429 c = abc_pushstring(c, "[entering global init function]");
430 c = abc_callpropvoid(c, "[package]::trace", 1);
432 global->init->method->body->code = c;
434 void* finalize_state()
436 if(state->level!=1) {
437 syntaxerror("unexpected end of file");
439 abc_method_body_t*m = global->init->method->body;
442 __ findpropstrict(m, "[package]::trace");
443 __ pushstring(m, "[leaving global init function]");
444 __ callpropvoid(m, "[package]::trace", 1);
450 static void startpackage(char*name)
453 syntaxerror("Packages can not be nested.");
456 /*printf("entering package \"%s\"\n", name);*/
457 state->package = name;
459 static void endpackage()
461 /*printf("leaving package \"%s\"\n", state->package);*/
466 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
469 syntaxerror("inner classes now allowed");
472 state->cls = rfx_calloc(sizeof(classstate_t));
475 classinfo_list_t*mlist=0;
476 /*printf("entering class %s\n", name);
477 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n");
479 printf(" extends: %s.%s\n", extends->package, extends->name);
480 printf(" implements (%d): ", list_length(implements));
481 for(mlist=implements;mlist;mlist=mlist->next) {
482 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
487 if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL))
488 syntaxerror("invalid modifier(s)");
490 if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
491 syntaxerror("public and internal not supported at the same time.");
493 /* create the class name, together with the proper attributes */
497 if(!(flags&FLAG_PUBLIC) && !state->package) {
498 access = ACCESS_PRIVATE; package = current_filename;
499 } else if(!(flags&FLAG_PUBLIC) && state->package) {
500 access = ACCESS_PACKAGEINTERNAL; package = state->package;
501 } else if(state->package) {
502 access = ACCESS_PACKAGE; package = state->package;
504 syntaxerror("public classes only allowed inside a package");
507 if(registry_findclass(package, classname)) {
508 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
512 /* build info struct */
513 int num_interfaces = (list_length(implements));
514 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
515 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
517 classinfo_list_t*l = implements;
518 for(l=implements;l;l=l->next) {
519 state->cls->info->interfaces[pos++] = l->classinfo;
522 multiname_t*extends2 = sig2mname(extends);
524 MULTINAME(classname2,state->cls->info);
527 state->cls_init = abc_getlocal_0(state->cls_init);
528 state->cls_init = abc_constructsuper(state->cls_init, 0);
531 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
532 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
533 if(flags&FLAG_DYNAMIC) abc_class_sealed(state->cls->abc);
534 if(interface) abc_class_interface(state->cls->abc);
536 for(mlist=implements;mlist;mlist=mlist->next) {
537 MULTINAME(m, mlist->classinfo);
538 abc_class_add_interface(state->cls->abc, &m);
541 /* now write the construction code for this class */
542 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
544 abc_method_body_t*m = global->init->method->body;
545 __ getglobalscope(m);
546 classinfo_t*s = extends;
551 //TODO: take a look at the current scope stack, maybe
552 // we can re-use something
557 multiname_t*s2 = sig2mname(s);
559 multiname_destroy(s2);
561 __ pushscope(m); count++;
562 m->code = m->code->prev->prev; // invert
564 /* continue appending after last op end */
565 while(m->code && m->code->next) m->code = m->code->next;
567 /* TODO: if this is one of *our* classes, we can also
568 do a getglobalscope/getslot <nr> (which references
569 the init function's slots) */
571 __ getlex2(m, extends2);
573 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
574 stack is not the superclass */
575 __ pushscope(m);count++;
578 /* notice: we get a verify error #1107 if the top element on the scope
579 stack is not the global object */
581 __ pushscope(m);count++;
583 __ newclass(m,state->cls->abc);
587 __ setslot(m, slotindex);
589 /* flash.display.MovieClip handling */
590 if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
591 if(state->package && state->package[0]) {
592 globalclass = concat3str(state->package, ".", classname);
594 globalclass = strdup(classname);
597 multiname_destroy(extends2);
600 static void endclass()
602 if(state->cls->init) {
603 if(!state->cls->abc->constructor) {
604 abc_method_t*m = abc_class_constructor(state->cls->abc, 0);
605 m->body->code = code_append(m->body->code, state->cls->init);
606 m->body->code = abc_returnvoid(m->body->code);
608 code_t*c = state->cls->abc->constructor->body->code;
609 c = code_append(state->cls->init, c);
610 state->cls->abc->constructor->body->code = c;
614 if(state->cls->static_init) {
615 if(!state->cls->abc->static_constructor) {
616 abc_method_t*m = abc_class_staticconstructor(state->cls->abc, 0);
617 m->body->code = code_append(m->body->code, state->cls->static_init);
618 m->body->code = abc_returnvoid(m->body->code);
620 state->cls->abc->static_constructor->body->code =
621 code_append(state->cls->static_init, state->cls->abc->static_constructor->body->code);
628 typedef struct _variable {
633 static int find_variable(char*name, classinfo_t**m)
635 state_list_t* s = state_stack;
639 v = dict_lookup(s->state->vars, name);
650 static int find_variable_safe(char*name, classinfo_t**m)
652 int i = find_variable(name, m);
654 syntaxerror("undefined variable: %s", name);
657 static char variable_exists(char*name)
659 return dict_lookup(state->vars, name)!=0;
661 static int new_variable(char*name, classinfo_t*type)
664 v->index = global->variable_count;
666 dict_put(state->vars, name, v);
667 return global->variable_count++;
669 #define TEMPVARNAME "__as3_temp__"
670 static int gettempvar()
672 int i = find_variable(TEMPVARNAME, 0);
674 i = new_variable(TEMPVARNAME, 0);
679 code_t* killvars(code_t*c)
682 for(t=0;t<state->vars->hashsize;t++) {
683 dictentry_t*e =state->vars->slots[t];
685 variable_t*v = (variable_t*)e->data;
686 //do this always, otherwise register types don't match
687 //in the verifier when doing nested loops
688 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
689 c = abc_kill(c, v->index);
697 static void check_constant_against_type(classinfo_t*t, constant_t*c)
699 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
700 if(TYPE_IS_NUMBER(t)) {
701 xassert(c->type == CONSTANT_FLOAT
702 || c->type == CONSTANT_INT
703 || c->type == CONSTANT_UINT);
704 } else if(TYPE_IS_UINT(t)) {
705 xassert(c->type == CONSTANT_UINT ||
706 (c->type == CONSTANT_INT && c->i>0));
707 } else if(TYPE_IS_INT(t)) {
708 xassert(c->type == CONSTANT_INT);
709 } else if(TYPE_IS_BOOLEAN(t)) {
710 xassert(c->type == CONSTANT_TRUE
711 || c->type == CONSTANT_FALSE);
715 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
717 memberinfo_t*minfo = 0;
718 if(getset != KW_GET && getset != KW_SET) {
719 if(registry_findmember(state->cls->info, name)) {
720 syntaxerror("class already contains a member/method called '%s'", name);
722 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
723 minfo->return_type = return_type;
724 // getslot on a member slot only returns "undefined", so no need
725 // to actually store these
726 //state->minfo->slot = state->method->abc->method->trait->slot_id;
728 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
732 else if(params->list)
733 type = params->list->param->type;
734 if((minfo=registry_findmember(state->cls->info, name))) {
735 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
736 syntaxerror("class already contains a member or method called '%s'", name);
738 syntaxerror("getter/setter for '%s' already defined", name);
739 /* make a setter or getter into a getset */
744 if(type && minfo->type != type)
745 syntaxerror("different type in getter and setter");
747 minfo = memberinfo_register(state->cls->info, name, gs);
750 /* can't assign a slot as getter and setter might have different slots */
751 //minfo->slot = slot;
753 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
754 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
755 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
756 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
757 if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
761 static int flags2access(int flags)
764 if(flags&FLAG_PUBLIC) {
765 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
766 access = ACCESS_PACKAGE;
767 } else if(flags&FLAG_PRIVATE) {
768 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
769 access = ACCESS_PRIVATE;
770 } else if(flags&FLAG_PROTECTED) {
771 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
772 access = ACCESS_PROTECTED;
774 access = ACCESS_PACKAGEINTERNAL;
779 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
780 params_t*params, classinfo_t*return_type)
783 syntaxerror("not able to start another method scope");
786 state->method = rfx_calloc(sizeof(methodstate_t));
787 state->method->initcode = 0;
788 state->method->is_constructor = !strcmp(state->cls->info->name,name);
789 state->method->has_super = 0;
791 global->variable_count = 0;
793 /* state->vars is initialized by state_new */
794 if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info)!=0) syntaxerror("Internal error");
796 for(p=params->list;p;p=p->next) {
797 new_variable(p->param->name, p->param->type);
799 if(state->method->is_constructor)
800 name = "__as3_constructor__";
801 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
804 static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
805 params_t*params, classinfo_t*return_type, code_t*body)
807 namespace_t mname_ns = {flags2access(flags), ""};
808 multiname_t mname = {QNAME, &mname_ns, 0, name};
812 multiname_t*type2 = sig2mname(return_type);
814 if(state->method->is_constructor) {
815 f = abc_class_constructor(state->cls->abc, type2);
817 if(flags&FLAG_STATIC)
818 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
820 f = abc_class_method(state->cls->abc, type2, &mname);
821 slot = f->trait->slot_id;
823 //flash doesn't seem to allow us to access function slots
824 //state->method->info->slot = slot;
826 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
827 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
828 if(params->varargs) f->flags |= METHOD_NEED_REST;
832 for(p=params->list;p;p=p->next) {
833 if(params->varargs && !p->next) {
834 break; //varargs: omit last parameter in function signature
836 multiname_t*m = sig2mname(p->param->type);
837 list_append(f->parameters, m);
838 if(p->param->value) {
839 check_constant_against_type(p->param->type, p->param->value);
840 opt=1;list_append(f->optional_parameters, p->param->value);
842 syntaxerror("non-optional parameter not allowed after optional parameters");
845 f->body->code = body;
852 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
857 void breakjumpsto(code_t*c, code_t*jump)
862 if(c->opcode == OPCODE___BREAK__) {
863 c->opcode = OPCODE_JUMP;
870 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
873 return registry_getanytype();
874 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
875 return registry_getanytype();
878 return registry_getanytype();
880 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
885 return abc_coerce_a(c);
889 // cast an "any" type to a specific type. subject to
890 // runtime exceptions
891 return abc_coerce2(c, &m);
894 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
895 return abc_coerce2(c, &m);
897 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
898 return abc_coerce2(c, &m);
900 /* these are subject to overflow */
901 if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) {
902 return abc_coerce2(c, &m);
904 if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) {
905 return abc_coerce2(c, &m);
908 classinfo_t*supertype = from;
910 if(supertype == to) {
911 // target type is one of from's superclasses
912 return abc_coerce2(c, &m);
915 while(supertype->interfaces[t]) {
916 if(supertype->interfaces[t]==to) {
917 // to type is one of from's interfaces
918 return abc_coerce2(c, &m);
922 supertype = supertype->superclass;
924 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
926 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
928 syntaxerror("can't convert type %s to %s", from->name, to->name);
931 code_t*defaultvalue(code_t*c, classinfo_t*type)
933 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) {
934 c = abc_pushbyte(c, 0);
935 } else if(TYPE_IS_BOOLEAN(type)) {
936 c = abc_pushfalse(c);
943 char is_pushundefined(code_t*c)
945 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
948 void parserassert(int b)
950 if(!b) syntaxerror("internal error: assertion failed");
953 static classinfo_t* find_class(char*name)
957 c = registry_findclass(state->package, name);
959 /* try explicit imports */
960 dictentry_t* e = dict_get_slot(state->imports, name);
964 if(!strcmp(e->key, name)) {
965 c = (classinfo_t*)e->data;
970 /* try package.* imports */
971 import_list_t*l = state->wildcard_imports;
975 //printf("does package %s contain a class %s?\n", l->import->package, name);
976 c = registry_findclass(l->import->package, name);
980 /* try global package */
982 c = registry_findclass("", name);
987 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
991 [prefix code] [read instruction]
995 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
998 if(in && in->opcode == OPCODE_COERCE_A) {
999 in = code_cutlast(in);
1002 syntaxerror("internal error");
1004 /* chop off read instruction */
1008 prefix = r->prev;r->prev = 0;
1014 char use_temp_var = readbefore;
1016 /* generate the write instruction, and maybe append a dup to the prefix code */
1017 code_t* write = abc_nop(0);
1018 if(r->opcode == OPCODE_GETPROPERTY) {
1019 write->opcode = OPCODE_SETPROPERTY;
1020 multiname_t*m = (multiname_t*)r->data[0];
1021 write->data[0] = multiname_clone(m);
1022 if(m->type == QNAME || m->type == MULTINAME) {
1024 prefix = abc_dup(prefix); // we need the object, too
1027 } else if(m->type == MULTINAMEL) {
1029 /* dupping two values on the stack requires 5 operations and one register-
1030 couldn't adobe just have given us a dup2? */
1031 int temp = gettempvar();
1032 prefix = abc_setlocal(prefix, temp);
1033 prefix = abc_dup(prefix);
1034 prefix = abc_getlocal(prefix, temp);
1035 prefix = abc_swap(prefix);
1036 prefix = abc_getlocal(prefix, temp);
1040 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1042 } else if(r->opcode == OPCODE_GETSLOT) {
1043 write->opcode = OPCODE_SETSLOT;
1044 write->data[0] = r->data[0];
1046 prefix = abc_dup(prefix); // we need the object, too
1049 } else if(r->opcode == OPCODE_GETLOCAL) {
1050 write->opcode = OPCODE_SETLOCAL;
1051 write->data[0] = r->data[0];
1052 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1053 write->opcode = OPCODE_SETLOCAL_0;
1054 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1055 write->opcode = OPCODE_SETLOCAL_1;
1056 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1057 write->opcode = OPCODE_SETLOCAL_2;
1058 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1059 write->opcode = OPCODE_SETLOCAL_3;
1061 code_dump(r, 0, 0, "", stdout);
1062 syntaxerror("illegal lvalue: can't assign a value to this expression");
1069 /* with getproperty/getslot, we have to be extra careful not
1070 to execute the read code twice, as it might have side-effects
1071 (e.g. if the property is in fact a setter/getter combination)
1073 So read the value, modify it, and write it again,
1074 using prefix only once and making sure (by using a temporary
1075 register) that the return value is what we just wrote */
1076 temp = gettempvar();
1077 c = code_append(c, prefix);
1078 c = code_append(c, r);
1081 c = abc_setlocal(c, temp);
1083 c = code_append(c, middlepart);
1086 c = abc_setlocal(c, temp);
1088 c = code_append(c, write);
1089 c = abc_getlocal(c, temp);
1090 c = abc_kill(c, temp);
1092 /* if we're allowed to execute the read code twice *and*
1093 the middlepart doesn't modify the code, things are easier.
1095 code_t* r2 = code_dup(r);
1096 //c = code_append(c, prefix);
1097 parserassert(!prefix);
1098 c = code_append(c, r);
1099 c = code_append(c, middlepart);
1100 c = code_append(c, write);
1101 c = code_append(c, r2);
1104 /* even smaller version: overwrite the value without reading
1108 c = code_append(c, prefix);
1111 c = code_append(c, middlepart);
1112 c = code_append(c, write);
1113 c = code_append(c, r);
1115 temp = gettempvar();
1117 c = code_append(c, prefix);
1120 c = code_append(c, middlepart);
1122 c = abc_setlocal(c, temp);
1123 c = code_append(c, write);
1124 c = abc_getlocal(c, temp);
1131 #define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
1132 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1139 /* ------------ code blocks / statements ---------------- */
1143 MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/}
1144 MAYBECODE: {$$=code_new();}
1146 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1147 CODE: CODEPIECE {$$=$1;}
1149 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
1150 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
1151 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
1152 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
1153 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
1154 CODEPIECE: ';' {$$=code_new();}
1155 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
1156 CODEPIECE: VOIDEXPRESSION {$$=$1}
1157 CODEPIECE: FOR {$$=$1}
1158 CODEPIECE: WHILE {$$=$1}
1159 CODEPIECE: BREAK {$$=$1}
1160 CODEPIECE: RETURN {$$=$1}
1161 CODEPIECE: IF {$$=$1}
1162 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
1163 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
1165 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
1166 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1167 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1169 /* ------------ variables --------------------------- */
1171 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1172 | {$$.c=abc_pushundefined(0);
1176 VAR : "const" | "var"
1177 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
1179 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1180 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1182 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1184 if(variable_exists($2))
1185 syntaxerror("Variable %s already defined", $2);
1187 if(!is_subtype_of($4.t, $3)) {
1188 syntaxerror("Can't convert %s to %s", $4.t->name,
1192 int index = new_variable($2, $3);
1195 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1197 $$ = converttype($$, $4.t, $3);
1198 $$ = abc_setlocal($$, index);
1200 $$ = defaultvalue(0, $3);
1201 $$ = abc_setlocal($$, index);
1204 /* if this is a typed variable:
1205 push default value for type on stack */
1207 state->method->initcode = defaultvalue(state->method->initcode, $3);
1208 state->method->initcode = abc_setlocal(state->method->initcode, index);
1211 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1213 $$ = abc_coerce_a($$);
1214 $$ = abc_setlocal($$, index);
1220 /* that's the default for a local register, anyway
1222 state->method->initcode = abc_pushundefined(state->method->initcode);
1223 state->method->initcode = abc_setlocal(state->method->initcode, index);
1225 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1228 /* ------------ control flow ------------------------- */
1230 MAYBEELSE: %prec below_else {$$ = code_new();}
1231 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1232 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1234 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1235 $$ = state->method->initcode;state->method->initcode=0;
1237 $$ = code_append($$, $4.c);
1238 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1240 $$ = code_append($$, $6);
1242 myjmp = $$ = abc_jump($$, 0);
1244 myif->branch = $$ = abc_label($$);
1246 $$ = code_append($$, $7);
1247 myjmp->branch = $$ = abc_label($$);
1250 $$ = killvars($$);old_state();
1253 FOR_INIT : {$$=code_new();}
1254 FOR_INIT : VARIABLE_DECLARATION
1255 FOR_INIT : VOIDEXPRESSION
1257 FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1258 $$ = state->method->initcode;state->method->initcode=0;
1260 $$ = code_append($$, $4);
1261 code_t*loopstart = $$ = abc_label($$);
1262 $$ = code_append($$, $6.c);
1263 code_t*myif = $$ = abc_iffalse($$, 0);
1264 $$ = code_append($$, $10);
1265 $$ = code_append($$, $8);
1266 $$ = abc_jump($$, loopstart);
1267 code_t*out = $$ = abc_label($$);
1268 breakjumpsto($$, out);
1271 $$ = killvars($$);old_state();
1274 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1275 $$ = state->method->initcode;state->method->initcode=0;
1277 code_t*myjmp = $$ = abc_jump($$, 0);
1278 code_t*loopstart = $$ = abc_label($$);
1279 $$ = code_append($$, $6);
1280 myjmp->branch = $$ = abc_label($$);
1281 $$ = code_append($$, $4.c);
1282 $$ = abc_iftrue($$, loopstart);
1283 code_t*out = $$ = abc_label($$);
1284 breakjumpsto($$, out);
1286 $$ = killvars($$);old_state();
1290 $$ = abc___break__(0);
1293 /* ------------ packages and imports ---------------- */
1295 X_IDENTIFIER: T_IDENTIFIER
1296 | "package" {$$="package";}
1298 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3str($1,".",$3);}
1299 PACKAGE: X_IDENTIFIER {$$=$1;}
1301 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1302 PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()}
1304 IMPORT : "import" QNAME {
1307 syntaxerror("Couldn't import class\n");
1308 state_has_imports();
1309 dict_put(state->imports, c->name, c);
1312 IMPORT : "import" PACKAGE '.' '*' {
1315 state_has_imports();
1316 list_append(state->wildcard_imports, i);
1320 /* ------------ classes and interfaces (header) -------------- */
1322 MAYBE_MODIFIERS : {$$=0;}
1323 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
1324 MODIFIER_LIST : MODIFIER {$$=$1;}
1325 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
1327 MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
1328 | KW_PRIVATE {$$=FLAG_PRIVATE;}
1329 | KW_PROTECTED {$$=FLAG_PROTECTED;}
1330 | KW_STATIC {$$=FLAG_STATIC;}
1331 | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
1332 | KW_FINAL {$$=FLAG_FINAL;}
1333 | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
1334 | KW_NATIVE {$$=FLAG_NATIVE;}
1335 | KW_INTERNAL {$$=FLAG_INTERNAL;}
1337 EXTENDS : {$$=registry_getobjectclass();}
1338 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1340 EXTENDS_LIST : {$$=list_new();}
1341 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1343 IMPLEMENTS_LIST : {$$=list_new();}
1344 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1346 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
1347 EXTENDS IMPLEMENTS_LIST
1348 '{' {startclass($1,$3,$4,$5, 0);}
1349 MAYBE_DECLARATION_LIST
1352 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
1354 '{' {startclass($1,$3,0,$4,1);}
1355 MAYBE_IDECLARATION_LIST
1358 /* ------------ classes and interfaces (body) -------------- */
1360 MAYBE_DECLARATION_LIST :
1361 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1362 DECLARATION_LIST : DECLARATION
1363 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1365 DECLARATION : SLOT_DECLARATION
1366 DECLARATION : FUNCTION_DECLARATION
1368 MAYBE_IDECLARATION_LIST :
1369 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1370 IDECLARATION_LIST : IDECLARATION
1371 IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
1373 IDECLARATION : "var" T_IDENTIFIER {
1374 syntaxerror("variable declarations not allowed in interfaces");
1376 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
1378 if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
1379 syntaxerror("invalid method modifiers: interface methods always need to be public");
1381 startfunction(0,$1,$3,$4,&$6,$8);
1382 endfunction(0,$1,$3,$4,&$6,$8, 0);
1385 /* ------------ classes and interfaces (body, slots ) ------- */
1387 VARCONST: "var" | "const"
1389 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1391 memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
1393 info->flags = flags;
1396 namespace_t mname_ns = {flags2access(flags), ""};
1397 multiname_t mname = {QNAME, &mname_ns, 0, $3};
1399 if(!(flags&FLAG_STATIC)) {
1402 t=abc_class_slot(state->cls->abc, &mname, &m);
1404 t=abc_class_slot(state->cls->abc, &mname, 0);
1406 info->slot = t->slot_id;
1410 t=abc_class_staticslot(state->cls->abc, &mname, &m);
1412 t=abc_class_staticslot(state->cls->abc, &mname, 0);
1414 info->slot = t->slot_id;
1416 if($5.c && !is_pushundefined($5.c)) {
1418 c = abc_getlocal_0(c);
1419 c = code_append(c, $5.c);
1420 c = converttype(c, $5.t, $4);
1421 c = abc_setslot(c, t->slot_id);
1422 if(!(flags&FLAG_STATIC))
1423 state->cls->init = code_append(state->cls->init, c);
1425 state->cls->static_init = code_append(state->cls->static_init, c);
1428 t->kind= TRAIT_CONST;
1432 /* ------------ constants -------------------------------------- */
1434 MAYBESTATICCONSTANT: {$$=0;}
1435 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1437 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1438 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1439 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1440 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1441 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
1442 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1443 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
1444 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
1445 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
1447 /* ------------ classes and interfaces (body, functions) ------- */
1449 // non-vararg version
1451 memset(&$$,0,sizeof($$));
1453 MAYBE_PARAM_LIST: PARAM_LIST {
1458 MAYBE_PARAM_LIST: "..." PARAM {
1459 memset(&$$,0,sizeof($$));
1461 list_append($$.list, $2);
1463 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1466 list_append($$.list, $4);
1470 PARAM_LIST: PARAM_LIST ',' PARAM {
1472 list_append($$.list, $3);
1475 memset(&$$,0,sizeof($$));
1476 list_append($$.list, $1);
1479 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1480 $$ = malloc(sizeof(param_t));
1485 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1486 $$ = malloc(sizeof(param_t));
1488 $$->type = TYPE_ANY;
1491 GETSET : "get" {$$=$1;}
1495 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1496 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1499 if(state->method->late_binding) {
1500 c = abc_getlocal_0(c);
1501 c = abc_pushscope(c);
1503 if(state->method->is_constructor && !state->method->has_super) {
1504 // generate default constructor
1505 c = abc_getlocal_0(c);
1506 c = abc_constructsuper(c, 0);
1509 c = code_append(c, state->method->initcode);
1510 c = code_append(c, $11);
1512 /* append return if necessary */
1513 if(!c || c->opcode != OPCODE_RETURNVOID &&
1514 c->opcode != OPCODE_RETURNVALUE) {
1515 c = abc_returnvoid(c);
1517 endfunction(0,$1,$3,$4,&$6,$8,c);
1520 /* ------------- package + class ids --------------- */
1522 CLASS: T_IDENTIFIER {
1524 /* try current package */
1525 $$ = find_class($1);
1526 if(!$$) syntaxerror("Could not find class %s\n", $1);
1529 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1530 $$ = registry_findclass($1, $3);
1531 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
1534 QNAME: PACKAGEANDCLASS
1537 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1538 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1540 TYPE : QNAME {$$=$1;}
1541 | '*' {$$=registry_getanytype();}
1543 | "String" {$$=registry_getstringclass();}
1544 | "int" {$$=registry_getintclass();}
1545 | "uint" {$$=registry_getuintclass();}
1546 | "Boolean" {$$=registry_getbooleanclass();}
1547 | "Number" {$$=registry_getnumberclass();}
1550 MAYBETYPE: ':' TYPE {$$=$2;}
1553 /* ----------function calls, delete, constructor calls ------ */
1555 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1556 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1558 MAYBE_EXPRESSION_LIST : {$$=0;}
1559 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1560 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1561 typedcode_t*t = malloc(sizeof(typedcode_t));
1563 list_append($$, t);}
1564 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1565 typedcode_t*t = malloc(sizeof(typedcode_t));
1567 list_append($$, t);}
1569 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1574 $$.c = abc_getglobalscope($$.c);
1575 $$.c = abc_getslot($$.c, $2->slot);
1577 $$.c = abc_findpropstrict2($$.c, &m);
1580 typedcode_list_t*l = $3;
1583 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1588 $$.c = abc_construct($$.c, len);
1590 $$.c = abc_constructprop2($$.c, &m, len);
1594 /* TODO: use abc_call (for calling local variables),
1595 abc_callstatic (for calling own methods)
1598 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1599 typedcode_list_t*l = $3;
1601 code_t*paramcode = 0;
1603 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1609 if($$.c->opcode == OPCODE_COERCE_A) {
1610 $$.c = code_cutlast($$.c);
1614 multiname_t*name = 0;
1615 if($$.c->opcode == OPCODE_GETPROPERTY) {
1616 name = multiname_clone($$.c->data[0]);
1617 $$.c = code_cutlast($$.c);
1618 $$.c = code_append($$.c, paramcode);
1619 $$.c = abc_callproperty2($$.c, name, len);
1620 } else if($$.c->opcode == OPCODE_GETSLOT) {
1621 int slot = (int)(ptroff_t)$$.c->data[0];
1622 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
1623 if(t->kind!=TRAIT_METHOD) {
1624 //flash allows to assign closures to members.
1625 //syntaxerror("not a function");
1628 $$.c = code_cutlast($$.c);
1629 $$.c = code_append($$.c, paramcode);
1630 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1631 $$.c = abc_callproperty2($$.c, name, len);
1632 } else if($$.c->opcode == OPCODE_GETSUPER) {
1633 name = multiname_clone($$.c->data[0]);
1634 $$.c = code_cutlast($$.c);
1635 $$.c = code_append($$.c, paramcode);
1636 $$.c = abc_callsuper2($$.c, name, len);
1638 $$.c = abc_getlocal_0($$.c);
1639 $$.c = code_append($$.c, paramcode);
1640 $$.c = abc_call($$.c, len);
1645 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
1646 $$.t = $1.t->function->return_type;
1648 $$.c = abc_coerce_a($$.c);
1652 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
1653 if(!state->cls) syntaxerror("super() not allowed outside of a class");
1654 if(!state->method) syntaxerror("super() not allowed outside of a function");
1655 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
1658 $$.c = abc_getlocal_0($$.c);
1659 typedcode_list_t*l = 0;
1661 for(l=$3;l;l=l->next) {
1662 $$.c = code_append($$.c, l->typedcode->c);len++;
1665 this is dependent on the control path, check this somewhere else
1666 if(state->method->has_super)
1667 syntaxerror("constructor may call super() only once");
1669 state->method->has_super = 1;
1670 $$.c = abc_constructsuper($$.c, len);
1671 $$.c = abc_pushundefined($$.c);
1675 DELETE: "delete" E {
1677 if($$.c->opcode == OPCODE_COERCE_A) {
1678 $$.c = code_cutlast($$.c);
1680 multiname_t*name = 0;
1681 if($$.c->opcode == OPCODE_GETPROPERTY) {
1682 $$.c->opcode = OPCODE_DELETEPROPERTY;
1683 } else if($$.c->opcode == OPCODE_GETSLOT) {
1684 int slot = (int)(ptroff_t)$$.c->data[0];
1685 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
1686 $$.c = code_cutlast($$.c);
1687 $$.c = abc_deleteproperty2($$.c, name);
1689 $$.c = abc_getlocal_0($$.c);
1690 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
1691 $$.c = abc_deleteproperty2($$.c, &m);
1693 $$.t = TYPE_BOOLEAN;
1696 RETURN: "return" %prec prec_none {
1697 $$ = abc_returnvoid(0);
1699 RETURN: "return" EXPRESSION {
1701 $$ = abc_returnvalue($$);
1704 // ----------------------- expression types -------------------------------------
1706 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
1707 EXPRESSION : E %prec below_minus {$$ = $1;}
1708 EXPRESSION : EXPRESSION ',' E %prec below_minus {
1710 $$.c = cut_last_push($$.c);
1711 $$.c = code_append($$.c,$3.c);
1714 VOIDEXPRESSION : EXPRESSION %prec below_minus {
1715 $$=cut_last_push($1.c);
1718 // ----------------------- expression evaluation -------------------------------------
1721 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1723 E : DELETE {$$ = $1;}
1724 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1728 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1729 //MULTINAME(m, registry_getintclass());
1730 //$$.c = abc_coerce2($$.c, &m); // FIXME
1733 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1736 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1739 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1742 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1745 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
1748 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
1751 CONSTANT : "true" {$$.c = abc_pushtrue(0);
1752 $$.t = TYPE_BOOLEAN;
1754 CONSTANT : "false" {$$.c = abc_pushfalse(0);
1755 $$.t = TYPE_BOOLEAN;
1757 CONSTANT : "null" {$$.c = abc_pushnull(0);
1762 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1763 $$.t = TYPE_BOOLEAN;
1765 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1766 $$.t = TYPE_BOOLEAN;
1768 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1769 $$.t = TYPE_BOOLEAN;
1771 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1772 $$.t = TYPE_BOOLEAN;
1774 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1775 $$.t = TYPE_BOOLEAN;
1777 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1778 $$.t = TYPE_BOOLEAN;
1780 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
1781 $$.t = TYPE_BOOLEAN;
1783 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1784 $$.t = TYPE_BOOLEAN;
1787 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1789 $$.c = converttype($$.c, $1.t, $$.t);
1790 $$.c = abc_dup($$.c);
1791 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1792 $$.c = cut_last_push($$.c);
1793 $$.c = code_append($$.c,$3.c);
1794 $$.c = converttype($$.c, $3.t, $$.t);
1795 code_t*label = $$.c = abc_label($$.c);
1796 jmp->branch = label;
1799 $$.t = join_types($1.t, $3.t, 'A');
1800 /*printf("%08x:\n",$1.t);
1801 code_dump($1.c, 0, 0, "", stdout);
1802 printf("%08x:\n",$3.t);
1803 code_dump($3.c, 0, 0, "", stdout);
1804 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
1806 $$.c = converttype($$.c, $1.t, $$.t);
1807 $$.c = abc_dup($$.c);
1808 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1809 $$.c = cut_last_push($$.c);
1810 $$.c = code_append($$.c,$3.c);
1811 $$.c = converttype($$.c, $3.t, $$.t);
1812 code_t*label = $$.c = abc_label($$.c);
1813 jmp->branch = label;
1816 E : '!' E {$$.c=$2.c;
1817 $$.c = abc_not($$.c);
1818 $$.t = TYPE_BOOLEAN;
1821 E : '~' E {$$.c=$2.c;
1822 $$.c = abc_bitnot($$.c);
1826 E : E '&' E {$$.c = code_append($1.c,$3.c);
1827 $$.c = abc_bitand($$.c);
1831 E : E '^' E {$$.c = code_append($1.c,$3.c);
1832 $$.c = abc_bitxor($$.c);
1836 E : E '|' E {$$.c = code_append($1.c,$3.c);
1837 $$.c = abc_bitor($$.c);
1841 E : E '-' E {$$.c = code_append($1.c,$3.c);
1842 if(BOTH_INT($1,$3)) {
1843 $$.c = abc_subtract_i($$.c);
1846 $$.c = abc_subtract($$.c);
1850 E : E ">>" E {$$.c = code_append($1.c,$3.c);
1851 $$.c = abc_rshift($$.c);
1854 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
1855 $$.c = abc_urshift($$.c);
1858 E : E "<<" E {$$.c = code_append($1.c,$3.c);
1859 $$.c = abc_lshift($$.c);
1863 E : E '/' E {$$.c = code_append($1.c,$3.c);
1864 $$.c = abc_divide($$.c);
1867 E : E '+' E {$$.c = code_append($1.c,$3.c);
1868 $$.c = abc_add($$.c);
1871 E : E '%' E {$$.c = code_append($1.c,$3.c);
1872 $$.c = abc_modulo($$.c);
1875 E : E '*' E {$$.c = code_append($1.c,$3.c);
1876 if(BOTH_INT($1,$3)) {
1877 $$.c = abc_multiply_i($$.c);
1880 $$.c = abc_multiply($$.c);
1885 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
1886 if(use_astype && TYPE_IS_CLASS($3.t)) {
1887 MULTINAME(m,$3.t->cls);
1888 $$.c = abc_astype2($1.c, &m);
1891 $$.c = code_append($1.c, $3.c);
1892 $$.c = abc_astypelate($$.c);
1897 E : E "is" E {$$.c = code_append($1.c, $3.c);
1898 $$.c = abc_istypelate($$.c);
1899 $$.t = TYPE_BOOLEAN;
1902 E : "typeof" '(' E ')' {
1904 $$.c = abc_typeof($$.c);
1909 $$.c = cut_last_push($2.c);
1910 $$.c = abc_pushundefined($$.c);
1914 E : "void" { $$.c = abc_pushundefined(0);
1918 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
1923 $$.c=abc_negate_i($$.c);
1926 $$.c=abc_negate($$.c);
1933 $$.c = code_append($$.c, $3.c);
1935 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
1936 $$.c = abc_getproperty2($$.c, &m);
1937 $$.t = 0; // array elements have unknown type
1942 if(BOTH_INT($1,$3)) {
1943 c=abc_multiply_i(c);
1947 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
1948 $$.c = toreadwrite($1.c, c, 0, 0);
1953 code_t*c = abc_modulo($3.c);
1954 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
1955 $$.c = toreadwrite($1.c, c, 0, 0);
1959 code_t*c = abc_lshift($3.c);
1960 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
1961 $$.c = toreadwrite($1.c, c, 0, 0);
1965 code_t*c = abc_rshift($3.c);
1966 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
1967 $$.c = toreadwrite($1.c, c, 0, 0);
1971 code_t*c = abc_urshift($3.c);
1972 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
1973 $$.c = toreadwrite($1.c, c, 0, 0);
1977 code_t*c = abc_divide($3.c);
1978 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
1979 $$.c = toreadwrite($1.c, c, 0, 0);
1984 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1989 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
1991 $$.c = toreadwrite($1.c, c, 0, 0);
1994 E : E "-=" E { code_t*c = $3.c;
1995 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1996 c=abc_subtract_i(c);
2000 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2002 $$.c = toreadwrite($1.c, c, 0, 0);
2005 E : E '=' E { code_t*c = 0;
2006 c = code_append(c, $3.c);
2007 c = converttype(c, $3.t, $1.t);
2008 $$.c = toreadwrite($1.c, c, 1, 0);
2012 E : E '?' E ':' E %prec below_assignment {
2014 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2015 $$.c = code_append($$.c, $3.c);
2016 code_t*j2 = $$.c = abc_jump($$.c, 0);
2017 $$.c = j1->branch = abc_label($$.c);
2018 $$.c = code_append($$.c, $5.c);
2019 $$.c = j2->branch = abc_label($$.c);
2020 $$.t = join_types($3.t,$5.t,'?');
2023 // TODO: use inclocal where appropriate
2024 E : E "++" { code_t*c = 0;
2025 classinfo_t*type = $1.t;
2026 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2027 c=abc_increment_i(c);
2033 c=converttype(c, type, $1.t);
2034 $$.c = toreadwrite($1.c, c, 0, 1);
2037 E : E "--" { code_t*c = 0;
2038 classinfo_t*type = $1.t;
2039 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2040 c=abc_decrement_i(c);
2046 c=converttype(c, type, $1.t);
2047 $$.c = toreadwrite($1.c, c, 0, 1);
2051 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2052 classinfo_t*type = $2.t;
2053 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2054 c=abc_increment_i(c);
2060 c=converttype(c, type, $2.t);
2061 $$.c = toreadwrite($2.c, c, 0, 0);
2065 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2066 classinfo_t*type = $2.t;
2067 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2068 c=abc_decrement_i(c);
2074 c=converttype(c, type, $2.t);
2075 $$.c = toreadwrite($2.c, c, 0, 0);
2079 E : "super" '.' T_IDENTIFIER
2080 { if(!state->cls->info)
2081 syntaxerror("super keyword not allowed outside a class");
2082 classinfo_t*t = state->cls->info->superclass;
2083 if(!t) t = TYPE_OBJECT;
2085 memberinfo_t*f = registry_findmember(t, $3);
2086 namespace_t ns = {flags2access(f->flags), ""};
2087 MEMBER_MULTINAME(m, f);
2089 $$.c = abc_getlocal_0($$.c);
2090 $$.c = abc_getsuper2($$.c, &m);
2091 $$.t = memberinfo_gettype(f);
2094 E : E '.' T_IDENTIFIER
2096 classinfo_t*t = $1.t;
2098 if(TYPE_IS_CLASS(t) && t->cls) {
2103 memberinfo_t*f = registry_findmember(t, $3);
2105 if(f && !is_static != !(f->flags&FLAG_STATIC))
2107 if(f && f->slot && !noslot) {
2108 $$.c = abc_getslot($$.c, f->slot);
2110 MEMBER_MULTINAME(m, f);
2111 $$.c = abc_getproperty2($$.c, &m);
2113 /* determine type */
2114 $$.t = memberinfo_gettype(f);
2116 $$.c = abc_coerce_a($$.c);
2118 /* when resolving a property on an unknown type, we do know the
2119 name of the property (and don't seem to need the package), but
2120 we need to make avm2 try out all access modes */
2121 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2122 $$.c = abc_getproperty2($$.c, &m);
2123 $$.c = abc_coerce_a($$.c);
2124 $$.t = registry_getanytype();
2128 VAR_READ : T_IDENTIFIER {
2135 /* look at variables */
2136 if((i = find_variable($1, &$$.t)) >= 0) {
2137 // $1 is a local variable
2138 $$.c = abc_getlocal($$.c, i);
2140 /* look at current class' members */
2141 } else if((f = registry_findmember(state->cls->info, $1))) {
2142 // $1 is a function in this class
2143 int var_is_static = (f->flags&FLAG_STATIC);
2144 int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
2145 if(var_is_static != i_am_static) {
2146 /* there doesn't seem to be any "static" way to access
2147 static properties of a class */
2148 state->method->late_binding = 1;
2150 namespace_t ns = {flags2access(f->flags), ""};
2151 multiname_t m = {QNAME, &ns, 0, $1};
2152 $$.c = abc_findpropstrict2($$.c, &m);
2153 $$.c = abc_getproperty2($$.c, &m);
2156 $$.c = abc_getlocal_0($$.c);
2157 $$.c = abc_getslot($$.c, f->slot);
2159 namespace_t ns = {flags2access(f->flags), ""};
2160 multiname_t m = {QNAME, &ns, 0, $1};
2161 $$.c = abc_getlocal_0($$.c);
2162 $$.c = abc_getproperty2($$.c, &m);
2165 if(f->kind == MEMBER_METHOD) {
2166 $$.t = TYPE_FUNCTION(f);
2171 /* look at classes in the current package and imported classes */
2172 } else if((a = find_class($1))) {
2174 $$.c = abc_getglobalscope($$.c);
2175 $$.c = abc_getslot($$.c, a->slot);
2178 $$.c = abc_getlex2($$.c, &m);
2180 $$.t = TYPE_CLASS(a);
2182 /* unknown object, let the avm2 resolve it */
2184 if(strcmp($1,"trace"))
2185 warning("Couldn't resolve '%s', doing late binding", $1);
2186 state->method->late_binding = 1;
2188 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
2191 $$.c = abc_findpropstrict2($$.c, &m);
2192 $$.c = abc_getproperty2($$.c, &m);
2197 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
2198 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
2199 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
2201 // ----------------- namespaces -------------------------------------------------
2203 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
2204 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
2205 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}
2207 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER