1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2008
21 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
50 using namespace ActionBlockConstants
;
52 // Abstracts the name computation. For stack allocation.
56 Name(Cogen
* cogen
, Ctx
* ctx
, Expr
* expr
, bool strict
);
57 Name(Cogen
* cogen
, Ctx
* ctx
, QualifiedName
* name
);
68 void computeName(QualifiedName
* qname
, Ctx
* ctx
);
71 // Compute the base object (on the stack) and the name information.
72 Name::Name(Cogen
* cogen
, Ctx
* ctx
, Expr
* expr
, bool strict
)
77 Tag tag
= expr
->tag();
78 AvmAssert( tag
== TAG_objectRef
|| tag
== TAG_qualifiedName
);
79 if (tag
== TAG_objectRef
)
80 ((ObjectRef
*)expr
)->obj
->cogen(cogen
, ctx
);
81 computeName((tag
== TAG_objectRef
? ((ObjectRef
*)expr
)->name
: (QualifiedName
*)expr
), ctx
);
82 if (tag
== TAG_qualifiedName
) {
84 cogen
->I_findpropstrict(sym
);
86 cogen
->I_findproperty(sym
);
90 // Compute the name information.
91 Name::Name(Cogen
* cogen
, Ctx
* ctx
, QualifiedName
* qname
)
96 computeName(qname
, ctx
);
99 // Significant performance improvements for qualified names if the namespace can be resolved at compile time.
100 // See notes in the implementation of the 'namespace' definition; the machinery has to be implemented for
101 // 'use default namespace' in any case.
103 void Name::computeName(QualifiedName
* qname
, Ctx
* ctx
)
105 Compiler
* compiler
= cogen
->compiler
;
106 bool ns_wildcard
= false;
107 if (qname
->qualifier
!= NULL
) {
108 switch (qname
->qualifier
->tag()) {
109 case TAG_simpleName
: {
110 uint32_t id
= cogen
->abc
->addQName(compiler
->NS_public
,
111 cogen
->emitString(((SimpleName
*)(qname
->qualifier
))->name
),
113 nsreg
= cogen
->getTemp();
114 cogen
->I_findpropstrict(id
);
115 cogen
->I_getproperty(id
);
116 cogen
->I_coerce(compiler
->ID_Namespace
);
117 cogen
->I_setlocal(nsreg
);
120 case TAG_wildcardName
:
124 compiler
->internalError(qname
->pos
, "QName qualifiers can't be computed names");
127 NameComponent
* name
= qname
->name
;
128 switch (name
->tag()) {
131 AvmAssert(!ns_wildcard
);
132 sym
= cogen
->abc
->addRTQName(cogen
->emitString(((SimpleName
*)name
)->name
),
136 VarScopeCtx
* vs
= ctx
->findVarScope();
137 // here we either have just the public namespace, or the public namespace
138 // plus some open namespaces; in the latter case we need to generate a
139 // multiname presumably. We don't want to have to call addNsset here
140 // every time so be sure to cache the nsset for the current scope somewhere.
141 if (vs
->nsset
!= 0 && ns_wildcard
== 0 && !qname
->is_attr
)
142 sym
= cogen
->abc
->addMultiname(vs
->nsset
,
143 cogen
->emitString(((SimpleName
*)name
)->name
),
146 sym
= cogen
->abc
->addQName((ns_wildcard
? 0 : compiler
->NS_public
),
147 cogen
->emitString(((SimpleName
*)name
)->name
),
151 case TAG_wildcardName
:
153 sym
= cogen
->abc
->addRTQName(0, qname
->is_attr
);
155 sym
= cogen
->abc
->addMultiname(compiler
->NSS_public
, 0, qname
->is_attr
);
157 case TAG_computedName
:
159 compiler
->syntaxError(qname
->pos
, SYNTAXERR_ILLEGAL_QNAME
);
160 namereg
= cogen
->getTemp();
161 ((ComputedName
*)name
)->expr
->cogen(cogen
, ctx
);
162 cogen
->I_setlocal(namereg
);
164 sym
= cogen
->abc
->addRTQNameL(qname
->is_attr
);
167 sym
= compiler
->MNL_public_attr
;
169 sym
= compiler
->MNL_public
;
177 if (nsreg
!= 0) cogen
->I_kill(nsreg
);
178 if (namereg
!= 0) cogen
->I_kill(namereg
);
184 cogen
->I_getlocal(nsreg
);
186 cogen
->I_getlocal(namereg
);
189 void QualifiedName::cogen(Cogen
* cogen
, Ctx
* ctx
)
192 Name
n(cogen
, ctx
, this);
194 cogen
->I_findpropstrict(n
.sym
);
196 cogen
->I_getproperty(n
.sym
);
199 void ObjectRef::cogen(Cogen
* cogen
, Ctx
* ctx
)
201 obj
->cogen(cogen
, ctx
);
202 if ((name
->qualifier
== NULL
|| name
->qualifier
->tag() == TAG_wildcardName
) && name
->name
->tag() == TAG_wildcardName
&& !name
->is_attr
)
203 cogen
->I_callproperty(cogen
->compiler
->ID_children
, 0);
205 Name
n(cogen
, ctx
, name
);
207 cogen
->I_getproperty(n
.sym
);
211 void RefLocalExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
214 cogen
->I_getlocal(local
);
217 void ConditionalExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
219 Label
* L0
= cogen
->newLabel();
220 Label
* L1
= cogen
->newLabel();
222 e1
->cogen(cogen
, ctx
);
223 cogen
->I_iffalse(L0
);
224 e2
->cogen(cogen
, ctx
);
228 e3
->cogen(cogen
, ctx
);
233 void AssignExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
235 AvmAssert( lhs
->tag() == TAG_objectRef
|| lhs
->tag() == TAG_qualifiedName
);
237 // Compute the object onto the stack, and elements of the name into locals if necessary
238 bool is_assign
= op
== OPR_assign
|| op
== OPR_init
;
239 Name
n(cogen
, ctx
, lhs
, !is_assign
);
241 // Read the value if we need it
245 cogen
->I_getproperty(n
.sym
);
249 rhs
->cogen(cogen
, ctx
);
251 // Compute the operator if we need it
254 cogen
->I_opcode(cogen
->binopToOpcode(op
, &isNegated
));
259 // Perform the update and generate the result
260 uint32_t t
= cogen
->getTemp();
261 cogen
->I_setlocal(t
);
263 cogen
->I_getlocal(t
);
264 if (op
== OPR_assign
)
265 cogen
->I_setproperty(n
.sym
);
267 cogen
->I_initproperty(n
.sym
);
268 cogen
->I_getlocal(t
);
272 void BinaryExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
274 if (op
== OPR_logicalAnd
) {
275 Label
* L0
= cogen
->newLabel();
277 lhs
->cogen(cogen
, ctx
);
278 cogen
->I_coerce_a(); // wrong, should coerce to LUB of lhs and rhs
281 cogen
->I_iffalse(L0
);
283 rhs
->cogen(cogen
, ctx
);
284 cogen
->I_coerce_a(); // wrong, should coerce to LUB of lhs and rhs
287 else if (op
== OPR_logicalOr
) {
288 Label
* L0
= cogen
->newLabel();
290 lhs
->cogen(cogen
, ctx
);
291 cogen
->I_coerce_a(); // wrong, should coerce to LUB of lhs and rhs
296 rhs
->cogen(cogen
, ctx
);
297 cogen
->I_coerce_a(); // wrong, should coerce to LUB of lhs and rhs
300 else if (op
== OPR_comma
) {
301 lhs
->cogen(cogen
, ctx
);
303 rhs
->cogen(cogen
, ctx
);
306 lhs
->cogen(cogen
, ctx
);
307 rhs
->cogen(cogen
, ctx
);
309 cogen
->I_opcode(cogen
->binopToOpcode(op
, &isNegated
));
315 void UnaryExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
317 Compiler
* compiler
= cogen
->compiler
;
320 if (expr
->tag() == TAG_qualifiedName
|| expr
->tag() == TAG_objectRef
) {
321 Name
n(cogen
, ctx
, expr
, false);
323 cogen
->I_deleteproperty(n
.sym
);
326 // FIXME: e4x requires that if the value computed here is an XMLList then a TypeError (ID 1119) is thrown.
327 expr
->cogen(cogen
, ctx
);
335 expr
->cogen(cogen
, ctx
);
337 cogen
->I_pushundefined();
341 if (expr
->tag() == TAG_qualifiedName
) {
342 Name
n(cogen
, ctx
, (QualifiedName
*)expr
);
344 cogen
->I_findproperty(n
.sym
);
346 cogen
->I_getproperty(n
.sym
);
349 expr
->cogen(cogen
, ctx
);
354 incdec(cogen
, ctx
, true, true);
358 incdec(cogen
, ctx
, true, false);
362 incdec(cogen
, ctx
, false, true);
366 incdec(cogen
, ctx
, false, false);
370 expr
->cogen(cogen
, ctx
);
375 expr
->cogen(cogen
, ctx
);
380 expr
->cogen(cogen
, ctx
);
385 expr
->cogen(cogen
, ctx
);
390 compiler
->internalError(pos
, "Unrecognized unary operation");
394 void UnaryExpr::incdec(Cogen
* cogen
, Ctx
* ctx
, bool pre
, bool inc
)
397 Name
n(cogen
, ctx
, expr
, true);
400 cogen
->I_getproperty(n
.sym
);
402 uint32_t t
= cogen
->getTemp();
406 cogen
->I_increment();
408 cogen
->I_decrement();
410 cogen
->I_setlocal(t
);
413 // Postfix ops return value after conversion to number.
416 cogen
->I_setlocal(t
);
418 cogen
->I_increment();
420 cogen
->I_decrement();
424 cogen
->I_setproperty(n
.sym
);
426 cogen
->I_getlocal(t
);
430 void ThisExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
433 cogen
->I_getlocal(0);
436 void LiteralFunction::cogen(Cogen
* cogen
, Ctx
* ctx
)
438 if (function
->name
!= NULL
) {
439 // For a named function expression F with name N, create a new
440 // expression (function() { F; return N })() and generate code
441 // for that instead. Note that F then becomes a local function
443 Allocator
* allocator
= cogen
->allocator
;
444 Expr
* e
= ALLOC(CallExpr
,
445 (ALLOC(LiteralFunction
,
450 (function
->name
, NULL
, TAG_varBinding
)))),
455 ALLOC(Seq
<FunctionDefn
*>,
461 (0, ALLOC(QualifiedName
,
462 (NULL
, ALLOC(SimpleName
,
471 e
->cogen(cogen
, ctx
);
474 ABCMethodInfo
* fn_info
;
475 ABCMethodBodyInfo
* fn_body
;
476 function
->cogenGuts(cogen
->compiler
, ctx
, &fn_info
, &fn_body
);
477 cogen
->I_newfunction(fn_info
->index
);
481 void LiteralObject::cogen(Cogen
* cogen
, Ctx
* ctx
)
485 for ( Seq
<LiteralField
*>* fields
= this->fields
; fields
!= NULL
; fields
= fields
->tl
) {
486 cogen
->I_pushstring(cogen
->emitString(fields
->hd
->name
));
487 fields
->hd
->value
->cogen(cogen
, ctx
);
490 cogen
->I_newobject(i
);
493 void LiteralArray::cogen(Cogen
* cogen
, Ctx
* ctx
)
496 Seq
<Expr
*>* exprs
= elements
;
497 Compiler
* compiler
= cogen
->compiler
;
499 // Use newarray to construct the dense prefix
500 for ( ; exprs
!= NULL
; exprs
= exprs
->tl
) {
504 e
->cogen(cogen
, ctx
);
507 cogen
->I_newarray(i
);
509 // Then init the other defined slots one by one
511 bool last_was_undefined
= false;
512 for ( ; exprs
!= NULL
; exprs
= exprs
->tl
, i
++ ) {
516 e
->cogen(cogen
, ctx
);
517 cogen
->I_setproperty(cogen
->abc
->addQName(compiler
->NS_public
,cogen
->emitString(compiler
->intern(i
))));
518 last_was_undefined
= false;
521 last_was_undefined
= true;
523 if (last_was_undefined
) {
525 cogen
->I_pushint(cogen
->emitInt(i
));
526 cogen
->I_setproperty(compiler
->ID_length
);
531 void LiteralRegExp::cogen(Cogen
* cogen
, Ctx
* ctx
)
535 Compiler
* compiler
= cogen
->compiler
;
537 // value is "/.../flags"
539 // OPTIMIZEME: silly to recompile the regular expression every time it's evaluated, even if
540 // ES3.1 allows it (not sure what AS3 allows / requires; ES3 requires compilation once).
541 const wchar
* s
= value
->s
;
542 const wchar
* t
= s
+ value
->length
- 1;
546 // Creating a new RegExp object every time is not compatible with ES3, but it is
547 // what ASC does, and with luck ES3.1 will change to match this behavior.
549 // FIXME: semantics: findpropstrict(""::RegExp) is not quite right here.
550 // Doing so creates a spoofing hole / surprising trap. We want an OP_newregexp instruction.
551 cogen
->I_findpropstrict(compiler
->ID_RegExp
);
552 cogen
->I_pushstring(cogen
->emitString(compiler
->intern(s
+1, uint32_t(t
-s
-1))));
553 cogen
->I_pushstring(cogen
->emitString(compiler
->intern(t
+1, uint32_t(value
->length
-(t
-s
+1)))));
554 cogen
->I_constructprop(compiler
->ID_RegExp
, 2);
557 void LiteralNull::cogen(Cogen
* cogen
, Ctx
* ctx
)
563 void LiteralUndefined::cogen(Cogen
* cogen
, Ctx
* ctx
)
566 cogen
->I_pushundefined();
569 void LiteralInt::cogen(Cogen
* cogen
, Ctx
* ctx
)
572 if (value
>= -128 && value
< 128)
573 cogen
->I_pushbyte((uint8_t)(value
& 0xFF));
575 cogen
->I_pushint(cogen
->emitInt(value
));
578 void LiteralUInt::cogen(Cogen
* cogen
, Ctx
* ctx
)
582 cogen
->I_pushbyte((uint8_t)(value
& 0xFF));
584 cogen
->I_pushuint(cogen
->emitUInt(value
));
587 void LiteralDouble::cogen(Cogen
* cogen
, Ctx
* ctx
)
590 if (MathUtils::isNaN(value
))
593 cogen
->I_pushdouble(cogen
->emitDouble(value
));
596 void LiteralBoolean::cogen(Cogen
* cogen
, Ctx
* ctx
)
602 cogen
->I_pushfalse();
605 void LiteralString::cogen(Cogen
* cogen
, Ctx
* ctx
)
608 cogen
->I_pushstring(cogen
->emitString(value
));
611 uint32_t Cogen::arguments(Seq
<Expr
*>* args
, Ctx
* ctx
)
614 for ( ; args
!= NULL
; args
= args
->tl
, i
++ )
615 args
->hd
->cogen(this, ctx
);
619 void CallExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
622 case TAG_qualifiedName
: {
623 // This code is incorrect if the name that's being referenced is
624 // bound by 'with', because in that case the binding object should be
625 // pushed as the receiver object (according to ES-262). But the AVM+
626 // does not have an instruction that performs the correct operation:
627 // callproplex passes NULL as the receiver object, while callproperty
628 // passes a non-NULL object. So in the context of a WITH we would
629 // have to simulate the correct behavior by performing a scope chain
630 // walk, querying each WITH object for the property and calling it
631 // if present, otherwise calling the function if it is lexically bound,
632 // otherwise calling the global function. ASC has the same problem
633 // (and also does not solve it), so no actual bug here, just an
635 Name
n(cogen
, ctx
, fn
, true);
637 cogen
->I_callproplex(n
.sym
, cogen
->arguments(arguments
, ctx
));
640 case TAG_objectRef
: {
641 Name
n(cogen
, ctx
, fn
, false);
643 cogen
->I_callproperty(n
.sym
, cogen
->arguments(arguments
, ctx
));
647 fn
->cogen(cogen
, ctx
);
649 cogen
->I_call(cogen
->arguments(arguments
, ctx
));
653 void NewExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
655 fn
->cogen(cogen
, ctx
);
656 cogen
->I_construct(cogen
->arguments(arguments
, ctx
));
659 void XmlInitializer::cogen(Cogen
* cogen
, Ctx
* ctx
)
661 Compiler
* compiler
= cogen
->compiler
;
662 uint32_t id
= is_list
? compiler
->ID_XMLList
: compiler
->ID_XML
;
664 cogen
->I_findpropstrict(id
);
665 cogen
->I_getproperty(id
);
666 cogen
->I_pushstring(cogen
->emitString(compiler
->SYM_
));
667 for ( Seq
<Expr
*>* exprs
= this->exprs
; exprs
!= NULL
; exprs
= exprs
->tl
) {
668 exprs
->hd
->cogen(cogen
, ctx
);
669 cogen
->I_convert_s();
672 #if 0 && defined DEBUG
674 cogen
->I_findpropstrict(compiler
->ID_print
);
676 cogen
->I_callpropvoid(compiler
->ID_print
, 1);
678 cogen
->I_construct(1);
681 void EscapeExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
683 expr
->cogen(cogen
, ctx
);
685 case ESC_attributeValue
:
686 cogen
->I_esc_xattr();
688 case ESC_elementValue
:
689 cogen
->I_esc_xelem();
696 // OPTIMIZEME? A more space-conserving method would be to pass a predicate
697 // and an object to a common filter function. But it only makes a difference
698 // if filter expressions are very common, and they probably aren't.
700 void FilterExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
702 Compiler
* compiler
= cogen
->compiler
;
703 uint32_t t_xmllist
= cogen
->getTemp();
704 uint32_t t_length
= cogen
->getTemp();
705 uint32_t t_result
= cogen
->getTemp();
706 uint32_t t_result_index
= cogen
->getTemp();
707 uint32_t t_index
= cogen
->getTemp();
708 uint32_t t_scope
= cogen
->getTemp();
709 uint32_t t_item
= cogen
->getTemp();
710 Label
* L_again
= cogen
->newLabel();
711 Label
* L_skip
= cogen
->newLabel();
712 Label
* L_done
= cogen
->newLabel();
714 obj
->cogen(cogen
, ctx
);
715 cogen
->I_checkfilter();
717 // convert to XMLList
719 cogen
->I_setlocal(t_xmllist
);
720 cogen
->I_findpropstrict(compiler
->ID_XMLList
);
721 cogen
->I_getproperty(compiler
->ID_XMLList
);
723 cogen
->I_getlocal(t_xmllist
);
726 cogen
->I_setlocal(t_xmllist
);
728 // get the length of the list and save it
729 cogen
->I_getlocal(t_xmllist
);
730 cogen
->I_callproperty(compiler
->ID_length
, 0);
732 cogen
->I_setlocal(t_length
);
734 // create a new, empty list for the result
735 cogen
->I_findpropstrict(compiler
->ID_XMLList
);
736 cogen
->I_getproperty(compiler
->ID_XMLList
);
737 cogen
->I_construct(0);
739 cogen
->I_setlocal(t_result
);
740 cogen
->I_pushbyte(0);
741 cogen
->I_setlocal(t_result_index
);
744 cogen
->I_pushbyte(0);
745 cogen
->I_setlocal(t_index
);
747 // iterate across the list
748 cogen
->I_label(L_again
);
750 // while index < length
751 cogen
->I_getlocal(t_index
);
752 cogen
->I_getlocal(t_length
);
753 cogen
->I_ifge(L_done
);
755 // item := list[index]
756 cogen
->I_getlocal(t_xmllist
);
757 cogen
->I_getlocal(t_index
);
758 cogen
->I_getproperty(compiler
->MNL_public
);
760 cogen
->I_setlocal(t_item
);
762 // with (item) b := <filter>
763 cogen
->I_getlocal(t_item
);
765 filter
->cogen(cogen
, ctx
);
768 // if b result += item
769 cogen
->I_iffalse(L_skip
);
771 cogen
->I_getlocal(t_result
);
772 cogen
->I_getlocal(t_result_index
);
773 cogen
->I_getlocal(t_item
);
774 cogen
->I_setproperty(compiler
->MNL_public
);
775 cogen
->I_inclocal_i(t_result_index
);
777 cogen
->I_label(L_skip
);
780 cogen
->I_inclocal_i(t_index
);
781 cogen
->I_jump(L_again
);
784 cogen
->I_label(L_done
);
785 cogen
->I_getlocal(t_result
);
786 cogen
->I_kill(t_xmllist
);
787 cogen
->I_kill(t_length
);
788 cogen
->I_kill(t_result
);
789 cogen
->I_kill(t_result_index
);
790 cogen
->I_kill(t_index
);
791 cogen
->I_kill(t_scope
);
792 cogen
->I_kill(t_item
);
795 void DescendantsExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
797 obj
->cogen(cogen
, ctx
);
798 Name
n(cogen
, ctx
, name
);
800 cogen
->I_getdescendants(n
.sym
);
803 void SuperExpr::cogen(Cogen
* cogen
, Ctx
* ctx
)
806 Compiler
* compiler
= cogen
->compiler
;
807 compiler
->internalError(pos
, "Unimplemented: superExpr");