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);
533 for(mlist=implements;mlist;mlist=mlist->next) {
534 MULTINAME(m, mlist->classinfo);
535 abc_class_add_interface(state->cls->abc, &m);
538 /* now write the construction code for this class */
539 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
541 abc_method_body_t*m = global->init->method->body;
542 __ getglobalscope(m);
543 classinfo_t*s = extends;
548 //TODO: take a look at the current scope stack, maybe
549 // we can re-use something
554 multiname_t*s2 = sig2mname(s);
556 multiname_destroy(s2);
558 __ pushscope(m); count++;
559 m->code = m->code->prev->prev; // invert
561 /* continue appending after last op end */
562 while(m->code && m->code->next) m->code = m->code->next;
564 /* TODO: if this is one of *our* classes, we can also
565 do a getglobalscope/getslot <nr> (which references
566 the init function's slots) */
568 __ getlex2(m, extends2);
570 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
571 stack is not the superclass */
572 __ pushscope(m);count++;
575 /* notice: we get a verify error #1107 if the top element on the scope
576 stack is not the global object */
578 __ pushscope(m);count++;
580 __ newclass(m,state->cls->abc);
584 __ setslot(m, slotindex);
586 /* flash.display.MovieClip handling */
587 if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
588 if(state->package && state->package[0]) {
589 globalclass = concat3str(state->package, ".", classname);
591 globalclass = strdup(classname);
594 multiname_destroy(extends2);
597 static void endclass()
599 if(state->cls->init) {
600 if(!state->cls->abc->constructor) {
601 abc_method_t*m = abc_class_constructor(state->cls->abc, 0);
602 m->body->code = code_append(m->body->code, state->cls->init);
603 m->body->code = abc_returnvoid(m->body->code);
605 code_t*c = state->cls->abc->constructor->body->code;
606 c = code_append(state->cls->init, c);
607 state->cls->abc->constructor->body->code = c;
611 if(state->cls->static_init) {
612 if(!state->cls->abc->static_constructor) {
613 abc_method_t*m = abc_class_staticconstructor(state->cls->abc, 0);
614 m->body->code = code_append(m->body->code, state->cls->static_init);
615 m->body->code = abc_returnvoid(m->body->code);
617 state->cls->abc->static_constructor->body->code =
618 code_append(state->cls->static_init, state->cls->abc->static_constructor->body->code);
625 typedef struct _variable {
630 static int find_variable(char*name, classinfo_t**m)
632 state_list_t* s = state_stack;
636 v = dict_lookup(s->state->vars, name);
647 static int find_variable_safe(char*name, classinfo_t**m)
649 int i = find_variable(name, m);
651 syntaxerror("undefined variable: %s", name);
654 static char variable_exists(char*name)
656 return dict_lookup(state->vars, name)!=0;
658 static int new_variable(char*name, classinfo_t*type)
661 v->index = global->variable_count;
663 dict_put(state->vars, name, v);
664 return global->variable_count++;
666 #define TEMPVARNAME "__as3_temp__"
667 static int gettempvar()
669 int i = find_variable(TEMPVARNAME, 0);
671 i = new_variable(TEMPVARNAME, 0);
676 code_t* killvars(code_t*c)
679 for(t=0;t<state->vars->hashsize;t++) {
680 dictentry_t*e =state->vars->slots[t];
682 variable_t*v = (variable_t*)e->data;
683 //do this always, otherwise register types don't match
684 //in the verifier when doing nested loops
685 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
686 c = abc_kill(c, v->index);
694 static void check_constant_against_type(classinfo_t*t, constant_t*c)
696 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
697 if(TYPE_IS_NUMBER(t)) {
698 xassert(c->type == CONSTANT_FLOAT
699 || c->type == CONSTANT_INT
700 || c->type == CONSTANT_UINT);
701 } else if(TYPE_IS_UINT(t)) {
702 xassert(c->type == CONSTANT_UINT ||
703 (c->type == CONSTANT_INT && c->i>0));
704 } else if(TYPE_IS_INT(t)) {
705 xassert(c->type == CONSTANT_INT);
706 } else if(TYPE_IS_BOOLEAN(t)) {
707 xassert(c->type == CONSTANT_TRUE
708 || c->type == CONSTANT_FALSE);
712 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
714 memberinfo_t*minfo = 0;
715 if(getset != KW_GET && getset != KW_SET) {
716 if(registry_findmember(state->cls->info, name)) {
717 syntaxerror("class already contains a member/method called '%s'", name);
719 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
720 minfo->return_type = return_type;
721 // getslot on a member slot only returns "undefined", so no need
722 // to actually store these
723 //state->minfo->slot = state->method->abc->method->trait->slot_id;
725 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
729 else if(params->list)
730 type = params->list->param->type;
731 if((minfo=registry_findmember(state->cls->info, name))) {
732 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
733 syntaxerror("class already contains a member or method called '%s'", name);
735 syntaxerror("getter/setter for '%s' already defined", name);
736 /* make a setter or getter into a getset */
741 if(type && minfo->type != type)
742 syntaxerror("different type in getter and setter");
744 minfo = memberinfo_register(state->cls->info, name, gs);
747 /* can't assign a slot as getter and setter might have different slots */
748 //minfo->slot = slot;
750 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
751 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
752 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
753 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
754 if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
758 static int flags2access(int flags)
761 if(flags&FLAG_PUBLIC) {
762 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
763 access = ACCESS_PACKAGE;
764 } else if(flags&FLAG_PRIVATE) {
765 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
766 access = ACCESS_PRIVATE;
767 } else if(flags&FLAG_PROTECTED) {
768 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
769 access = ACCESS_PROTECTED;
771 access = ACCESS_PACKAGEINTERNAL;
776 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
777 params_t*params, classinfo_t*return_type)
780 syntaxerror("not able to start another method scope");
783 state->method = rfx_calloc(sizeof(methodstate_t));
784 state->method->initcode = 0;
785 state->method->is_constructor = !strcmp(state->cls->info->name,name);
786 state->method->has_super = 0;
788 global->variable_count = 0;
790 /* state->vars is initialized by state_new */
791 if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info)!=0) syntaxerror("Internal error");
793 for(p=params->list;p;p=p->next) {
794 new_variable(p->param->name, p->param->type);
796 if(state->method->is_constructor)
797 name = "__as3_constructor__";
798 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
801 static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
802 params_t*params, classinfo_t*return_type, code_t*body)
804 namespace_t mname_ns = {flags2access(flags), ""};
805 multiname_t mname = {QNAME, &mname_ns, 0, name};
809 multiname_t*type2 = sig2mname(return_type);
811 if(state->method->is_constructor) {
812 f = abc_class_constructor(state->cls->abc, type2);
814 if(flags&FLAG_STATIC)
815 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
817 f = abc_class_method(state->cls->abc, type2, &mname);
818 slot = f->trait->slot_id;
820 //flash doesn't seem to allow us to access function slots
821 //state->method->info->slot = slot;
823 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
824 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
825 if(params->varargs) f->flags |= METHOD_NEED_REST;
829 for(p=params->list;p;p=p->next) {
830 if(params->varargs && !p->next) {
831 break; //varargs: omit last parameter in function signature
833 multiname_t*m = sig2mname(p->param->type);
834 list_append(f->parameters, m);
835 if(p->param->value) {
836 check_constant_against_type(p->param->type, p->param->value);
837 opt=1;list_append(f->optional_parameters, p->param->value);
839 syntaxerror("non-optional parameter not allowed after optional parameters");
842 f->body->code = body;
849 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
854 void breakjumpsto(code_t*c, code_t*jump)
859 if(c->opcode == OPCODE___BREAK__) {
860 c->opcode = OPCODE_JUMP;
867 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
870 return registry_getanytype();
871 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
872 return registry_getanytype();
875 return registry_getanytype();
877 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
882 return abc_coerce_a(c);
886 // cast an "any" type to a specific type. subject to
887 // runtime exceptions
888 return abc_coerce2(c, &m);
891 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
892 return abc_coerce2(c, &m);
894 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
895 return abc_coerce2(c, &m);
897 /* these are subject to overflow */
898 if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) {
899 return abc_coerce2(c, &m);
901 if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) {
902 return abc_coerce2(c, &m);
905 classinfo_t*supertype = from;
907 if(supertype == to) {
908 // target type is one of from's superclasses
909 return abc_coerce2(c, &m);
912 while(supertype->interfaces[t]) {
913 if(supertype->interfaces[t]==to) {
914 // to type is one of from's interfaces
915 return abc_coerce2(c, &m);
919 supertype = supertype->superclass;
921 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
923 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
925 syntaxerror("can't convert type %s to %s", from->name, to->name);
928 code_t*defaultvalue(code_t*c, classinfo_t*type)
930 if(TYPE_IS_INT(type)) {
931 c = abc_pushbyte(c, 0);
932 } else if(TYPE_IS_UINT(type)) {
933 c = abc_pushuint(c, 0);
934 } else if(TYPE_IS_FLOAT(type)) {
936 } else if(TYPE_IS_BOOLEAN(type)) {
937 c = abc_pushfalse(c);
944 char is_pushundefined(code_t*c)
946 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
949 void parserassert(int b)
951 if(!b) syntaxerror("internal error: assertion failed");
954 static classinfo_t* find_class(char*name)
958 c = registry_findclass(state->package, name);
960 /* try explicit imports */
961 dictentry_t* e = dict_get_slot(state->imports, name);
965 if(!strcmp(e->key, name)) {
966 c = (classinfo_t*)e->data;
971 /* try package.* imports */
972 import_list_t*l = state->wildcard_imports;
976 //printf("does package %s contain a class %s?\n", l->import->package, name);
977 c = registry_findclass(l->import->package, name);
981 /* try global package */
983 c = registry_findclass("", name);
988 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
992 [prefix code] [read instruction]
996 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
999 if(in && in->opcode == OPCODE_COERCE_A) {
1000 in = code_cutlast(in);
1003 syntaxerror("internal error");
1005 /* chop off read instruction */
1009 prefix = r->prev;r->prev = 0;
1015 char use_temp_var = readbefore;
1017 /* generate the write instruction, and maybe append a dup to the prefix code */
1018 code_t* write = abc_nop(0);
1019 if(r->opcode == OPCODE_GETPROPERTY) {
1020 write->opcode = OPCODE_SETPROPERTY;
1021 multiname_t*m = (multiname_t*)r->data[0];
1022 write->data[0] = multiname_clone(m);
1023 if(m->type == QNAME || m->type == MULTINAME) {
1025 prefix = abc_dup(prefix); // we need the object, too
1028 } else if(m->type == MULTINAMEL) {
1030 /* dupping two values on the stack requires 5 operations and one register-
1031 couldn't adobe just have given us a dup2? */
1032 int temp = gettempvar();
1033 prefix = abc_setlocal(prefix, temp);
1034 prefix = abc_dup(prefix);
1035 prefix = abc_getlocal(prefix, temp);
1036 prefix = abc_swap(prefix);
1037 prefix = abc_getlocal(prefix, temp);
1041 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1043 } else if(r->opcode == OPCODE_GETSLOT) {
1044 write->opcode = OPCODE_SETSLOT;
1045 write->data[0] = r->data[0];
1047 prefix = abc_dup(prefix); // we need the object, too
1050 } else if(r->opcode == OPCODE_GETLOCAL) {
1051 write->opcode = OPCODE_SETLOCAL;
1052 write->data[0] = r->data[0];
1053 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1054 write->opcode = OPCODE_SETLOCAL_0;
1055 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1056 write->opcode = OPCODE_SETLOCAL_1;
1057 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1058 write->opcode = OPCODE_SETLOCAL_2;
1059 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1060 write->opcode = OPCODE_SETLOCAL_3;
1062 code_dump(r, 0, 0, "", stdout);
1063 syntaxerror("illegal lvalue: can't assign a value to this expression");
1070 /* with getproperty/getslot, we have to be extra careful not
1071 to execute the read code twice, as it might have side-effects
1072 (e.g. if the property is in fact a setter/getter combination)
1074 So read the value, modify it, and write it again,
1075 using prefix only once and making sure (by using a temporary
1076 register) that the return value is what we just wrote */
1077 temp = gettempvar();
1078 c = code_append(c, prefix);
1079 c = code_append(c, r);
1082 c = abc_setlocal(c, temp);
1084 c = code_append(c, middlepart);
1087 c = abc_setlocal(c, temp);
1089 c = code_append(c, write);
1090 c = abc_getlocal(c, temp);
1091 c = abc_kill(c, temp);
1093 /* if we're allowed to execute the read code twice *and*
1094 the middlepart doesn't modify the code, things are easier.
1096 code_t* r2 = code_dup(r);
1097 //c = code_append(c, prefix);
1098 parserassert(!prefix);
1099 c = code_append(c, r);
1100 c = code_append(c, middlepart);
1101 c = code_append(c, write);
1102 c = code_append(c, r2);
1105 /* even smaller version: overwrite the value without reading
1109 c = code_append(c, prefix);
1112 c = code_append(c, middlepart);
1113 c = code_append(c, write);
1114 c = code_append(c, r);
1116 temp = gettempvar();
1118 c = code_append(c, prefix);
1121 c = code_append(c, middlepart);
1123 c = abc_setlocal(c, temp);
1124 c = code_append(c, write);
1125 c = abc_getlocal(c, temp);
1132 #define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
1133 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1140 /* ------------ code blocks / statements ---------------- */
1144 MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/}
1145 MAYBECODE: {$$=code_new();}
1147 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1148 CODE: CODEPIECE {$$=$1;}
1150 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
1151 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
1152 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
1153 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
1154 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
1155 CODEPIECE: ';' {$$=code_new();}
1156 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
1157 CODEPIECE: VOIDEXPRESSION {$$=$1}
1158 CODEPIECE: FOR {$$=$1}
1159 CODEPIECE: WHILE {$$=$1}
1160 CODEPIECE: BREAK {$$=$1}
1161 CODEPIECE: RETURN {$$=$1}
1162 CODEPIECE: IF {$$=$1}
1163 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
1164 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
1166 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
1167 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1168 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1170 /* ------------ variables --------------------------- */
1172 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1173 | {$$.c=abc_pushundefined(0);
1177 VAR : "const" | "var"
1178 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
1180 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1181 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1183 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1185 if(variable_exists($2))
1186 syntaxerror("Variable %s already defined", $2);
1188 if(!is_subtype_of($4.t, $3)) {
1189 syntaxerror("Can't convert %s to %s", $4.t->name,
1193 int index = new_variable($2, $3);
1196 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1198 $$ = converttype($$, $4.t, $3);
1199 $$ = abc_setlocal($$, index);
1201 $$ = defaultvalue(0, $3);
1202 $$ = abc_setlocal($$, index);
1205 /* if this is a typed variable:
1206 push default value for type on stack */
1208 state->method->initcode = defaultvalue(state->method->initcode, $3);
1209 state->method->initcode = abc_setlocal(state->method->initcode, index);
1212 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1214 $$ = abc_coerce_a($$);
1215 $$ = abc_setlocal($$, index);
1221 /* that's the default for a local register, anyway
1223 state->method->initcode = abc_pushundefined(state->method->initcode);
1224 state->method->initcode = abc_setlocal(state->method->initcode, index);
1226 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1229 /* ------------ control flow ------------------------- */
1231 MAYBEELSE: %prec below_else {$$ = code_new();}
1232 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1233 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1235 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
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 {
1259 $$ = code_append($$, $4);
1260 code_t*loopstart = $$ = abc_label($$);
1261 $$ = code_append($$, $6.c);
1262 code_t*myif = $$ = abc_iffalse($$, 0);
1263 $$ = code_append($$, $10);
1264 $$ = code_append($$, $8);
1265 $$ = abc_jump($$, loopstart);
1266 code_t*out = $$ = abc_label($$);
1267 breakjumpsto($$, out);
1270 $$ = killvars($$);old_state();
1273 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1275 code_t*myjmp = $$ = abc_jump($$, 0);
1276 code_t*loopstart = $$ = abc_label($$);
1277 $$ = code_append($$, $6);
1278 myjmp->branch = $$ = abc_label($$);
1279 $$ = code_append($$, $4.c);
1280 $$ = abc_iftrue($$, loopstart);
1281 code_t*out = $$ = abc_label($$);
1282 breakjumpsto($$, out);
1284 $$ = killvars($$);old_state();
1288 $$ = abc___break__(0);
1291 /* ------------ packages and imports ---------------- */
1293 X_IDENTIFIER: T_IDENTIFIER
1294 | "package" {$$="package";}
1296 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3str($1,".",$3);}
1297 PACKAGE: X_IDENTIFIER {$$=$1;}
1299 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1300 PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()}
1302 IMPORT : "import" QNAME {
1305 syntaxerror("Couldn't import class\n");
1306 state_has_imports();
1307 dict_put(state->imports, c->name, c);
1310 IMPORT : "import" PACKAGE '.' '*' {
1313 state_has_imports();
1314 list_append(state->wildcard_imports, i);
1318 /* ------------ classes and interfaces (header) -------------- */
1320 MAYBE_MODIFIERS : {$$=0;}
1321 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
1322 MODIFIER_LIST : MODIFIER {$$=$1;}
1323 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
1325 MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
1326 | KW_PRIVATE {$$=FLAG_PRIVATE;}
1327 | KW_PROTECTED {$$=FLAG_PROTECTED;}
1328 | KW_STATIC {$$=FLAG_STATIC;}
1329 | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
1330 | KW_FINAL {$$=FLAG_FINAL;}
1331 | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
1332 | KW_NATIVE {$$=FLAG_NATIVE;}
1333 | KW_INTERNAL {$$=FLAG_INTERNAL;}
1335 EXTENDS : {$$=registry_getobjectclass();}
1336 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1338 EXTENDS_LIST : {$$=list_new();}
1339 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1341 IMPLEMENTS_LIST : {$$=list_new();}
1342 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1344 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
1345 EXTENDS IMPLEMENTS_LIST
1346 '{' {startclass($1,$3,$4,$5, 0);}
1347 MAYBE_DECLARATION_LIST
1350 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
1352 '{' {startclass($1,$3,0,$4,1);}
1353 MAYBE_IDECLARATION_LIST
1356 /* ------------ classes and interfaces (body) -------------- */
1358 MAYBE_DECLARATION_LIST :
1359 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1360 DECLARATION_LIST : DECLARATION
1361 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1363 DECLARATION : SLOT_DECLARATION
1364 DECLARATION : FUNCTION_DECLARATION
1366 MAYBE_IDECLARATION_LIST :
1367 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1368 IDECLARATION_LIST : IDECLARATION
1369 IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
1371 IDECLARATION : "var" T_IDENTIFIER {
1372 syntaxerror("variable declarations not allowed in interfaces");
1374 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
1376 if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
1377 syntaxerror("invalid method modifiers: interface methods always need to be public");
1379 startfunction(0,$1,$3,$4,&$6,$8);
1380 endfunction(0,$1,$3,$4,&$6,$8, 0);
1383 /* ------------ classes and interfaces (body, slots ) ------- */
1385 VARCONST: "var" | "const"
1387 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1389 memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
1391 info->flags = flags;
1394 namespace_t mname_ns = {flags2access(flags), ""};
1395 multiname_t mname = {QNAME, &mname_ns, 0, $3};
1397 if(!(flags&FLAG_STATIC)) {
1400 t=abc_class_slot(state->cls->abc, &mname, &m);
1402 t=abc_class_slot(state->cls->abc, &mname, 0);
1404 info->slot = t->slot_id;
1408 t=abc_class_staticslot(state->cls->abc, &mname, &m);
1410 t=abc_class_staticslot(state->cls->abc, &mname, 0);
1412 info->slot = t->slot_id;
1414 if($5.c && !is_pushundefined($5.c)) {
1416 c = abc_getlocal_0(c);
1417 c = code_append(c, $5.c);
1418 c = converttype(c, $5.t, $4);
1419 c = abc_setslot(c, t->slot_id);
1420 if(!(flags&FLAG_STATIC))
1421 state->cls->init = code_append(state->cls->init, c);
1423 state->cls->static_init = code_append(state->cls->static_init, c);
1426 t->kind= TRAIT_CONST;
1430 /* ------------ constants -------------------------------------- */
1432 MAYBESTATICCONSTANT: {$$=0;}
1433 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1435 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1436 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1437 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1438 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1439 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
1440 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1441 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
1442 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
1443 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
1445 /* ------------ classes and interfaces (body, functions) ------- */
1447 // non-vararg version
1449 memset(&$$,0,sizeof($$));
1451 MAYBE_PARAM_LIST: PARAM_LIST {
1456 MAYBE_PARAM_LIST: "..." PARAM {
1457 memset(&$$,0,sizeof($$));
1459 list_append($$.list, $2);
1461 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1464 list_append($$.list, $4);
1468 PARAM_LIST: PARAM_LIST ',' PARAM {
1470 list_append($$.list, $3);
1473 memset(&$$,0,sizeof($$));
1474 list_append($$.list, $1);
1477 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1478 $$ = malloc(sizeof(param_t));
1483 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1484 $$ = malloc(sizeof(param_t));
1486 $$->type = TYPE_ANY;
1489 GETSET : "get" {$$=$1;}
1493 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1494 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1497 if(state->method->late_binding) {
1498 c = abc_getlocal_0(c);
1499 c = abc_pushscope(c);
1501 if(state->method->is_constructor && !state->method->has_super) {
1502 // generate default constructor
1503 c = abc_getlocal_0(c);
1504 c = abc_constructsuper(c, 0);
1507 c = code_append(c, state->method->initcode);
1508 c = code_append(c, $11);
1510 /* append return if necessary */
1511 if(!c || c->opcode != OPCODE_RETURNVOID &&
1512 c->opcode != OPCODE_RETURNVALUE) {
1513 c = abc_returnvoid(c);
1515 endfunction(0,$1,$3,$4,&$6,$8,c);
1518 /* ------------- package + class ids --------------- */
1520 CLASS: T_IDENTIFIER {
1522 /* try current package */
1523 $$ = find_class($1);
1524 if(!$$) syntaxerror("Could not find class %s\n", $1);
1527 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1528 $$ = registry_findclass($1, $3);
1529 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
1532 QNAME: PACKAGEANDCLASS
1535 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1536 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1538 TYPE : QNAME {$$=$1;}
1539 | '*' {$$=registry_getanytype();}
1541 | "String" {$$=registry_getstringclass();}
1542 | "int" {$$=registry_getintclass();}
1543 | "uint" {$$=registry_getuintclass();}
1544 | "Boolean" {$$=registry_getbooleanclass();}
1545 | "Number" {$$=registry_getnumberclass();}
1548 MAYBETYPE: ':' TYPE {$$=$2;}
1551 /* ----------function calls, delete, constructor calls ------ */
1553 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1554 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1556 MAYBE_EXPRESSION_LIST : {$$=0;}
1557 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1558 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1559 typedcode_t*t = malloc(sizeof(typedcode_t));
1561 list_append($$, t);}
1562 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1563 typedcode_t*t = malloc(sizeof(typedcode_t));
1565 list_append($$, t);}
1567 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1572 $$.c = abc_getglobalscope($$.c);
1573 $$.c = abc_getslot($$.c, $2->slot);
1575 $$.c = abc_findpropstrict2($$.c, &m);
1578 typedcode_list_t*l = $3;
1581 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1586 $$.c = abc_construct($$.c, len);
1588 $$.c = abc_constructprop2($$.c, &m, len);
1592 /* TODO: use abc_call (for calling local variables),
1593 abc_callstatic (for calling own methods)
1596 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1597 typedcode_list_t*l = $3;
1599 code_t*paramcode = 0;
1601 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1607 if($$.c->opcode == OPCODE_COERCE_A) {
1608 $$.c = code_cutlast($$.c);
1612 multiname_t*name = 0;
1613 if($$.c->opcode == OPCODE_GETPROPERTY) {
1614 name = multiname_clone($$.c->data[0]);
1615 $$.c = code_cutlast($$.c);
1616 $$.c = code_append($$.c, paramcode);
1617 $$.c = abc_callproperty2($$.c, name, len);
1618 } else if($$.c->opcode == OPCODE_GETSLOT) {
1619 int slot = (int)(ptroff_t)$$.c->data[0];
1620 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
1621 if(t->kind!=TRAIT_METHOD) {
1622 //flash allows to assign closures to members.
1623 //syntaxerror("not a function");
1626 $$.c = code_cutlast($$.c);
1627 $$.c = code_append($$.c, paramcode);
1628 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1629 $$.c = abc_callproperty2($$.c, name, len);
1630 } else if($$.c->opcode == OPCODE_GETSUPER) {
1631 name = multiname_clone($$.c->data[0]);
1632 $$.c = code_cutlast($$.c);
1633 $$.c = code_append($$.c, paramcode);
1634 $$.c = abc_callsuper2($$.c, name, len);
1636 $$.c = abc_getlocal_0($$.c);
1637 $$.c = code_append($$.c, paramcode);
1638 $$.c = abc_call($$.c, len);
1643 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
1644 $$.t = $1.t->function->return_type;
1646 $$.c = abc_coerce_a($$.c);
1650 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
1651 if(!state->cls) syntaxerror("super() not allowed outside of a class");
1652 if(!state->method) syntaxerror("super() not allowed outside of a function");
1653 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
1656 $$.c = abc_getlocal_0($$.c);
1657 typedcode_list_t*l = 0;
1659 for(l=$3;l;l=l->next) {
1660 $$.c = code_append($$.c, l->typedcode->c);len++;
1663 this is dependent on the control path, check this somewhere else
1664 if(state->method->has_super)
1665 syntaxerror("constructor may call super() only once");
1667 state->method->has_super = 1;
1668 $$.c = abc_constructsuper($$.c, len);
1669 $$.c = abc_pushundefined($$.c);
1673 DELETE: "delete" E {
1675 if($$.c->opcode == OPCODE_COERCE_A) {
1676 $$.c = code_cutlast($$.c);
1678 multiname_t*name = 0;
1679 if($$.c->opcode == OPCODE_GETPROPERTY) {
1680 $$.c->opcode = OPCODE_DELETEPROPERTY;
1681 } else if($$.c->opcode == OPCODE_GETSLOT) {
1682 int slot = (int)(ptroff_t)$$.c->data[0];
1683 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
1684 $$.c = code_cutlast($$.c);
1685 $$.c = abc_deleteproperty2($$.c, name);
1687 $$.c = abc_getlocal_0($$.c);
1688 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
1689 $$.c = abc_deleteproperty2($$.c, &m);
1691 $$.t = TYPE_BOOLEAN;
1694 RETURN: "return" %prec prec_none {
1695 $$ = abc_returnvoid(0);
1697 RETURN: "return" EXPRESSION {
1699 $$ = abc_returnvalue($$);
1702 // ----------------------- expression types -------------------------------------
1704 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
1705 EXPRESSION : E %prec below_minus {$$ = $1;}
1706 EXPRESSION : EXPRESSION ',' E %prec below_minus {
1708 $$.c = cut_last_push($$.c);
1709 $$.c = code_append($$.c,$3.c);
1712 VOIDEXPRESSION : EXPRESSION %prec below_minus {
1713 $$=cut_last_push($1.c);
1716 // ----------------------- expression evaluation -------------------------------------
1719 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1721 E : DELETE {$$ = $1;}
1722 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1726 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1727 //MULTINAME(m, registry_getintclass());
1728 //$$.c = abc_coerce2($$.c, &m); // FIXME
1731 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1734 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1737 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1740 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1743 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
1746 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
1749 CONSTANT : "true" {$$.c = abc_pushtrue(0);
1750 $$.t = TYPE_BOOLEAN;
1752 CONSTANT : "false" {$$.c = abc_pushfalse(0);
1753 $$.t = TYPE_BOOLEAN;
1755 CONSTANT : "null" {$$.c = abc_pushnull(0);
1760 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1761 $$.t = TYPE_BOOLEAN;
1763 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1764 $$.t = TYPE_BOOLEAN;
1766 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1767 $$.t = TYPE_BOOLEAN;
1769 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1770 $$.t = TYPE_BOOLEAN;
1772 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1773 $$.t = TYPE_BOOLEAN;
1775 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1776 $$.t = TYPE_BOOLEAN;
1778 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
1779 $$.t = TYPE_BOOLEAN;
1781 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1782 $$.t = TYPE_BOOLEAN;
1785 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1787 $$.c = converttype($$.c, $1.t, $$.t);
1788 $$.c = abc_dup($$.c);
1789 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1790 $$.c = cut_last_push($$.c);
1791 $$.c = code_append($$.c,$3.c);
1792 $$.c = converttype($$.c, $3.t, $$.t);
1793 code_t*label = $$.c = abc_label($$.c);
1794 jmp->branch = label;
1797 $$.t = join_types($1.t, $3.t, 'A');
1798 /*printf("%08x:\n",$1.t);
1799 code_dump($1.c, 0, 0, "", stdout);
1800 printf("%08x:\n",$3.t);
1801 code_dump($3.c, 0, 0, "", stdout);
1802 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
1804 $$.c = converttype($$.c, $1.t, $$.t);
1805 $$.c = abc_dup($$.c);
1806 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1807 $$.c = cut_last_push($$.c);
1808 $$.c = code_append($$.c,$3.c);
1809 $$.c = converttype($$.c, $3.t, $$.t);
1810 code_t*label = $$.c = abc_label($$.c);
1811 jmp->branch = label;
1814 E : '!' E {$$.c=$2.c;
1815 $$.c = abc_not($$.c);
1816 $$.t = TYPE_BOOLEAN;
1819 E : '~' E {$$.c=$2.c;
1820 $$.c = abc_bitnot($$.c);
1824 E : E '&' E {$$.c = code_append($1.c,$3.c);
1825 $$.c = abc_bitand($$.c);
1829 E : E '^' E {$$.c = code_append($1.c,$3.c);
1830 $$.c = abc_bitxor($$.c);
1834 E : E '|' E {$$.c = code_append($1.c,$3.c);
1835 $$.c = abc_bitor($$.c);
1839 E : E '-' E {$$.c = code_append($1.c,$3.c);
1840 if(BOTH_INT($1,$3)) {
1841 $$.c = abc_subtract_i($$.c);
1844 $$.c = abc_subtract($$.c);
1848 E : E ">>" E {$$.c = code_append($1.c,$3.c);
1849 $$.c = abc_rshift($$.c);
1852 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
1853 $$.c = abc_urshift($$.c);
1856 E : E "<<" E {$$.c = code_append($1.c,$3.c);
1857 $$.c = abc_lshift($$.c);
1861 E : E '/' E {$$.c = code_append($1.c,$3.c);
1862 $$.c = abc_divide($$.c);
1865 E : E '+' E {$$.c = code_append($1.c,$3.c);
1866 $$.c = abc_add($$.c);
1869 E : E '%' E {$$.c = code_append($1.c,$3.c);
1870 $$.c = abc_modulo($$.c);
1873 E : E '*' E {$$.c = code_append($1.c,$3.c);
1874 if(BOTH_INT($1,$3)) {
1875 $$.c = abc_multiply_i($$.c);
1878 $$.c = abc_multiply($$.c);
1883 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
1884 if(use_astype && TYPE_IS_CLASS($3.t)) {
1885 MULTINAME(m,$3.t->cls);
1886 $$.c = abc_astype2($1.c, &m);
1889 $$.c = code_append($1.c, $3.c);
1890 $$.c = abc_astypelate($$.c);
1895 E : E "instanceof" E
1896 {$$.c = code_append($1.c, $3.c);
1897 $$.c = abc_instanceof($$.c);
1898 $$.t = TYPE_BOOLEAN;
1901 E : E "is" E {$$.c = code_append($1.c, $3.c);
1902 $$.c = abc_istypelate($$.c);
1903 $$.t = TYPE_BOOLEAN;
1906 E : "typeof" '(' E ')' {
1908 $$.c = abc_typeof($$.c);
1913 $$.c = cut_last_push($2.c);
1914 $$.c = abc_pushundefined($$.c);
1918 E : "void" { $$.c = abc_pushundefined(0);
1922 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
1927 $$.c=abc_negate_i($$.c);
1930 $$.c=abc_negate($$.c);
1937 $$.c = code_append($$.c, $3.c);
1939 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
1940 $$.c = abc_getproperty2($$.c, &m);
1941 $$.t = 0; // array elements have unknown type
1946 if(BOTH_INT($1,$3)) {
1947 c=abc_multiply_i(c);
1951 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
1952 $$.c = toreadwrite($1.c, c, 0, 0);
1957 code_t*c = abc_modulo($3.c);
1958 c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
1959 $$.c = toreadwrite($1.c, c, 0, 0);
1963 code_t*c = abc_lshift($3.c);
1964 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
1965 $$.c = toreadwrite($1.c, c, 0, 0);
1969 code_t*c = abc_rshift($3.c);
1970 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
1971 $$.c = toreadwrite($1.c, c, 0, 0);
1975 code_t*c = abc_urshift($3.c);
1976 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
1977 $$.c = toreadwrite($1.c, c, 0, 0);
1981 code_t*c = abc_divide($3.c);
1982 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
1983 $$.c = toreadwrite($1.c, c, 0, 0);
1988 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1993 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
1995 $$.c = toreadwrite($1.c, c, 0, 0);
1998 E : E "-=" E { code_t*c = $3.c;
1999 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2000 c=abc_subtract_i(c);
2004 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2006 $$.c = toreadwrite($1.c, c, 0, 0);
2009 E : E '=' E { code_t*c = 0;
2010 c = code_append(c, $3.c);
2011 c = converttype(c, $3.t, $1.t);
2012 $$.c = toreadwrite($1.c, c, 1, 0);
2016 E : E '?' E ':' E %prec below_assignment {
2018 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2019 $$.c = code_append($$.c, $3.c);
2020 code_t*j2 = $$.c = abc_jump($$.c, 0);
2021 $$.c = j1->branch = abc_label($$.c);
2022 $$.c = code_append($$.c, $5.c);
2023 $$.c = j2->branch = abc_label($$.c);
2024 $$.t = join_types($3.t,$5.t,'?');
2027 // TODO: use inclocal where appropriate
2028 E : E "++" { code_t*c = 0;
2029 classinfo_t*type = $1.t;
2030 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2031 c=abc_increment_i(c);
2037 c=converttype(c, type, $1.t);
2038 $$.c = toreadwrite($1.c, c, 0, 1);
2041 E : E "--" { code_t*c = 0;
2042 classinfo_t*type = $1.t;
2043 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2044 c=abc_decrement_i(c);
2050 c=converttype(c, type, $1.t);
2051 $$.c = toreadwrite($1.c, c, 0, 1);
2055 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2056 classinfo_t*type = $2.t;
2057 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2058 c=abc_increment_i(c);
2064 c=converttype(c, type, $2.t);
2065 $$.c = toreadwrite($2.c, c, 0, 0);
2069 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2070 classinfo_t*type = $2.t;
2071 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2072 c=abc_decrement_i(c);
2078 c=converttype(c, type, $2.t);
2079 $$.c = toreadwrite($2.c, c, 0, 0);
2083 E : "super" '.' T_IDENTIFIER
2084 { if(!state->cls->info)
2085 syntaxerror("super keyword not allowed outside a class");
2086 classinfo_t*t = state->cls->info->superclass;
2087 if(!t) t = TYPE_OBJECT;
2089 memberinfo_t*f = registry_findmember(t, $3);
2090 namespace_t ns = {flags2access(f->flags), ""};
2091 MEMBER_MULTINAME(m, f, $3);
2093 $$.c = abc_getlocal_0($$.c);
2094 $$.c = abc_getsuper2($$.c, &m);
2095 $$.t = memberinfo_gettype(f);
2098 E : E '.' T_IDENTIFIER
2100 classinfo_t*t = $1.t;
2102 if(TYPE_IS_CLASS(t) && t->cls) {
2107 memberinfo_t*f = registry_findmember(t, $3);
2109 if(f && !is_static != !(f->flags&FLAG_STATIC))
2111 if(f && f->slot && !noslot) {
2112 $$.c = abc_getslot($$.c, f->slot);
2114 MEMBER_MULTINAME(m, f, $3);
2115 $$.c = abc_getproperty2($$.c, &m);
2117 /* determine type */
2118 $$.t = memberinfo_gettype(f);
2120 $$.c = abc_coerce_a($$.c);
2122 /* when resolving a property on an unknown type, we do know the
2123 name of the property (and don't seem to need the package), but
2124 we need to make avm2 try out all access modes */
2125 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2126 $$.c = abc_getproperty2($$.c, &m);
2127 $$.c = abc_coerce_a($$.c);
2128 $$.t = registry_getanytype();
2132 VAR_READ : T_IDENTIFIER {
2139 /* look at variables */
2140 if((i = find_variable($1, &$$.t)) >= 0) {
2141 // $1 is a local variable
2142 $$.c = abc_getlocal($$.c, i);
2144 /* look at current class' members */
2145 } else if((f = registry_findmember(state->cls->info, $1))) {
2146 // $1 is a function in this class
2147 int var_is_static = (f->flags&FLAG_STATIC);
2148 int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
2149 if(var_is_static != i_am_static) {
2150 /* there doesn't seem to be any "static" way to access
2151 static properties of a class */
2152 state->method->late_binding = 1;
2154 namespace_t ns = {flags2access(f->flags), ""};
2155 multiname_t m = {QNAME, &ns, 0, $1};
2156 $$.c = abc_findpropstrict2($$.c, &m);
2157 $$.c = abc_getproperty2($$.c, &m);
2160 $$.c = abc_getlocal_0($$.c);
2161 $$.c = abc_getslot($$.c, f->slot);
2163 namespace_t ns = {flags2access(f->flags), ""};
2164 multiname_t m = {QNAME, &ns, 0, $1};
2165 $$.c = abc_getlocal_0($$.c);
2166 $$.c = abc_getproperty2($$.c, &m);
2169 if(f->kind == MEMBER_METHOD) {
2170 $$.t = TYPE_FUNCTION(f);
2175 /* look at classes in the current package and imported classes */
2176 } else if((a = find_class($1))) {
2178 $$.c = abc_getglobalscope($$.c);
2179 $$.c = abc_getslot($$.c, a->slot);
2182 $$.c = abc_getlex2($$.c, &m);
2184 $$.t = TYPE_CLASS(a);
2186 /* unknown object, let the avm2 resolve it */
2188 if(strcmp($1,"trace"))
2189 warning("Couldn't resolve '%s', doing late binding", $1);
2190 state->method->late_binding = 1;
2192 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
2195 $$.c = abc_findpropstrict2($$.c, &m);
2196 $$.c = abc_getproperty2($$.c, &m);
2201 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
2202 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
2203 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
2205 // ----------------- namespaces -------------------------------------------------
2207 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
2208 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
2209 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}
2211 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER