1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "frontend/CallOrNewEmitter.h"
9 #include "frontend/BytecodeEmitter.h"
10 #include "frontend/NameOpEmitter.h"
11 #include "vm/Opcodes.h"
14 using namespace js::frontend
;
16 CallOrNewEmitter::CallOrNewEmitter(BytecodeEmitter
* bce
, JSOp op
,
17 ArgumentsKind argumentsKind
,
18 ValueUsage valueUsage
)
19 : bce_(bce
), op_(op
), argumentsKind_(argumentsKind
) {
20 if (op_
== JSOp::Call
&& valueUsage
== ValueUsage::IgnoreValue
) {
21 op_
= JSOp::CallIgnoresRv
;
24 MOZ_ASSERT(isCall() || isNew() || isSuperCall());
27 bool CallOrNewEmitter::emitNameCallee(TaggedParserAtomIndex name
) {
28 MOZ_ASSERT(state_
== State::Start
);
34 isCall() ? NameOpEmitter::Kind::Call
: NameOpEmitter::Kind::Get
);
36 // [stack] # if isCall()
37 // [stack] CALLEE THIS
38 // [stack] # if isNew() or isSuperCall()
43 state_
= State::NameCallee
;
47 [[nodiscard
]] PropOpEmitter
& CallOrNewEmitter::prepareForPropCallee(
49 MOZ_ASSERT(state_
== State::Start
);
50 MOZ_ASSERT(bce_
->emitterMode
!= BytecodeEmitter::SelfHosting
);
55 isCall() ? PropOpEmitter::Kind::Call
: PropOpEmitter::Kind::Get
,
56 isSuperProp
? PropOpEmitter::ObjKind::Super
57 : PropOpEmitter::ObjKind::Other
);
59 state_
= State::PropCallee
;
63 [[nodiscard
]] ElemOpEmitter
& CallOrNewEmitter::prepareForElemCallee(
65 MOZ_ASSERT(state_
== State::Start
);
66 MOZ_ASSERT(bce_
->emitterMode
!= BytecodeEmitter::SelfHosting
);
71 isCall() ? ElemOpEmitter::Kind::Call
: ElemOpEmitter::Kind::Get
,
72 isSuperElem
? ElemOpEmitter::ObjKind::Super
73 : ElemOpEmitter::ObjKind::Other
);
75 state_
= State::ElemCallee
;
79 PrivateOpEmitter
& CallOrNewEmitter::prepareForPrivateCallee(
80 TaggedParserAtomIndex privateName
) {
81 MOZ_ASSERT(state_
== State::Start
);
82 MOZ_ASSERT(bce_
->emitterMode
!= BytecodeEmitter::SelfHosting
);
88 isCall() ? PrivateOpEmitter::Kind::Call
: PrivateOpEmitter::Kind::Get
,
90 state_
= State::PrivateCallee
;
94 bool CallOrNewEmitter::prepareForFunctionCallee() {
95 MOZ_ASSERT(state_
== State::Start
);
96 MOZ_ASSERT(bce_
->emitterMode
!= BytecodeEmitter::SelfHosting
);
100 state_
= State::FunctionCallee
;
104 bool CallOrNewEmitter::emitSuperCallee() {
105 MOZ_ASSERT(state_
== State::Start
);
106 MOZ_ASSERT(bce_
->emitterMode
!= BytecodeEmitter::SelfHosting
);
110 if (!bce_
->emitThisEnvironmentCallee()) {
114 if (!bce_
->emit1(JSOp::SuperFun
)) {
118 if (!bce_
->emit1(JSOp::IsConstructing
)) {
119 // [stack] SUPER_FUN IS_CONSTRUCTING
123 state_
= State::SuperCallee
;
127 bool CallOrNewEmitter::prepareForOtherCallee() {
128 MOZ_ASSERT(state_
== State::Start
);
129 MOZ_ASSERT(bce_
->emitterMode
!= BytecodeEmitter::SelfHosting
);
133 state_
= State::OtherCallee
;
137 bool CallOrNewEmitter::emitThis() {
138 MOZ_ASSERT(state_
== State::NameCallee
|| state_
== State::PropCallee
||
139 state_
== State::ElemCallee
|| state_
== State::PrivateCallee
||
140 state_
== State::FunctionCallee
|| state_
== State::SuperCallee
||
141 state_
== State::OtherCallee
);
143 // [stack] # if isCall()
144 // [stack] CALLEE THIS?
145 // [stack] # if isNew() or isSuperCall()
148 bool needsThis
= false;
150 case State::NameCallee
:
155 case State::PropCallee
:
161 case State::ElemCallee
:
167 case State::PrivateCallee
:
173 case State::FunctionCallee
:
176 case State::SuperCallee
:
178 case State::OtherCallee
:
184 if (isNew() || isSuperCall()) {
185 if (!bce_
->emit1(JSOp::IsConstructing
)) {
186 // [stack] CALLEE IS_CONSTRUCTING
190 if (!bce_
->emit1(JSOp::Undefined
)) {
191 // [stack] CALLEE THIS
197 // [stack] CALLEE THIS
199 state_
= State::This
;
203 bool CallOrNewEmitter::prepareForNonSpreadArguments() {
204 MOZ_ASSERT(state_
== State::This
);
205 MOZ_ASSERT(!isSpread());
207 // [stack] CALLEE THIS
209 state_
= State::Arguments
;
213 // See the usage in the comment at the top of the class.
214 bool CallOrNewEmitter::wantSpreadOperand() {
215 MOZ_ASSERT(state_
== State::This
);
216 MOZ_ASSERT(isSpread());
218 // [stack] CALLEE THIS
220 state_
= State::WantSpreadOperand
;
221 return isSingleSpread() || isPassthroughRest();
224 bool CallOrNewEmitter::prepareForSpreadArguments() {
225 MOZ_ASSERT(state_
== State::WantSpreadOperand
);
226 MOZ_ASSERT(isSpread());
227 MOZ_ASSERT(!isSingleSpread() && !isPassthroughRest());
229 // [stack] CALLEE THIS
231 state_
= State::Arguments
;
235 bool CallOrNewEmitter::emitSpreadArgumentsTest() {
236 // Caller should check wantSpreadOperand before this.
237 MOZ_ASSERT(state_
== State::WantSpreadOperand
);
238 MOZ_ASSERT(isSpread());
239 MOZ_ASSERT(isSingleSpread() || isPassthroughRest());
241 // [stack] CALLEE THIS ARG0
243 if (isSingleSpread()) {
244 // Emit a preparation code to optimize the spread call:
248 // If the spread operand is a packed array, skip the spread
249 // operation and pass it directly to spread call operation.
250 // See the comment in OptimizeSpreadCall in Interpreter.cpp
251 // for the optimizable conditions.
252 // [stack] CALLEE THIS ARG0
254 ifNotOptimizable_
.emplace(bce_
);
255 if (!bce_
->emit1(JSOp::Dup
)) {
256 // [stack] CALLEE THIS ARG0 ARG0
259 if (!bce_
->emit1(JSOp::OptimizeSpreadCall
)) {
260 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF
264 if (!bce_
->emit1(JSOp::Dup
)) {
265 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF ARRAY_OR_UNDEF
268 if (!bce_
->emit1(JSOp::Undefined
)) {
269 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF ARRAY_OR_UNDEF UNDEF
272 if (!bce_
->emit1(JSOp::StrictEq
)) {
273 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF EQ
277 if (!ifNotOptimizable_
->emitThenElse()) {
278 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF
281 if (!bce_
->emit1(JSOp::Pop
)) {
282 // [stack] CALLEE THIS ARG0
287 state_
= State::SpreadArgumentsTest
;
291 bool CallOrNewEmitter::wantSpreadIteration() {
292 MOZ_ASSERT(state_
== State::SpreadArgumentsTest
);
293 MOZ_ASSERT(isSpread());
295 state_
= State::SpreadIteration
;
296 return !isPassthroughRest();
299 bool CallOrNewEmitter::emitSpreadArgumentsTestEnd() {
300 MOZ_ASSERT(state_
== State::SpreadIteration
);
301 MOZ_ASSERT(isSpread());
303 if (isSingleSpread()) {
304 if (!ifNotOptimizable_
->emitElse()) {
305 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF
308 if (!bce_
->emit1(JSOp::Swap
)) {
309 // [stack] CALLEE THIS ARRAY_OR_UNDEF ARG0
312 if (!bce_
->emit1(JSOp::Pop
)) {
313 // [stack] CALLEE THIS ARRAY_OR_UNDEF
317 if (!ifNotOptimizable_
->emitEnd()) {
318 // [stack] CALLEE THIS ARR
322 ifNotOptimizable_
.reset();
325 state_
= State::Arguments
;
329 bool CallOrNewEmitter::emitEnd(uint32_t argc
, uint32_t beginPos
) {
330 MOZ_ASSERT(state_
== State::Arguments
);
332 // [stack] # if isCall()
333 // [stack] CALLEE THIS ARG0 ... ARGN
334 // [stack] # if isNew() or isSuperCall()
335 // [stack] CALLEE IS_CONSTRUCTING ARG0 ... ARGN NEW.TARGET?
337 if (!bce_
->updateSourceCoordNotes(beginPos
)) {
340 if (!bce_
->markSimpleBreakpoint()) {
344 if (!bce_
->emitCall(op_
, argc
)) {
349 if (!bce_
->emit1(op_
)) {
356 uint32_t lineNum
= bce_
->errorReporter().lineAt(beginPos
);
357 if (!bce_
->emitUint32Operand(JSOp::Lineno
, lineNum
)) {