Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / js / src / frontend / CallOrNewEmitter.cpp
blobfc4e449d56b3bf0f44a269f81080f59b4fb2236f
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"
13 using namespace js;
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);
30 // [stack]
32 NameOpEmitter noe(
33 bce_, name,
34 isCall() ? NameOpEmitter::Kind::Call : NameOpEmitter::Kind::Get);
35 if (!noe.emitGet()) {
36 // [stack] # if isCall()
37 // [stack] CALLEE THIS
38 // [stack] # if isNew() or isSuperCall()
39 // [stack] CALLEE
40 return false;
43 state_ = State::NameCallee;
44 return true;
47 [[nodiscard]] PropOpEmitter& CallOrNewEmitter::prepareForPropCallee(
48 bool isSuperProp) {
49 MOZ_ASSERT(state_ == State::Start);
50 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
52 // [stack]
54 poe_.emplace(bce_,
55 isCall() ? PropOpEmitter::Kind::Call : PropOpEmitter::Kind::Get,
56 isSuperProp ? PropOpEmitter::ObjKind::Super
57 : PropOpEmitter::ObjKind::Other);
59 state_ = State::PropCallee;
60 return *poe_;
63 [[nodiscard]] ElemOpEmitter& CallOrNewEmitter::prepareForElemCallee(
64 bool isSuperElem) {
65 MOZ_ASSERT(state_ == State::Start);
66 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
68 // [stack]
70 eoe_.emplace(bce_,
71 isCall() ? ElemOpEmitter::Kind::Call : ElemOpEmitter::Kind::Get,
72 isSuperElem ? ElemOpEmitter::ObjKind::Super
73 : ElemOpEmitter::ObjKind::Other);
75 state_ = State::ElemCallee;
76 return *eoe_;
79 PrivateOpEmitter& CallOrNewEmitter::prepareForPrivateCallee(
80 TaggedParserAtomIndex privateName) {
81 MOZ_ASSERT(state_ == State::Start);
82 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
84 // [stack]
86 xoe_.emplace(
87 bce_,
88 isCall() ? PrivateOpEmitter::Kind::Call : PrivateOpEmitter::Kind::Get,
89 privateName);
90 state_ = State::PrivateCallee;
91 return *xoe_;
94 bool CallOrNewEmitter::prepareForFunctionCallee() {
95 MOZ_ASSERT(state_ == State::Start);
96 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
98 // [stack]
100 state_ = State::FunctionCallee;
101 return true;
104 bool CallOrNewEmitter::emitSuperCallee() {
105 MOZ_ASSERT(state_ == State::Start);
106 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
108 // [stack]
110 if (!bce_->emitThisEnvironmentCallee()) {
111 // [stack] CALLEE
112 return false;
114 if (!bce_->emit1(JSOp::SuperFun)) {
115 // [stack] SUPER_FUN
116 return false;
118 if (!bce_->emit1(JSOp::IsConstructing)) {
119 // [stack] SUPER_FUN IS_CONSTRUCTING
120 return false;
123 state_ = State::SuperCallee;
124 return true;
127 bool CallOrNewEmitter::prepareForOtherCallee() {
128 MOZ_ASSERT(state_ == State::Start);
129 MOZ_ASSERT(bce_->emitterMode != BytecodeEmitter::SelfHosting);
131 // [stack]
133 state_ = State::OtherCallee;
134 return true;
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()
146 // [stack] CALLEE
148 bool needsThis = false;
149 switch (state_) {
150 case State::NameCallee:
151 if (!isCall()) {
152 needsThis = true;
154 break;
155 case State::PropCallee:
156 poe_.reset();
157 if (!isCall()) {
158 needsThis = true;
160 break;
161 case State::ElemCallee:
162 eoe_.reset();
163 if (!isCall()) {
164 needsThis = true;
166 break;
167 case State::PrivateCallee:
168 xoe_.reset();
169 if (!isCall()) {
170 needsThis = true;
172 break;
173 case State::FunctionCallee:
174 needsThis = true;
175 break;
176 case State::SuperCallee:
177 break;
178 case State::OtherCallee:
179 needsThis = true;
180 break;
181 default:;
183 if (needsThis) {
184 if (isNew() || isSuperCall()) {
185 if (!bce_->emit1(JSOp::IsConstructing)) {
186 // [stack] CALLEE IS_CONSTRUCTING
187 return false;
189 } else {
190 if (!bce_->emit1(JSOp::Undefined)) {
191 // [stack] CALLEE THIS
192 return false;
197 // [stack] CALLEE THIS
199 state_ = State::This;
200 return true;
203 bool CallOrNewEmitter::prepareForNonSpreadArguments() {
204 MOZ_ASSERT(state_ == State::This);
205 MOZ_ASSERT(!isSpread());
207 // [stack] CALLEE THIS
209 state_ = State::Arguments;
210 return true;
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;
232 return true;
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:
246 // g(...args);
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
257 return false;
259 if (!bce_->emit1(JSOp::OptimizeSpreadCall)) {
260 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF
261 return false;
264 if (!bce_->emit1(JSOp::Dup)) {
265 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF ARRAY_OR_UNDEF
266 return false;
268 if (!bce_->emit1(JSOp::Undefined)) {
269 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF ARRAY_OR_UNDEF UNDEF
270 return false;
272 if (!bce_->emit1(JSOp::StrictEq)) {
273 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF EQ
274 return false;
277 if (!ifNotOptimizable_->emitThenElse()) {
278 // [stack] CALLEE THIS ARG0 ARRAY_OR_UNDEF
279 return false;
281 if (!bce_->emit1(JSOp::Pop)) {
282 // [stack] CALLEE THIS ARG0
283 return false;
287 state_ = State::SpreadArgumentsTest;
288 return true;
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
306 return false;
308 if (!bce_->emit1(JSOp::Swap)) {
309 // [stack] CALLEE THIS ARRAY_OR_UNDEF ARG0
310 return false;
312 if (!bce_->emit1(JSOp::Pop)) {
313 // [stack] CALLEE THIS ARRAY_OR_UNDEF
314 return false;
317 if (!ifNotOptimizable_->emitEnd()) {
318 // [stack] CALLEE THIS ARR
319 return false;
322 ifNotOptimizable_.reset();
325 state_ = State::Arguments;
326 return true;
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)) {
338 return false;
340 if (!bce_->markSimpleBreakpoint()) {
341 return false;
343 if (!isSpread()) {
344 if (!bce_->emitCall(op_, argc)) {
345 // [stack] RVAL
346 return false;
348 } else {
349 if (!bce_->emit1(op_)) {
350 // [stack] RVAL
351 return false;
355 if (isEval()) {
356 uint32_t lineNum = bce_->errorReporter().lineAt(beginPos);
357 if (!bce_->emitUint32Operand(JSOp::Lineno, lineNum)) {
358 // [stack] RVAL
359 return false;
363 state_ = State::End;
364 return true;