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 {
253 char has_constructor;
256 typedef struct _methodstate {
260 /* code that needs to be executed at the start of
261 a method (like initializing local registers) */
267 typedef struct _state {
271 import_list_t*wildcard_imports;
273 char has_own_imports;
276 methodstate_t*method;
281 typedef struct _global {
288 static global_t*global = 0;
289 static state_t* state = 0;
293 #define MULTINAME(m,x) \
296 registry_fill_multiname(&m, &m##_ns, x);
298 #define MEMBER_MULTINAME(m,f,n) \
302 m##_ns.access = flags2access(f->flags); \
306 m.namespace_set = 0; \
309 m.type = MULTINAME; \
311 m.namespace_set = &nopackage_namespace_set; \
315 /* warning: list length of namespace set is undefined */
316 #define MULTINAME_LATE(m, access, package) \
317 namespace_t m##_ns = {access, package}; \
318 namespace_set_t m##_nsset; \
319 namespace_list_t m##_l;m##_l.next = 0; \
320 m##_nsset.namespaces = &m##_l; \
321 m##_nsset = m##_nsset; \
322 m##_l.namespace = &m##_ns; \
323 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
325 static namespace_t ns1 = {ACCESS_PRIVATE, ""};
326 static namespace_t ns2 = {ACCESS_PROTECTED, ""};
327 static namespace_t ns3 = {ACCESS_PACKAGEINTERNAL, ""};
328 static namespace_t ns4 = {ACCESS_PACKAGE, ""};
329 static namespace_list_t nl4 = {&ns4,0};
330 static namespace_list_t nl3 = {&ns3,&nl4};
331 static namespace_list_t nl2 = {&ns2,&nl3};
332 static namespace_list_t nl1 = {&ns1,&nl2};
333 static namespace_set_t nopackage_namespace_set = {&nl1};
335 static state_list_t*state_stack=0;
337 static void init_globals()
339 global = rfx_calloc(sizeof(global_t));
342 static void new_state()
345 NEW(state_list_t, sl);
347 state_t*oldstate = state;
349 memcpy(s, state, sizeof(state_t)); //shallow copy
350 sl->next = state_stack;
353 s->imports = dict_new();
358 state->has_own_imports = 0;
359 state->vars = dict_new();
361 static void state_has_imports()
363 state->wildcard_imports = list_clone(state->wildcard_imports);
364 state->imports = dict_clone(state->imports);
365 state->has_own_imports = 1;
368 static void old_state()
370 if(!state_stack || !state_stack->next)
371 syntaxerror("invalid nesting");
372 state_t*oldstate = state;
373 state_list_t*old = state_stack;
374 state_stack = state_stack->next;
376 state = state_stack->state;
377 /*if(state->method->initcode) {
378 printf("residual initcode\n");
379 code_dump(state->method->initcode, 0, 0, "", stdout);
381 if(oldstate->has_own_imports) {
382 list_free(oldstate->wildcard_imports);
383 dict_destroy(oldstate->imports);oldstate->imports=0;
386 void initialize_state()
391 global->file = abc_file_new();
392 global->file->flags &= ~ABCFILE_LAZY;
394 global->init = abc_initscript(global->file, 0);
395 code_t*c = global->init->method->body->code;
397 c = abc_getlocal_0(c);
398 c = abc_pushscope(c);
400 /* findpropstrict doesn't just return a scope object- it
401 also makes it "active" somehow. Push local_0 on the
402 scope stack and read it back with findpropstrict, it'll
403 contain properties like "trace". Trying to find the same
404 property on a "vanilla" local_0 yields only a "undefined" */
405 //c = abc_findpropstrict(c, "[package]::trace");
407 /*c = abc_getlocal_0(c);
408 c = abc_findpropstrict(c, "[package]::trace");
410 c = abc_setlocal_1(c);
412 c = abc_pushbyte(c, 0);
413 c = abc_setlocal_2(c);
415 code_t*xx = c = abc_label(c);
416 c = abc_findpropstrict(c, "[package]::trace");
417 c = abc_pushstring(c, "prop:");
418 c = abc_hasnext2(c, 1, 2);
420 c = abc_setlocal_3(c);
421 c = abc_callpropvoid(c, "[package]::trace", 2);
422 c = abc_getlocal_3(c);
424 c = abc_iftrue(c,xx);*/
426 c = abc_findpropstrict(c, "[package]::trace");
427 c = abc_pushstring(c, "[entering global init function]");
428 c = abc_callpropvoid(c, "[package]::trace", 1);
430 global->init->method->body->code = c;
432 void* finalize_state()
434 if(state->level!=1) {
435 syntaxerror("unexpected end of file");
437 abc_method_body_t*m = global->init->method->body;
440 __ findpropstrict(m, "[package]::trace");
441 __ pushstring(m, "[leaving global init function]");
442 __ callpropvoid(m, "[package]::trace", 1);
448 static void startpackage(char*name)
451 syntaxerror("Packages can not be nested.");
454 /*printf("entering package \"%s\"\n", name);*/
455 state->package = name;
457 static void endpackage()
459 /*printf("leaving package \"%s\"\n", state->package);*/
464 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
467 syntaxerror("inner classes now allowed");
470 state->cls = rfx_calloc(sizeof(classstate_t));
473 classinfo_list_t*mlist=0;
474 /*printf("entering class %s\n", name);
475 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n");
477 printf(" extends: %s.%s\n", extends->package, extends->name);
478 printf(" implements (%d): ", list_length(implements));
479 for(mlist=implements;mlist;mlist=mlist->next) {
480 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
485 if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL))
486 syntaxerror("invalid modifier(s)");
488 if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
489 syntaxerror("public and internal not supported at the same time.");
491 /* create the class name, together with the proper attributes */
495 if(!(flags&FLAG_PUBLIC) && !state->package) {
496 access = ACCESS_PRIVATE; package = current_filename;
497 } else if(!(flags&FLAG_PUBLIC) && state->package) {
498 access = ACCESS_PACKAGEINTERNAL; package = state->package;
499 } else if(state->package) {
500 access = ACCESS_PACKAGE; package = state->package;
502 syntaxerror("public classes only allowed inside a package");
505 if(registry_findclass(package, classname)) {
506 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
510 /* build info struct */
511 int num_interfaces = (list_length(implements));
512 state->cls->info = classinfo_register(access, package, classname, num_interfaces);
513 state->cls->info->superclass = extends?extends:TYPE_OBJECT;
515 classinfo_list_t*l = implements;
516 for(l=implements;l;l=l->next) {
517 state->cls->info->interfaces[pos++] = l->classinfo;
520 multiname_t*extends2 = sig2mname(extends);
522 MULTINAME(classname2,state->cls->info);
525 state->cls_init = abc_getlocal_0(state->cls_init);
526 state->cls_init = abc_constructsuper(state->cls_init, 0);
529 state->cls->abc = abc_class_new(global->file, &classname2, extends2);
530 if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
531 if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
532 if(interface) abc_class_interface(state->cls->abc);
533 abc_class_protectedNS(state->cls->abc, classname);
535 for(mlist=implements;mlist;mlist=mlist->next) {
536 MULTINAME(m, mlist->classinfo);
537 abc_class_add_interface(state->cls->abc, &m);
540 /* now write the construction code for this class */
541 int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
543 abc_method_body_t*m = global->init->method->body;
544 __ getglobalscope(m);
545 classinfo_t*s = extends;
550 //TODO: take a look at the current scope stack, maybe
551 // we can re-use something
556 multiname_t*s2 = sig2mname(s);
558 multiname_destroy(s2);
560 __ pushscope(m); count++;
561 m->code = m->code->prev->prev; // invert
563 /* continue appending after last op end */
564 while(m->code && m->code->next) m->code = m->code->next;
566 /* TODO: if this is one of *our* classes, we can also
567 do a getglobalscope/getslot <nr> (which references
568 the init function's slots) */
570 __ getlex2(m, extends2);
572 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
573 stack is not the superclass */
574 __ pushscope(m);count++;
577 /* notice: we get a verify error #1107 if the top element on the scope
578 stack is not the global object */
580 __ pushscope(m);count++;
582 __ newclass(m,state->cls->abc);
586 __ setslot(m, slotindex);
588 /* flash.display.MovieClip handling */
589 if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
590 if(state->package && state->package[0]) {
591 globalclass = concat3str(state->package, ".", classname);
593 globalclass = strdup(classname);
596 multiname_destroy(extends2);
599 static code_t* wrap_function(code_t*c,code_t*initcode, code_t*body)
601 c = code_append(c, initcode);
602 c = code_append(c, body);
603 /* append return if necessary */
604 if(!c || c->opcode != OPCODE_RETURNVOID &&
605 c->opcode != OPCODE_RETURNVALUE) {
606 c = abc_returnvoid(c);
611 static void endclass()
613 if(!state->cls->has_constructor) {
615 c = abc_getlocal_0(c);
616 c = abc_constructsuper(c, 0);
617 state->cls->init = code_append(state->cls->init, c);
620 if(state->cls->init) {
621 abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
622 m->body->code = wrap_function(0, state->cls->init, m->body->code);
624 if(state->cls->static_init) {
625 abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0);
626 m->body->code = wrap_function(0, state->cls->static_init, m->body->code);
628 // handy for scope testing
632 abc_class_getstaticconstructor(state->cls->abc,0)->body->code = c;*/
638 typedef struct _variable {
643 static int find_variable(char*name, classinfo_t**m)
645 state_list_t* s = state_stack;
649 v = dict_lookup(s->state->vars, name);
660 static int find_variable_safe(char*name, classinfo_t**m)
662 int i = find_variable(name, m);
664 syntaxerror("undefined variable: %s", name);
667 static char variable_exists(char*name)
669 return dict_lookup(state->vars, name)!=0;
671 static int new_variable(char*name, classinfo_t*type)
674 v->index = global->variable_count;
676 dict_put(state->vars, name, v);
677 return global->variable_count++;
679 #define TEMPVARNAME "__as3_temp__"
680 static int gettempvar()
682 int i = find_variable(TEMPVARNAME, 0);
684 i = new_variable(TEMPVARNAME, 0);
689 code_t* killvars(code_t*c)
692 for(t=0;t<state->vars->hashsize;t++) {
693 dictentry_t*e =state->vars->slots[t];
695 variable_t*v = (variable_t*)e->data;
696 //do this always, otherwise register types don't match
697 //in the verifier when doing nested loops
698 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
699 c = abc_kill(c, v->index);
707 static void check_constant_against_type(classinfo_t*t, constant_t*c)
709 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
710 if(TYPE_IS_NUMBER(t)) {
711 xassert(c->type == CONSTANT_FLOAT
712 || c->type == CONSTANT_INT
713 || c->type == CONSTANT_UINT);
714 } else if(TYPE_IS_UINT(t)) {
715 xassert(c->type == CONSTANT_UINT ||
716 (c->type == CONSTANT_INT && c->i>0));
717 } else if(TYPE_IS_INT(t)) {
718 xassert(c->type == CONSTANT_INT);
719 } else if(TYPE_IS_BOOLEAN(t)) {
720 xassert(c->type == CONSTANT_TRUE
721 || c->type == CONSTANT_FALSE);
725 static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot)
727 memberinfo_t*minfo = 0;
728 if(getset != KW_GET && getset != KW_SET) {
729 if(registry_findmember(state->cls->info, name)) {
730 syntaxerror("class already contains a member/method called '%s'", name);
732 minfo = memberinfo_register(state->cls->info, name, MEMBER_METHOD);
733 minfo->return_type = return_type;
734 // getslot on a member slot only returns "undefined", so no need
735 // to actually store these
736 //state->minfo->slot = state->method->abc->method->trait->slot_id;
738 int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET;
742 else if(params->list)
743 type = params->list->param->type;
744 if((minfo=registry_findmember(state->cls->info, name))) {
745 if(minfo->kind & ~(MEMBER_GET|MEMBER_SET))
746 syntaxerror("class already contains a member or method called '%s'", name);
748 syntaxerror("getter/setter for '%s' already defined", name);
749 /* make a setter or getter into a getset */
754 if(type && minfo->type != type)
755 syntaxerror("different type in getter and setter");
757 minfo = memberinfo_register(state->cls->info, name, gs);
760 /* can't assign a slot as getter and setter might have different slots */
761 //minfo->slot = slot;
763 if(flags&FLAG_STATIC) minfo->flags |= FLAG_STATIC;
764 if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC;
765 if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE;
766 if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED;
767 if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL;
771 static int flags2access(int flags)
774 if(flags&FLAG_PUBLIC) {
775 if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
776 access = ACCESS_PACKAGE;
777 } else if(flags&FLAG_PRIVATE) {
778 if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
779 access = ACCESS_PRIVATE;
780 } else if(flags&FLAG_PROTECTED) {
781 if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels");
782 access = ACCESS_PROTECTED;
784 access = ACCESS_PACKAGEINTERNAL;
789 static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
790 params_t*params, classinfo_t*return_type)
793 syntaxerror("not able to start another method scope");
796 state->method = rfx_calloc(sizeof(methodstate_t));
797 state->method->initcode = 0;
798 state->method->is_constructor = !strcmp(state->cls->info->name,name);
799 state->method->has_super = 0;
801 state->cls->has_constructor |= state->method->is_constructor;
803 global->variable_count = 0;
805 /* state->vars is initialized by state_new */
806 if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info)!=0) syntaxerror("Internal error");
808 for(p=params->list;p;p=p->next) {
809 new_variable(p->param->name, p->param->type);
811 if(state->method->is_constructor)
812 name = "__as3_constructor__";
813 state->method->info = registerfunction(getset, flags, name, params, return_type, 0);
816 static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name,
817 params_t*params, classinfo_t*return_type, code_t*body)
819 namespace_t mname_ns = {flags2access(flags), ""};
820 multiname_t mname = {QNAME, &mname_ns, 0, name};
824 multiname_t*type2 = sig2mname(return_type);
826 if(state->method->is_constructor) {
827 f = abc_class_getconstructor(state->cls->abc, type2);
829 if(flags&FLAG_STATIC)
830 f = abc_class_staticmethod(state->cls->abc, type2, &mname);
832 f = abc_class_method(state->cls->abc, type2, &mname);
833 slot = f->trait->slot_id;
835 //flash doesn't seem to allow us to access function slots
836 //state->method->info->slot = slot;
838 if(getset == KW_GET) f->trait->kind = TRAIT_GETTER;
839 if(getset == KW_SET) f->trait->kind = TRAIT_SETTER;
840 if(params->varargs) f->flags |= METHOD_NEED_REST;
844 for(p=params->list;p;p=p->next) {
845 if(params->varargs && !p->next) {
846 break; //varargs: omit last parameter in function signature
848 multiname_t*m = sig2mname(p->param->type);
849 list_append(f->parameters, m);
850 if(p->param->value) {
851 check_constant_against_type(p->param->type, p->param->value);
852 opt=1;list_append(f->optional_parameters, p->param->value);
854 syntaxerror("non-optional parameter not allowed after optional parameters");
857 f->body->code = body;
864 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
869 void breakjumpsto(code_t*c, code_t*jump)
874 if(c->opcode == OPCODE___BREAK__) {
875 c->opcode = OPCODE_JUMP;
882 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
885 return registry_getanytype();
886 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
887 return registry_getanytype();
890 return registry_getanytype();
892 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
897 return abc_coerce_a(c);
901 // cast an "any" type to a specific type. subject to
902 // runtime exceptions
903 return abc_coerce2(c, &m);
906 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
907 return abc_coerce2(c, &m);
909 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
910 return abc_coerce2(c, &m);
912 /* these are subject to overflow */
913 if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) {
914 return abc_coerce2(c, &m);
916 if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) {
917 return abc_coerce2(c, &m);
920 classinfo_t*supertype = from;
922 if(supertype == to) {
923 // target type is one of from's superclasses
924 return abc_coerce2(c, &m);
927 while(supertype->interfaces[t]) {
928 if(supertype->interfaces[t]==to) {
929 // to type is one of from's interfaces
930 return abc_coerce2(c, &m);
934 supertype = supertype->superclass;
936 if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to))
938 if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to))
940 syntaxerror("can't convert type %s to %s", from->name, to->name);
943 code_t*defaultvalue(code_t*c, classinfo_t*type)
945 if(TYPE_IS_INT(type)) {
946 c = abc_pushbyte(c, 0);
947 } else if(TYPE_IS_UINT(type)) {
948 c = abc_pushuint(c, 0);
949 } else if(TYPE_IS_FLOAT(type)) {
951 } else if(TYPE_IS_BOOLEAN(type)) {
952 c = abc_pushfalse(c);
959 char is_pushundefined(code_t*c)
961 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
964 void parserassert(int b)
966 if(!b) syntaxerror("internal error: assertion failed");
969 static classinfo_t* find_class(char*name)
973 c = registry_findclass(state->package, name);
975 /* try explicit imports */
976 dictentry_t* e = dict_get_slot(state->imports, name);
980 if(!strcmp(e->key, name)) {
981 c = (classinfo_t*)e->data;
986 /* try package.* imports */
987 import_list_t*l = state->wildcard_imports;
991 //printf("does package %s contain a class %s?\n", l->import->package, name);
992 c = registry_findclass(l->import->package, name);
996 /* try global package */
998 c = registry_findclass("", name);
1003 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
1007 [prefix code] [read instruction]
1011 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
1014 if(in && in->opcode == OPCODE_COERCE_A) {
1015 in = code_cutlast(in);
1018 syntaxerror("internal error");
1020 /* chop off read instruction */
1024 prefix = r->prev;r->prev = 0;
1030 char use_temp_var = readbefore;
1032 /* generate the write instruction, and maybe append a dup to the prefix code */
1033 code_t* write = abc_nop(0);
1034 if(r->opcode == OPCODE_GETPROPERTY) {
1035 write->opcode = OPCODE_SETPROPERTY;
1036 multiname_t*m = (multiname_t*)r->data[0];
1037 write->data[0] = multiname_clone(m);
1038 if(m->type == QNAME || m->type == MULTINAME) {
1040 prefix = abc_dup(prefix); // we need the object, too
1043 } else if(m->type == MULTINAMEL) {
1045 /* dupping two values on the stack requires 5 operations and one register-
1046 couldn't adobe just have given us a dup2? */
1047 int temp = gettempvar();
1048 prefix = abc_setlocal(prefix, temp);
1049 prefix = abc_dup(prefix);
1050 prefix = abc_getlocal(prefix, temp);
1051 prefix = abc_swap(prefix);
1052 prefix = abc_getlocal(prefix, temp);
1056 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
1058 } else if(r->opcode == OPCODE_GETSLOT) {
1059 write->opcode = OPCODE_SETSLOT;
1060 write->data[0] = r->data[0];
1062 prefix = abc_dup(prefix); // we need the object, too
1065 } else if(r->opcode == OPCODE_GETLOCAL) {
1066 write->opcode = OPCODE_SETLOCAL;
1067 write->data[0] = r->data[0];
1068 } else if(r->opcode == OPCODE_GETLOCAL_0) {
1069 write->opcode = OPCODE_SETLOCAL_0;
1070 } else if(r->opcode == OPCODE_GETLOCAL_1) {
1071 write->opcode = OPCODE_SETLOCAL_1;
1072 } else if(r->opcode == OPCODE_GETLOCAL_2) {
1073 write->opcode = OPCODE_SETLOCAL_2;
1074 } else if(r->opcode == OPCODE_GETLOCAL_3) {
1075 write->opcode = OPCODE_SETLOCAL_3;
1077 code_dump(r, 0, 0, "", stdout);
1078 syntaxerror("illegal lvalue: can't assign a value to this expression");
1085 /* with getproperty/getslot, we have to be extra careful not
1086 to execute the read code twice, as it might have side-effects
1087 (e.g. if the property is in fact a setter/getter combination)
1089 So read the value, modify it, and write it again,
1090 using prefix only once and making sure (by using a temporary
1091 register) that the return value is what we just wrote */
1092 temp = gettempvar();
1093 c = code_append(c, prefix);
1094 c = code_append(c, r);
1097 c = abc_setlocal(c, temp);
1099 c = code_append(c, middlepart);
1102 c = abc_setlocal(c, temp);
1104 c = code_append(c, write);
1105 c = abc_getlocal(c, temp);
1106 c = abc_kill(c, temp);
1108 /* if we're allowed to execute the read code twice *and*
1109 the middlepart doesn't modify the code, things are easier.
1111 code_t* r2 = code_dup(r);
1112 //c = code_append(c, prefix);
1113 parserassert(!prefix);
1114 c = code_append(c, r);
1115 c = code_append(c, middlepart);
1116 c = code_append(c, write);
1117 c = code_append(c, r2);
1120 /* even smaller version: overwrite the value without reading
1124 c = code_append(c, prefix);
1127 c = code_append(c, middlepart);
1128 c = code_append(c, write);
1129 c = code_append(c, r);
1131 temp = gettempvar();
1133 c = code_append(c, prefix);
1136 c = code_append(c, middlepart);
1138 c = abc_setlocal(c, temp);
1139 c = code_append(c, write);
1140 c = abc_getlocal(c, temp);
1147 #define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t))
1148 #define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
1155 /* ------------ code blocks / statements ---------------- */
1159 MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/}
1160 MAYBECODE: {$$=code_new();}
1162 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1163 CODE: CODEPIECE {$$=$1;}
1165 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
1166 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
1167 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
1168 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
1169 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
1170 CODEPIECE: ';' {$$=code_new();}
1171 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
1172 CODEPIECE: VOIDEXPRESSION {$$=$1}
1173 CODEPIECE: FOR {$$=$1}
1174 CODEPIECE: WHILE {$$=$1}
1175 CODEPIECE: BREAK {$$=$1}
1176 CODEPIECE: RETURN {$$=$1}
1177 CODEPIECE: IF {$$=$1}
1178 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
1179 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
1181 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
1182 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1183 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1185 /* ------------ variables --------------------------- */
1187 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1188 | {$$.c=abc_pushundefined(0);
1192 VAR : "const" | "var"
1193 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
1195 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1196 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1198 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1200 if(variable_exists($2))
1201 syntaxerror("Variable %s already defined", $2);
1203 if(!is_subtype_of($4.t, $3)) {
1204 syntaxerror("Can't convert %s to %s", $4.t->name,
1208 int index = new_variable($2, $3);
1211 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1213 $$ = converttype($$, $4.t, $3);
1214 $$ = abc_setlocal($$, index);
1216 $$ = defaultvalue(0, $3);
1217 $$ = abc_setlocal($$, index);
1220 /* if this is a typed variable:
1221 push default value for type on stack */
1223 state->method->initcode = defaultvalue(state->method->initcode, $3);
1224 state->method->initcode = abc_setlocal(state->method->initcode, index);
1227 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1229 $$ = abc_coerce_a($$);
1230 $$ = abc_setlocal($$, index);
1236 /* that's the default for a local register, anyway
1238 state->method->initcode = abc_pushundefined(state->method->initcode);
1239 state->method->initcode = abc_setlocal(state->method->initcode, index);
1241 //printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1244 /* ------------ control flow ------------------------- */
1246 MAYBEELSE: %prec below_else {$$ = code_new();}
1247 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1248 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1250 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1252 $$ = code_append($$, $4.c);
1253 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1255 $$ = code_append($$, $6);
1257 myjmp = $$ = abc_jump($$, 0);
1259 myif->branch = $$ = abc_nop($$);
1261 $$ = code_append($$, $7);
1262 myjmp->branch = $$ = abc_nop($$);
1265 $$ = killvars($$);old_state();
1268 FOR_INIT : {$$=code_new();}
1269 FOR_INIT : VARIABLE_DECLARATION
1270 FOR_INIT : VOIDEXPRESSION
1272 FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1274 $$ = code_append($$, $4);
1275 code_t*loopstart = $$ = abc_label($$);
1276 $$ = code_append($$, $6.c);
1277 code_t*myif = $$ = abc_iffalse($$, 0);
1278 $$ = code_append($$, $10);
1279 $$ = code_append($$, $8);
1280 $$ = abc_jump($$, loopstart);
1281 code_t*out = $$ = abc_nop($$);
1282 breakjumpsto($$, out);
1285 $$ = killvars($$);old_state();
1288 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1291 code_t*myjmp = $$ = abc_jump($$, 0);
1292 code_t*loopstart = $$ = abc_label($$);
1293 $$ = code_append($$, $6);
1294 myjmp->branch = $$ = abc_nop($$);
1295 $$ = code_append($$, $4.c);
1296 $$ = abc_iftrue($$, loopstart);
1297 code_t*out = $$ = abc_nop($$);
1298 breakjumpsto($$, out);
1300 $$ = killvars($$);old_state();
1304 $$ = abc___break__(0);
1307 /* ------------ packages and imports ---------------- */
1309 X_IDENTIFIER: T_IDENTIFIER
1310 | "package" {$$="package";}
1312 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3str($1,".",$3);}
1313 PACKAGE: X_IDENTIFIER {$$=$1;}
1315 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1316 PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()}
1318 IMPORT : "import" QNAME {
1321 syntaxerror("Couldn't import class\n");
1322 state_has_imports();
1323 dict_put(state->imports, c->name, c);
1326 IMPORT : "import" PACKAGE '.' '*' {
1329 state_has_imports();
1330 list_append(state->wildcard_imports, i);
1334 /* ------------ classes and interfaces (header) -------------- */
1336 MAYBE_MODIFIERS : {$$=0;}
1337 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
1338 MODIFIER_LIST : MODIFIER {$$=$1;}
1339 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
1341 MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;}
1342 | KW_PRIVATE {$$=FLAG_PRIVATE;}
1343 | KW_PROTECTED {$$=FLAG_PROTECTED;}
1344 | KW_STATIC {$$=FLAG_STATIC;}
1345 | KW_DYNAMIC {$$=FLAG_DYNAMIC;}
1346 | KW_FINAL {$$=FLAG_FINAL;}
1347 | KW_OVERRIDE {$$=FLAG_OVERRIDE;}
1348 | KW_NATIVE {$$=FLAG_NATIVE;}
1349 | KW_INTERNAL {$$=FLAG_INTERNAL;}
1351 EXTENDS : {$$=registry_getobjectclass();}
1352 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1354 EXTENDS_LIST : {$$=list_new();}
1355 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1357 IMPLEMENTS_LIST : {$$=list_new();}
1358 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1360 CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER
1361 EXTENDS IMPLEMENTS_LIST
1362 '{' {startclass($1,$3,$4,$5, 0);}
1363 MAYBE_DECLARATION_LIST
1366 INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER
1368 '{' {startclass($1,$3,0,$4,1);}
1369 MAYBE_IDECLARATION_LIST
1372 /* ------------ classes and interfaces (body) -------------- */
1374 MAYBE_DECLARATION_LIST :
1375 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1376 DECLARATION_LIST : DECLARATION
1377 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1379 DECLARATION : SLOT_DECLARATION
1380 DECLARATION : FUNCTION_DECLARATION
1382 MAYBE_IDECLARATION_LIST :
1383 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1384 IDECLARATION_LIST : IDECLARATION
1385 IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION
1387 IDECLARATION : "var" T_IDENTIFIER {
1388 syntaxerror("variable declarations not allowed in interfaces");
1390 IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE {
1392 if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) {
1393 syntaxerror("invalid method modifiers: interface methods always need to be public");
1395 startfunction(0,$1,$3,$4,&$6,$8);
1396 endfunction(0,$1,$3,$4,&$6,$8, 0);
1399 /* ------------ classes and interfaces (body, slots ) ------- */
1401 VARCONST: "var" | "const"
1403 SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1405 memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT);
1407 info->flags = flags;
1410 namespace_t mname_ns = {flags2access(flags), ""};
1411 multiname_t mname = {QNAME, &mname_ns, 0, $3};
1413 if(!(flags&FLAG_STATIC)) {
1416 t=abc_class_slot(state->cls->abc, &mname, &m);
1418 t=abc_class_slot(state->cls->abc, &mname, 0);
1420 info->slot = t->slot_id;
1424 t=abc_class_staticslot(state->cls->abc, &mname, &m);
1426 t=abc_class_staticslot(state->cls->abc, &mname, 0);
1428 info->slot = t->slot_id;
1430 if($5.c && !is_pushundefined($5.c)) {
1432 c = abc_getlocal_0(c);
1433 c = code_append(c, $5.c);
1434 c = converttype(c, $5.t, $4);
1435 c = abc_setslot(c, t->slot_id);
1436 if(!(flags&FLAG_STATIC))
1437 state->cls->init = code_append(state->cls->init, c);
1439 state->cls->static_init = code_append(state->cls->static_init, c);
1442 t->kind= TRAIT_CONST;
1446 /* ------------ constants -------------------------------------- */
1448 MAYBESTATICCONSTANT: {$$=0;}
1449 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1451 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1452 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1453 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1454 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1455 STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);}
1456 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1457 STATICCONSTANT : "true" {$$ = constant_new_true($1);}
1458 STATICCONSTANT : "false" {$$ = constant_new_false($1);}
1459 STATICCONSTANT : "null" {$$ = constant_new_null($1);}
1461 /* ------------ classes and interfaces (body, functions) ------- */
1463 // non-vararg version
1465 memset(&$$,0,sizeof($$));
1467 MAYBE_PARAM_LIST: PARAM_LIST {
1472 MAYBE_PARAM_LIST: "..." PARAM {
1473 memset(&$$,0,sizeof($$));
1475 list_append($$.list, $2);
1477 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1480 list_append($$.list, $4);
1484 PARAM_LIST: PARAM_LIST ',' PARAM {
1486 list_append($$.list, $3);
1489 memset(&$$,0,sizeof($$));
1490 list_append($$.list, $1);
1493 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1494 $$ = malloc(sizeof(param_t));
1499 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1500 $$ = malloc(sizeof(param_t));
1502 $$->type = TYPE_ANY;
1505 GETSET : "get" {$$=$1;}
1509 FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1510 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1513 if(state->method->late_binding) {
1514 c = abc_getlocal_0(c);
1515 c = abc_pushscope(c);
1517 if(state->method->is_constructor && !state->method->has_super) {
1518 // call default constructor
1519 c = abc_getlocal_0(c);
1520 c = abc_constructsuper(c, 0);
1522 c = wrap_function(c, state->method->initcode, $11);
1523 endfunction(0,$1,$3,$4,&$6,$8,c);
1526 /* ------------- package + class ids --------------- */
1528 CLASS: T_IDENTIFIER {
1530 /* try current package */
1531 $$ = find_class($1);
1532 if(!$$) syntaxerror("Could not find class %s\n", $1);
1535 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1536 $$ = registry_findclass($1, $3);
1537 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1, $3);
1540 QNAME: PACKAGEANDCLASS
1543 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1544 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1546 TYPE : QNAME {$$=$1;}
1547 | '*' {$$=registry_getanytype();}
1549 | "String" {$$=registry_getstringclass();}
1550 | "int" {$$=registry_getintclass();}
1551 | "uint" {$$=registry_getuintclass();}
1552 | "Boolean" {$$=registry_getbooleanclass();}
1553 | "Number" {$$=registry_getnumberclass();}
1556 MAYBETYPE: ':' TYPE {$$=$2;}
1559 /* ----------function calls, delete, constructor calls ------ */
1561 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1562 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1564 MAYBE_EXPRESSION_LIST : {$$=0;}
1565 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1566 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1567 typedcode_t*t = malloc(sizeof(typedcode_t));
1569 list_append($$, t);}
1570 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1571 typedcode_t*t = malloc(sizeof(typedcode_t));
1573 list_append($$, t);}
1575 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1580 $$.c = abc_getglobalscope($$.c);
1581 $$.c = abc_getslot($$.c, $2->slot);
1583 $$.c = abc_findpropstrict2($$.c, &m);
1586 typedcode_list_t*l = $3;
1589 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1594 $$.c = abc_construct($$.c, len);
1596 $$.c = abc_constructprop2($$.c, &m, len);
1600 /* TODO: use abc_call (for calling local variables),
1601 abc_callstatic (for calling own methods)
1604 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1605 typedcode_list_t*l = $3;
1607 code_t*paramcode = 0;
1609 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1615 if($$.c->opcode == OPCODE_COERCE_A) {
1616 $$.c = code_cutlast($$.c);
1620 multiname_t*name = 0;
1621 if($$.c->opcode == OPCODE_GETPROPERTY) {
1622 name = multiname_clone($$.c->data[0]);
1623 $$.c = code_cutlast($$.c);
1624 $$.c = code_append($$.c, paramcode);
1625 $$.c = abc_callproperty2($$.c, name, len);
1626 } else if($$.c->opcode == OPCODE_GETSLOT) {
1627 int slot = (int)(ptroff_t)$$.c->data[0];
1628 trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
1629 if(t->kind!=TRAIT_METHOD) {
1630 //flash allows to assign closures to members.
1631 //syntaxerror("not a function");
1634 $$.c = code_cutlast($$.c);
1635 $$.c = code_append($$.c, paramcode);
1636 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1637 $$.c = abc_callproperty2($$.c, name, len);
1638 } else if($$.c->opcode == OPCODE_GETSUPER) {
1639 name = multiname_clone($$.c->data[0]);
1640 $$.c = code_cutlast($$.c);
1641 $$.c = code_append($$.c, paramcode);
1642 $$.c = abc_callsuper2($$.c, name, len);
1644 $$.c = abc_getlocal_0($$.c);
1645 $$.c = code_append($$.c, paramcode);
1646 $$.c = abc_call($$.c, len);
1651 if(TYPE_IS_FUNCTION($1.t) && $1.t->function) {
1652 $$.t = $1.t->function->return_type;
1654 $$.c = abc_coerce_a($$.c);
1658 FUNCTIONCALL : "super" '(' MAYBE_EXPRESSION_LIST ')' {
1659 if(!state->cls) syntaxerror("super() not allowed outside of a class");
1660 if(!state->method) syntaxerror("super() not allowed outside of a function");
1661 if(!state->method->is_constructor) syntaxerror("super() not allowed outside of a constructor");
1664 $$.c = abc_getlocal_0($$.c);
1665 typedcode_list_t*l = 0;
1667 for(l=$3;l;l=l->next) {
1668 $$.c = code_append($$.c, l->typedcode->c);len++;
1671 this is dependent on the control path, check this somewhere else
1672 if(state->method->has_super)
1673 syntaxerror("constructor may call super() only once");
1675 state->method->has_super = 1;
1676 $$.c = abc_constructsuper($$.c, len);
1677 $$.c = abc_pushundefined($$.c);
1681 DELETE: "delete" E {
1683 if($$.c->opcode == OPCODE_COERCE_A) {
1684 $$.c = code_cutlast($$.c);
1686 multiname_t*name = 0;
1687 if($$.c->opcode == OPCODE_GETPROPERTY) {
1688 $$.c->opcode = OPCODE_DELETEPROPERTY;
1689 } else if($$.c->opcode == OPCODE_GETSLOT) {
1690 int slot = (int)(ptroff_t)$$.c->data[0];
1691 multiname_t*name = abc_class_find_slotid(state->cls->abc,slot)->name;
1692 $$.c = code_cutlast($$.c);
1693 $$.c = abc_deleteproperty2($$.c, name);
1695 $$.c = abc_getlocal_0($$.c);
1696 MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
1697 $$.c = abc_deleteproperty2($$.c, &m);
1699 $$.t = TYPE_BOOLEAN;
1702 RETURN: "return" %prec prec_none {
1703 $$ = abc_returnvoid(0);
1705 RETURN: "return" EXPRESSION {
1707 $$ = abc_returnvalue($$);
1710 // ----------------------- expression types -------------------------------------
1712 NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
1713 EXPRESSION : E %prec below_minus {$$ = $1;}
1714 EXPRESSION : EXPRESSION ',' E %prec below_minus {
1716 $$.c = cut_last_push($$.c);
1717 $$.c = code_append($$.c,$3.c);
1720 VOIDEXPRESSION : EXPRESSION %prec below_minus {
1721 $$=cut_last_push($1.c);
1724 // ----------------------- expression evaluation -------------------------------------
1727 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1729 E : DELETE {$$ = $1;}
1730 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1734 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1735 //MULTINAME(m, registry_getintclass());
1736 //$$.c = abc_coerce2($$.c, &m); // FIXME
1739 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1742 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1745 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1748 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1751 CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);
1754 CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
1757 CONSTANT : "true" {$$.c = abc_pushtrue(0);
1758 $$.t = TYPE_BOOLEAN;
1760 CONSTANT : "false" {$$.c = abc_pushfalse(0);
1761 $$.t = TYPE_BOOLEAN;
1763 CONSTANT : "null" {$$.c = abc_pushnull(0);
1768 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1769 $$.t = TYPE_BOOLEAN;
1771 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1772 $$.t = TYPE_BOOLEAN;
1774 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1775 $$.t = TYPE_BOOLEAN;
1777 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1778 $$.t = TYPE_BOOLEAN;
1780 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1781 $$.t = TYPE_BOOLEAN;
1783 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1784 $$.t = TYPE_BOOLEAN;
1786 E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
1787 $$.t = TYPE_BOOLEAN;
1789 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1790 $$.t = TYPE_BOOLEAN;
1793 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1795 $$.c = converttype($$.c, $1.t, $$.t);
1796 $$.c = abc_dup($$.c);
1797 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1798 $$.c = cut_last_push($$.c);
1799 $$.c = code_append($$.c,$3.c);
1800 $$.c = converttype($$.c, $3.t, $$.t);
1801 code_t*label = $$.c = abc_label($$.c);
1802 jmp->branch = label;
1805 $$.t = join_types($1.t, $3.t, 'A');
1806 /*printf("%08x:\n",$1.t);
1807 code_dump($1.c, 0, 0, "", stdout);
1808 printf("%08x:\n",$3.t);
1809 code_dump($3.c, 0, 0, "", stdout);
1810 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
1812 $$.c = converttype($$.c, $1.t, $$.t);
1813 $$.c = abc_dup($$.c);
1814 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1815 $$.c = cut_last_push($$.c);
1816 $$.c = code_append($$.c,$3.c);
1817 $$.c = converttype($$.c, $3.t, $$.t);
1818 code_t*label = $$.c = abc_label($$.c);
1819 jmp->branch = label;
1822 E : '!' E {$$.c=$2.c;
1823 $$.c = abc_not($$.c);
1824 $$.t = TYPE_BOOLEAN;
1827 E : '~' E {$$.c=$2.c;
1828 $$.c = abc_bitnot($$.c);
1832 E : E '&' E {$$.c = code_append($1.c,$3.c);
1833 $$.c = abc_bitand($$.c);
1837 E : E '^' E {$$.c = code_append($1.c,$3.c);
1838 $$.c = abc_bitxor($$.c);
1842 E : E '|' E {$$.c = code_append($1.c,$3.c);
1843 $$.c = abc_bitor($$.c);
1847 E : E '-' E {$$.c = code_append($1.c,$3.c);
1848 if(BOTH_INT($1,$3)) {
1849 $$.c = abc_subtract_i($$.c);
1852 $$.c = abc_subtract($$.c);
1856 E : E ">>" E {$$.c = code_append($1.c,$3.c);
1857 $$.c = abc_rshift($$.c);
1860 E : E ">>>" E {$$.c = code_append($1.c,$3.c);
1861 $$.c = abc_urshift($$.c);
1864 E : E "<<" E {$$.c = code_append($1.c,$3.c);
1865 $$.c = abc_lshift($$.c);
1869 E : E '/' E {$$.c = code_append($1.c,$3.c);
1870 $$.c = abc_divide($$.c);
1873 E : E '+' E {$$.c = code_append($1.c,$3.c);
1874 $$.c = abc_add($$.c);
1877 E : E '%' E {$$.c = code_append($1.c,$3.c);
1878 $$.c = abc_modulo($$.c);
1881 E : E '*' E {$$.c = code_append($1.c,$3.c);
1882 if(BOTH_INT($1,$3)) {
1883 $$.c = abc_multiply_i($$.c);
1886 $$.c = abc_multiply($$.c);
1891 E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
1892 if(use_astype && TYPE_IS_CLASS($3.t)) {
1893 MULTINAME(m,$3.t->cls);
1894 $$.c = abc_astype2($1.c, &m);
1897 $$.c = code_append($1.c, $3.c);
1898 $$.c = abc_astypelate($$.c);
1903 E : E "instanceof" E
1904 {$$.c = code_append($1.c, $3.c);
1905 $$.c = abc_instanceof($$.c);
1906 $$.t = TYPE_BOOLEAN;
1909 E : E "is" E {$$.c = code_append($1.c, $3.c);
1910 $$.c = abc_istypelate($$.c);
1911 $$.t = TYPE_BOOLEAN;
1914 E : "typeof" '(' E ')' {
1916 $$.c = abc_typeof($$.c);
1921 $$.c = cut_last_push($2.c);
1922 $$.c = abc_pushundefined($$.c);
1926 E : "void" { $$.c = abc_pushundefined(0);
1930 E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
1935 $$.c=abc_negate_i($$.c);
1938 $$.c=abc_negate($$.c);
1945 $$.c = code_append($$.c, $3.c);
1947 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
1948 $$.c = abc_getproperty2($$.c, &m);
1949 $$.t = 0; // array elements have unknown type
1954 if(BOTH_INT($1,$3)) {
1955 c=abc_multiply_i(c);
1959 c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
1960 $$.c = toreadwrite($1.c, c, 0, 0);
1965 code_t*c = abc_modulo($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_lshift($3.c);
1972 c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
1973 $$.c = toreadwrite($1.c, c, 0, 0);
1977 code_t*c = abc_rshift($3.c);
1978 c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
1979 $$.c = toreadwrite($1.c, c, 0, 0);
1983 code_t*c = abc_urshift($3.c);
1984 c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
1985 $$.c = toreadwrite($1.c, c, 0, 0);
1989 code_t*c = abc_divide($3.c);
1990 c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
1991 $$.c = toreadwrite($1.c, c, 0, 0);
1996 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2001 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
2003 $$.c = toreadwrite($1.c, c, 0, 0);
2006 E : E "-=" E { code_t*c = $3.c;
2007 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
2008 c=abc_subtract_i(c);
2012 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
2014 $$.c = toreadwrite($1.c, c, 0, 0);
2017 E : E '=' E { code_t*c = 0;
2018 c = code_append(c, $3.c);
2019 c = converttype(c, $3.t, $1.t);
2020 $$.c = toreadwrite($1.c, c, 1, 0);
2024 E : E '?' E ':' E %prec below_assignment {
2026 code_t*j1 = $$.c = abc_iffalse($$.c, 0);
2027 $$.c = code_append($$.c, $3.c);
2028 code_t*j2 = $$.c = abc_jump($$.c, 0);
2029 $$.c = j1->branch = abc_label($$.c);
2030 $$.c = code_append($$.c, $5.c);
2031 $$.c = j2->branch = abc_label($$.c);
2032 $$.t = join_types($3.t,$5.t,'?');
2035 // TODO: use inclocal where appropriate
2036 E : E "++" { code_t*c = 0;
2037 classinfo_t*type = $1.t;
2038 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2039 c=abc_increment_i(c);
2045 c=converttype(c, type, $1.t);
2046 $$.c = toreadwrite($1.c, c, 0, 1);
2049 E : E "--" { code_t*c = 0;
2050 classinfo_t*type = $1.t;
2051 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2052 c=abc_decrement_i(c);
2058 c=converttype(c, type, $1.t);
2059 $$.c = toreadwrite($1.c, c, 0, 1);
2063 E : "++" %prec plusplus_prefix E { code_t*c = 0;
2064 classinfo_t*type = $2.t;
2065 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2066 c=abc_increment_i(c);
2072 c=converttype(c, type, $2.t);
2073 $$.c = toreadwrite($2.c, c, 0, 0);
2077 E : "--" %prec minusminus_prefix E { code_t*c = 0;
2078 classinfo_t*type = $2.t;
2079 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
2080 c=abc_decrement_i(c);
2086 c=converttype(c, type, $2.t);
2087 $$.c = toreadwrite($2.c, c, 0, 0);
2091 E : "super" '.' T_IDENTIFIER
2092 { if(!state->cls->info)
2093 syntaxerror("super keyword not allowed outside a class");
2094 classinfo_t*t = state->cls->info->superclass;
2095 if(!t) t = TYPE_OBJECT;
2097 memberinfo_t*f = registry_findmember(t, $3);
2098 namespace_t ns = {flags2access(f->flags), ""};
2099 MEMBER_MULTINAME(m, f, $3);
2101 $$.c = abc_getlocal_0($$.c);
2102 $$.c = abc_getsuper2($$.c, &m);
2103 $$.t = memberinfo_gettype(f);
2106 E : E '.' T_IDENTIFIER
2108 classinfo_t*t = $1.t;
2110 if(TYPE_IS_CLASS(t) && t->cls) {
2115 memberinfo_t*f = registry_findmember(t, $3);
2117 if(f && !is_static != !(f->flags&FLAG_STATIC))
2119 if(f && f->slot && !noslot) {
2120 $$.c = abc_getslot($$.c, f->slot);
2122 MEMBER_MULTINAME(m, f, $3);
2123 $$.c = abc_getproperty2($$.c, &m);
2125 /* determine type */
2126 $$.t = memberinfo_gettype(f);
2128 $$.c = abc_coerce_a($$.c);
2130 /* when resolving a property on an unknown type, we do know the
2131 name of the property (and don't seem to need the package), but
2132 we need to make avm2 try out all access modes */
2133 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
2134 $$.c = abc_getproperty2($$.c, &m);
2135 $$.c = abc_coerce_a($$.c);
2136 $$.t = registry_getanytype();
2140 VAR_READ : T_IDENTIFIER {
2147 /* look at variables */
2148 if((i = find_variable($1, &$$.t)) >= 0) {
2149 // $1 is a local variable
2150 $$.c = abc_getlocal($$.c, i);
2152 /* look at current class' members */
2153 } else if((f = registry_findmember(state->cls->info, $1))) {
2154 // $1 is a function in this class
2155 int var_is_static = (f->flags&FLAG_STATIC);
2156 int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC);
2157 if(var_is_static != i_am_static) {
2158 /* there doesn't seem to be any "static" way to access
2159 static properties of a class */
2160 state->method->late_binding = 1;
2162 namespace_t ns = {flags2access(f->flags), ""};
2163 multiname_t m = {QNAME, &ns, 0, $1};
2164 $$.c = abc_findpropstrict2($$.c, &m);
2165 $$.c = abc_getproperty2($$.c, &m);
2168 $$.c = abc_getlocal_0($$.c);
2169 $$.c = abc_getslot($$.c, f->slot);
2171 namespace_t ns = {flags2access(f->flags), ""};
2172 multiname_t m = {QNAME, &ns, 0, $1};
2173 $$.c = abc_getlocal_0($$.c);
2174 $$.c = abc_getproperty2($$.c, &m);
2177 if(f->kind == MEMBER_METHOD) {
2178 $$.t = TYPE_FUNCTION(f);
2183 /* look at classes in the current package and imported classes */
2184 } else if((a = find_class($1))) {
2186 $$.c = abc_getglobalscope($$.c);
2187 $$.c = abc_getslot($$.c, a->slot);
2190 $$.c = abc_getlex2($$.c, &m);
2192 $$.t = TYPE_CLASS(a);
2194 /* unknown object, let the avm2 resolve it */
2196 if(strcmp($1,"trace"))
2197 warning("Couldn't resolve '%s', doing late binding", $1);
2198 state->method->late_binding = 1;
2200 multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
2203 $$.c = abc_findpropstrict2($$.c, &m);
2204 $$.c = abc_getproperty2($$.c, &m);
2209 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
2210 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
2211 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
2213 // ----------------- namespaces -------------------------------------------------
2215 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;}
2216 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;}
2217 NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;}
2219 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER