Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / src / frontend / DecoratorEmitter.cpp
blobd607667e2666602b909e2f45bc531ec07f1899bb
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"
21 using namespace js;
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
27 // parsed.
28 bool DecoratorEmitter::reverseDecoratorsToApplicationOrder(
29 const ListNode* decorators, DecoratorsVector& vec) {
30 if (!vec.resize(decorators->count())) {
31 ReportOutOfMemory(bce_->fc);
32 return false;
34 int end = decorators->count() - 1;
35 for (ParseNode* decorator : decorators->contents()) {
36 vec[end--] = decorator;
38 return true;
41 bool DecoratorEmitter::emitApplyDecoratorsToElementDefinition(
42 DecoratorEmitter::Kind kind, ParseNode* key, ListNode* decorators,
43 bool isStatic) {
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)) {
60 return false;
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()) {
69 return false;
72 // TODO: See Bug 1869000 to support addInitializer for methods.
73 if (!bce_->emitDupAt(1)) {
74 // [stack] ADDINIT VAL ADDINIT
75 return false;
78 if (!emitCallDecoratorForElement(kind, key, isStatic, decorator)) {
79 // [stack] ADDINIT RETVAL
80 return false;
83 // Step 5.i. Set decorationState.[[Finished]] to true.
84 if (!emitUpdateDecorationState()) {
85 return false;
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
92 return false;
95 InternalIfEmitter ie(bce_);
96 if (!ie.emitThenElse()) {
97 // [stack] ADDINIT VAL RETVAL
98 return false;
101 // Pop the undefined RETVAL from the stack, leaving the original value in
102 // place.
103 if (!bce_->emitPopN(1)) {
104 // [stack] ADDINIT VAL
105 return false;
108 if (!ie.emitElseIf(mozilla::Nothing())) {
109 return false;
112 // Step 5.l.i. If IsCallable(newValue) is true, then
113 if (!bce_->emitCheckIsCallable()) {
114 // [stack] ADDINIT VAL RETVAL ISCALLABLE_RESULT
115 return false;
118 if (!ie.emitThenElse()) {
119 // [stack] ADDINIT VAL RETVAL
120 return false;
122 // Step 5.l. Else,
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
129 return false;
131 if (!bce_->emitPopN(1)) {
132 // [stack] ADDINIT RETVAL
133 return false;
135 // Step 5.j.ii. Else if initializer is not undefined, throw a TypeError
136 // exception.
137 // Step 5.l.ii. Else if newValue is not undefined, throw a
138 // TypeError exception.
139 if (!ie.emitElse()) {
140 return false;
143 if (!bce_->emitPopN(1)) {
144 // [stack] ADDINIT RETVAL
145 return false;
148 if (!bce_->emit2(JSOp::ThrowMsg,
149 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType))) {
150 return false;
153 if (!ie.emitEnd()) {
154 return false;
158 return true;
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:
166 // [stack] ADDINIT
167 // end:
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
181 return false;
184 if (!emitPropertyKey(key)) {
185 // [stack] ADDINIT ARRAY NAME
186 return false;
189 if (!bce_->emitUint32Operand(JSOp::InitElemArray, 0)) {
190 // [stack] ADDINIT ARRAY
191 return false;
194 if (!bce_->emit1(JSOp::One)) {
195 // [stack] ADDINIT ARRAY INDEX
196 return false;
199 DecoratorsVector dec_vecs;
200 if (!reverseDecoratorsToApplicationOrder(decorators, dec_vecs)) {
201 return false;
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()) {
211 return false;
214 if (!bce_->emitDupAt(2)) {
215 // [stack] ADDINIT ARRAY INDEX ADDINIT
216 return false;
219 if (!emitCallDecoratorForElement(Kind::Field, key, isStatic, decorator)) {
220 // [stack] ADDINIT ARRAY INDEX RETVAL
221 return false;
224 // Step 5.i. Set decorationState.[[Finished]] to true.
225 if (!emitUpdateDecorationState()) {
226 // [stack] ADDINIT ARRAY INDEX RETVAL
227 return false;
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
234 return false;
237 InternalIfEmitter ie(bce_);
238 if (!ie.emitThenElse()) {
239 // [stack] ADDINIT ARRAY INDEX RETVAL
240 return false;
243 // Pop the undefined RETVAL from the stack, leaving the original value in
244 // place.
245 if (!bce_->emitPopN(1)) {
246 // [stack] ADDINIT ARRAY INDEX
247 return false;
250 if (!ie.emitElseIf(mozilla::Nothing())) {
251 return false;
254 // Step 5.l.i. If IsCallable(newValue) is true, then
256 if (!bce_->emitCheckIsCallable()) {
257 // [stack] ARRAY INDEX RETVAL ISCALLABLE_RESULT
258 return false;
261 if (!ie.emitThenElse()) {
262 // [stack] ADDINIT ARRAY INDEX RETVAL
263 return false;
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
271 return false;
274 // Step 5.j.ii. Else if initializer is not undefined, throw a TypeError
275 // exception.
276 // Step 5.l.ii. Else if newValue is not undefined, throw a
277 // TypeError exception.
278 if (!ie.emitElse()) {
279 return false;
282 if (!bce_->emitPopN(1)) {
283 // [stack] ADDINIT ARRAY INDEX
284 return false;
287 if (!bce_->emit2(JSOp::ThrowMsg,
288 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType))) {
289 return false;
292 if (!ie.emitEnd()) {
293 return false;
297 // Pop INDEX
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
307 // end:
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
322 return false;
325 if (!bce_->emitGetPrivateName(&key->as<NameNode>())) {
326 // [stack] ADDINIT GETTER SETTER ARRAY NAME
327 return false;
330 if (!bce_->emitUint32Operand(JSOp::InitElemArray, 0)) {
331 // [stack] ADDINIT GETTER SETTER ARRAY
332 return false;
335 if (!bce_->emit1(JSOp::One)) {
336 // [stack] ADDINIT GETTER SETTER ARRAY INDEX
337 return false;
340 DecoratorsVector dec_vecs;
341 if (!reverseDecoratorsToApplicationOrder(decorators, dec_vecs)) {
342 return false;
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()) {
352 return false;
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
359 return false;
362 // Step 5.g.ii. Perform ! CreateDataPropertyOrThrow(value, "get",
363 // elementRecord.[[Get]]).
364 if (!oe.prepareForPropValue(decorator->pn_pos.begin,
365 PropertyEmitter::Kind::Prototype)) {
366 return false;
368 if (!bce_->emitDupAt(4)) {
369 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE GETTER
370 return false;
372 if (!oe.emitInit(frontend::AccessorType::None,
373 frontend::TaggedParserAtomIndex::WellKnown::get())) {
374 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE
375 return false;
378 // Step 5.g.iii. Perform ! CreateDataPropertyOrThrow(value, "set",
379 // elementRecord.[[Set]]).
380 if (!oe.prepareForPropValue(decorator->pn_pos.begin,
381 PropertyEmitter::Kind::Prototype)) {
382 return false;
384 if (!bce_->emitDupAt(3)) {
385 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE SETTER
386 return false;
388 if (!oe.emitInit(frontend::AccessorType::None,
389 frontend::TaggedParserAtomIndex::WellKnown::set())) {
390 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE
391 return false;
394 if (!oe.emitEnd()) {
395 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE
396 return false;
399 if (!bce_->emitDupAt(5)) {
400 // [stack] ADDINIT GETTER SETTER ARRAY INDEX VALUE ADDINIT
401 return false;
404 // Step 5.j. Let newValue be ? Call(decorator, decoratorReceiver,
405 // « value, context »).
406 if (!emitCallDecoratorForElement(Kind::Accessor, key, isStatic,
407 decorator)) {
408 // [stack] ADDINIT GETTER SETTER ARRAY INDEX RETVAL
409 return false;
412 // Step 5.k. Set decorationState.[[Finished]] to true.
413 if (!emitUpdateDecorationState()) {
414 // [stack] ADDINIT GETTER SETTER ARRAY INDEX RETVAL
415 return false;
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
422 return false;
425 InternalIfEmitter ie(bce_);
426 if (!ie.emitThenElse()) {
427 // [stack] ADDINIT GETTER SETTER ARRAY INDEX RETVAL
428 return false;
431 // Pop the undefined RETVAL from the stack, leaving the original values in
432 // place.
433 if (!bce_->emitPopN(1)) {
434 // [stack] ADDINIT GETTER SETTER ARRAY INDEX
435 return false;
438 if (!ie.emitElse()) {
439 return false;
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
448 return false;
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)) {
459 return false;
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)) {
469 return false;
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)) {
479 return false;
482 // Pop RETVAL from stack
483 if (!bce_->emitPopN(1)) {
484 // [stack] ADDINIT GETTER SETTER ARRAY INDEX
485 return false;
488 if (!ie.emitEnd()) {
489 return false;
493 // Pop 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.
503 // [stack] CTOR
505 DecoratorsVector dec_vecs;
506 if (!reverseDecoratorsToApplicationOrder(decorators, dec_vecs)) {
507 return false;
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()) {
518 return false;
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
527 return false;
530 if (!cone.prepareForNonSpreadArguments()) {
531 return false;
534 // Duplicate the class definition to pass it as an argument
535 // to the decorator.
536 if (!bce_->emitDupAt(2)) {
537 // [stack] CTOR CALLEE THIS CTOR
538 return false;
541 // Step 1.d. Let context be CreateDecoratorContextObject(class, className,
542 // extraInitializers, decorationState).
543 // TODO: See Bug 1868221 for support for addInitializer for class
544 // decorators.
545 if (!bce_->emit1(JSOp::Undefined)) {
546 // [stack] CTOR CALLEE THIS CTOR ADDINIT
547 return false;
549 if (!emitCreateDecoratorContextObject(Kind::Class, key, false,
550 decorator->pn_pos)) {
551 // [stack] CTOR CALLEE THIS CTOR context
552 return false;
555 // Step 1.e. Let newDef be ? Call(decorator, decoratorReceiver, « classDef,
556 // context »).
557 if (!cone.emitEnd(2, decorator->pn_pos.begin)) {
558 // [stack] CTOR NEWCTOR
559 return false;
562 // Step 1.f. Set decorationState.[[Finished]] to true.
563 if (!emitUpdateDecorationState()) {
564 return false;
567 if (!emitCheckIsUndefined()) {
568 // [stack] CTOR NEWCTOR ISUNDEFINED
569 return false;
572 InternalIfEmitter ie(bce_);
573 if (!ie.emitThenElse()) {
574 // [stack] CTOR NEWCTOR
575 return false;
578 // Pop the undefined NEWDEF from the stack, leaving the original value in
579 // place.
580 if (!bce_->emitPopN(1)) {
581 // [stack] CTOR
582 return false;
585 if (!ie.emitElseIf(mozilla::Nothing())) {
586 return false;
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
593 return false;
596 if (!ie.emitThenElse()) {
597 // [stack] CTOR NEWCTOR
598 return false;
601 if (!bce_->emit1(JSOp::Swap)) {
602 // [stack] NEWCTOR CTOR
603 return false;
605 if (!bce_->emitPopN(1)) {
606 // [stack] NEWCTOR
607 return false;
610 // Step 1.h. Else if newDef is not undefined, then
611 // Step 1.h.i. Throw a TypeError exception.
612 if (!ie.emitElse()) {
613 return false;
616 if (!bce_->emitPopN(1)) {
617 // [stack] CTOR
618 return false;
620 if (!bce_->emit2(JSOp::ThrowMsg,
621 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType))) {
622 return false;
625 if (!ie.emitEnd()) {
626 return false;
630 // Step 2. Return classDef.
631 return true;
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
647 return false;
650 if (!bce_->emit1(JSOp::Zero)) {
651 // [stack] THIS INITIALIZERS INITIALIZERS INDEX
652 return false;
655 if (!bce_->emit1(JSOp::GetElem)) {
656 // [stack] THIS INITIALIZERS FIELDNAME
657 return false;
660 // Retrieve initial value of the field
661 if (!bce_->emit1(JSOp::Dup)) {
662 // [stack] THIS INITIALIZERS FIELDNAME FIELDNAME
663 return false;
666 if (!bce_->emitDupAt(3)) {
667 // [stack] THIS INITIALIZERS FIELDNAME FIELDNAME THIS
668 return false;
671 if (!bce_->emit1(JSOp::Swap)) {
672 // [stack] THIS INITIALIZERS FIELDNAME THIS FIELDNAME
673 return false;
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
683 return false;
686 if (!bce_->emit2(JSOp::Pick, 2)) {
687 // [stack] THIS FIELDNAME VALUE INITIALIZERS
688 return false;
691 // Retrieve the length of the initializers array.
692 if (!bce_->emit1(JSOp::Dup)) {
693 // [stack] THIS FIELDNAME VALUE INITIALIZERS INITIALIZERS
694 return false;
697 if (!bce_->emitAtomOp(JSOp::GetProp,
698 TaggedParserAtomIndex::WellKnown::length())) {
699 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH
700 return false;
703 if (!bce_->emit1(JSOp::One)) {
704 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
705 return false;
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
715 return false;
718 if (!bce_->emit1(JSOp::Dup)) {
719 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX INDEX
720 return false;
723 if (!bce_->emitDupAt(2)) {
724 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX INDEX
725 // LENGTH
726 return false;
729 if (!bce_->emit1(JSOp::Lt)) {
730 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX BOOL
731 return false;
734 // Step 5.a. Set initValue to ? Call(initializer, receiver, « initValue»).
735 if (!wh.emitBody()) {
736 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
737 return false;
740 if (!bce_->emitDupAt(2)) {
741 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
742 // INITIALIZERS
743 return false;
746 if (!bce_->emitDupAt(1)) {
747 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
748 // INITIALIZERS INDEX
749 return false;
752 if (!bce_->emit1(JSOp::GetElem)) {
753 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX FUNC
754 return false;
757 if (!bce_->emitDupAt(6)) {
758 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX FUNC THIS
759 return false;
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
765 return false;
768 // Callee is always internal function.
769 if (!bce_->emitCall(JSOp::Call, 1)) {
770 // [stack] THIS FIELDNAME INITIALIZERS LENGTH INDEX RVAL
771 return false;
774 // Store returned value for next iteration
775 if (!bce_->emit2(JSOp::Unpick, 3)) {
776 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
777 return false;
780 if (!bce_->emit1(JSOp::Inc)) {
781 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
782 return false;
785 if (!wh.emitEnd()) {
786 // [stack] THIS FIELDNAME VALUE INITIALIZERS LENGTH INDEX
787 return false;
790 // Step 6. If fieldName is a Private Name, then
791 // Step 6.a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue).
792 // Step 7. Else,
793 // Step 7.a. Assert: IsPropertyKey(fieldName) is true.
794 // Step 7.b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName,
795 // initValue).
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
800 return false;
803 if (!bce_->emit1(JSOp::InitElem)) {
804 // [stack] THIS
805 return false;
808 // Step 8. Return unused.
809 return bce_->emitPopN(1);
810 // [stack]
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.
817 MOZ_ASSERT(
818 extraInitializers ==
819 TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_());
821 if (!bce_->emitGetName(extraInitializers)) {
822 // [stack] ARRAY
823 return false;
826 if (!bce_->emit1(JSOp::Dup)) {
827 // [stack] ARRAY ARRAY
828 return false;
831 if (!bce_->emitAtomOp(JSOp::GetProp,
832 TaggedParserAtomIndex::WellKnown::length())) {
833 // [stack] ARRAY LENGTH
834 return false;
837 if (!bce_->emit1(JSOp::Zero)) {
838 // [stack] ARRAY LENGTH INDEX
839 return false;
842 InternalWhileEmitter wh(bce_);
843 if (!wh.emitCond()) {
844 // [stack] ARRAY LENGTH INDEX
845 return false;
848 if (!bce_->emit1(JSOp::Dup)) {
849 // [stack] ARRAY LENGTH INDEX INDEX
850 return false;
853 if (!bce_->emitDupAt(2)) {
854 // [stack] ARRAY LENGTH INDEX INDEX LENGTH
855 return false;
858 if (!bce_->emit1(JSOp::Lt)) {
859 // [stack] ARRAY LENGTH INDEX BOOL
860 return false;
863 if (!wh.emitBody()) {
864 // [stack] ARRAY LENGTH INDEX
865 return false;
868 if (!bce_->emitDupAt(2)) {
869 // [stack] ARRAY LENGTH INDEX ARRAY
870 return false;
873 if (!bce_->emitDupAt(1)) {
874 // [stack] ARRAY LENGTH INDEX ARRAY INDEX
875 return false;
878 // Retrieve initializer
879 if (!bce_->emit1(JSOp::GetElem)) {
880 // [stack] ARRAY LENGTH INDEX INITIALIZER
881 return false;
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
887 return false;
890 // Callee is always internal function.
891 if (!bce_->emitCall(JSOp::CallIgnoresRv, 0)) {
892 // [stack] ARRAY LENGTH INDEX RVAL
893 return false;
896 if (!bce_->emit1(JSOp::Pop)) {
897 // [stack] ARRAY LENGTH INDEX
898 return false;
901 if (!bce_->emit1(JSOp::Inc)) {
902 // [stack] ARRAY LENGTH INDEX
903 return false;
906 if (!wh.emitEnd()) {
907 // [stack] ARRAY LENGTH INDEX
908 return false;
911 return bce_->emitPopN(3);
912 // [stack]
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())) {
920 // [stack] NAME
921 return false;
923 } else {
924 MOZ_ASSERT(keyAsNameNode->privateNameKind() == PrivateNameKind::Field);
925 if (!bce_->emitGetPrivateName(keyAsNameNode)) {
926 // [stack] NAME
927 return false;
930 } else if (key->isKind(ParseNodeKind::NumberExpr)) {
931 if (!bce_->emitNumberOp(key->as<NumericLiteral>().value())) {
932 // [stack] NAME
933 return false;
935 } else {
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>())) {
941 // [stack] NAME
942 return false;
946 return true;
949 bool DecoratorEmitter::emitDecorationState() {
950 // TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1868841
951 return true;
954 bool DecoratorEmitter::emitUpdateDecorationState() {
955 // TODO: See https://bugzilla.mozilla.org/show_bug.cgi?id=1868841.
956 return true;
959 bool DecoratorEmitter::emitCallDecoratorForElement(Kind kind, ParseNode* key,
960 bool isStatic,
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
977 return false;
980 if (!cone.prepareForNonSpreadArguments()) {
981 return false;
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
988 return false;
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
999 return false;
1001 } else {
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
1009 return false;
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
1016 return false;
1018 if (!emitCreateDecoratorContextObject(kind, key, isStatic,
1019 decorator->pn_pos)) {
1020 // [stack] VAL? CALLEE THIS VAL context
1021 return false;
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)) {
1033 return false;
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.
1041 // [stack] VAL
1042 if (!bce_->emit1(JSOp::Dup)) {
1043 // [stack] VAL VAL
1044 return false;
1046 if (!bce_->emit1(JSOp::Undefined)) {
1047 // [stack] VAL VAL undefined
1048 return false;
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;
1060 // } else {
1061 // throw DecoratorInvalidReturnType;
1062 // }
1063 // }
1064 MOZ_ASSERT(addInitializerFunction);
1065 // TODO: Add support for static and class extra initializers, see bug 1868220
1066 // and bug 1868221.
1067 MOZ_ASSERT(
1068 initializers ==
1069 TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_());
1071 FunctionEmitter fe(bce_, addInitializerFunction->funbox(),
1072 FunctionSyntaxKind::Statement,
1073 FunctionEmitter::IsHoisted::No);
1074 if (!fe.prepareForNonLazy()) {
1075 return false;
1078 BytecodeEmitter bce2(bce_, addInitializerFunction->funbox());
1079 if (!bce2.init()) {
1080 return false;
1083 FunctionScriptEmitter fse(&bce2, addInitializerFunction->funbox(),
1084 mozilla::Nothing(), mozilla::Nothing());
1085 if (!fse.prepareForParameters()) {
1086 return false;
1089 if (!bce2.emitFunctionFormalParameters(addInitializerFunction->body())) {
1090 return false;
1093 if (!fse.prepareForBody()) {
1094 return false;
1097 LexicalScopeNode* lexicalScope = addInitializerFunction->body()->body();
1098 LexicalScopeEmitter lse(&bce2);
1099 if (lexicalScope->isEmptyScope()) {
1100 if (!lse.emitEmptyScope()) {
1101 return false;
1103 } else {
1104 if (!lse.emitScope(lexicalScope->kind(), lexicalScope->scopeBindings())) {
1105 return false;
1109 NameLocation loc =
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
1115 return false;
1118 if (!bce2.emitCheckIsCallable()) {
1119 // [stack] INITIALIZER ISCALLABLE
1120 return false;
1123 InternalIfEmitter ifCallable(&bce2);
1124 if (!ifCallable.emitThenElse()) {
1125 // [stack] INITIALIZER
1126 return false;
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
1133 return false;
1135 if (!bce2.emitEnvCoordOp(JSOp::CheckAliasedLexical,
1136 loc.environmentCoordinate())) {
1137 // [stack] INITIALIZER ARRAY
1138 return false;
1140 if (!bce2.emit1(JSOp::Dup)) {
1141 // [stack] INITIALIZER ARRAY ARRAY
1142 return false;
1144 if (!bce2.emitAtomOp(JSOp::GetProp,
1145 TaggedParserAtomIndex::WellKnown::length())) {
1146 // [stack] INITIALIZER ARRAY LENGTH
1147 return false;
1149 if (!bce2.emitPickN(2)) {
1150 // [stack] ARRAY LENGTH INITIALIZER
1151 return false;
1153 if (!bce2.emit1(JSOp::InitElemInc)) {
1154 // [stack] ARRAY LENGTH
1155 return false;
1157 if (!bce2.emitPopN(2)) {
1158 // [stack]
1159 return false;
1162 if (!ifCallable.emitElse()) {
1163 // [stack] INITIALIZER
1164 return false;
1167 if (!bce2.emitPopN(1)) {
1168 // [stack]
1169 return false;
1171 if (!bce2.emit2(JSOp::ThrowMsg,
1172 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType))) {
1173 return false;
1176 if (!ifCallable.emitEnd()) {
1177 return false;
1180 if (!lse.emitEnd()) {
1181 return false;
1184 if (!fse.emitEndBody()) {
1185 return false;
1188 if (!fse.intoStencil()) {
1189 return false;
1192 return fe.emitNonLazyEnd();
1193 // [stack] ADDINIT
1196 bool DecoratorEmitter::emitCreateDecoratorContextObject(Kind kind,
1197 ParseNode* key,
1198 bool isStatic,
1199 TokenPos pos) {
1200 // We expect the addInitializerFunction to already be on the stack.
1201 // [stack] ADDINIT
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
1208 return false;
1210 if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
1211 return false;
1214 TaggedParserAtomIndex kindStr;
1215 switch (kind) {
1216 case Kind::Method:
1217 // Step 2. If kind is method, let kindStr be "method".
1218 kindStr = frontend::TaggedParserAtomIndex::WellKnown::method();
1219 break;
1220 case Kind::Getter:
1221 // Step 3. Else if kind is getter, let kindStr be "getter".
1222 kindStr = frontend::TaggedParserAtomIndex::WellKnown::getter();
1223 break;
1224 case Kind::Setter:
1225 // Step 4. Else if kind is setter, let kindStr be "setter".
1226 kindStr = frontend::TaggedParserAtomIndex::WellKnown::setter();
1227 break;
1228 case Kind::Accessor:
1229 // Step 5. Else if kind is accessor, let kindStr be "accessor".
1230 kindStr = frontend::TaggedParserAtomIndex::WellKnown::accessor();
1231 break;
1232 case Kind::Field:
1233 // Step 6. Else if kind is field, let kindStr be "field".
1234 kindStr = frontend::TaggedParserAtomIndex::WellKnown::field();
1235 break;
1236 case Kind::Class:
1237 // Step 7. Else,
1238 // Step 7.a. Assert: kind is class.
1239 // Step 7.b. Let kindStr be "class".
1240 kindStr = frontend::TaggedParserAtomIndex::WellKnown::class_();
1241 break;
1242 default:
1243 MOZ_ASSERT_UNREACHABLE("Unknown kind");
1244 break;
1246 if (!bce_->emitStringOp(JSOp::String, kindStr)) {
1247 // [stack] ADDINIT context kindStr
1248 return false;
1251 // Step 8. Perform ! CreateDataPropertyOrThrow(contextObj, "kind", kindStr).
1252 if (!oe.emitInit(frontend::AccessorType::None,
1253 frontend::TaggedParserAtomIndex::WellKnown::kind())) {
1254 // [stack] ADDINIT context
1255 return false;
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)) {
1264 return false;
1266 if (!emitCreateDecoratorAccessObject()) {
1267 return false;
1269 if (!oe.emitInit(frontend::AccessorType::None,
1270 frontend::TaggedParserAtomIndex::WellKnown::access())) {
1271 // [stack] ADDINIT context
1272 return false;
1274 // Step 9.b. If isStatic is present, perform
1275 // ! CreateDataPropertyOrThrow(contextObj, "static", isStatic).
1276 if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
1277 return false;
1279 if (!bce_->emit1(isStatic ? JSOp::True : JSOp::False)) {
1280 // [stack] ADDINIT context isStatic
1281 return false;
1283 if (!oe.emitInit(frontend::AccessorType::None,
1284 frontend::TaggedParserAtomIndex::WellKnown::static_())) {
1285 // [stack] ADDINIT context
1286 return false;
1288 // Step 9.c. If name is a Private Name, then
1289 // Step 9.c.i. Perform ! CreateDataPropertyOrThrow(contextObj, "private",
1290 // true).
1291 // Step 9.d. Else, Step 9.d.i. Perform
1292 // ! CreateDataPropertyOrThrow(contextObj, "private", false).
1293 if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
1294 return false;
1296 if (!bce_->emit1(key->isKind(ParseNodeKind::PrivateName) ? JSOp::True
1297 : JSOp::False)) {
1298 // [stack] ADDINIT context private
1299 return false;
1301 if (!oe.emitInit(frontend::AccessorType::None,
1302 frontend::TaggedParserAtomIndex::WellKnown::private_())) {
1303 // [stack] ADDINIT context
1304 return false;
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)) {
1312 return false;
1314 if (key->is<NameNode>()) {
1315 if (!bce_->emitStringOp(JSOp::String, key->as<NameNode>().atom())) {
1316 return false;
1318 } else {
1319 if (!emitPropertyKey(key)) {
1320 return false;
1323 if (!oe.emitInit(frontend::AccessorType::None,
1324 frontend::TaggedParserAtomIndex::WellKnown::name())) {
1325 // [stack] ADDINIT context
1326 return false;
1328 } else {
1329 // Step 10. Else,
1330 // Step 10.a. Perform ! CreateDataPropertyOrThrow(contextObj, "name", name).
1331 if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
1332 return false;
1334 if (key != nullptr) {
1335 if (!bce_->emitStringOp(JSOp::String, key->as<NameNode>().atom())) {
1336 return false;
1338 } else {
1339 if (!bce_->emit1(JSOp::Undefined)) {
1340 return false;
1343 if (!oe.emitInit(frontend::AccessorType::None,
1344 frontend::TaggedParserAtomIndex::WellKnown::name())) {
1345 // [stack] ADDINIT context
1346 return false;
1349 // Step 11. Let addInitializer be CreateAddInitializerFunction(initializers,
1350 // decorationState).
1351 if (!oe.prepareForPropValue(pos.begin, PropertyEmitter::Kind::Prototype)) {
1352 return false;
1355 if (!bce_->emitPickN(1)) {
1356 // [stack] context ADDINIT
1357 return false;
1359 // Step 12. Perform ! CreateDataPropertyOrThrow(contextObj, "addInitializer",
1360 // addInitializer).
1361 if (!oe.emitInit(
1362 frontend::AccessorType::None,
1363 frontend::TaggedParserAtomIndex::WellKnown::addInitializer())) {
1364 // [stack] context
1365 return false;
1367 // Step 13. Return contextObj.
1368 return oe.emitEnd();
1371 bool DecoratorEmitter::emitHandleNewValueField(TaggedParserAtomIndex atom,
1372 int8_t offset) {
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
1383 return false;
1385 if (!bce_->emitStringOp(JSOp::String, atom)) {
1386 // [stack] GETTER SETTER ARRAY INDEX RETVAL RETVAL ATOM
1387 return false;
1389 if (!bce_->emit1(JSOp::GetElem)) {
1390 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1391 // NEW_VALUE
1392 return false;
1395 if (!emitCheckIsUndefined()) {
1396 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1397 // NEW_VALUE ISUNDEFINED
1398 return false;
1401 InternalIfEmitter ifCallable(bce_);
1402 if (!ifCallable.emitThenElse()) {
1403 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1404 // NEW_VALUE
1405 return false;
1408 // Pop the undefined getter or setter from the stack, leaving the original
1409 // values in place.
1410 if (!bce_->emitPopN(1)) {
1411 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1412 return false;
1415 if (!ifCallable.emitElseIf(mozilla::Nothing())) {
1416 return false;
1418 if (!bce_->emitCheckIsCallable()) {
1419 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1420 // NEW_VALUE ISCALLABLE_RESULT
1421 return false;
1423 if (!ifCallable.emitThenElse()) {
1424 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1425 // NEW_VALUE
1426 return false;
1428 if (offset != 0) {
1429 if (!bce_->emitPickN(offset)) {
1430 // [stack] GETTER? SETTER? ARRAY INDEX RETVAL
1431 // NEW_VALUE GETTER_OR_SETTER
1432 return false;
1434 if (!bce_->emitPopN(1)) {
1435 // [stack] GETTER? SETTER? ARRAY INDEX RETVAL
1436 // NEW_VALUE
1437 return false;
1439 if (!bce_->emitUnpickN(offset - 1)) {
1440 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1441 return false;
1443 } else {
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
1448 return false;
1451 if (!bce_->emitUnpickN(3)) {
1452 // [stack] GETTER SETTER RETVAL ARRAY INDEX NEW_VALUE
1453 return false;
1456 if (!bce_->emit1(JSOp::InitElemInc)) {
1457 // [stack] GETTER SETTER RETVAL ARRAY INDEX
1458 return false;
1461 if (!bce_->emitPickN(2)) {
1462 // [stack] GETTER SETTER ARRAY INDEX RETVAL
1463 return false;
1467 if (!ifCallable.emitElse()) {
1468 return false;
1471 if (!bce_->emitPopN(1)) {
1472 // [stack] GETTER SETTER ARRAY INDEX
1473 return false;
1476 if (!bce_->emit2(JSOp::ThrowMsg,
1477 uint8_t(ThrowMsgKind::DecoratorInvalidReturnType))) {
1478 return false;
1481 return ifCallable.emitEnd();