1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "frontend/DecoratorEmitter.h"
7 #include "mozilla/Assertions.h"
9 #include "frontend/BytecodeEmitter.h"
10 #include "frontend/CallOrNewEmitter.h"
11 #include "frontend/FunctionEmitter.h"
12 #include "frontend/IfEmitter.h"
13 #include "frontend/LexicalScopeEmitter.h"
14 #include "frontend/NameAnalysisTypes.h"
15 #include "frontend/ObjectEmitter.h"
16 #include "frontend/ParseNode.h"
17 #include "frontend/ParserAtom.h"
18 #include "frontend/WhileEmitter.h"
19 #include "vm/ThrowMsgKind.h"
22 using namespace js::frontend
;
24 DecoratorEmitter::DecoratorEmitter(BytecodeEmitter
* bce
) : bce_(bce
) {}
26 // A helper function to read the decorators in reverse order to how they were
28 bool DecoratorEmitter::reverseDecoratorsToApplicationOrder(
29 const ListNode
* decorators
, DecoratorsVector
& vec
) {
30 if (!vec
.resize(decorators
->count())) {
31 ReportOutOfMemory(bce_
->fc
);
34 int end
= decorators
->count() - 1;
35 for (ParseNode
* decorator
: decorators
->contents()) {
36 vec
[end
--] = decorator
;
41 bool DecoratorEmitter::emitApplyDecoratorsToElementDefinition(
42 DecoratorEmitter::Kind kind
, ParseNode
* key
, ListNode
* decorators
,
44 MOZ_ASSERT(kind
!= Kind::Field
&& kind
!= Kind::Accessor
);
46 // The DecoratorEmitter expects the value to be decorated to be at the top
47 // of the stack prior to this call. It will apply the decorators to this
48 // value, possibly replacing the value with a value returned by a decorator.
49 // [stack] ADDINIT VAL
51 // Decorators Proposal
52 // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoelementdefinition.
53 // Step 1. Let decorators be elementRecord.[[Decorators]].
54 // Step 2. If decorators is empty, return unused.
55 // This is checked by the caller.
56 MOZ_ASSERT(!decorators
->empty());
58 DecoratorsVector dec_vecs
;
59 if (!reverseDecoratorsToApplicationOrder(decorators
, dec_vecs
)) {
63 // Step 3. Let key be elementRecord.[[Key]].
64 // Step 4. Let kind be elementRecord.[[Kind]].
65 // Step 5. For each element decorator of decorators, do
66 for (auto decorator
: dec_vecs
) {
67 // Step 5.a. Let decorationState be the Record { [[Finished]]: false }.
68 if (!emitDecorationState()) {
72 // TODO: See Bug 1869000 to support addInitializer for methods.
73 if (!bce_
->emitDupAt(1)) {
74 // [stack] ADDINIT VAL ADDINIT
78 if (!emitCallDecoratorForElement(kind
, key
, isStatic
, decorator
)) {
79 // [stack] ADDINIT RETVAL
83 // Step 5.i. Set decorationState.[[Finished]] to true.
84 if (!emitUpdateDecorationState()) {
88 // We need to check if the decorator returned undefined, a callable value,
89 // or any other value.
90 if (!emitCheckIsUndefined()) {
91 // [stack] ADDINIT VAL RETVAL ISUNDEFINED
95 InternalIfEmitter
ie(bce_
);
96 if (!ie
.emitThenElse()) {
97 // [stack] ADDINIT VAL RETVAL
101 // Pop the undefined RETVAL from the stack, leaving the original value in
103 if (!bce_
->emitPopN(1)) {
104 // [stack] ADDINIT VAL
108 if (!ie
.emitElseIf(mozilla::Nothing())) {
112 // Step 5.l.i. If IsCallable(newValue) is true, then
113 if (!bce_
->emitCheckIsCallable()) {
114 // [stack] ADDINIT VAL RETVAL ISCALLABLE_RESULT
118 if (!ie
.emitThenElse()) {
119 // [stack] ADDINIT VAL RETVAL
123 // Step 5.l.i.1. Perform MakeMethod(newValue, homeObject).
124 // MakeMethod occurs in the caller, here we just drop the original method
125 // which was an argument to the decorator, and leave the new method
126 // returned by the decorator on the stack.
127 if (!bce_
->emit1(JSOp::Swap
)) {
128 // [stack] ADDINIT RETVAL VAL
131 if (!bce_
->emitPopN(1)) {
132 // [stack] ADDINIT RETVAL
135 // Step 5.j.ii. Else if initializer is not undefined, throw a TypeError
137 // Step 5.l.ii. Else if newValue is not undefined, throw a
138 // TypeError exception.
139 if (!ie
.emitElse()) {
143 if (!bce_
->emitPopN(1)) {
144 // [stack] ADDINIT RETVAL
148 if (!bce_
->emit2(JSOp::ThrowMsg
,
149 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType
))) {
159 // [stack] ADDINIT RETVAL
162 bool DecoratorEmitter::emitApplyDecoratorsToFieldDefinition(
163 ParseNode
* key
, ListNode
* decorators
, bool isStatic
) {
164 // This method creates a new array to contain initializers added by decorators
165 // to the stack. start:
168 // [stack] ADDINIT ARRAY
170 // Decorators Proposal
171 // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoelementdefinition.
172 // Step 1. Let decorators be elementRecord.[[Decorators]].
173 // Step 2. If decorators is empty, return unused.
174 // This is checked by the caller.
175 MOZ_ASSERT(!decorators
->empty());
177 // If we're apply decorators to a field, we'll push a new array to the stack
178 // to hold newly created initializers.
179 if (!bce_
->emitUint32Operand(JSOp::NewArray
, 1)) {
180 // [stack] ADDINIT ARRAY
184 if (!emitPropertyKey(key
)) {
185 // [stack] ADDINIT ARRAY NAME
189 if (!bce_
->emitUint32Operand(JSOp::InitElemArray
, 0)) {
190 // [stack] ADDINIT ARRAY
194 if (!bce_
->emit1(JSOp::One
)) {
195 // [stack] ADDINIT ARRAY INDEX
199 DecoratorsVector dec_vecs
;
200 if (!reverseDecoratorsToApplicationOrder(decorators
, dec_vecs
)) {
204 // Step 3. Let key be elementRecord.[[Key]].
205 // Step 4. Let kind be elementRecord.[[Kind]].
206 // Step 5. For each element decorator of decorators, do
207 for (auto it
= dec_vecs
.begin(); it
!= dec_vecs
.end(); it
++) {
208 ParseNode
* decorator
= *it
;
209 // Step 5.a. Let decorationState be the Record { [[Finished]]: false }.
210 if (!emitDecorationState()) {
214 if (!bce_
->emitDupAt(2)) {
215 // [stack] ADDINIT ARRAY INDEX ADDINIT
219 if (!emitCallDecoratorForElement(Kind::Field
, key
, isStatic
, decorator
)) {
220 // [stack] ADDINIT ARRAY INDEX RETVAL
224 // Step 5.i. Set decorationState.[[Finished]] to true.
225 if (!emitUpdateDecorationState()) {
226 // [stack] ADDINIT ARRAY INDEX RETVAL
230 // We need to check if the decorator returned undefined, a callable value,
231 // or any other value.
232 if (!emitCheckIsUndefined()) {
233 // [stack] ADDINIT ARRAY INDEX RETVAL ISUNDEFINED
237 InternalIfEmitter
ie(bce_
);
238 if (!ie
.emitThenElse()) {
239 // [stack] ADDINIT ARRAY INDEX RETVAL
243 // Pop the undefined RETVAL from the stack, leaving the original value in
245 if (!bce_
->emitPopN(1)) {
246 // [stack] ADDINIT ARRAY INDEX
250 if (!ie
.emitElseIf(mozilla::Nothing())) {
254 // Step 5.l.i. If IsCallable(newValue) is true, then
256 if (!bce_
->emitCheckIsCallable()) {
257 // [stack] ARRAY INDEX RETVAL ISCALLABLE_RESULT
261 if (!ie
.emitThenElse()) {
262 // [stack] ADDINIT ARRAY INDEX RETVAL
266 // Step 5.j. If kind is field, then
267 // Step 5.j.i. If IsCallable(initializer) is true, append initializer to
268 // elementRecord.[[Initializers]].
269 if (!bce_
->emit1(JSOp::InitElemInc
)) {
270 // [stack] ADDINIT ARRAY INDEX
274 // Step 5.j.ii. Else if initializer is not undefined, throw a TypeError
276 // Step 5.l.ii. Else if newValue is not undefined, throw a
277 // TypeError exception.
278 if (!ie
.emitElse()) {
282 if (!bce_
->emitPopN(1)) {
283 // [stack] ADDINIT ARRAY INDEX
287 if (!bce_
->emit2(JSOp::ThrowMsg
,
288 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType
))) {
298 return bce_
->emitPopN(1);
299 // [stack] ADDINIT ARRAY
302 bool DecoratorEmitter::emitApplyDecoratorsToAccessorDefinition(
303 ParseNode
* key
, ListNode
* decorators
, bool isStatic
) {
304 // This method creates a new array to contain initializers added by decorators
305 // to the stack. start:
306 // [stack] ADDINIT GETTER SETTER
308 // [stack] ADDINIT GETTER SETTER ARRAY
309 MOZ_ASSERT(key
->is
<NameNode
>());
311 // Decorators Proposal
312 // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoelementdefinition.
313 // Step 1. Let decorators be elementRecord.[[Decorators]].
314 // Step 2. If decorators is empty, return unused.
315 // This is checked by the caller.
316 MOZ_ASSERT(!decorators
->empty());
318 // If we're applying decorators to a field, we'll push a new array to the
319 // stack to hold newly created initializers.
320 if (!bce_
->emitUint32Operand(JSOp::NewArray
, 1)) {
321 // [stack] ADDINIT GETTER SETTER ARRAY
325 if (!bce_
->emitGetPrivateName(&key
->as
<NameNode
>())) {
326 // [stack] ADDINIT GETTER SETTER ARRAY NAME
330 if (!bce_
->emitUint32Operand(JSOp::InitElemArray
, 0)) {
331 // [stack] ADDINIT GETTER SETTER ARRAY
335 if (!bce_
->emit1(JSOp::One
)) {
336 // [stack] ADDINIT GETTER SETTER ARRAY INDEX
340 DecoratorsVector dec_vecs
;
341 if (!reverseDecoratorsToApplicationOrder(decorators
, dec_vecs
)) {
345 // Step 3. Let key be elementRecord.[[Key]].
346 // Step 4. Let kind be elementRecord.[[Kind]].
347 // Step 5. For each element decorator of decorators, do
348 for (auto it
= dec_vecs
.begin(); it
!= dec_vecs
.end(); it
++) {
349 ParseNode
* decorator
= *it
;
350 // 5.a. Let decorationState be the Record { [[Finished]]: false }.
351 if (!emitDecorationState()) {
355 // Step 5.g.i. Set value to OrdinaryObjectCreate(%Object.prototype%).
356 ObjectEmitter
oe(bce_
);
357 if (!oe
.emitObject(2)) {
358 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE
362 // Step 5.g.ii. Perform ! CreateDataPropertyOrThrow(value, "get",
363 // elementRecord.[[Get]]).
364 if (!oe
.prepareForPropValue(decorator
->pn_pos
.begin
,
365 PropertyEmitter::Kind::Prototype
)) {
368 if (!bce_
->emitDupAt(4)) {
369 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE GETTER
372 if (!oe
.emitInit(frontend::AccessorType::None
,
373 frontend::TaggedParserAtomIndex::WellKnown::get())) {
374 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE
378 // Step 5.g.iii. Perform ! CreateDataPropertyOrThrow(value, "set",
379 // elementRecord.[[Set]]).
380 if (!oe
.prepareForPropValue(decorator
->pn_pos
.begin
,
381 PropertyEmitter::Kind::Prototype
)) {
384 if (!bce_
->emitDupAt(3)) {
385 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE SETTER
388 if (!oe
.emitInit(frontend::AccessorType::None
,
389 frontend::TaggedParserAtomIndex::WellKnown::set())) {
390 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE
395 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE
399 if (!bce_
->emitDupAt(5)) {
400 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE ADDINIT
404 // Step 5.j. Let newValue be ? Call(decorator, decoratorReceiver,
405 // « value, context »).
406 if (!emitCallDecoratorForElement(Kind::Accessor
, key
, isStatic
,
408 // [stack] ADDINIT GETTER SETTER ARRAY INDEX RETVAL
412 // Step 5.k. Set decorationState.[[Finished]] to true.
413 if (!emitUpdateDecorationState()) {
414 // [stack] ADDINIT GETTER SETTER ARRAY INDEX RETVAL
418 // We need to check if the decorator returned undefined, a callable value,
419 // or any other value.
420 if (!emitCheckIsUndefined()) {
421 // [stack] ADDINIT GETTER SETTER ARRAY INDEX RETVAL ISUNDEFINED
425 InternalIfEmitter
ie(bce_
);
426 if (!ie
.emitThenElse()) {
427 // [stack] ADDINIT GETTER SETTER ARRAY INDEX RETVAL
431 // Pop the undefined RETVAL from the stack, leaving the original values in
433 if (!bce_
->emitPopN(1)) {
434 // [stack] ADDINIT GETTER SETTER ARRAY INDEX
438 if (!ie
.emitElse()) {
442 // Step 5.k. Else if kind is accessor, then
443 // Step 5.k.ii. Else if newValue is not undefined, throw a TypeError
444 // exception. (Reordered)
445 if (!bce_
->emit2(JSOp::CheckIsObj
,
446 uint8_t(CheckIsObjectKind::DecoratorReturn
))) {
447 // [stack] ADDINIT GETTER SETTER ARRAY INDEX RETVAL
451 // Step 5.k.i. If newValue is an Object, then
452 // Step 5.k.i.1. Let newGetter be ? Get(newValue, "get").
453 // Step 5.k.i.2. If IsCallable(newGetter) is true, set
454 // elementRecord.[[Get]] to newGetter.
455 // Step 5.k.i.3. Else if newGetter is not undefined, throw a
456 // TypeError exception.
457 if (!emitHandleNewValueField(
458 frontend::TaggedParserAtomIndex::WellKnown::get(), 5)) {
462 // Step 5.k.i.4. Let newSetter be ? Get(newValue, "set").
463 // Step 5.k.i.5. If IsCallable(newSetter) is true, set
464 // elementRecord.[[Set]] to newSetter.
465 // Step 5.k.i.6. Else if newSetter is not undefined, throw a
466 // TypeError exception.
467 if (!emitHandleNewValueField(
468 frontend::TaggedParserAtomIndex::WellKnown::set(), 4)) {
472 // Step 5.k.i.7. Let initializer be ? Get(newValue, "init").
473 // Step 5.k.i.8. If IsCallable(initializer) is true, append
474 // initializer to elementRecord.[[Initializers]].
475 // Step 5.k.i.9. Else if initializer is not undefined, throw a
476 // TypeError exception.
477 if (!emitHandleNewValueField(
478 frontend::TaggedParserAtomIndex::WellKnown::init(), 0)) {
482 // Pop RETVAL from stack
483 if (!bce_
->emitPopN(1)) {
484 // [stack] ADDINIT GETTER SETTER ARRAY INDEX
494 return bce_
->emitPopN(1);
495 // [stack] ADDINIT GETTER SETTER ARRAY
498 bool DecoratorEmitter::emitApplyDecoratorsToClassDefinition(
499 ParseNode
* key
, ListNode
* decorators
) {
500 // This function expects a class constructor to already be on the stack. It
501 // applies each decorator to the class constructor, possibly replacing it with
502 // the return value of the decorator.
505 DecoratorsVector dec_vecs
;
506 if (!reverseDecoratorsToApplicationOrder(decorators
, dec_vecs
)) {
510 // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoclassdefinition
511 // Step 1. For each element decoratorRecord of decorators, do
512 for (auto it
= dec_vecs
.begin(); it
!= dec_vecs
.end(); it
++) {
513 ParseNode
* decorator
= *it
;
514 // Step 1.a. Let decorator be decoratorRecord.[[Decorator]].
515 // Step 1.b. Let decoratorReceiver be decoratorRecord.[[Receiver]].
516 // Step 1.c. Let decorationState be the Record { [[Finished]]: false }.
517 if (!emitDecorationState()) {
521 CallOrNewEmitter
cone(bce_
, JSOp::Call
,
522 CallOrNewEmitter::ArgumentsKind::Other
,
523 ValueUsage::WantValue
);
525 if (!bce_
->emitCalleeAndThis(decorator
, nullptr, cone
)) {
526 // [stack] VAL? CALLEE THIS
530 if (!cone
.prepareForNonSpreadArguments()) {
534 // Duplicate the class definition to pass it as an argument
536 if (!bce_
->emitDupAt(2)) {
537 // [stack] CTOR CALLEE THIS CTOR
541 // Step 1.d. Let context be CreateDecoratorContextObject(class, className,
542 // extraInitializers, decorationState).
543 // TODO: See Bug 1868221 for support for addInitializer for class
545 if (!bce_
->emit1(JSOp::Undefined
)) {
546 // [stack] CTOR CALLEE THIS CTOR ADDINIT
549 if (!emitCreateDecoratorContextObject(Kind::Class
, key
, false,
550 decorator
->pn_pos
)) {
551 // [stack] CTOR CALLEE THIS CTOR context
555 // Step 1.e. Let newDef be ? Call(decorator, decoratorReceiver, « classDef,
557 if (!cone
.emitEnd(2, decorator
->pn_pos
.begin
)) {
558 // [stack] CTOR NEWCTOR
562 // Step 1.f. Set decorationState.[[Finished]] to true.
563 if (!emitUpdateDecorationState()) {
567 if (!emitCheckIsUndefined()) {
568 // [stack] CTOR NEWCTOR ISUNDEFINED
572 InternalIfEmitter
ie(bce_
);
573 if (!ie
.emitThenElse()) {
574 // [stack] CTOR NEWCTOR
578 // Pop the undefined NEWDEF from the stack, leaving the original value in
580 if (!bce_
->emitPopN(1)) {
585 if (!ie
.emitElseIf(mozilla::Nothing())) {
589 // Step 1.g. If IsCallable(newDef) is true, then
590 // Step 1.g.i. Set classDef to newDef.
591 if (!bce_
->emitCheckIsCallable()) {
592 // [stack] CTOR NEWCTOR ISCALLABLE_RESULT
596 if (!ie
.emitThenElse()) {
597 // [stack] CTOR NEWCTOR
601 if (!bce_
->emit1(JSOp::Swap
)) {
602 // [stack] NEWCTOR CTOR
605 if (!bce_
->emitPopN(1)) {
610 // Step 1.h. Else if newDef is not undefined, then
611 // Step 1.h.i. Throw a TypeError exception.
612 if (!ie
.emitElse()) {
616 if (!bce_
->emitPopN(1)) {
620 if (!bce_
->emit2(JSOp::ThrowMsg
,
621 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType
))) {
630 // Step 2. Return classDef.
634 bool DecoratorEmitter::emitInitializeFieldOrAccessor() {
635 // [stack] THIS INITIALIZERS
637 // Decorators Proposal
638 // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-applydecoratorstoelementdefinition.
640 // Step 1. Assert: elementRecord.[[Kind]] is field or accessor.
641 // Step 2. If elementRecord.[[BackingStorageKey]] is present, let fieldName be
642 // elementRecord.[[BackingStorageKey]].
643 // Step 3. Else, let fieldName be elementRecord.[[Key]].
644 // We've stored the fieldname in the first element of the initializers array.
645 if (!bce_
->emit1(JSOp::Dup
)) {
646 // [stack] THIS INITIALIZERS INITIALIZERS
650 if (!bce_
->emit1(JSOp::Zero
)) {
651 // [stack] THIS INITIALIZERS INITIALIZERS INDEX
655 if (!bce_
->emit1(JSOp::GetElem
)) {
656 // [stack] THIS INITIALIZERS FIELDNAME
660 // Retrieve initial value of the field
661 if (!bce_
->emit1(JSOp::Dup
)) {
662 // [stack] THIS INITIALIZERS FIELDNAME FIELDNAME
666 if (!bce_
->emitDupAt(3)) {
667 // [stack] THIS INITIALIZERS FIELDNAME FIELDNAME THIS
671 if (!bce_
->emit1(JSOp::Swap
)) {
672 // [stack] THIS INITIALIZERS FIELDNAME THIS FIELDNAME
676 // Step 4. Let initValue be undefined.
677 // TODO: (See Bug 1817993) At the moment, we're applying the initialization
678 // logic in two steps. The pre-decorator initialization code runs, stores
679 // the initial value, and then we retrieve it here and apply the initializers
680 // added by decorators. We should unify these two steps.
681 if (!bce_
->emit1(JSOp::GetElem
)) {
682 // [stack] THIS INITIALIZERS FIELDNAME VALUE
686 if (!bce_
->emit2(JSOp::Pick
, 2)) {
687 // [stack] THIS FIELDNAME VALUE INITIALIZERS
691 // Retrieve the length of the initializers array.
692 if (!bce_
->emit1(JSOp::Dup
)) {
693 // [stack] THIS FIELDNAME VALUE INITIALIZERS INITIALIZERS
697 if (!bce_
->emitAtomOp(JSOp::GetProp
,
698 TaggedParserAtomIndex::WellKnown::length())) {
699 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH
703 if (!bce_
->emit1(JSOp::One
)) {
704 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
708 // Step 5. For each element initializer of elementRecord.[[Initializers]], do
709 InternalWhileEmitter
wh(bce_
);
710 // At this point, we have no context to determine offsets in the
711 // code for this while statement. Ideally, it would correspond to
712 // the field we're initializing.
713 if (!wh
.emitCond()) {
714 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
718 if (!bce_
->emit1(JSOp::Dup
)) {
719 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX INDEX
723 if (!bce_
->emitDupAt(2)) {
724 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX INDEX
729 if (!bce_
->emit1(JSOp::Lt
)) {
730 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX BOOL
734 // Step 5.a. Set initValue to ? Call(initializer, receiver, « initValue»).
735 if (!wh
.emitBody()) {
736 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
740 if (!bce_
->emitDupAt(2)) {
741 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
746 if (!bce_
->emitDupAt(1)) {
747 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
748 // INITIALIZERS INDEX
752 if (!bce_
->emit1(JSOp::GetElem
)) {
753 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX FUNC
757 if (!bce_
->emitDupAt(6)) {
758 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX FUNC THIS
762 // Pass value in as argument to the initializer
763 if (!bce_
->emit2(JSOp::Pick
, 5)) {
764 // [stack] THIS FIELDNAME INITIALIZERS LENGTH INDEX FUNC THIS VALUE
768 // Callee is always internal function.
769 if (!bce_
->emitCall(JSOp::Call
, 1)) {
770 // [stack] THIS FIELDNAME INITIALIZERS LENGTH INDEX RVAL
774 // Store returned value for next iteration
775 if (!bce_
->emit2(JSOp::Unpick
, 3)) {
776 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
780 if (!bce_
->emit1(JSOp::Inc
)) {
781 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
786 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
790 // Step 6. If fieldName is a Private Name, then
791 // Step 6.a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue).
793 // Step 7.a. Assert: IsPropertyKey(fieldName) is true.
794 // Step 7.b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName,
796 // TODO: (See Bug 1817993) Because the field already exists, we just store the
797 // updated value here.
798 if (!bce_
->emitPopN(3)) {
799 // [stack] THIS FIELDNAME VALUE
803 if (!bce_
->emit1(JSOp::InitElem
)) {
808 // Step 8. Return unused.
809 return bce_
->emitPopN(1);
813 bool DecoratorEmitter::emitCallExtraInitializers(
814 TaggedParserAtomIndex extraInitializers
) {
815 // Support for static and class extra initializers will be added in
816 // bug 1868220 and bug 1868221.
819 TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_());
821 if (!bce_
->emitGetName(extraInitializers
)) {
826 if (!bce_
->emit1(JSOp::Dup
)) {
827 // [stack] ARRAY ARRAY
831 if (!bce_
->emitAtomOp(JSOp::GetProp
,
832 TaggedParserAtomIndex::WellKnown::length())) {
833 // [stack] ARRAY LENGTH
837 if (!bce_
->emit1(JSOp::Zero
)) {
838 // [stack] ARRAY LENGTH INDEX
842 InternalWhileEmitter
wh(bce_
);
843 if (!wh
.emitCond()) {
844 // [stack] ARRAY LENGTH INDEX
848 if (!bce_
->emit1(JSOp::Dup
)) {
849 // [stack] ARRAY LENGTH INDEX INDEX
853 if (!bce_
->emitDupAt(2)) {
854 // [stack] ARRAY LENGTH INDEX INDEX LENGTH
858 if (!bce_
->emit1(JSOp::Lt
)) {
859 // [stack] ARRAY LENGTH INDEX BOOL
863 if (!wh
.emitBody()) {
864 // [stack] ARRAY LENGTH INDEX
868 if (!bce_
->emitDupAt(2)) {
869 // [stack] ARRAY LENGTH INDEX ARRAY
873 if (!bce_
->emitDupAt(1)) {
874 // [stack] ARRAY LENGTH INDEX ARRAY INDEX
878 // Retrieve initializer
879 if (!bce_
->emit1(JSOp::GetElem
)) {
880 // [stack] ARRAY LENGTH INDEX INITIALIZER
884 // This is guaranteed to run after super(), so we don't need TDZ checks.
885 if (!bce_
->emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) {
886 // [stack] ARRAY LENGTH INDEX INITIALIZER THIS
890 // Callee is always internal function.
891 if (!bce_
->emitCall(JSOp::CallIgnoresRv
, 0)) {
892 // [stack] ARRAY LENGTH INDEX RVAL
896 if (!bce_
->emit1(JSOp::Pop
)) {
897 // [stack] ARRAY LENGTH INDEX
901 if (!bce_
->emit1(JSOp::Inc
)) {
902 // [stack] ARRAY LENGTH INDEX
907 // [stack] ARRAY LENGTH INDEX
911 return bce_
->emitPopN(3);
915 bool DecoratorEmitter::emitPropertyKey(ParseNode
* key
) {
916 if (key
->is
<NameNode
>()) {
917 NameNode
* keyAsNameNode
= &key
->as
<NameNode
>();
918 if (keyAsNameNode
->privateNameKind() == PrivateNameKind::None
) {
919 if (!bce_
->emitStringOp(JSOp::String
, keyAsNameNode
->atom())) {
924 MOZ_ASSERT(keyAsNameNode
->privateNameKind() == PrivateNameKind::Field
);
925 if (!bce_
->emitGetPrivateName(keyAsNameNode
)) {
930 } else if (key
->isKind(ParseNodeKind::NumberExpr
)) {
931 if (!bce_
->emitNumberOp(key
->as
<NumericLiteral
>().value())) {
936 // Otherwise this is a computed property name. BigInt keys are parsed
937 // as (synthetic) computed property names, too.
938 MOZ_ASSERT(key
->isKind(ParseNodeKind::ComputedName
));
940 if (!bce_
->emitComputedPropertyName(&key
->as
<UnaryNode
>())) {
949 bool DecoratorEmitter::emitDecorationState() {
950 // TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1868841
954 bool DecoratorEmitter::emitUpdateDecorationState() {
955 // TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1868841.
959 bool DecoratorEmitter::emitCallDecoratorForElement(Kind kind
, ParseNode
* key
,
961 ParseNode
* decorator
) {
962 MOZ_ASSERT(kind
!= Kind::Class
);
963 // Except for fields, this method expects the value to be passed
964 // to the decorator to be on top of the stack. For methods, getters and
965 // setters this is the method itself. For accessors it is an object
966 // containing the getter and setter associated with the accessor.
967 // This method also expects the addInitializerFunction to be present on
968 // the top of the stack.
969 // [stack] VAL? ADDINIT
970 // Prepare to call decorator
971 CallOrNewEmitter
cone(bce_
, JSOp::Call
,
972 CallOrNewEmitter::ArgumentsKind::Other
,
973 ValueUsage::WantValue
);
975 if (!bce_
->emitCalleeAndThis(decorator
, nullptr, cone
)) {
976 // [stack] VAL? ADDINIT CALLEE THIS
980 if (!cone
.prepareForNonSpreadArguments()) {
984 if (kind
== Kind::Field
) {
985 // Step 5.c. Let value be undefined.
986 if (!bce_
->emit1(JSOp::Undefined
)) {
987 // [stack] ADDINIT CALLEE THIS undefined
990 } else if (kind
== Kind::Getter
|| kind
== Kind::Method
||
991 kind
== Kind::Setter
) {
992 // Step 5.d. If kind is method, set value to elementRecord.[[Value]].
993 // Step 5.e. Else if kind is getter, set value to elementRecord.[[Get]].
994 // Step 5.f. Else if kind is setter, set value to elementRecord.[[Set]].
995 // The DecoratorEmitter expects the method to already be on the stack.
996 // We dup the value here so we can use it as an argument to the decorator.
997 if (!bce_
->emitDupAt(3)) {
998 // [stack] VAL ADDINIT CALLEE THIS VAL
1002 // Step 5.g. Else if kind is accessor, then
1003 // Step 5.g.i. Set value to OrdinaryObjectCreate(%Object.prototype%).
1004 // For accessor decorators, we've already created the value object prior
1005 // to calling this method.
1006 MOZ_ASSERT(kind
== Kind::Accessor
);
1007 if (!bce_
->emitPickN(3)) {
1008 // [stack] ADDINIT CALLEE THIS VAL
1012 // Step 5.b. Let context be CreateDecoratorContextObject(kind, key,
1013 // extraInitializers, decorationState, isStatic).
1014 if (!bce_
->emitPickN(3)) {
1015 // [stack] VAL? CALLEE THIS VAL ADDINIT
1018 if (!emitCreateDecoratorContextObject(kind
, key
, isStatic
,
1019 decorator
->pn_pos
)) {
1020 // [stack] VAL? CALLEE THIS VAL context
1024 // Step 5.h. Let newValue be ? Call(decorator, undefined, « value, context»).
1025 return cone
.emitEnd(2, decorator
->pn_pos
.begin
);
1026 // [stack] VAL? RETVAL
1029 bool DecoratorEmitter::emitCreateDecoratorAccessObject() {
1030 // TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1800725.
1031 ObjectEmitter
oe(bce_
);
1032 if (!oe
.emitObject(0)) {
1035 return oe
.emitEnd();
1038 bool DecoratorEmitter::emitCheckIsUndefined() {
1039 // This emits code to check if the value at the top of the stack is
1040 // undefined. The value is left on the stack.
1042 if (!bce_
->emit1(JSOp::Dup
)) {
1046 if (!bce_
->emit1(JSOp::Undefined
)) {
1047 // [stack] VAL VAL undefined
1050 return bce_
->emit1(JSOp::Eq
);
1051 // [stack] VAL ISUNDEFINED
1054 bool DecoratorEmitter::emitCreateAddInitializerFunction(
1055 FunctionNode
* addInitializerFunction
, TaggedParserAtomIndex initializers
) {
1056 // This synthesizes a function corresponding to this JavaScript code:
1057 // function(initializer) {
1058 // if (IsCallable(initializer)) {
1059 // initializers[initializers.length++] = initializer;
1061 // throw DecoratorInvalidReturnType;
1064 MOZ_ASSERT(addInitializerFunction
);
1065 // TODO: Add support for static and class extra initializers, see bug 1868220
1069 TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_());
1071 FunctionEmitter
fe(bce_
, addInitializerFunction
->funbox(),
1072 FunctionSyntaxKind::Statement
,
1073 FunctionEmitter::IsHoisted::No
);
1074 if (!fe
.prepareForNonLazy()) {
1078 BytecodeEmitter
bce2(bce_
, addInitializerFunction
->funbox());
1083 FunctionScriptEmitter
fse(&bce2
, addInitializerFunction
->funbox(),
1084 mozilla::Nothing(), mozilla::Nothing());
1085 if (!fse
.prepareForParameters()) {
1089 if (!bce2
.emitFunctionFormalParameters(addInitializerFunction
->body())) {
1093 if (!fse
.prepareForBody()) {
1097 LexicalScopeNode
* lexicalScope
= addInitializerFunction
->body()->body();
1098 LexicalScopeEmitter
lse(&bce2
);
1099 if (lexicalScope
->isEmptyScope()) {
1100 if (!lse
.emitEmptyScope()) {
1104 if (!lse
.emitScope(lexicalScope
->kind(), lexicalScope
->scopeBindings())) {
1110 bce2
.lookupName(TaggedParserAtomIndex::WellKnown::initializer());
1111 MOZ_ASSERT(loc
.kind() == NameLocation::Kind::ArgumentSlot
);
1113 if (!bce2
.emitArgOp(JSOp::GetArg
, loc
.argumentSlot())) {
1114 // [stack] INITIALIZER
1118 if (!bce2
.emitCheckIsCallable()) {
1119 // [stack] INITIALIZER ISCALLABLE
1123 InternalIfEmitter
ifCallable(&bce2
);
1124 if (!ifCallable
.emitThenElse()) {
1125 // [stack] INITIALIZER
1129 loc
= bce2
.lookupName(initializers
);
1130 MOZ_ASSERT(loc
.kind() == NameLocation::Kind::EnvironmentCoordinate
);
1131 if (!bce2
.emitEnvCoordOp(JSOp::GetAliasedVar
, loc
.environmentCoordinate())) {
1132 // [stack] INITIALIZER ARRAY
1135 if (!bce2
.emitEnvCoordOp(JSOp::CheckAliasedLexical
,
1136 loc
.environmentCoordinate())) {
1137 // [stack] INITIALIZER ARRAY
1140 if (!bce2
.emit1(JSOp::Dup
)) {
1141 // [stack] INITIALIZER ARRAY ARRAY
1144 if (!bce2
.emitAtomOp(JSOp::GetProp
,
1145 TaggedParserAtomIndex::WellKnown::length())) {
1146 // [stack] INITIALIZER ARRAY LENGTH
1149 if (!bce2
.emitPickN(2)) {
1150 // [stack] ARRAY LENGTH INITIALIZER
1153 if (!bce2
.emit1(JSOp::InitElemInc
)) {
1154 // [stack] ARRAY LENGTH
1157 if (!bce2
.emitPopN(2)) {
1162 if (!ifCallable
.emitElse()) {
1163 // [stack] INITIALIZER
1167 if (!bce2
.emitPopN(1)) {
1171 if (!bce2
.emit2(JSOp::ThrowMsg
,
1172 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType
))) {
1176 if (!ifCallable
.emitEnd()) {
1180 if (!lse
.emitEnd()) {
1184 if (!fse
.emitEndBody()) {
1188 if (!fse
.intoStencil()) {
1192 return fe
.emitNonLazyEnd();
1196 bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind
,
1200 // We expect the addInitializerFunction to already be on the stack.
1203 // Step 1. Let contextObj be OrdinaryObjectCreate(%Object.prototype%).
1204 ObjectEmitter
oe(bce_
);
1205 size_t propertyCount
= kind
== Kind::Class
? 3 : 6;
1206 if (!oe
.emitObject(propertyCount
)) {
1207 // [stack] ADDINIT context
1210 if (!oe
.prepareForPropValue(pos
.begin
, PropertyEmitter::Kind::Prototype
)) {
1214 TaggedParserAtomIndex kindStr
;
1217 // Step 2. If kind is method, let kindStr be "method".
1218 kindStr
= frontend::TaggedParserAtomIndex::WellKnown::method();
1221 // Step 3. Else if kind is getter, let kindStr be "getter".
1222 kindStr
= frontend::TaggedParserAtomIndex::WellKnown::getter();
1225 // Step 4. Else if kind is setter, let kindStr be "setter".
1226 kindStr
= frontend::TaggedParserAtomIndex::WellKnown::setter();
1228 case Kind::Accessor
:
1229 // Step 5. Else if kind is accessor, let kindStr be "accessor".
1230 kindStr
= frontend::TaggedParserAtomIndex::WellKnown::accessor();
1233 // Step 6. Else if kind is field, let kindStr be "field".
1234 kindStr
= frontend::TaggedParserAtomIndex::WellKnown::field();
1238 // Step 7.a. Assert: kind is class.
1239 // Step 7.b. Let kindStr be "class".
1240 kindStr
= frontend::TaggedParserAtomIndex::WellKnown::class_();
1243 MOZ_ASSERT_UNREACHABLE("Unknown kind");
1246 if (!bce_
->emitStringOp(JSOp::String
, kindStr
)) {
1247 // [stack] ADDINIT context kindStr
1251 // Step 8. Perform ! CreateDataPropertyOrThrow(contextObj, "kind", kindStr).
1252 if (!oe
.emitInit(frontend::AccessorType::None
,
1253 frontend::TaggedParserAtomIndex::WellKnown::kind())) {
1254 // [stack] ADDINIT context
1257 // Step 9. If kind is not class, then
1258 if (kind
!= Kind::Class
) {
1259 MOZ_ASSERT(key
!= nullptr, "Expect key to be present except for classes");
1261 // Step 9.a. Perform ! CreateDataPropertyOrThrow(contextObj, "access",
1262 // CreateDecoratorAccessObject(kind, name)).
1263 if (!oe
.prepareForPropValue(pos
.begin
, PropertyEmitter::Kind::Prototype
)) {
1266 if (!emitCreateDecoratorAccessObject()) {
1269 if (!oe
.emitInit(frontend::AccessorType::None
,
1270 frontend::TaggedParserAtomIndex::WellKnown::access())) {
1271 // [stack] ADDINIT context
1274 // Step 9.b. If isStatic is present, perform
1275 // ! CreateDataPropertyOrThrow(contextObj, "static", isStatic).
1276 if (!oe
.prepareForPropValue(pos
.begin
, PropertyEmitter::Kind::Prototype
)) {
1279 if (!bce_
->emit1(isStatic
? JSOp::True
: JSOp::False
)) {
1280 // [stack] ADDINIT context isStatic
1283 if (!oe
.emitInit(frontend::AccessorType::None
,
1284 frontend::TaggedParserAtomIndex::WellKnown::static_())) {
1285 // [stack] ADDINIT context
1288 // Step 9.c. If name is a Private Name, then
1289 // Step 9.c.i. Perform ! CreateDataPropertyOrThrow(contextObj, "private",
1291 // Step 9.d. Else, Step 9.d.i. Perform
1292 // ! CreateDataPropertyOrThrow(contextObj, "private", false).
1293 if (!oe
.prepareForPropValue(pos
.begin
, PropertyEmitter::Kind::Prototype
)) {
1296 if (!bce_
->emit1(key
->isKind(ParseNodeKind::PrivateName
) ? JSOp::True
1298 // [stack] ADDINIT context private
1301 if (!oe
.emitInit(frontend::AccessorType::None
,
1302 frontend::TaggedParserAtomIndex::WellKnown::private_())) {
1303 // [stack] ADDINIT context
1306 // Step 9.c.ii. Perform ! CreateDataPropertyOrThrow(contextObj,
1307 // "name", name.[[Description]]).
1309 // Step 9.d.ii. Perform ! CreateDataPropertyOrThrow(contextObj,
1310 // "name", name.[[Description]]).)
1311 if (!oe
.prepareForPropValue(pos
.begin
, PropertyEmitter::Kind::Prototype
)) {
1314 if (key
->is
<NameNode
>()) {
1315 if (!bce_
->emitStringOp(JSOp::String
, key
->as
<NameNode
>().atom())) {
1319 if (!emitPropertyKey(key
)) {
1323 if (!oe
.emitInit(frontend::AccessorType::None
,
1324 frontend::TaggedParserAtomIndex::WellKnown::name())) {
1325 // [stack] ADDINIT context
1330 // Step 10.a. Perform ! CreateDataPropertyOrThrow(contextObj, "name", name).
1331 if (!oe
.prepareForPropValue(pos
.begin
, PropertyEmitter::Kind::Prototype
)) {
1334 if (key
!= nullptr) {
1335 if (!bce_
->emitStringOp(JSOp::String
, key
->as
<NameNode
>().atom())) {
1339 if (!bce_
->emit1(JSOp::Undefined
)) {
1343 if (!oe
.emitInit(frontend::AccessorType::None
,
1344 frontend::TaggedParserAtomIndex::WellKnown::name())) {
1345 // [stack] ADDINIT context
1349 // Step 11. Let addInitializer be CreateAddInitializerFunction(initializers,
1350 // decorationState).
1351 if (!oe
.prepareForPropValue(pos
.begin
, PropertyEmitter::Kind::Prototype
)) {
1355 if (!bce_
->emitPickN(1)) {
1356 // [stack] context ADDINIT
1359 // Step 12. Perform ! CreateDataPropertyOrThrow(contextObj, "addInitializer",
1362 frontend::AccessorType::None
,
1363 frontend::TaggedParserAtomIndex::WellKnown::addInitializer())) {
1367 // Step 13. Return contextObj.
1368 return oe
.emitEnd();
1371 bool DecoratorEmitter::emitHandleNewValueField(TaggedParserAtomIndex atom
,
1373 // This function handles retrieving the new value from a field in the RETVAL
1374 // object returned by the decorator. The `atom` is the atom of the field to be
1375 // examined. The offset is the offset of the existing value on the stack,
1376 // which will be replaced by the new value. If the offset is zero, we're
1377 // handling the initializer which will be added to the array of initializers
1378 // already on the stack.
1379 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1381 if (!bce_
->emit1(JSOp::Dup
)) {
1382 // [stack] GETTER SETTER ARRAY INDEX RETVAL RETVAL
1385 if (!bce_
->emitStringOp(JSOp::String
, atom
)) {
1386 // [stack] GETTER SETTER ARRAY INDEX RETVAL RETVAL ATOM
1389 if (!bce_
->emit1(JSOp::GetElem
)) {
1390 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1395 if (!emitCheckIsUndefined()) {
1396 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1397 // NEW_VALUE ISUNDEFINED
1401 InternalIfEmitter
ifCallable(bce_
);
1402 if (!ifCallable
.emitThenElse()) {
1403 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1408 // Pop the undefined getter or setter from the stack, leaving the original
1410 if (!bce_
->emitPopN(1)) {
1411 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1415 if (!ifCallable
.emitElseIf(mozilla::Nothing())) {
1418 if (!bce_
->emitCheckIsCallable()) {
1419 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1420 // NEW_VALUE ISCALLABLE_RESULT
1423 if (!ifCallable
.emitThenElse()) {
1424 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1429 if (!bce_
->emitPickN(offset
)) {
1430 // [stack] GETTER? SETTER? ARRAY INDEX RETVAL
1431 // NEW_VALUE GETTER_OR_SETTER
1434 if (!bce_
->emitPopN(1)) {
1435 // [stack] GETTER? SETTER? ARRAY INDEX RETVAL
1439 if (!bce_
->emitUnpickN(offset
- 1)) {
1440 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1444 // Offset == 0 means we're retrieving the initializer, this is
1445 // stored in the initializer array on the stack.
1446 if (!bce_
->emit1(JSOp::Swap
)) {
1447 // [stack] GETTER SETTER ARRAY INDEX NEW_VALUE RETVAL
1451 if (!bce_
->emitUnpickN(3)) {
1452 // [stack] GETTER SETTER RETVAL ARRAY INDEX NEW_VALUE
1456 if (!bce_
->emit1(JSOp::InitElemInc
)) {
1457 // [stack] GETTER SETTER RETVAL ARRAY INDEX
1461 if (!bce_
->emitPickN(2)) {
1462 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1467 if (!ifCallable
.emitElse()) {
1471 if (!bce_
->emitPopN(1)) {
1472 // [stack] GETTER SETTER ARRAY INDEX
1476 if (!bce_
->emit2(JSOp::ThrowMsg
,
1477 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType
))) {
1481 return ifCallable
.emitEnd();