1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/interpreter/bytecode-generator.h"
6
7 #include <map>
8 #include <unordered_map>
9 #include <unordered_set>
10
11 #include "include/v8-extension.h"
12 #include "src/api/api-inl.h"
13 #include "src/ast/ast-source-ranges.h"
14 #include "src/ast/ast.h"
15 #include "src/ast/scopes.h"
16 #include "src/builtins/builtins-constructor.h"
17 #include "src/codegen/compiler.h"
18 #include "src/codegen/unoptimized-compilation-info.h"
19 #include "src/common/globals.h"
20 #include "src/compiler-dispatcher/lazy-compile-dispatcher.h"
21 #include "src/heap/parked-scope.h"
22 #include "src/interpreter/bytecode-flags.h"
23 #include "src/interpreter/bytecode-jump-table.h"
24 #include "src/interpreter/bytecode-label.h"
25 #include "src/interpreter/bytecode-register-allocator.h"
26 #include "src/interpreter/bytecode-register.h"
27 #include "src/interpreter/control-flow-builders.h"
28 #include "src/logging/local-logger.h"
29 #include "src/logging/log.h"
30 #include "src/numbers/conversions.h"
31 #include "src/objects/debug-objects.h"
32 #include "src/objects/literal-objects-inl.h"
33 #include "src/objects/objects-inl.h"
34 #include "src/objects/smi.h"
35 #include "src/objects/template-objects-inl.h"
36 #include "src/parsing/parse-info.h"
37 #include "src/parsing/token.h"
38 #include "src/utils/ostreams.h"
39
40 namespace v8 {
41 namespace internal {
42 namespace interpreter {
43
44 // Scoped class tracking context objects created by the visitor. Represents
45 // mutations of the context chain within the function body, allowing pushing and
46 // popping of the current {context_register} during visitation.
47 class V8_NODISCARD BytecodeGenerator::ContextScope {
48 public:
ContextScope(BytecodeGenerator * generator,Scope * scope,Register outer_context_reg=Register ())49 ContextScope(BytecodeGenerator* generator, Scope* scope,
50 Register outer_context_reg = Register())
51 : generator_(generator),
52 scope_(scope),
53 outer_(generator_->execution_context()),
54 register_(Register::current_context()),
55 depth_(0) {
56 DCHECK(scope->NeedsContext() || outer_ == nullptr);
57 if (outer_) {
58 depth_ = outer_->depth_ + 1;
59
60 // Push the outer context into a new context register.
61 if (!outer_context_reg.is_valid()) {
62 outer_context_reg = generator_->register_allocator()->NewRegister();
63 }
64 outer_->set_register(outer_context_reg);
65 generator_->builder()->PushContext(outer_context_reg);
66 }
67 generator_->set_execution_context(this);
68 }
69
~ContextScope()70 ~ContextScope() {
71 if (outer_) {
72 DCHECK_EQ(register_.index(), Register::current_context().index());
73 generator_->builder()->PopContext(outer_->reg());
74 outer_->set_register(register_);
75 }
76 generator_->set_execution_context(outer_);
77 }
78
79 ContextScope(const ContextScope&) = delete;
80 ContextScope& operator=(const ContextScope&) = delete;
81
82 // Returns the depth of the given |scope| for the current execution context.
ContextChainDepth(Scope * scope)83 int ContextChainDepth(Scope* scope) {
84 return scope_->ContextChainLength(scope);
85 }
86
87 // Returns the execution context at |depth| in the current context chain if it
88 // is a function local execution context, otherwise returns nullptr.
Previous(int depth)89 ContextScope* Previous(int depth) {
90 if (depth > depth_) {
91 return nullptr;
92 }
93
94 ContextScope* previous = this;
95 for (int i = depth; i > 0; --i) {
96 previous = previous->outer_;
97 }
98 return previous;
99 }
100
reg() const101 Register reg() const { return register_; }
102
103 private:
builder() const104 const BytecodeArrayBuilder* builder() const { return generator_->builder(); }
105
set_register(Register reg)106 void set_register(Register reg) { register_ = reg; }
107
108 BytecodeGenerator* generator_;
109 Scope* scope_;
110 ContextScope* outer_;
111 Register register_;
112 int depth_;
113 };
114
115 // Scoped class for tracking control statements entered by the
116 // visitor.
117 class V8_NODISCARD BytecodeGenerator::ControlScope {
118 public:
ControlScope(BytecodeGenerator * generator)119 explicit ControlScope(BytecodeGenerator* generator)
120 : generator_(generator),
121 outer_(generator->execution_control()),
122 context_(generator->execution_context()) {
123 generator_->set_execution_control(this);
124 }
~ControlScope()125 ~ControlScope() { generator_->set_execution_control(outer()); }
126 ControlScope(const ControlScope&) = delete;
127 ControlScope& operator=(const ControlScope&) = delete;
128
Break(Statement * stmt)129 void Break(Statement* stmt) {
130 PerformCommand(CMD_BREAK, stmt, kNoSourcePosition);
131 }
Continue(Statement * stmt)132 void Continue(Statement* stmt) {
133 PerformCommand(CMD_CONTINUE, stmt, kNoSourcePosition);
134 }
ReturnAccumulator(int source_position)135 void ReturnAccumulator(int source_position) {
136 PerformCommand(CMD_RETURN, nullptr, source_position);
137 }
AsyncReturnAccumulator(int source_position)138 void AsyncReturnAccumulator(int source_position) {
139 PerformCommand(CMD_ASYNC_RETURN, nullptr, source_position);
140 }
141
142 class DeferredCommands;
143
144 protected:
145 enum Command {
146 CMD_BREAK,
147 CMD_CONTINUE,
148 CMD_RETURN,
149 CMD_ASYNC_RETURN,
150 CMD_RETHROW
151 };
CommandUsesAccumulator(Command command)152 static constexpr bool CommandUsesAccumulator(Command command) {
153 return command != CMD_BREAK && command != CMD_CONTINUE;
154 }
155
156 void PerformCommand(Command command, Statement* statement,
157 int source_position);
158 virtual bool Execute(Command command, Statement* statement,
159 int source_position) = 0;
160
161 // Helper to pop the context chain to a depth expected by this control scope.
162 // Note that it is the responsibility of each individual {Execute} method to
163 // trigger this when commands are handled and control-flow continues locally.
164 void PopContextToExpectedDepth();
165
generator() const166 BytecodeGenerator* generator() const { return generator_; }
outer() const167 ControlScope* outer() const { return outer_; }
context() const168 ContextScope* context() const { return context_; }
169
170 private:
171 BytecodeGenerator* generator_;
172 ControlScope* outer_;
173 ContextScope* context_;
174 };
175
176 // Helper class for a try-finally control scope. It can record intercepted
177 // control-flow commands that cause entry into a finally-block, and re-apply
178 // them after again leaving that block. Special tokens are used to identify
179 // paths going through the finally-block to dispatch after leaving the block.
180 class V8_NODISCARD BytecodeGenerator::ControlScope::DeferredCommands final {
181 public:
182 // Fixed value tokens for paths we know we need.
183 // Fallthrough is set to -1 to make it the fallthrough case of the jump table,
184 // where the remaining cases start at 0.
185 static const int kFallthroughToken = -1;
186 // TODO(leszeks): Rethrow being 0 makes it use up a valuable LdaZero, which
187 // means that other commands (such as break or return) have to use LdaSmi.
188 // This can very slightly bloat bytecode, so perhaps token values should all
189 // be shifted down by 1.
190 static const int kRethrowToken = 0;
191
DeferredCommands(BytecodeGenerator * generator,Register token_register,Register result_register)192 DeferredCommands(BytecodeGenerator* generator, Register token_register,
193 Register result_register)
194 : generator_(generator),
195 deferred_(generator->zone()),
196 token_register_(token_register),
197 result_register_(result_register),
198 return_token_(-1),
199 async_return_token_(-1) {
200 // There's always a rethrow path.
201 // TODO(leszeks): We could decouple deferred_ index and token to allow us
202 // to still push this lazily.
203 STATIC_ASSERT(kRethrowToken == 0);
204 deferred_.push_back({CMD_RETHROW, nullptr, kRethrowToken});
205 }
206
207 // One recorded control-flow command.
208 struct Entry {
209 Command command; // The command type being applied on this path.
210 Statement* statement; // The target statement for the command or {nullptr}.
211 int token; // A token identifying this particular path.
212 };
213
214 // Records a control-flow command while entering the finally-block. This also
215 // generates a new dispatch token that identifies one particular path. This
216 // expects the result to be in the accumulator.
RecordCommand(Command command,Statement * statement)217 void RecordCommand(Command command, Statement* statement) {
218 int token = GetTokenForCommand(command, statement);
219
220 DCHECK_LT(token, deferred_.size());
221 DCHECK_EQ(deferred_[token].command, command);
222 DCHECK_EQ(deferred_[token].statement, statement);
223 DCHECK_EQ(deferred_[token].token, token);
224
225 if (CommandUsesAccumulator(command)) {
226 builder()->StoreAccumulatorInRegister(result_register_);
227 }
228 builder()->LoadLiteral(Smi::FromInt(token));
229 builder()->StoreAccumulatorInRegister(token_register_);
230 if (!CommandUsesAccumulator(command)) {
231 // If we're not saving the accumulator in the result register, shove a
232 // harmless value there instead so that it is still considered "killed" in
233 // the liveness analysis. Normally we would LdaUndefined first, but the
234 // Smi token value is just as good, and by reusing it we save a bytecode.
235 builder()->StoreAccumulatorInRegister(result_register_);
236 }
237 }
238
239 // Records the dispatch token to be used to identify the re-throw path when
240 // the finally-block has been entered through the exception handler. This
241 // expects the exception to be in the accumulator.
RecordHandlerReThrowPath()242 void RecordHandlerReThrowPath() {
243 // The accumulator contains the exception object.
244 RecordCommand(CMD_RETHROW, nullptr);
245 }
246
247 // Records the dispatch token to be used to identify the implicit fall-through
248 // path at the end of a try-block into the corresponding finally-block.
RecordFallThroughPath()249 void RecordFallThroughPath() {
250 builder()->LoadLiteral(Smi::FromInt(kFallthroughToken));
251 builder()->StoreAccumulatorInRegister(token_register_);
252 // Since we're not saving the accumulator in the result register, shove a
253 // harmless value there instead so that it is still considered "killed" in
254 // the liveness analysis. Normally we would LdaUndefined first, but the Smi
255 // token value is just as good, and by reusing it we save a bytecode.
256 builder()->StoreAccumulatorInRegister(result_register_);
257 }
258
259 // Applies all recorded control-flow commands after the finally-block again.
260 // This generates a dynamic dispatch on the token from the entry point.
ApplyDeferredCommands()261 void ApplyDeferredCommands() {
262 if (deferred_.size() == 0) return;
263
264 BytecodeLabel fall_through;
265
266 if (deferred_.size() == 1) {
267 // For a single entry, just jump to the fallthrough if we don't match the
268 // entry token.
269 const Entry& entry = deferred_[0];
270
271 builder()
272 ->LoadLiteral(Smi::FromInt(entry.token))
273 .CompareReference(token_register_)
274 .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &fall_through);
275
276 if (CommandUsesAccumulator(entry.command)) {
277 builder()->LoadAccumulatorWithRegister(result_register_);
278 }
279 execution_control()->PerformCommand(entry.command, entry.statement,
280 kNoSourcePosition);
281 } else {
282 // For multiple entries, build a jump table and switch on the token,
283 // jumping to the fallthrough if none of them match.
284
285 BytecodeJumpTable* jump_table =
286 builder()->AllocateJumpTable(static_cast<int>(deferred_.size()), 0);
287 builder()
288 ->LoadAccumulatorWithRegister(token_register_)
289 .SwitchOnSmiNoFeedback(jump_table)
290 .Jump(&fall_through);
291 for (const Entry& entry : deferred_) {
292 builder()->Bind(jump_table, entry.token);
293
294 if (CommandUsesAccumulator(entry.command)) {
295 builder()->LoadAccumulatorWithRegister(result_register_);
296 }
297 execution_control()->PerformCommand(entry.command, entry.statement,
298 kNoSourcePosition);
299 }
300 }
301
302 builder()->Bind(&fall_through);
303 }
304
builder()305 BytecodeArrayBuilder* builder() { return generator_->builder(); }
execution_control()306 ControlScope* execution_control() { return generator_->execution_control(); }
307
308 private:
GetTokenForCommand(Command command,Statement * statement)309 int GetTokenForCommand(Command command, Statement* statement) {
310 switch (command) {
311 case CMD_RETURN:
312 return GetReturnToken();
313 case CMD_ASYNC_RETURN:
314 return GetAsyncReturnToken();
315 case CMD_RETHROW:
316 return kRethrowToken;
317 default:
318 // TODO(leszeks): We could also search for entries with the same
319 // command and statement.
320 return GetNewTokenForCommand(command, statement);
321 }
322 }
323
GetReturnToken()324 int GetReturnToken() {
325 if (return_token_ == -1) {
326 return_token_ = GetNewTokenForCommand(CMD_RETURN, nullptr);
327 }
328 return return_token_;
329 }
330
GetAsyncReturnToken()331 int GetAsyncReturnToken() {
332 if (async_return_token_ == -1) {
333 async_return_token_ = GetNewTokenForCommand(CMD_ASYNC_RETURN, nullptr);
334 }
335 return async_return_token_;
336 }
337
GetNewTokenForCommand(Command command,Statement * statement)338 int GetNewTokenForCommand(Command command, Statement* statement) {
339 int token = static_cast<int>(deferred_.size());
340 deferred_.push_back({command, statement, token});
341 return token;
342 }
343
344 BytecodeGenerator* generator_;
345 ZoneVector<Entry> deferred_;
346 Register token_register_;
347 Register result_register_;
348
349 // Tokens for commands that don't need a statement.
350 int return_token_;
351 int async_return_token_;
352 };
353
354 // Scoped class for dealing with control flow reaching the function level.
355 class BytecodeGenerator::ControlScopeForTopLevel final
356 : public BytecodeGenerator::ControlScope {
357 public:
ControlScopeForTopLevel(BytecodeGenerator * generator)358 explicit ControlScopeForTopLevel(BytecodeGenerator* generator)
359 : ControlScope(generator) {}
360
361 protected:
Execute(Command command,Statement * statement,int source_position)362 bool Execute(Command command, Statement* statement,
363 int source_position) override {
364 switch (command) {
365 case CMD_BREAK: // We should never see break/continue in top-level.
366 case CMD_CONTINUE:
367 UNREACHABLE();
368 case CMD_RETURN:
369 // No need to pop contexts, execution leaves the method body.
370 generator()->BuildReturn(source_position);
371 return true;
372 case CMD_ASYNC_RETURN:
373 // No need to pop contexts, execution leaves the method body.
374 generator()->BuildAsyncReturn(source_position);
375 return true;
376 case CMD_RETHROW:
377 // No need to pop contexts, execution leaves the method body.
378 generator()->BuildReThrow();
379 return true;
380 }
381 return false;
382 }
383 };
384
385 // Scoped class for enabling break inside blocks and switch blocks.
386 class BytecodeGenerator::ControlScopeForBreakable final
387 : public BytecodeGenerator::ControlScope {
388 public:
ControlScopeForBreakable(BytecodeGenerator * generator,BreakableStatement * statement,BreakableControlFlowBuilder * control_builder)389 ControlScopeForBreakable(BytecodeGenerator* generator,
390 BreakableStatement* statement,
391 BreakableControlFlowBuilder* control_builder)
392 : ControlScope(generator),
393 statement_(statement),
394 control_builder_(control_builder) {}
395
396 protected:
Execute(Command command,Statement * statement,int source_position)397 bool Execute(Command command, Statement* statement,
398 int source_position) override {
399 if (statement != statement_) return false;
400 switch (command) {
401 case CMD_BREAK:
402 PopContextToExpectedDepth();
403 control_builder_->Break();
404 return true;
405 case CMD_CONTINUE:
406 case CMD_RETURN:
407 case CMD_ASYNC_RETURN:
408 case CMD_RETHROW:
409 break;
410 }
411 return false;
412 }
413
414 private:
415 Statement* statement_;
416 BreakableControlFlowBuilder* control_builder_;
417 };
418
419 // Scoped class for enabling 'break' and 'continue' in iteration
420 // constructs, e.g. do...while, while..., for...
421 class BytecodeGenerator::ControlScopeForIteration final
422 : public BytecodeGenerator::ControlScope {
423 public:
ControlScopeForIteration(BytecodeGenerator * generator,IterationStatement * statement,LoopBuilder * loop_builder)424 ControlScopeForIteration(BytecodeGenerator* generator,
425 IterationStatement* statement,
426 LoopBuilder* loop_builder)
427 : ControlScope(generator),
428 statement_(statement),
429 loop_builder_(loop_builder) {}
430
431 protected:
Execute(Command command,Statement * statement,int source_position)432 bool Execute(Command command, Statement* statement,
433 int source_position) override {
434 if (statement != statement_) return false;
435 switch (command) {
436 case CMD_BREAK:
437 PopContextToExpectedDepth();
438 loop_builder_->Break();
439 return true;
440 case CMD_CONTINUE:
441 PopContextToExpectedDepth();
442 loop_builder_->Continue();
443 return true;
444 case CMD_RETURN:
445 case CMD_ASYNC_RETURN:
446 case CMD_RETHROW:
447 break;
448 }
449 return false;
450 }
451
452 private:
453 Statement* statement_;
454 LoopBuilder* loop_builder_;
455 };
456
457 // Scoped class for enabling 'throw' in try-catch constructs.
458 class BytecodeGenerator::ControlScopeForTryCatch final
459 : public BytecodeGenerator::ControlScope {
460 public:
ControlScopeForTryCatch(BytecodeGenerator * generator,TryCatchBuilder * try_catch_builder)461 ControlScopeForTryCatch(BytecodeGenerator* generator,
462 TryCatchBuilder* try_catch_builder)
463 : ControlScope(generator) {}
464
465 protected:
Execute(Command command,Statement * statement,int source_position)466 bool Execute(Command command, Statement* statement,
467 int source_position) override {
468 switch (command) {
469 case CMD_BREAK:
470 case CMD_CONTINUE:
471 case CMD_RETURN:
472 case CMD_ASYNC_RETURN:
473 break;
474 case CMD_RETHROW:
475 // No need to pop contexts, execution re-enters the method body via the
476 // stack unwinding mechanism which itself restores contexts correctly.
477 generator()->BuildReThrow();
478 return true;
479 }
480 return false;
481 }
482 };
483
484 // Scoped class for enabling control flow through try-finally constructs.
485 class BytecodeGenerator::ControlScopeForTryFinally final
486 : public BytecodeGenerator::ControlScope {
487 public:
ControlScopeForTryFinally(BytecodeGenerator * generator,TryFinallyBuilder * try_finally_builder,DeferredCommands * commands)488 ControlScopeForTryFinally(BytecodeGenerator* generator,
489 TryFinallyBuilder* try_finally_builder,
490 DeferredCommands* commands)
491 : ControlScope(generator),
492 try_finally_builder_(try_finally_builder),
493 commands_(commands) {}
494
495 protected:
Execute(Command command,Statement * statement,int source_position)496 bool Execute(Command command, Statement* statement,
497 int source_position) override {
498 switch (command) {
499 case CMD_BREAK:
500 case CMD_CONTINUE:
501 case CMD_RETURN:
502 case CMD_ASYNC_RETURN:
503 case CMD_RETHROW:
504 PopContextToExpectedDepth();
505 // We don't record source_position here since we don't generate return
506 // bytecode right here and will generate it later as part of finally
507 // block. Each return bytecode generated in finally block will get own
508 // return source position from corresponded return statement or we'll
509 // use end of function if no return statement is presented.
510 commands_->RecordCommand(command, statement);
511 try_finally_builder_->LeaveTry();
512 return true;
513 }
514 return false;
515 }
516
517 private:
518 TryFinallyBuilder* try_finally_builder_;
519 DeferredCommands* commands_;
520 };
521
522 // Allocate and fetch the coverage indices tracking NaryLogical Expressions.
523 class BytecodeGenerator::NaryCodeCoverageSlots {
524 public:
NaryCodeCoverageSlots(BytecodeGenerator * generator,NaryOperation * expr)525 NaryCodeCoverageSlots(BytecodeGenerator* generator, NaryOperation* expr)
526 : generator_(generator) {
527 if (generator_->block_coverage_builder_ == nullptr) return;
528 for (size_t i = 0; i < expr->subsequent_length(); i++) {
529 coverage_slots_.push_back(
530 generator_->AllocateNaryBlockCoverageSlotIfEnabled(expr, i));
531 }
532 }
533
GetSlotFor(size_t subsequent_expr_index) const534 int GetSlotFor(size_t subsequent_expr_index) const {
535 if (generator_->block_coverage_builder_ == nullptr) {
536 return BlockCoverageBuilder::kNoCoverageArraySlot;
537 }
538 DCHECK(coverage_slots_.size() > subsequent_expr_index);
539 return coverage_slots_[subsequent_expr_index];
540 }
541
542 private:
543 BytecodeGenerator* generator_;
544 std::vector<int> coverage_slots_;
545 };
546
PerformCommand(Command command,Statement * statement,int source_position)547 void BytecodeGenerator::ControlScope::PerformCommand(Command command,
548 Statement* statement,
549 int source_position) {
550 ControlScope* current = this;
551 do {
552 if (current->Execute(command, statement, source_position)) {
553 return;
554 }
555 current = current->outer();
556 } while (current != nullptr);
557 UNREACHABLE();
558 }
559
PopContextToExpectedDepth()560 void BytecodeGenerator::ControlScope::PopContextToExpectedDepth() {
561 // Pop context to the expected depth. Note that this can in fact pop multiple
562 // contexts at once because the {PopContext} bytecode takes a saved register.
563 if (generator()->execution_context() != context()) {
564 generator()->builder()->PopContext(context()->reg());
565 }
566 }
567
568 class V8_NODISCARD BytecodeGenerator::RegisterAllocationScope final {
569 public:
RegisterAllocationScope(BytecodeGenerator * generator)570 explicit RegisterAllocationScope(BytecodeGenerator* generator)
571 : generator_(generator),
572 outer_next_register_index_(
573 generator->register_allocator()->next_register_index()) {}
574
~RegisterAllocationScope()575 ~RegisterAllocationScope() {
576 generator_->register_allocator()->ReleaseRegisters(
577 outer_next_register_index_);
578 }
579
580 RegisterAllocationScope(const RegisterAllocationScope&) = delete;
581 RegisterAllocationScope& operator=(const RegisterAllocationScope&) = delete;
582
generator() const583 BytecodeGenerator* generator() const { return generator_; }
584
585 private:
586 BytecodeGenerator* generator_;
587 int outer_next_register_index_;
588 };
589
590 class V8_NODISCARD BytecodeGenerator::AccumulatorPreservingScope final {
591 public:
AccumulatorPreservingScope(BytecodeGenerator * generator,AccumulatorPreservingMode mode)592 explicit AccumulatorPreservingScope(BytecodeGenerator* generator,
593 AccumulatorPreservingMode mode)
594 : generator_(generator) {
595 if (mode == AccumulatorPreservingMode::kPreserve) {
596 saved_accumulator_register_ =
597 generator_->register_allocator()->NewRegister();
598 generator_->builder()->StoreAccumulatorInRegister(
599 saved_accumulator_register_);
600 }
601 }
602
~AccumulatorPreservingScope()603 ~AccumulatorPreservingScope() {
604 if (saved_accumulator_register_.is_valid()) {
605 generator_->builder()->LoadAccumulatorWithRegister(
606 saved_accumulator_register_);
607 }
608 }
609
610 AccumulatorPreservingScope(const AccumulatorPreservingScope&) = delete;
611 AccumulatorPreservingScope& operator=(const AccumulatorPreservingScope&) =
612 delete;
613
614 private:
615 BytecodeGenerator* generator_;
616 Register saved_accumulator_register_;
617 };
618
619 // Scoped base class for determining how the result of an expression will be
620 // used.
621 class V8_NODISCARD BytecodeGenerator::ExpressionResultScope {
622 public:
ExpressionResultScope(BytecodeGenerator * generator,Expression::Context kind)623 ExpressionResultScope(BytecodeGenerator* generator, Expression::Context kind)
624 : outer_(generator->execution_result()),
625 allocator_(generator),
626 kind_(kind),
627 type_hint_(TypeHint::kAny) {
628 generator->set_execution_result(this);
629 }
630
~ExpressionResultScope()631 ~ExpressionResultScope() {
632 allocator_.generator()->set_execution_result(outer_);
633 }
634
635 ExpressionResultScope(const ExpressionResultScope&) = delete;
636 ExpressionResultScope& operator=(const ExpressionResultScope&) = delete;
637
IsEffect() const638 bool IsEffect() const { return kind_ == Expression::kEffect; }
IsValue() const639 bool IsValue() const { return kind_ == Expression::kValue; }
IsTest() const640 bool IsTest() const { return kind_ == Expression::kTest; }
641
AsTest()642 TestResultScope* AsTest() {
643 DCHECK(IsTest());
644 return reinterpret_cast<TestResultScope*>(this);
645 }
646
647 // Specify expression always returns a Boolean result value.
SetResultIsBoolean()648 void SetResultIsBoolean() {
649 DCHECK_EQ(type_hint_, TypeHint::kAny);
650 type_hint_ = TypeHint::kBoolean;
651 }
652
SetResultIsString()653 void SetResultIsString() {
654 DCHECK_EQ(type_hint_, TypeHint::kAny);
655 type_hint_ = TypeHint::kString;
656 }
657
type_hint() const658 TypeHint type_hint() const { return type_hint_; }
659
660 private:
661 ExpressionResultScope* outer_;
662 RegisterAllocationScope allocator_;
663 Expression::Context kind_;
664 TypeHint type_hint_;
665 };
666
667 // Scoped class used when the result of the current expression is not
668 // expected to produce a result.
669 class BytecodeGenerator::EffectResultScope final
670 : public ExpressionResultScope {
671 public:
EffectResultScope(BytecodeGenerator * generator)672 explicit EffectResultScope(BytecodeGenerator* generator)
673 : ExpressionResultScope(generator, Expression::kEffect) {}
674 };
675
676 // Scoped class used when the result of the current expression to be
677 // evaluated should go into the interpreter's accumulator.
678 class V8_NODISCARD BytecodeGenerator::ValueResultScope final
679 : public ExpressionResultScope {
680 public:
ValueResultScope(BytecodeGenerator * generator)681 explicit ValueResultScope(BytecodeGenerator* generator)
682 : ExpressionResultScope(generator, Expression::kValue) {}
683 };
684
685 // Scoped class used when the result of the current expression to be
686 // evaluated is only tested with jumps to two branches.
687 class V8_NODISCARD BytecodeGenerator::TestResultScope final
688 : public ExpressionResultScope {
689 public:
TestResultScope(BytecodeGenerator * generator,BytecodeLabels * then_labels,BytecodeLabels * else_labels,TestFallthrough fallthrough)690 TestResultScope(BytecodeGenerator* generator, BytecodeLabels* then_labels,
691 BytecodeLabels* else_labels, TestFallthrough fallthrough)
692 : ExpressionResultScope(generator, Expression::kTest),
693 result_consumed_by_test_(false),
694 fallthrough_(fallthrough),
695 then_labels_(then_labels),
696 else_labels_(else_labels) {}
697
698 TestResultScope(const TestResultScope&) = delete;
699 TestResultScope& operator=(const TestResultScope&) = delete;
700
701 // Used when code special cases for TestResultScope and consumes any
702 // possible value by testing and jumping to a then/else label.
SetResultConsumedByTest()703 void SetResultConsumedByTest() { result_consumed_by_test_ = true; }
result_consumed_by_test()704 bool result_consumed_by_test() { return result_consumed_by_test_; }
705
706 // Inverts the control flow of the operation, swapping the then and else
707 // labels and the fallthrough.
InvertControlFlow()708 void InvertControlFlow() {
709 std::swap(then_labels_, else_labels_);
710 fallthrough_ = inverted_fallthrough();
711 }
712
NewThenLabel()713 BytecodeLabel* NewThenLabel() { return then_labels_->New(); }
NewElseLabel()714 BytecodeLabel* NewElseLabel() { return else_labels_->New(); }
715
then_labels() const716 BytecodeLabels* then_labels() const { return then_labels_; }
else_labels() const717 BytecodeLabels* else_labels() const { return else_labels_; }
718
set_then_labels(BytecodeLabels * then_labels)719 void set_then_labels(BytecodeLabels* then_labels) {
720 then_labels_ = then_labels;
721 }
set_else_labels(BytecodeLabels * else_labels)722 void set_else_labels(BytecodeLabels* else_labels) {
723 else_labels_ = else_labels;
724 }
725
fallthrough() const726 TestFallthrough fallthrough() const { return fallthrough_; }
inverted_fallthrough() const727 TestFallthrough inverted_fallthrough() const {
728 switch (fallthrough_) {
729 case TestFallthrough::kThen:
730 return TestFallthrough::kElse;
731 case TestFallthrough::kElse:
732 return TestFallthrough::kThen;
733 default:
734 return TestFallthrough::kNone;
735 }
736 }
set_fallthrough(TestFallthrough fallthrough)737 void set_fallthrough(TestFallthrough fallthrough) {
738 fallthrough_ = fallthrough;
739 }
740
741 private:
742 bool result_consumed_by_test_;
743 TestFallthrough fallthrough_;
744 BytecodeLabels* then_labels_;
745 BytecodeLabels* else_labels_;
746 };
747
748 // Used to build a list of toplevel declaration data.
749 class BytecodeGenerator::TopLevelDeclarationsBuilder final : public ZoneObject {
750 public:
751 template <typename IsolateT>
AllocateDeclarations(UnoptimizedCompilationInfo * info,BytecodeGenerator * generator,Handle<Script> script,IsolateT * isolate)752 Handle<FixedArray> AllocateDeclarations(UnoptimizedCompilationInfo* info,
753 BytecodeGenerator* generator,
754 Handle<Script> script,
755 IsolateT* isolate) {
756 DCHECK(has_constant_pool_entry_);
757
758 Handle<FixedArray> data =
759 isolate->factory()->NewFixedArray(entry_slots_, AllocationType::kOld);
760
761 int array_index = 0;
762 if (info->scope()->is_module_scope()) {
763 for (Declaration* decl : *info->scope()->declarations()) {
764 Variable* var = decl->var();
765 if (!var->is_used()) continue;
766 if (var->location() != VariableLocation::MODULE) continue;
767 #ifdef DEBUG
768 int start = array_index;
769 #endif
770 if (decl->IsFunctionDeclaration()) {
771 FunctionLiteral* f = static_cast<FunctionDeclaration*>(decl)->fun();
772 Handle<SharedFunctionInfo> sfi(
773 Compiler::GetSharedFunctionInfo(f, script, isolate));
774 // Return a null handle if any initial values can't be created. Caller
775 // will set stack overflow.
776 if (sfi.is_null()) return Handle<FixedArray>();
777 data->set(array_index++, *sfi);
778 int literal_index = generator->GetCachedCreateClosureSlot(f);
779 data->set(array_index++, Smi::FromInt(literal_index));
780 DCHECK(var->IsExport());
781 data->set(array_index++, Smi::FromInt(var->index()));
782 DCHECK_EQ(start + kModuleFunctionDeclarationSize, array_index);
783 } else if (var->IsExport() && var->binding_needs_init()) {
784 data->set(array_index++, Smi::FromInt(var->index()));
785 DCHECK_EQ(start + kModuleVariableDeclarationSize, array_index);
786 }
787 }
788 } else {
789 for (Declaration* decl : *info->scope()->declarations()) {
790 Variable* var = decl->var();
791 if (!var->is_used()) continue;
792 if (var->location() != VariableLocation::UNALLOCATED) continue;
793 #ifdef DEBUG
794 int start = array_index;
795 #endif
796 if (decl->IsVariableDeclaration()) {
797 data->set(array_index++, *var->raw_name()->string());
798 DCHECK_EQ(start + kGlobalVariableDeclarationSize, array_index);
799 } else {
800 FunctionLiteral* f = static_cast<FunctionDeclaration*>(decl)->fun();
801 Handle<SharedFunctionInfo> sfi(
802 Compiler::GetSharedFunctionInfo(f, script, isolate));
803 // Return a null handle if any initial values can't be created. Caller
804 // will set stack overflow.
805 if (sfi.is_null()) return Handle<FixedArray>();
806 data->set(array_index++, *sfi);
807 int literal_index = generator->GetCachedCreateClosureSlot(f);
808 data->set(array_index++, Smi::FromInt(literal_index));
809 DCHECK_EQ(start + kGlobalFunctionDeclarationSize, array_index);
810 }
811 }
812 }
813 DCHECK_EQ(array_index, data->length());
814 return data;
815 }
816
constant_pool_entry()817 size_t constant_pool_entry() {
818 DCHECK(has_constant_pool_entry_);
819 return constant_pool_entry_;
820 }
821
set_constant_pool_entry(size_t constant_pool_entry)822 void set_constant_pool_entry(size_t constant_pool_entry) {
823 DCHECK(has_top_level_declaration());
824 DCHECK(!has_constant_pool_entry_);
825 constant_pool_entry_ = constant_pool_entry;
826 has_constant_pool_entry_ = true;
827 }
828
record_global_variable_declaration()829 void record_global_variable_declaration() {
830 entry_slots_ += kGlobalVariableDeclarationSize;
831 }
record_global_function_declaration()832 void record_global_function_declaration() {
833 entry_slots_ += kGlobalFunctionDeclarationSize;
834 }
record_module_variable_declaration()835 void record_module_variable_declaration() {
836 entry_slots_ += kModuleVariableDeclarationSize;
837 }
record_module_function_declaration()838 void record_module_function_declaration() {
839 entry_slots_ += kModuleFunctionDeclarationSize;
840 }
has_top_level_declaration()841 bool has_top_level_declaration() { return entry_slots_ > 0; }
processed()842 bool processed() { return processed_; }
mark_processed()843 void mark_processed() { processed_ = true; }
844
845 private:
846 const int kGlobalVariableDeclarationSize = 1;
847 const int kGlobalFunctionDeclarationSize = 2;
848 const int kModuleVariableDeclarationSize = 1;
849 const int kModuleFunctionDeclarationSize = 3;
850
851 size_t constant_pool_entry_ = 0;
852 int entry_slots_ = 0;
853 bool has_constant_pool_entry_ = false;
854 bool processed_ = false;
855 };
856
857 class V8_NODISCARD BytecodeGenerator::CurrentScope final {
858 public:
CurrentScope(BytecodeGenerator * generator,Scope * scope)859 CurrentScope(BytecodeGenerator* generator, Scope* scope)
860 : generator_(generator), outer_scope_(generator->current_scope()) {
861 if (scope != nullptr) {
862 DCHECK_EQ(outer_scope_, scope->outer_scope());
863 generator_->set_current_scope(scope);
864 }
865 }
~CurrentScope()866 ~CurrentScope() {
867 if (outer_scope_ != generator_->current_scope()) {
868 generator_->set_current_scope(outer_scope_);
869 }
870 }
871 CurrentScope(const CurrentScope&) = delete;
872 CurrentScope& operator=(const CurrentScope&) = delete;
873
874 private:
875 BytecodeGenerator* generator_;
876 Scope* outer_scope_;
877 };
878
879 class V8_NODISCARD BytecodeGenerator::MultipleEntryBlockContextScope {
880 public:
MultipleEntryBlockContextScope(BytecodeGenerator * generator,Scope * scope)881 MultipleEntryBlockContextScope(BytecodeGenerator* generator, Scope* scope)
882 : generator_(generator), scope_(scope), is_in_scope_(false) {
883 if (scope) {
884 inner_context_ = generator->register_allocator()->NewRegister();
885 outer_context_ = generator->register_allocator()->NewRegister();
886 generator->BuildNewLocalBlockContext(scope_);
887 generator->builder()->StoreAccumulatorInRegister(inner_context_);
888 }
889 }
890
SetEnteredIf(bool condition)891 void SetEnteredIf(bool condition) {
892 RegisterAllocationScope register_scope(generator_);
893 if (condition && scope_ != nullptr && !is_in_scope_) {
894 EnterScope();
895 } else if (!condition && is_in_scope_) {
896 ExitScope();
897 }
898 }
899
900 MultipleEntryBlockContextScope(const MultipleEntryBlockContextScope&) =
901 delete;
902 MultipleEntryBlockContextScope& operator=(
903 const MultipleEntryBlockContextScope&) = delete;
904
905 private:
EnterScope()906 void EnterScope() {
907 DCHECK(inner_context_.is_valid());
908 DCHECK(outer_context_.is_valid());
909 DCHECK(!is_in_scope_);
910 Register temp = generator_->register_allocator()->NewRegister();
911 generator_->builder()->StoreAccumulatorInRegister(temp);
912 generator_->builder()->LoadAccumulatorWithRegister(inner_context_);
913 current_scope_.emplace(generator_, scope_);
914 context_scope_.emplace(generator_, scope_, outer_context_);
915 generator_->builder()->LoadAccumulatorWithRegister(temp);
916 is_in_scope_ = true;
917 }
918
ExitScope()919 void ExitScope() {
920 DCHECK(inner_context_.is_valid());
921 DCHECK(outer_context_.is_valid());
922 DCHECK(is_in_scope_);
923 Register temp = generator_->register_allocator()->NewRegister();
924 generator_->builder()->StoreAccumulatorInRegister(temp);
925 context_scope_ = base::nullopt;
926 current_scope_ = base::nullopt;
927 generator_->builder()->LoadAccumulatorWithRegister(temp);
928 is_in_scope_ = false;
929 }
930
931 BytecodeGenerator* generator_;
932 Scope* scope_;
933 Register inner_context_;
934 Register outer_context_;
935 bool is_in_scope_;
936 base::Optional<CurrentScope> current_scope_;
937 base::Optional<ContextScope> context_scope_;
938 };
939
940 class BytecodeGenerator::FeedbackSlotCache : public ZoneObject {
941 public:
942 enum class SlotKind {
943 kStoreGlobalSloppy,
944 kStoreGlobalStrict,
945 kSetNamedStrict,
946 kSetNamedSloppy,
947 kLoadProperty,
948 kLoadSuperProperty,
949 kLoadGlobalNotInsideTypeof,
950 kLoadGlobalInsideTypeof,
951 kClosureFeedbackCell
952 };
953
FeedbackSlotCache(Zone * zone)954 explicit FeedbackSlotCache(Zone* zone) : map_(zone) {}
955
Put(SlotKind slot_kind,Variable * variable,int slot_index)956 void Put(SlotKind slot_kind, Variable* variable, int slot_index) {
957 PutImpl(slot_kind, 0, variable, slot_index);
958 }
Put(SlotKind slot_kind,AstNode * node,int slot_index)959 void Put(SlotKind slot_kind, AstNode* node, int slot_index) {
960 PutImpl(slot_kind, 0, node, slot_index);
961 }
Put(SlotKind slot_kind,int variable_index,const AstRawString * name,int slot_index)962 void Put(SlotKind slot_kind, int variable_index, const AstRawString* name,
963 int slot_index) {
964 PutImpl(slot_kind, variable_index, name, slot_index);
965 }
Put(SlotKind slot_kind,const AstRawString * name,int slot_index)966 void Put(SlotKind slot_kind, const AstRawString* name, int slot_index) {
967 PutImpl(slot_kind, 0, name, slot_index);
968 }
969
Get(SlotKind slot_kind,Variable * variable) const970 int Get(SlotKind slot_kind, Variable* variable) const {
971 return GetImpl(slot_kind, 0, variable);
972 }
Get(SlotKind slot_kind,AstNode * node) const973 int Get(SlotKind slot_kind, AstNode* node) const {
974 return GetImpl(slot_kind, 0, node);
975 }
Get(SlotKind slot_kind,int variable_index,const AstRawString * name) const976 int Get(SlotKind slot_kind, int variable_index,
977 const AstRawString* name) const {
978 return GetImpl(slot_kind, variable_index, name);
979 }
Get(SlotKind slot_kind,const AstRawString * name) const980 int Get(SlotKind slot_kind, const AstRawString* name) const {
981 return GetImpl(slot_kind, 0, name);
982 }
983
984 private:
985 using Key = std::tuple<SlotKind, int, const void*>;
986
PutImpl(SlotKind slot_kind,int index,const void * node,int slot_index)987 void PutImpl(SlotKind slot_kind, int index, const void* node,
988 int slot_index) {
989 Key key = std::make_tuple(slot_kind, index, node);
990 auto entry = std::make_pair(key, slot_index);
991 map_.insert(entry);
992 }
993
GetImpl(SlotKind slot_kind,int index,const void * node) const994 int GetImpl(SlotKind slot_kind, int index, const void* node) const {
995 Key key = std::make_tuple(slot_kind, index, node);
996 auto iter = map_.find(key);
997 if (iter != map_.end()) {
998 return iter->second;
999 }
1000 return -1;
1001 }
1002
1003 ZoneMap<Key, int> map_;
1004 };
1005
1006 class BytecodeGenerator::IteratorRecord final {
1007 public:
IteratorRecord(Register object_register,Register next_register,IteratorType type=IteratorType::kNormal)1008 IteratorRecord(Register object_register, Register next_register,
1009 IteratorType type = IteratorType::kNormal)
1010 : type_(type), object_(object_register), next_(next_register) {
1011 DCHECK(object_.is_valid() && next_.is_valid());
1012 }
1013
type() const1014 inline IteratorType type() const { return type_; }
object() const1015 inline Register object() const { return object_; }
next() const1016 inline Register next() const { return next_; }
1017
1018 private:
1019 IteratorType type_;
1020 Register object_;
1021 Register next_;
1022 };
1023
1024 class V8_NODISCARD BytecodeGenerator::OptionalChainNullLabelScope final {
1025 public:
OptionalChainNullLabelScope(BytecodeGenerator * bytecode_generator)1026 explicit OptionalChainNullLabelScope(BytecodeGenerator* bytecode_generator)
1027 : bytecode_generator_(bytecode_generator),
1028 labels_(bytecode_generator->zone()) {
1029 prev_ = bytecode_generator_->optional_chaining_null_labels_;
1030 bytecode_generator_->optional_chaining_null_labels_ = &labels_;
1031 }
1032
~OptionalChainNullLabelScope()1033 ~OptionalChainNullLabelScope() {
1034 bytecode_generator_->optional_chaining_null_labels_ = prev_;
1035 }
1036
labels()1037 BytecodeLabels* labels() { return &labels_; }
1038
1039 private:
1040 BytecodeGenerator* bytecode_generator_;
1041 BytecodeLabels labels_;
1042 BytecodeLabels* prev_;
1043 };
1044
1045 // LoopScope delimits the scope of {loop}, from its header to its final jump.
1046 // It should be constructed iff a (conceptual) back edge should be produced. In
1047 // the case of creating a LoopBuilder but never emitting the loop, it is valid
1048 // to skip the creation of LoopScope.
1049 class V8_NODISCARD BytecodeGenerator::LoopScope final {
1050 public:
LoopScope(BytecodeGenerator * bytecode_generator,LoopBuilder * loop)1051 explicit LoopScope(BytecodeGenerator* bytecode_generator, LoopBuilder* loop)
1052 : bytecode_generator_(bytecode_generator),
1053 parent_loop_scope_(bytecode_generator_->current_loop_scope()),
1054 loop_builder_(loop) {
1055 loop_builder_->LoopHeader();
1056 bytecode_generator_->set_current_loop_scope(this);
1057 bytecode_generator_->loop_depth_++;
1058 }
1059
~LoopScope()1060 ~LoopScope() {
1061 bytecode_generator_->loop_depth_--;
1062 bytecode_generator_->set_current_loop_scope(parent_loop_scope_);
1063 DCHECK_GE(bytecode_generator_->loop_depth_, 0);
1064 loop_builder_->JumpToHeader(
1065 bytecode_generator_->loop_depth_,
1066 parent_loop_scope_ ? parent_loop_scope_->loop_builder_ : nullptr);
1067 }
1068
1069 private:
1070 BytecodeGenerator* const bytecode_generator_;
1071 LoopScope* const parent_loop_scope_;
1072 LoopBuilder* const loop_builder_;
1073 };
1074
1075 namespace {
1076
1077 template <typename PropertyT>
1078 struct Accessors : public ZoneObject {
Accessorsv8::internal::interpreter::__anon4d1f688d0111::Accessors1079 Accessors() : getter(nullptr), setter(nullptr) {}
1080 PropertyT* getter;
1081 PropertyT* setter;
1082 };
1083
1084 // A map from property names to getter/setter pairs allocated in the zone that
1085 // also provides a way of accessing the pairs in the order they were first
1086 // added so that the generated bytecode is always the same.
1087 template <typename PropertyT>
1088 class AccessorTable
1089 : public base::TemplateHashMap<Literal, Accessors<PropertyT>,
1090 bool (*)(void*, void*),
1091 ZoneAllocationPolicy> {
1092 public:
AccessorTable(Zone * zone)1093 explicit AccessorTable(Zone* zone)
1094 : base::TemplateHashMap<Literal, Accessors<PropertyT>,
1095 bool (*)(void*, void*), ZoneAllocationPolicy>(
1096 Literal::Match, ZoneAllocationPolicy(zone)),
1097 zone_(zone) {}
1098
LookupOrInsert(Literal * key)1099 Accessors<PropertyT>* LookupOrInsert(Literal* key) {
1100 auto it = this->find(key, true);
1101 if (it->second == nullptr) {
1102 it->second = zone_->New<Accessors<PropertyT>>();
1103 ordered_accessors_.push_back({key, it->second});
1104 }
1105 return it->second;
1106 }
1107
1108 const std::vector<std::pair<Literal*, Accessors<PropertyT>*>>&
ordered_accessors()1109 ordered_accessors() {
1110 return ordered_accessors_;
1111 }
1112
1113 private:
1114 std::vector<std::pair<Literal*, Accessors<PropertyT>*>> ordered_accessors_;
1115
1116 Zone* zone_;
1117 };
1118
1119 } // namespace
1120
1121 #ifdef DEBUG
1122
IsInEagerLiterals(FunctionLiteral * literal,const std::vector<FunctionLiteral * > & eager_literals)1123 static bool IsInEagerLiterals(
1124 FunctionLiteral* literal,
1125 const std::vector<FunctionLiteral*>& eager_literals) {
1126 for (FunctionLiteral* eager_literal : eager_literals) {
1127 if (literal == eager_literal) return true;
1128 }
1129 return false;
1130 }
1131
1132 #endif // DEBUG
1133
BytecodeGenerator(LocalIsolate * local_isolate,Zone * compile_zone,UnoptimizedCompilationInfo * info,const AstStringConstants * ast_string_constants,std::vector<FunctionLiteral * > * eager_inner_literals,Handle<Script> script)1134 BytecodeGenerator::BytecodeGenerator(
1135 LocalIsolate* local_isolate, Zone* compile_zone,
1136 UnoptimizedCompilationInfo* info,
1137 const AstStringConstants* ast_string_constants,
1138 std::vector<FunctionLiteral*>* eager_inner_literals, Handle<Script> script)
1139 : local_isolate_(local_isolate),
1140 zone_(compile_zone),
1141 builder_(zone(), info->num_parameters_including_this(),
1142 info->scope()->num_stack_slots(), info->feedback_vector_spec(),
1143 info->SourcePositionRecordingMode()),
1144 info_(info),
1145 ast_string_constants_(ast_string_constants),
1146 closure_scope_(info->scope()),
1147 current_scope_(info->scope()),
1148 eager_inner_literals_(eager_inner_literals),
1149 script_(script),
1150 feedback_slot_cache_(zone()->New<FeedbackSlotCache>(zone())),
1151 top_level_builder_(zone()->New<TopLevelDeclarationsBuilder>()),
1152 block_coverage_builder_(nullptr),
1153 function_literals_(0, zone()),
1154 native_function_literals_(0, zone()),
1155 object_literals_(0, zone()),
1156 array_literals_(0, zone()),
1157 class_literals_(0, zone()),
1158 template_objects_(0, zone()),
1159 execution_control_(nullptr),
1160 execution_context_(nullptr),
1161 execution_result_(nullptr),
1162 incoming_new_target_or_generator_(),
1163 optional_chaining_null_labels_(nullptr),
1164 dummy_feedback_slot_(feedback_spec(), FeedbackSlotKind::kCompareOp),
1165 generator_jump_table_(nullptr),
1166 suspend_count_(0),
1167 loop_depth_(0),
1168 current_loop_scope_(nullptr),
1169 catch_prediction_(HandlerTable::UNCAUGHT) {
1170 DCHECK_EQ(closure_scope(), closure_scope()->GetClosureScope());
1171 if (info->has_source_range_map()) {
1172 block_coverage_builder_ = zone()->New<BlockCoverageBuilder>(
1173 zone(), builder(), info->source_range_map());
1174 }
1175 }
1176
1177 namespace {
1178
1179 template <typename Isolate>
1180 struct NullContextScopeHelper;
1181
1182 template <>
1183 struct NullContextScopeHelper<Isolate> {
1184 using Type = NullContextScope;
1185 };
1186
1187 template <>
1188 struct NullContextScopeHelper<LocalIsolate> {
1189 class V8_NODISCARD DummyNullContextScope {
1190 public:
DummyNullContextScope(LocalIsolate *)1191 explicit DummyNullContextScope(LocalIsolate*) {}
1192 };
1193 using Type = DummyNullContextScope;
1194 };
1195
1196 template <typename Isolate>
1197 using NullContextScopeFor = typename NullContextScopeHelper<Isolate>::Type;
1198
1199 } // namespace
1200
1201 template <typename IsolateT>
FinalizeBytecode(IsolateT * isolate,Handle<Script> script)1202 Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode(
1203 IsolateT* isolate, Handle<Script> script) {
1204 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
1205 #ifdef DEBUG
1206 // Unoptimized compilation should be context-independent. Verify that we don't
1207 // access the native context by nulling it out during finalization.
1208 NullContextScopeFor<IsolateT> null_context_scope(isolate);
1209 #endif
1210
1211 AllocateDeferredConstants(isolate, script);
1212
1213 if (block_coverage_builder_) {
1214 Handle<CoverageInfo> coverage_info =
1215 isolate->factory()->NewCoverageInfo(block_coverage_builder_->slots());
1216 info()->set_coverage_info(coverage_info);
1217 if (FLAG_trace_block_coverage) {
1218 StdoutStream os;
1219 coverage_info->CoverageInfoPrint(os, info()->literal()->GetDebugName());
1220 }
1221 }
1222
1223 if (HasStackOverflow()) return Handle<BytecodeArray>();
1224 Handle<BytecodeArray> bytecode_array = builder()->ToBytecodeArray(isolate);
1225
1226 if (incoming_new_target_or_generator_.is_valid()) {
1227 bytecode_array->set_incoming_new_target_or_generator_register(
1228 incoming_new_target_or_generator_);
1229 }
1230
1231 return bytecode_array;
1232 }
1233
1234 template Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode(
1235 Isolate* isolate, Handle<Script> script);
1236 template Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode(
1237 LocalIsolate* isolate, Handle<Script> script);
1238
1239 template <typename IsolateT>
FinalizeSourcePositionTable(IsolateT * isolate)1240 Handle<ByteArray> BytecodeGenerator::FinalizeSourcePositionTable(
1241 IsolateT* isolate) {
1242 DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
1243 #ifdef DEBUG
1244 // Unoptimized compilation should be context-independent. Verify that we don't
1245 // access the native context by nulling it out during finalization.
1246 NullContextScopeFor<IsolateT> null_context_scope(isolate);
1247 #endif
1248
1249 Handle<ByteArray> source_position_table =
1250 builder()->ToSourcePositionTable(isolate);
1251
1252 LOG_CODE_EVENT(isolate,
1253 CodeLinePosInfoRecordEvent(
1254 info_->bytecode_array()->GetFirstBytecodeAddress(),
1255 *source_position_table, JitCodeEvent::BYTE_CODE));
1256
1257 return source_position_table;
1258 }
1259
1260 template Handle<ByteArray> BytecodeGenerator::FinalizeSourcePositionTable(
1261 Isolate* isolate);
1262 template Handle<ByteArray> BytecodeGenerator::FinalizeSourcePositionTable(
1263 LocalIsolate* isolate);
1264
1265 #ifdef DEBUG
CheckBytecodeMatches(BytecodeArray bytecode)1266 int BytecodeGenerator::CheckBytecodeMatches(BytecodeArray bytecode) {
1267 return builder()->CheckBytecodeMatches(bytecode);
1268 }
1269 #endif
1270
1271 template <typename IsolateT>
AllocateDeferredConstants(IsolateT * isolate,Handle<Script> script)1272 void BytecodeGenerator::AllocateDeferredConstants(IsolateT* isolate,
1273 Handle<Script> script) {
1274 if (top_level_builder()->has_top_level_declaration()) {
1275 // Build global declaration pair array.
1276 Handle<FixedArray> declarations = top_level_builder()->AllocateDeclarations(
1277 info(), this, script, isolate);
1278 if (declarations.is_null()) return SetStackOverflow();
1279 builder()->SetDeferredConstantPoolEntry(
1280 top_level_builder()->constant_pool_entry(), declarations);
1281 }
1282
1283 // Find or build shared function infos.
1284 for (std::pair<FunctionLiteral*, size_t> literal : function_literals_) {
1285 FunctionLiteral* expr = literal.first;
1286 Handle<SharedFunctionInfo> shared_info =
1287 Compiler::GetSharedFunctionInfo(expr, script, isolate);
1288 if (shared_info.is_null()) return SetStackOverflow();
1289 builder()->SetDeferredConstantPoolEntry(literal.second, shared_info);
1290 }
1291
1292 // Find or build shared function infos for the native function templates.
1293 for (std::pair<NativeFunctionLiteral*, size_t> literal :
1294 native_function_literals_) {
1295 // This should only happen for main-thread compilations.
1296 DCHECK((std::is_same<Isolate, v8::internal::Isolate>::value));
1297
1298 NativeFunctionLiteral* expr = literal.first;
1299 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
1300
1301 // Compute the function template for the native function.
1302 v8::Local<v8::FunctionTemplate> info =
1303 expr->extension()->GetNativeFunctionTemplate(
1304 v8_isolate, Utils::ToLocal(expr->name()));
1305 DCHECK(!info.IsEmpty());
1306
1307 Handle<SharedFunctionInfo> shared_info =
1308 FunctionTemplateInfo::GetOrCreateSharedFunctionInfo(
1309 isolate, Utils::OpenHandle(*info), expr->name());
1310 DCHECK(!shared_info.is_null());
1311 builder()->SetDeferredConstantPoolEntry(literal.second, shared_info);
1312 }
1313
1314 // Build object literal constant properties
1315 for (std::pair<ObjectLiteralBoilerplateBuilder*, size_t> literal :
1316 object_literals_) {
1317 ObjectLiteralBoilerplateBuilder* object_literal_builder = literal.first;
1318 if (object_literal_builder->properties_count() > 0) {
1319 // If constant properties is an empty fixed array, we've already added it
1320 // to the constant pool when visiting the object literal.
1321 Handle<ObjectBoilerplateDescription> constant_properties =
1322 object_literal_builder->GetOrBuildBoilerplateDescription(isolate);
1323
1324 builder()->SetDeferredConstantPoolEntry(literal.second,
1325 constant_properties);
1326 }
1327 }
1328
1329 // Build array literal constant elements
1330 for (std::pair<ArrayLiteralBoilerplateBuilder*, size_t> literal :
1331 array_literals_) {
1332 ArrayLiteralBoilerplateBuilder* array_literal_builder = literal.first;
1333 Handle<ArrayBoilerplateDescription> constant_elements =
1334 array_literal_builder->GetOrBuildBoilerplateDescription(isolate);
1335 builder()->SetDeferredConstantPoolEntry(literal.second, constant_elements);
1336 }
1337
1338 // Build class literal boilerplates.
1339 for (std::pair<ClassLiteral*, size_t> literal : class_literals_) {
1340 ClassLiteral* class_literal = literal.first;
1341 Handle<ClassBoilerplate> class_boilerplate =
1342 ClassBoilerplate::BuildClassBoilerplate(isolate, class_literal);
1343 builder()->SetDeferredConstantPoolEntry(literal.second, class_boilerplate);
1344 }
1345
1346 // Build template literals.
1347 for (std::pair<GetTemplateObject*, size_t> literal : template_objects_) {
1348 GetTemplateObject* get_template_object = literal.first;
1349 Handle<TemplateObjectDescription> description =
1350 get_template_object->GetOrBuildDescription(isolate);
1351 builder()->SetDeferredConstantPoolEntry(literal.second, description);
1352 }
1353 }
1354
1355 template void BytecodeGenerator::AllocateDeferredConstants(
1356 Isolate* isolate, Handle<Script> script);
1357 template void BytecodeGenerator::AllocateDeferredConstants(
1358 LocalIsolate* isolate, Handle<Script> script);
1359
1360 namespace {
NeedsContextInitialization(DeclarationScope * scope)1361 bool NeedsContextInitialization(DeclarationScope* scope) {
1362 return scope->NeedsContext() && !scope->is_script_scope() &&
1363 !scope->is_module_scope();
1364 }
1365 } // namespace
1366
GenerateBytecode(uintptr_t stack_limit)1367 void BytecodeGenerator::GenerateBytecode(uintptr_t stack_limit) {
1368 InitializeAstVisitor(stack_limit);
1369
1370 // Initialize the incoming context.
1371 ContextScope incoming_context(this, closure_scope());
1372
1373 // Initialize control scope.
1374 ControlScopeForTopLevel control(this);
1375
1376 RegisterAllocationScope register_scope(this);
1377
1378 AllocateTopLevelRegisters();
1379
1380 builder()->EmitFunctionStartSourcePosition(
1381 info()->literal()->start_position());
1382
1383 if (info()->literal()->CanSuspend()) {
1384 BuildGeneratorPrologue();
1385 }
1386
1387 if (NeedsContextInitialization(closure_scope())) {
1388 // Push a new inner context scope for the function.
1389 BuildNewLocalActivationContext();
1390 ContextScope local_function_context(this, closure_scope());
1391 BuildLocalActivationContextInitialization();
1392 GenerateBytecodeBody();
1393 } else {
1394 GenerateBytecodeBody();
1395 }
1396
1397 // Check that we are not falling off the end.
1398 DCHECK(builder()->RemainderOfBlockIsDead());
1399 }
1400
GenerateBytecodeBody()1401 void BytecodeGenerator::GenerateBytecodeBody() {
1402 // Build the arguments object if it is used.
1403 VisitArgumentsObject(closure_scope()->arguments());
1404
1405 // Build rest arguments array if it is used.
1406 Variable* rest_parameter = closure_scope()->rest_parameter();
1407 VisitRestArgumentsArray(rest_parameter);
1408
1409 // Build assignment to the function name or {.this_function}
1410 // variables if used.
1411 VisitThisFunctionVariable(closure_scope()->function_var());
1412 VisitThisFunctionVariable(closure_scope()->this_function_var());
1413
1414 // Build assignment to {new.target} variable if it is used.
1415 VisitNewTargetVariable(closure_scope()->new_target_var());
1416
1417 // Create a generator object if necessary and initialize the
1418 // {.generator_object} variable.
1419 FunctionLiteral* literal = info()->literal();
1420 if (IsResumableFunction(literal->kind())) {
1421 BuildGeneratorObjectVariableInitialization();
1422 }
1423
1424 // Emit tracing call if requested to do so.
1425 if (FLAG_trace) builder()->CallRuntime(Runtime::kTraceEnter);
1426
1427 // Emit type profile call.
1428 if (info()->flags().collect_type_profile()) {
1429 feedback_spec()->AddTypeProfileSlot();
1430 int num_parameters = closure_scope()->num_parameters();
1431 for (int i = 0; i < num_parameters; i++) {
1432 Register parameter(builder()->Parameter(i));
1433 builder()->LoadAccumulatorWithRegister(parameter).CollectTypeProfile(
1434 closure_scope()->parameter(i)->initializer_position());
1435 }
1436 }
1437
1438 // Increment the function-scope block coverage counter.
1439 BuildIncrementBlockCoverageCounterIfEnabled(literal, SourceRangeKind::kBody);
1440
1441 // Visit declarations within the function scope.
1442 if (closure_scope()->is_script_scope()) {
1443 VisitGlobalDeclarations(closure_scope()->declarations());
1444 } else if (closure_scope()->is_module_scope()) {
1445 VisitModuleDeclarations(closure_scope()->declarations());
1446 } else {
1447 VisitDeclarations(closure_scope()->declarations());
1448 }
1449
1450 // Emit initializing assignments for module namespace imports (if any).
1451 VisitModuleNamespaceImports();
1452
1453 // The derived constructor case is handled in VisitCallSuper.
1454 if (IsBaseConstructor(function_kind())) {
1455 if (literal->class_scope_has_private_brand()) {
1456 ClassScope* scope = info()->scope()->outer_scope()->AsClassScope();
1457 DCHECK_NOT_NULL(scope->brand());
1458 BuildPrivateBrandInitialization(builder()->Receiver(), scope->brand());
1459 }
1460
1461 if (literal->requires_instance_members_initializer()) {
1462 BuildInstanceMemberInitialization(Register::function_closure(),
1463 builder()->Receiver());
1464 }
1465 }
1466
1467 // Visit statements in the function body.
1468 VisitStatements(literal->body());
1469
1470 // Emit an implicit return instruction in case control flow can fall off the
1471 // end of the function without an explicit return being present on all paths.
1472 if (!builder()->RemainderOfBlockIsDead()) {
1473 builder()->LoadUndefined();
1474 BuildReturn(literal->return_position());
1475 }
1476 }
1477
AllocateTopLevelRegisters()1478 void BytecodeGenerator::AllocateTopLevelRegisters() {
1479 if (IsResumableFunction(info()->literal()->kind())) {
1480 // Either directly use generator_object_var or allocate a new register for
1481 // the incoming generator object.
1482 Variable* generator_object_var = closure_scope()->generator_object_var();
1483 if (generator_object_var->location() == VariableLocation::LOCAL) {
1484 incoming_new_target_or_generator_ =
1485 GetRegisterForLocalVariable(generator_object_var);
1486 } else {
1487 incoming_new_target_or_generator_ = register_allocator()->NewRegister();
1488 }
1489 } else if (closure_scope()->new_target_var()) {
1490 // Either directly use new_target_var or allocate a new register for
1491 // the incoming new target object.
1492 Variable* new_target_var = closure_scope()->new_target_var();
1493 if (new_target_var->location() == VariableLocation::LOCAL) {
1494 incoming_new_target_or_generator_ =
1495 GetRegisterForLocalVariable(new_target_var);
1496 } else {
1497 incoming_new_target_or_generator_ = register_allocator()->NewRegister();
1498 }
1499 }
1500 }
1501
BuildGeneratorPrologue()1502 void BytecodeGenerator::BuildGeneratorPrologue() {
1503 DCHECK_GT(info()->literal()->suspend_count(), 0);
1504 DCHECK(generator_object().is_valid());
1505 generator_jump_table_ =
1506 builder()->AllocateJumpTable(info()->literal()->suspend_count(), 0);
1507
1508 // If the generator is not undefined, this is a resume, so perform state
1509 // dispatch.
1510 builder()->SwitchOnGeneratorState(generator_object(), generator_jump_table_);
1511
1512 // Otherwise, fall-through to the ordinary function prologue, after which we
1513 // will run into the generator object creation and other extra code inserted
1514 // by the parser.
1515 }
1516
VisitBlock(Block * stmt)1517 void BytecodeGenerator::VisitBlock(Block* stmt) {
1518 // Visit declarations and statements.
1519 CurrentScope current_scope(this, stmt->scope());
1520 if (stmt->scope() != nullptr && stmt->scope()->NeedsContext()) {
1521 BuildNewLocalBlockContext(stmt->scope());
1522 ContextScope scope(this, stmt->scope());
1523 VisitBlockDeclarationsAndStatements(stmt);
1524 } else {
1525 VisitBlockDeclarationsAndStatements(stmt);
1526 }
1527 }
1528
VisitBlockDeclarationsAndStatements(Block * stmt)1529 void BytecodeGenerator::VisitBlockDeclarationsAndStatements(Block* stmt) {
1530 BlockBuilder block_builder(builder(), block_coverage_builder_, stmt);
1531 ControlScopeForBreakable execution_control(this, stmt, &block_builder);
1532 if (stmt->scope() != nullptr) {
1533 VisitDeclarations(stmt->scope()->declarations());
1534 }
1535 VisitStatements(stmt->statements());
1536 }
1537
VisitVariableDeclaration(VariableDeclaration * decl)1538 void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
1539 Variable* variable = decl->var();
1540 // Unused variables don't need to be visited.
1541 if (!variable->is_used()) return;
1542
1543 switch (variable->location()) {
1544 case VariableLocation::UNALLOCATED:
1545 case VariableLocation::MODULE:
1546 UNREACHABLE();
1547 case VariableLocation::LOCAL:
1548 if (variable->binding_needs_init()) {
1549 Register destination(builder()->Local(variable->index()));
1550 builder()->LoadTheHole().StoreAccumulatorInRegister(destination);
1551 }
1552 break;
1553 case VariableLocation::PARAMETER:
1554 if (variable->binding_needs_init()) {
1555 Register destination(builder()->Parameter(variable->index()));
1556 builder()->LoadTheHole().StoreAccumulatorInRegister(destination);
1557 }
1558 break;
1559 case VariableLocation::REPL_GLOBAL:
1560 // REPL let's are stored in script contexts. They get initialized
1561 // with the hole the same way as normal context allocated variables.
1562 case VariableLocation::CONTEXT:
1563 if (variable->binding_needs_init()) {
1564 DCHECK_EQ(0, execution_context()->ContextChainDepth(variable->scope()));
1565 builder()->LoadTheHole().StoreContextSlot(execution_context()->reg(),
1566 variable->index(), 0);
1567 }
1568 break;
1569 case VariableLocation::LOOKUP: {
1570 DCHECK_EQ(VariableMode::kDynamic, variable->mode());
1571 DCHECK(!variable->binding_needs_init());
1572
1573 Register name = register_allocator()->NewRegister();
1574
1575 builder()
1576 ->LoadLiteral(variable->raw_name())
1577 .StoreAccumulatorInRegister(name)
1578 .CallRuntime(Runtime::kDeclareEvalVar, name);
1579 break;
1580 }
1581 }
1582 }
1583
VisitFunctionDeclaration(FunctionDeclaration * decl)1584 void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
1585 Variable* variable = decl->var();
1586 DCHECK(variable->mode() == VariableMode::kLet ||
1587 variable->mode() == VariableMode::kVar ||
1588 variable->mode() == VariableMode::kDynamic);
1589 // Unused variables don't need to be visited.
1590 if (!variable->is_used()) return;
1591
1592 switch (variable->location()) {
1593 case VariableLocation::UNALLOCATED:
1594 case VariableLocation::MODULE:
1595 UNREACHABLE();
1596 case VariableLocation::PARAMETER:
1597 case VariableLocation::LOCAL: {
1598 VisitFunctionLiteral(decl->fun());
1599 BuildVariableAssignment(variable, Token::INIT, HoleCheckMode::kElided);
1600 break;
1601 }
1602 case VariableLocation::REPL_GLOBAL:
1603 case VariableLocation::CONTEXT: {
1604 DCHECK_EQ(0, execution_context()->ContextChainDepth(variable->scope()));
1605 VisitFunctionLiteral(decl->fun());
1606 builder()->StoreContextSlot(execution_context()->reg(), variable->index(),
1607 0);
1608 break;
1609 }
1610 case VariableLocation::LOOKUP: {
1611 RegisterList args = register_allocator()->NewRegisterList(2);
1612 builder()
1613 ->LoadLiteral(variable->raw_name())
1614 .StoreAccumulatorInRegister(args[0]);
1615 VisitFunctionLiteral(decl->fun());
1616 builder()->StoreAccumulatorInRegister(args[1]).CallRuntime(
1617 Runtime::kDeclareEvalFunction, args);
1618 break;
1619 }
1620 }
1621 DCHECK_IMPLIES(
1622 eager_inner_literals_ != nullptr && decl->fun()->ShouldEagerCompile(),
1623 IsInEagerLiterals(decl->fun(), *eager_inner_literals_));
1624 }
1625
VisitModuleNamespaceImports()1626 void BytecodeGenerator::VisitModuleNamespaceImports() {
1627 if (!closure_scope()->is_module_scope()) return;
1628
1629 RegisterAllocationScope register_scope(this);
1630 Register module_request = register_allocator()->NewRegister();
1631
1632 SourceTextModuleDescriptor* descriptor =
1633 closure_scope()->AsModuleScope()->module();
1634 for (auto entry : descriptor->namespace_imports()) {
1635 builder()
1636 ->LoadLiteral(Smi::FromInt(entry->module_request))
1637 .StoreAccumulatorInRegister(module_request)
1638 .CallRuntime(Runtime::kGetModuleNamespace, module_request);
1639 Variable* var = closure_scope()->LookupInModule(entry->local_name);
1640 BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kElided);
1641 }
1642 }
1643
BuildDeclareCall(Runtime::FunctionId id)1644 void BytecodeGenerator::BuildDeclareCall(Runtime::FunctionId id) {
1645 if (!top_level_builder()->has_top_level_declaration()) return;
1646 DCHECK(!top_level_builder()->processed());
1647
1648 top_level_builder()->set_constant_pool_entry(
1649 builder()->AllocateDeferredConstantPoolEntry());
1650
1651 // Emit code to declare globals.
1652 RegisterList args = register_allocator()->NewRegisterList(2);
1653 builder()
1654 ->LoadConstantPoolEntry(top_level_builder()->constant_pool_entry())
1655 .StoreAccumulatorInRegister(args[0])
1656 .MoveRegister(Register::function_closure(), args[1])
1657 .CallRuntime(id, args);
1658
1659 top_level_builder()->mark_processed();
1660 }
1661
VisitModuleDeclarations(Declaration::List * decls)1662 void BytecodeGenerator::VisitModuleDeclarations(Declaration::List* decls) {
1663 RegisterAllocationScope register_scope(this);
1664 for (Declaration* decl : *decls) {
1665 Variable* var = decl->var();
1666 if (!var->is_used()) continue;
1667 if (var->location() == VariableLocation::MODULE) {
1668 if (decl->IsFunctionDeclaration()) {
1669 DCHECK(var->IsExport());
1670 FunctionDeclaration* f = static_cast<FunctionDeclaration*>(decl);
1671 AddToEagerLiteralsIfEager(f->fun());
1672 top_level_builder()->record_module_function_declaration();
1673 } else if (var->IsExport() && var->binding_needs_init()) {
1674 DCHECK(decl->IsVariableDeclaration());
1675 top_level_builder()->record_module_variable_declaration();
1676 }
1677 } else {
1678 RegisterAllocationScope inner_register_scope(this);
1679 Visit(decl);
1680 }
1681 }
1682 BuildDeclareCall(Runtime::kDeclareModuleExports);
1683 }
1684
VisitGlobalDeclarations(Declaration::List * decls)1685 void BytecodeGenerator::VisitGlobalDeclarations(Declaration::List* decls) {
1686 RegisterAllocationScope register_scope(this);
1687 for (Declaration* decl : *decls) {
1688 Variable* var = decl->var();
1689 DCHECK(var->is_used());
1690 if (var->location() == VariableLocation::UNALLOCATED) {
1691 // var or function.
1692 if (decl->IsFunctionDeclaration()) {
1693 top_level_builder()->record_global_function_declaration();
1694 FunctionDeclaration* f = static_cast<FunctionDeclaration*>(decl);
1695 AddToEagerLiteralsIfEager(f->fun());
1696 } else {
1697 top_level_builder()->record_global_variable_declaration();
1698 }
1699 } else {
1700 // let or const. Handled in NewScriptContext.
1701 DCHECK(decl->IsVariableDeclaration());
1702 DCHECK(IsLexicalVariableMode(var->mode()));
1703 }
1704 }
1705
1706 BuildDeclareCall(Runtime::kDeclareGlobals);
1707 }
1708
VisitDeclarations(Declaration::List * declarations)1709 void BytecodeGenerator::VisitDeclarations(Declaration::List* declarations) {
1710 for (Declaration* decl : *declarations) {
1711 RegisterAllocationScope register_scope(this);
1712 Visit(decl);
1713 }
1714 }
1715
VisitStatements(const ZonePtrList<Statement> * statements)1716 void BytecodeGenerator::VisitStatements(
1717 const ZonePtrList<Statement>* statements) {
1718 for (int i = 0; i < statements->length(); i++) {
1719 // Allocate an outer register allocations scope for the statement.
1720 RegisterAllocationScope allocation_scope(this);
1721 Statement* stmt = statements->at(i);
1722 Visit(stmt);
1723 if (builder()->RemainderOfBlockIsDead()) break;
1724 }
1725 }
1726
VisitExpressionStatement(ExpressionStatement * stmt)1727 void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
1728 builder()->SetStatementPosition(stmt);
1729 VisitForEffect(stmt->expression());
1730 }
1731
VisitEmptyStatement(EmptyStatement * stmt)1732 void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {}
1733
VisitIfStatement(IfStatement * stmt)1734 void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
1735 ConditionalControlFlowBuilder conditional_builder(
1736 builder(), block_coverage_builder_, stmt);
1737 builder()->SetStatementPosition(stmt);
1738
1739 if (stmt->condition()->ToBooleanIsTrue()) {
1740 // Generate then block unconditionally as always true.
1741 conditional_builder.Then();
1742 Visit(stmt->then_statement());
1743 } else if (stmt->condition()->ToBooleanIsFalse()) {
1744 // Generate else block unconditionally if it exists.
1745 if (stmt->HasElseStatement()) {
1746 conditional_builder.Else();
1747 Visit(stmt->else_statement());
1748 }
1749 } else {
1750 // TODO(oth): If then statement is BreakStatement or
1751 // ContinueStatement we can reduce number of generated
1752 // jump/jump_ifs here. See BasicLoops test.
1753 VisitForTest(stmt->condition(), conditional_builder.then_labels(),
1754 conditional_builder.else_labels(), TestFallthrough::kThen);
1755
1756 conditional_builder.Then();
1757 Visit(stmt->then_statement());
1758
1759 if (stmt->HasElseStatement()) {
1760 conditional_builder.JumpToEnd();
1761 conditional_builder.Else();
1762 Visit(stmt->else_statement());
1763 }
1764 }
1765 }
1766
VisitSloppyBlockFunctionStatement(SloppyBlockFunctionStatement * stmt)1767 void BytecodeGenerator::VisitSloppyBlockFunctionStatement(
1768 SloppyBlockFunctionStatement* stmt) {
1769 Visit(stmt->statement());
1770 }
1771
VisitContinueStatement(ContinueStatement * stmt)1772 void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
1773 AllocateBlockCoverageSlotIfEnabled(stmt, SourceRangeKind::kContinuation);
1774 builder()->SetStatementPosition(stmt);
1775 execution_control()->Continue(stmt->target());
1776 }
1777
VisitBreakStatement(BreakStatement * stmt)1778 void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
1779 AllocateBlockCoverageSlotIfEnabled(stmt, SourceRangeKind::kContinuation);
1780 builder()->SetStatementPosition(stmt);
1781 execution_control()->Break(stmt->target());
1782 }
1783
VisitReturnStatement(ReturnStatement * stmt)1784 void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
1785 AllocateBlockCoverageSlotIfEnabled(stmt, SourceRangeKind::kContinuation);
1786 builder()->SetStatementPosition(stmt);
1787 VisitForAccumulatorValue(stmt->expression());
1788 int return_position = stmt->end_position();
1789 if (return_position == ReturnStatement::kFunctionLiteralReturnPosition) {
1790 return_position = info()->literal()->return_position();
1791 }
1792 if (stmt->is_async_return()) {
1793 execution_control()->AsyncReturnAccumulator(return_position);
1794 } else {
1795 execution_control()->ReturnAccumulator(return_position);
1796 }
1797 }
1798
VisitWithStatement(WithStatement * stmt)1799 void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) {
1800 builder()->SetStatementPosition(stmt);
1801 VisitForAccumulatorValue(stmt->expression());
1802 BuildNewLocalWithContext(stmt->scope());
1803 VisitInScope(stmt->statement(), stmt->scope());
1804 }
1805
1806 namespace {
1807
IsSmiLiteralSwitchCaseValue(Expression * expr)1808 bool IsSmiLiteralSwitchCaseValue(Expression* expr) {
1809 if (expr->IsSmiLiteral() ||
1810 (expr->IsLiteral() && expr->AsLiteral()->IsNumber() &&
1811 expr->AsLiteral()->AsNumber() == 0.0)) {
1812 return true;
1813 #ifdef DEBUG
1814 } else if (expr->IsLiteral() && expr->AsLiteral()->IsNumber()) {
1815 DCHECK(!IsSmiDouble(expr->AsLiteral()->AsNumber()));
1816 #endif
1817 }
1818 return false;
1819 }
1820
1821 // Precondition: we called IsSmiLiteral to check this.
ReduceToSmiSwitchCaseValue(Expression * expr)1822 inline int ReduceToSmiSwitchCaseValue(Expression* expr) {
1823 if (V8_LIKELY(expr->IsSmiLiteral())) {
1824 return expr->AsLiteral()->AsSmiLiteral().value();
1825 } else {
1826 // Only the zero case is possible otherwise.
1827 DCHECK(expr->IsLiteral() && expr->AsLiteral()->IsNumber() &&
1828 expr->AsLiteral()->AsNumber() == -0.0);
1829 return 0;
1830 }
1831 }
1832
1833 // Is the range of Smi's small enough relative to number of cases?
IsSpreadAcceptable(int spread,int ncases)1834 inline bool IsSpreadAcceptable(int spread, int ncases) {
1835 return spread < FLAG_switch_table_spread_threshold * ncases;
1836 }
1837
1838 struct SwitchInfo {
1839 static const int kDefaultNotFound = -1;
1840
1841 std::map<int, CaseClause*> covered_cases;
1842 int default_case;
1843
SwitchInfov8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1844 SwitchInfo() { default_case = kDefaultNotFound; }
1845
DefaultExistsv8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1846 bool DefaultExists() { return default_case != kDefaultNotFound; }
CaseExistsv8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1847 bool CaseExists(int j) {
1848 return covered_cases.find(j) != covered_cases.end();
1849 }
CaseExistsv8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1850 bool CaseExists(Expression* expr) {
1851 return IsSmiLiteralSwitchCaseValue(expr)
1852 ? CaseExists(ReduceToSmiSwitchCaseValue(expr))
1853 : false;
1854 }
GetClausev8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1855 CaseClause* GetClause(int j) { return covered_cases[j]; }
1856
IsDuplicatev8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1857 bool IsDuplicate(CaseClause* clause) {
1858 return IsSmiLiteralSwitchCaseValue(clause->label()) &&
1859 CaseExists(clause->label()) &&
1860 clause != GetClause(ReduceToSmiSwitchCaseValue(clause->label()));
1861 }
MinCasev8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1862 int MinCase() {
1863 return covered_cases.size() == 0 ? INT_MAX : covered_cases.begin()->first;
1864 }
MaxCasev8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1865 int MaxCase() {
1866 return covered_cases.size() == 0 ? INT_MIN : covered_cases.rbegin()->first;
1867 }
Printv8::internal::interpreter::__anon4d1f688d0411::SwitchInfo1868 void Print() {
1869 std::cout << "Covered_cases: " << '\n';
1870 for (auto iter = covered_cases.begin(); iter != covered_cases.end();
1871 ++iter) {
1872 std::cout << iter->first << "->" << iter->second << '\n';
1873 }
1874 std::cout << "Default_case: " << default_case << '\n';
1875 }
1876 };
1877
1878 // Checks whether we should use a jump table to implement a switch operation.
IsSwitchOptimizable(SwitchStatement * stmt,SwitchInfo * info)1879 bool IsSwitchOptimizable(SwitchStatement* stmt, SwitchInfo* info) {
1880 ZonePtrList<CaseClause>* cases = stmt->cases();
1881
1882 for (int i = 0; i < cases->length(); ++i) {
1883 CaseClause* clause = cases->at(i);
1884 if (clause->is_default()) {
1885 continue;
1886 } else if (!(clause->label()->IsLiteral())) {
1887 // Don't consider Smi cases after a non-literal, because we
1888 // need to evaluate the non-literal.
1889 break;
1890 } else if (IsSmiLiteralSwitchCaseValue(clause->label())) {
1891 int value = ReduceToSmiSwitchCaseValue(clause->label());
1892 info->covered_cases.insert({value, clause});
1893 }
1894 }
1895
1896 // GCC also jump-table optimizes switch statements with 6 cases or more.
1897 if (static_cast<int>(info->covered_cases.size()) >=
1898 FLAG_switch_table_min_cases) {
1899 // Due to case spread will be used as the size of jump-table,
1900 // we need to check if it doesn't overflow by casting its
1901 // min and max bounds to int64_t, and calculate if the difference is less
1902 // than or equal to INT_MAX.
1903 int64_t min = static_cast<int64_t>(info->MinCase());
1904 int64_t max = static_cast<int64_t>(info->MaxCase());
1905 int64_t spread = max - min + 1;
1906
1907 DCHECK_GT(spread, 0);
1908
1909 // Check if casted spread is acceptable and doesn't overflow.
1910 if (spread <= INT_MAX &&
1911 IsSpreadAcceptable(static_cast<int>(spread), cases->length())) {
1912 return true;
1913 }
1914 }
1915 // Invariant- covered_cases has all cases and only cases that will go in the
1916 // jump table.
1917 info->covered_cases.clear();
1918 return false;
1919 }
1920
1921 } // namespace
1922
1923 // This adds a jump table optimization for switch statements with Smi cases.
1924 // If there are 5+ non-duplicate Smi clauses, and they are sufficiently compact,
1925 // we generate a jump table. In the fall-through path, we put the compare-jumps
1926 // for the non-Smi cases.
1927
1928 // e.g.
1929 //
1930 // switch(x){
1931 // case -0: out = 10;
1932 // case 1: out = 11; break;
1933 // case 0: out = 12; break;
1934 // case 2: out = 13;
1935 // case 3: out = 14; break;
1936 // case 0.5: out = 15; break;
1937 // case 4: out = 16;
1938 // case y: out = 17;
1939 // case 5: out = 18;
1940 // default: out = 19; break;
1941 // }
1942
1943 // becomes this pseudo-bytecode:
1944
1945 // lda x
1946 // star r1
1947 // test_type number
1948 // jump_if_false @fallthrough
1949 // ldar r1
1950 // test_greater_than_or_equal_to smi_min
1951 // jump_if_false @fallthrough
1952 // ldar r1
1953 // test_less_than_or_equal_to smi_max
1954 // jump_if_false @fallthrough
1955 // ldar r1
1956 // bitwise_or 0
1957 // star r2
1958 // test_strict_equal r1
1959 // jump_if_false @fallthrough
1960 // ldar r2
1961 // switch_on_smi {1: @case_1, 2: @case_2, 3: @case_3, 4: @case_4}
1962 // @fallthrough:
1963 // jump_if_strict_equal -0.0 @case_minus_0.0
1964 // jump_if_strict_equal 0.5 @case_0.5
1965 // jump_if_strict_equal y @case_y
1966 // jump_if_strict_equal 5 @case_5
1967 // jump @default
1968 // @case_minus_0.0:
1969 // <out = 10>
1970 // @case_1
1971 // <out = 11, break>
1972 // @case_0:
1973 // <out = 12, break>
1974 // @case_2:
1975 // <out = 13>
1976 // @case_3:
1977 // <out = 14, break>
1978 // @case_0.5:
1979 // <out = 15, break>
1980 // @case_4:
1981 // <out = 16>
1982 // @case_y:
1983 // <out = 17>
1984 // @case_5:
1985 // <out = 18>
1986 // @default:
1987 // <out = 19, break>
1988
VisitSwitchStatement(SwitchStatement * stmt)1989 void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
1990 // We need this scope because we visit for register values. We have to
1991 // maintain a execution result scope where registers can be allocated.
1992 ZonePtrList<CaseClause>* clauses = stmt->cases();
1993
1994 SwitchInfo info;
1995 BytecodeJumpTable* jump_table = nullptr;
1996 bool use_jump_table = IsSwitchOptimizable(stmt, &info);
1997
1998 // N_comp_cases is number of cases we will generate comparison jumps for.
1999 // Note we ignore duplicate cases, since they are very unlikely.
2000
2001 int n_comp_cases = clauses->length();
2002 if (use_jump_table) {
2003 n_comp_cases -= static_cast<int>(info.covered_cases.size());
2004 jump_table = builder()->AllocateJumpTable(
2005 info.MaxCase() - info.MinCase() + 1, info.MinCase());
2006 }
2007
2008 // Are we still using any if-else bytecodes to evaluate the switch?
2009 bool use_jumps = n_comp_cases != 0;
2010
2011 SwitchBuilder switch_builder(builder(), block_coverage_builder_, stmt,
2012 n_comp_cases, jump_table);
2013 ControlScopeForBreakable scope(this, stmt, &switch_builder);
2014 builder()->SetStatementPosition(stmt);
2015
2016 VisitForAccumulatorValue(stmt->tag());
2017
2018 if (use_jump_table) {
2019 // This also fills empty slots in jump table.
2020 Register r2 = register_allocator()->NewRegister();
2021
2022 Register r1 = register_allocator()->NewRegister();
2023 builder()->StoreAccumulatorInRegister(r1);
2024
2025 builder()->CompareTypeOf(TestTypeOfFlags::LiteralFlag::kNumber);
2026 switch_builder.JumpToFallThroughIfFalse();
2027 builder()->LoadAccumulatorWithRegister(r1);
2028
2029 // TODO(leszeks): Note these are duplicated range checks with the
2030 // SwitchOnSmi handler for the most part.
2031
2032 builder()->LoadLiteral(Smi::kMinValue);
2033 builder()->StoreAccumulatorInRegister(r2);
2034 builder()->CompareOperation(
2035 Token::Value::GTE, r1,
2036 feedback_index(feedback_spec()->AddCompareICSlot()));
2037
2038 switch_builder.JumpToFallThroughIfFalse();
2039 builder()->LoadAccumulatorWithRegister(r1);
2040
2041 builder()->LoadLiteral(Smi::kMaxValue);
2042 builder()->StoreAccumulatorInRegister(r2);
2043 builder()->CompareOperation(
2044 Token::Value::LTE, r1,
2045 feedback_index(feedback_spec()->AddCompareICSlot()));
2046
2047 switch_builder.JumpToFallThroughIfFalse();
2048 builder()->LoadAccumulatorWithRegister(r1);
2049
2050 builder()->BinaryOperationSmiLiteral(
2051 Token::Value::BIT_OR, Smi::FromInt(0),
2052 feedback_index(feedback_spec()->AddBinaryOpICSlot()));
2053
2054 builder()->StoreAccumulatorInRegister(r2);
2055 builder()->CompareOperation(
2056 Token::Value::EQ_STRICT, r1,
2057 feedback_index(feedback_spec()->AddCompareICSlot()));
2058
2059 switch_builder.JumpToFallThroughIfFalse();
2060 builder()->LoadAccumulatorWithRegister(r2);
2061
2062 switch_builder.EmitJumpTableIfExists(info.MinCase(), info.MaxCase(),
2063 info.covered_cases);
2064
2065 if (use_jumps) {
2066 builder()->LoadAccumulatorWithRegister(r1);
2067 }
2068 }
2069
2070 int case_compare_ctr = 0;
2071 #ifdef DEBUG
2072 std::unordered_map<int, int> case_ctr_checker;
2073 #endif
2074
2075 if (use_jumps) {
2076 Register tag_holder = register_allocator()->NewRegister();
2077 FeedbackSlot slot = clauses->length() > 0
2078 ? feedback_spec()->AddCompareICSlot()
2079 : FeedbackSlot::Invalid();
2080 builder()->StoreAccumulatorInRegister(tag_holder);
2081
2082 for (int i = 0; i < clauses->length(); ++i) {
2083 CaseClause* clause = clauses->at(i);
2084 if (clause->is_default()) {
2085 info.default_case = i;
2086 } else if (!info.CaseExists(clause->label())) {
2087 // Perform label comparison as if via '===' with tag.
2088 VisitForAccumulatorValue(clause->label());
2089 builder()->CompareOperation(Token::Value::EQ_STRICT, tag_holder,
2090 feedback_index(slot));
2091 #ifdef DEBUG
2092 case_ctr_checker[i] = case_compare_ctr;
2093 #endif
2094 switch_builder.JumpToCaseIfTrue(ToBooleanMode::kAlreadyBoolean,
2095 case_compare_ctr++);
2096 }
2097 }
2098 }
2099
2100 // For fall-throughs after comparisons (or out-of-range/non-Smi's for jump
2101 // tables).
2102 if (info.DefaultExists()) {
2103 switch_builder.JumpToDefault();
2104 } else {
2105 switch_builder.Break();
2106 }
2107
2108 case_compare_ctr = 0;
2109 for (int i = 0; i < clauses->length(); ++i) {
2110 CaseClause* clause = clauses->at(i);
2111 if (i != info.default_case) {
2112 if (!info.IsDuplicate(clause)) {
2113 bool use_table = use_jump_table && info.CaseExists(clause->label());
2114 if (!use_table) {
2115 // Guarantee that we should generate compare/jump if no table.
2116 #ifdef DEBUG
2117 DCHECK(case_ctr_checker[i] == case_compare_ctr);
2118 #endif
2119 switch_builder.BindCaseTargetForCompareJump(case_compare_ctr++,
2120 clause);
2121 } else {
2122 // Use jump table if this is not a duplicate label.
2123 switch_builder.BindCaseTargetForJumpTable(
2124 ReduceToSmiSwitchCaseValue(clause->label()), clause);
2125 }
2126 }
2127 } else {
2128 switch_builder.BindDefault(clause);
2129 }
2130 // Regardless, generate code (in case of fall throughs).
2131 VisitStatements(clause->statements());
2132 }
2133 }
2134
2135 template <typename TryBodyFunc, typename CatchBodyFunc>
BuildTryCatch(TryBodyFunc try_body_func,CatchBodyFunc catch_body_func,HandlerTable::CatchPrediction catch_prediction,TryCatchStatement * stmt_for_coverage)2136 void BytecodeGenerator::BuildTryCatch(
2137 TryBodyFunc try_body_func, CatchBodyFunc catch_body_func,
2138 HandlerTable::CatchPrediction catch_prediction,
2139 TryCatchStatement* stmt_for_coverage) {
2140 if (builder()->RemainderOfBlockIsDead()) return;
2141
2142 TryCatchBuilder try_control_builder(
2143 builder(),
2144 stmt_for_coverage == nullptr ? nullptr : block_coverage_builder_,
2145 stmt_for_coverage, catch_prediction);
2146
2147 // Preserve the context in a dedicated register, so that it can be restored
2148 // when the handler is entered by the stack-unwinding machinery.
2149 // TODO(ignition): Be smarter about register allocation.
2150 Register context = register_allocator()->NewRegister();
2151 builder()->MoveRegister(Register::current_context(), context);
2152
2153 // Evaluate the try-block inside a control scope. This simulates a handler
2154 // that is intercepting 'throw' control commands.
2155 try_control_builder.BeginTry(context);
2156 {
2157 ControlScopeForTryCatch scope(this, &try_control_builder);
2158 try_body_func();
2159 }
2160 try_control_builder.EndTry();
2161
2162 catch_body_func(context);
2163
2164 try_control_builder.EndCatch();
2165 }
2166
2167 template <typename TryBodyFunc, typename FinallyBodyFunc>
BuildTryFinally(TryBodyFunc try_body_func,FinallyBodyFunc finally_body_func,HandlerTable::CatchPrediction catch_prediction,TryFinallyStatement * stmt_for_coverage)2168 void BytecodeGenerator::BuildTryFinally(
2169 TryBodyFunc try_body_func, FinallyBodyFunc finally_body_func,
2170 HandlerTable::CatchPrediction catch_prediction,
2171 TryFinallyStatement* stmt_for_coverage) {
2172 if (builder()->RemainderOfBlockIsDead()) return;
2173
2174 // We can't know whether the finally block will override ("catch") an
2175 // exception thrown in the try block, so we just adopt the outer prediction.
2176 TryFinallyBuilder try_control_builder(
2177 builder(),
2178 stmt_for_coverage == nullptr ? nullptr : block_coverage_builder_,
2179 stmt_for_coverage, catch_prediction);
2180
2181 // We keep a record of all paths that enter the finally-block to be able to
2182 // dispatch to the correct continuation point after the statements in the
2183 // finally-block have been evaluated.
2184 //
2185 // The try-finally construct can enter the finally-block in three ways:
2186 // 1. By exiting the try-block normally, falling through at the end.
2187 // 2. By exiting the try-block with a function-local control flow transfer
2188 // (i.e. through break/continue/return statements).
2189 // 3. By exiting the try-block with a thrown exception.
2190 //
2191 // The result register semantics depend on how the block was entered:
2192 // - ReturnStatement: It represents the return value being returned.
2193 // - ThrowStatement: It represents the exception being thrown.
2194 // - BreakStatement/ContinueStatement: Undefined and not used.
2195 // - Falling through into finally-block: Undefined and not used.
2196 Register token = register_allocator()->NewRegister();
2197 Register result = register_allocator()->NewRegister();
2198 ControlScope::DeferredCommands commands(this, token, result);
2199
2200 // Preserve the context in a dedicated register, so that it can be restored
2201 // when the handler is entered by the stack-unwinding machinery.
2202 // TODO(ignition): Be smarter about register allocation.
2203 Register context = register_allocator()->NewRegister();
2204 builder()->MoveRegister(Register::current_context(), context);
2205
2206 // Evaluate the try-block inside a control scope. This simulates a handler
2207 // that is intercepting all control commands.
2208 try_control_builder.BeginTry(context);
2209 {
2210 ControlScopeForTryFinally scope(this, &try_control_builder, &commands);
2211 try_body_func();
2212 }
2213 try_control_builder.EndTry();
2214
2215 // Record fall-through and exception cases.
2216 commands.RecordFallThroughPath();
2217 try_control_builder.LeaveTry();
2218 try_control_builder.BeginHandler();
2219 commands.RecordHandlerReThrowPath();
2220
2221 // Pending message object is saved on entry.
2222 try_control_builder.BeginFinally();
2223 Register message = context; // Reuse register.
2224
2225 // Clear message object as we enter the finally block.
2226 builder()->LoadTheHole().SetPendingMessage().StoreAccumulatorInRegister(
2227 message);
2228
2229 // Evaluate the finally-block.
2230 finally_body_func(token);
2231 try_control_builder.EndFinally();
2232
2233 // Pending message object is restored on exit.
2234 builder()->LoadAccumulatorWithRegister(message).SetPendingMessage();
2235
2236 // Dynamic dispatch after the finally-block.
2237 commands.ApplyDeferredCommands();
2238 }
2239
VisitIterationBody(IterationStatement * stmt,LoopBuilder * loop_builder)2240 void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt,
2241 LoopBuilder* loop_builder) {
2242 loop_builder->LoopBody();
2243 ControlScopeForIteration execution_control(this, stmt, loop_builder);
2244 Visit(stmt->body());
2245 loop_builder->BindContinueTarget();
2246 }
2247
VisitDoWhileStatement(DoWhileStatement * stmt)2248 void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
2249 LoopBuilder loop_builder(builder(), block_coverage_builder_, stmt);
2250 if (stmt->cond()->ToBooleanIsFalse()) {
2251 // Since we know that the condition is false, we don't create a loop.
2252 // Therefore, we don't create a LoopScope (and thus we don't create a header
2253 // and a JumpToHeader). However, we still need to iterate once through the
2254 // body.
2255 VisitIterationBody(stmt, &loop_builder);
2256 } else if (stmt->cond()->ToBooleanIsTrue()) {
2257 LoopScope loop_scope(this, &loop_builder);
2258 VisitIterationBody(stmt, &loop_builder);
2259 } else {
2260 LoopScope loop_scope(this, &loop_builder);
2261 VisitIterationBody(stmt, &loop_builder);
2262 builder()->SetExpressionAsStatementPosition(stmt->cond());
2263 BytecodeLabels loop_backbranch(zone());
2264 VisitForTest(stmt->cond(), &loop_backbranch, loop_builder.break_labels(),
2265 TestFallthrough::kThen);
2266 loop_backbranch.Bind(builder());
2267 }
2268 }
2269
VisitWhileStatement(WhileStatement * stmt)2270 void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
2271 LoopBuilder loop_builder(builder(), block_coverage_builder_, stmt);
2272
2273 if (stmt->cond()->ToBooleanIsFalse()) {
2274 // If the condition is false there is no need to generate the loop.
2275 return;
2276 }
2277
2278 LoopScope loop_scope(this, &loop_builder);
2279 if (!stmt->cond()->ToBooleanIsTrue()) {
2280 builder()->SetExpressionAsStatementPosition(stmt->cond());
2281 BytecodeLabels loop_body(zone());
2282 VisitForTest(stmt->cond(), &loop_body, loop_builder.break_labels(),
2283 TestFallthrough::kThen);
2284 loop_body.Bind(builder());
2285 }
2286 VisitIterationBody(stmt, &loop_builder);
2287 }
2288
VisitForStatement(ForStatement * stmt)2289 void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
2290 if (stmt->init() != nullptr) {
2291 Visit(stmt->init());
2292 }
2293
2294 LoopBuilder loop_builder(builder(), block_coverage_builder_, stmt);
2295 if (stmt->cond() && stmt->cond()->ToBooleanIsFalse()) {
2296 // If the condition is known to be false there is no need to generate
2297 // body, next or condition blocks. Init block should be generated.
2298 return;
2299 }
2300
2301 LoopScope loop_scope(this, &loop_builder);
2302 if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) {
2303 builder()->SetExpressionAsStatementPosition(stmt->cond());
2304 BytecodeLabels loop_body(zone());
2305 VisitForTest(stmt->cond(), &loop_body, loop_builder.break_labels(),
2306 TestFallthrough::kThen);
2307 loop_body.Bind(builder());
2308 }
2309 VisitIterationBody(stmt, &loop_builder);
2310 if (stmt->next() != nullptr) {
2311 builder()->SetStatementPosition(stmt->next());
2312 Visit(stmt->next());
2313 }
2314 }
2315
VisitForInStatement(ForInStatement * stmt)2316 void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
2317 if (stmt->subject()->IsNullLiteral() ||
2318 stmt->subject()->IsUndefinedLiteral()) {
2319 // ForIn generates lots of code, skip if it wouldn't produce any effects.
2320 return;
2321 }
2322
2323 BytecodeLabel subject_undefined_label;
2324 FeedbackSlot slot = feedback_spec()->AddForInSlot();
2325
2326 // Prepare the state for executing ForIn.
2327 builder()->SetExpressionAsStatementPosition(stmt->subject());
2328 VisitForAccumulatorValue(stmt->subject());
2329 builder()->JumpIfUndefinedOrNull(&subject_undefined_label);
2330 Register receiver = register_allocator()->NewRegister();
2331 builder()->ToObject(receiver);
2332
2333 // Used as kRegTriple and kRegPair in ForInPrepare and ForInNext.
2334 RegisterList triple = register_allocator()->NewRegisterList(3);
2335 Register cache_length = triple[2];
2336 builder()->ForInEnumerate(receiver);
2337 builder()->ForInPrepare(triple, feedback_index(slot));
2338
2339 // Set up loop counter
2340 Register index = register_allocator()->NewRegister();
2341 builder()->LoadLiteral(Smi::zero());
2342 builder()->StoreAccumulatorInRegister(index);
2343
2344 // The loop
2345 {
2346 LoopBuilder loop_builder(builder(), block_coverage_builder_, stmt);
2347 LoopScope loop_scope(this, &loop_builder);
2348 builder()->SetExpressionAsStatementPosition(stmt->each());
2349 builder()->ForInContinue(index, cache_length);
2350 loop_builder.BreakIfFalse(ToBooleanMode::kAlreadyBoolean);
2351 builder()->ForInNext(receiver, index, triple.Truncate(2),
2352 feedback_index(slot));
2353 loop_builder.ContinueIfUndefined();
2354
2355 // Assign accumulator value to the 'each' target.
2356 {
2357 EffectResultScope scope(this);
2358 // Make sure to preserve the accumulator across the PrepareAssignmentLhs
2359 // call.
2360 AssignmentLhsData lhs_data = PrepareAssignmentLhs(
2361 stmt->each(), AccumulatorPreservingMode::kPreserve);
2362 builder()->SetExpressionPosition(stmt->each());
2363 BuildAssignment(lhs_data, Token::ASSIGN, LookupHoistingMode::kNormal);
2364 }
2365
2366 VisitIterationBody(stmt, &loop_builder);
2367 builder()->ForInStep(index);
2368 builder()->StoreAccumulatorInRegister(index);
2369 }
2370 builder()->Bind(&subject_undefined_label);
2371 }
2372
2373 // Desugar a for-of statement into an application of the iteration protocol.
2374 //
2375 // for (EACH of SUBJECT) BODY
2376 //
2377 // becomes
2378 //
2379 // iterator = %GetIterator(SUBJECT)
2380 // try {
2381 //
2382 // loop {
2383 // // Make sure we are considered 'done' if .next(), .done or .value fail.
2384 // done = true
2385 // value = iterator.next()
2386 // if (value.done) break;
2387 // value = value.value
2388 // done = false
2389 //
2390 // EACH = value
2391 // BODY
2392 // }
2393 // done = true
2394 //
2395 // } catch(e) {
2396 // iteration_continuation = RETHROW
2397 // } finally {
2398 // %FinalizeIteration(iterator, done, iteration_continuation)
2399 // }
VisitForOfStatement(ForOfStatement * stmt)2400 void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
2401 EffectResultScope effect_scope(this);
2402
2403 builder()->SetExpressionAsStatementPosition(stmt->subject());
2404 VisitForAccumulatorValue(stmt->subject());
2405
2406 // Store the iterator in a dedicated register so that it can be closed on
2407 // exit, and the 'done' value in a dedicated register so that it can be
2408 // changed and accessed independently of the iteration result.
2409 IteratorRecord iterator = BuildGetIteratorRecord(stmt->type());
2410 Register done = register_allocator()->NewRegister();
2411 builder()->LoadFalse();
2412 builder()->StoreAccumulatorInRegister(done);
2413
2414 BuildTryFinally(
2415 // Try block.
2416 [&]() {
2417 Register next_result = register_allocator()->NewRegister();
2418
2419 LoopBuilder loop_builder(builder(), block_coverage_builder_, stmt);
2420 LoopScope loop_scope(this, &loop_builder);
2421
2422 builder()->LoadTrue().StoreAccumulatorInRegister(done);
2423
2424 // Call the iterator's .next() method. Break from the loop if the `done`
2425 // property is truthy, otherwise load the value from the iterator result
2426 // and append the argument.
2427 builder()->SetExpressionAsStatementPosition(stmt->each());
2428 BuildIteratorNext(iterator, next_result);
2429 builder()->LoadNamedProperty(
2430 next_result, ast_string_constants()->done_string(),
2431 feedback_index(feedback_spec()->AddLoadICSlot()));
2432 loop_builder.BreakIfTrue(ToBooleanMode::kConvertToBoolean);
2433
2434 builder()
2435 // value = value.value
2436 ->LoadNamedProperty(
2437 next_result, ast_string_constants()->value_string(),
2438 feedback_index(feedback_spec()->AddLoadICSlot()));
2439 // done = false, before the assignment to each happens, so that done is
2440 // false if the assignment throws.
2441 builder()
2442 ->StoreAccumulatorInRegister(next_result)
2443 .LoadFalse()
2444 .StoreAccumulatorInRegister(done);
2445
2446 // Assign to the 'each' target.
2447 AssignmentLhsData lhs_data = PrepareAssignmentLhs(stmt->each());
2448 builder()->LoadAccumulatorWithRegister(next_result);
2449 BuildAssignment(lhs_data, Token::ASSIGN, LookupHoistingMode::kNormal);
2450
2451 VisitIterationBody(stmt, &loop_builder);
2452 },
2453 // Finally block.
2454 [&](Register iteration_continuation_token) {
2455 // Finish the iteration in the finally block.
2456 BuildFinalizeIteration(iterator, done, iteration_continuation_token);
2457 },
2458 HandlerTable::UNCAUGHT);
2459 }
2460
VisitTryCatchStatement(TryCatchStatement * stmt)2461 void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
2462 // Update catch prediction tracking. The updated catch_prediction value lasts
2463 // until the end of the try_block in the AST node, and does not apply to the
2464 // catch_block.
2465 HandlerTable::CatchPrediction outer_catch_prediction = catch_prediction();
2466 set_catch_prediction(stmt->GetCatchPrediction(outer_catch_prediction));
2467
2468 BuildTryCatch(
2469 // Try body.
2470 [&]() {
2471 Visit(stmt->try_block());
2472 set_catch_prediction(outer_catch_prediction);
2473 },
2474 // Catch body.
2475 [&](Register context) {
2476 if (stmt->scope()) {
2477 // Create a catch scope that binds the exception.
2478 BuildNewLocalCatchContext(stmt->scope());
2479 builder()->StoreAccumulatorInRegister(context);
2480 }
2481
2482 // If requested, clear message object as we enter the catch block.
2483 if (stmt->ShouldClearPendingException(outer_catch_prediction)) {
2484 builder()->LoadTheHole().SetPendingMessage();
2485 }
2486
2487 // Load the catch context into the accumulator.
2488 builder()->LoadAccumulatorWithRegister(context);
2489
2490 // Evaluate the catch-block.
2491 if (stmt->scope()) {
2492 VisitInScope(stmt->catch_block(), stmt->scope());
2493 } else {
2494 VisitBlock(stmt->catch_block());
2495 }
2496 },
2497 catch_prediction(), stmt);
2498 }
2499
VisitTryFinallyStatement(TryFinallyStatement * stmt)2500 void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
2501 BuildTryFinally(
2502 // Try block.
2503 [&]() { Visit(stmt->try_block()); },
2504 // Finally block.
2505 [&](Register body_continuation_token) { Visit(stmt->finally_block()); },
2506 catch_prediction(), stmt);
2507 }
2508
VisitDebuggerStatement(DebuggerStatement * stmt)2509 void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
2510 builder()->SetStatementPosition(stmt);
2511 builder()->Debugger();
2512 }
2513
VisitFunctionLiteral(FunctionLiteral * expr)2514 void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
2515 DCHECK_EQ(expr->scope()->outer_scope(), current_scope());
2516 uint8_t flags = CreateClosureFlags::Encode(
2517 expr->pretenure(), closure_scope()->is_function_scope(),
2518 info()->flags().might_always_opt());
2519 size_t entry = builder()->AllocateDeferredConstantPoolEntry();
2520 builder()->CreateClosure(entry, GetCachedCreateClosureSlot(expr), flags);
2521 function_literals_.push_back(std::make_pair(expr, entry));
2522 AddToEagerLiteralsIfEager(expr);
2523 }
2524
AddToEagerLiteralsIfEager(FunctionLiteral * literal)2525 void BytecodeGenerator::AddToEagerLiteralsIfEager(FunctionLiteral* literal) {
2526 // Only parallel compile when there's a script (not the case for source
2527 // position collection).
2528 if (!script_.is_null() && literal->should_parallel_compile()) {
2529 // If we should normally be eagerly compiling this function, we must be here
2530 // because of post_parallel_compile_tasks_for_eager_toplevel.
2531 DCHECK_IMPLIES(
2532 literal->ShouldEagerCompile(),
2533 info()->flags().post_parallel_compile_tasks_for_eager_toplevel());
2534 // There exists a lazy compile dispatcher.
2535 DCHECK(info()->dispatcher());
2536 // There exists a cloneable character stream.
2537 DCHECK(info()->character_stream()->can_be_cloned_for_parallel_access());
2538
2539 UnparkedScope scope(local_isolate_);
2540 // If there doesn't already exist a SharedFunctionInfo for this function,
2541 // then create one and enqueue it. Otherwise, we're reparsing (e.g. for the
2542 // debugger, source position collection, call printing, recompile after
2543 // flushing, etc.) and don't want to over-compile.
2544 Handle<SharedFunctionInfo> shared_info;
2545 if (!Script::FindSharedFunctionInfo(script_, local_isolate_, literal)
2546 .ToHandle(&shared_info)) {
2547 shared_info =
2548 Compiler::GetSharedFunctionInfo(literal, script_, local_isolate_);
2549 info()->dispatcher()->Enqueue(local_isolate_, shared_info,
2550 info()->character_stream()->Clone());
2551 }
2552 } else if (eager_inner_literals_ && literal->ShouldEagerCompile()) {
2553 DCHECK(!IsInEagerLiterals(literal, *eager_inner_literals_));
2554 DCHECK(!literal->should_parallel_compile());
2555 eager_inner_literals_->push_back(literal);
2556 }
2557 }
2558
BuildClassLiteral(ClassLiteral * expr,Register name)2559 void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
2560 size_t class_boilerplate_entry =
2561 builder()->AllocateDeferredConstantPoolEntry();
2562 class_literals_.push_back(std::make_pair(expr, class_boilerplate_entry));
2563
2564 VisitDeclarations(expr->scope()->declarations());
2565 Register class_constructor = register_allocator()->NewRegister();
2566
2567 // Create the class brand symbol and store it on the context during class
2568 // evaluation. This will be stored in the instance later in the constructor.
2569 // We do this early so that invalid access to private methods or accessors
2570 // in computed property keys throw.
2571 if (expr->scope()->brand() != nullptr) {
2572 Register brand = register_allocator()->NewRegister();
2573 const AstRawString* class_name =
2574 expr->scope()->class_variable() != nullptr
2575 ? expr->scope()->class_variable()->raw_name()
2576 : ast_string_constants()->anonymous_string();
2577 builder()
2578 ->LoadLiteral(class_name)
2579 .StoreAccumulatorInRegister(brand)
2580 .CallRuntime(Runtime::kCreatePrivateBrandSymbol, brand);
2581 BuildVariableAssignment(expr->scope()->brand(), Token::INIT,
2582 HoleCheckMode::kElided);
2583 }
2584
2585 AccessorTable<ClassLiteral::Property> private_accessors(zone());
2586 for (int i = 0; i < expr->private_members()->length(); i++) {
2587 ClassLiteral::Property* property = expr->private_members()->at(i);
2588 DCHECK(property->is_private());
2589 switch (property->kind()) {
2590 case ClassLiteral::Property::FIELD: {
2591 // Initialize the private field variables early.
2592 // Create the private name symbols for fields during class
2593 // evaluation and store them on the context. These will be
2594 // used as keys later during instance or static initialization.
2595 RegisterAllocationScope private_name_register_scope(this);
2596 Register private_name = register_allocator()->NewRegister();
2597 VisitForRegisterValue(property->key(), private_name);
2598 builder()
2599 ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
2600 .StoreAccumulatorInRegister(private_name)
2601 .CallRuntime(Runtime::kCreatePrivateNameSymbol, private_name);
2602 DCHECK_NOT_NULL(property->private_name_var());
2603 BuildVariableAssignment(property->private_name_var(), Token::INIT,
2604 HoleCheckMode::kElided);
2605 break;
2606 }
2607 case ClassLiteral::Property::METHOD: {
2608 RegisterAllocationScope register_scope(this);
2609 VisitForAccumulatorValue(property->value());
2610 BuildVariableAssignment(property->private_name_var(), Token::INIT,
2611 HoleCheckMode::kElided);
2612 break;
2613 }
2614 // Collect private accessors into a table to merge the creation of
2615 // those closures later.
2616 case ClassLiteral::Property::GETTER: {
2617 Literal* key = property->key()->AsLiteral();
2618 DCHECK_NULL(private_accessors.LookupOrInsert(key)->getter);
2619 private_accessors.LookupOrInsert(key)->getter = property;
2620 break;
2621 }
2622 case ClassLiteral::Property::SETTER: {
2623 Literal* key = property->key()->AsLiteral();
2624 DCHECK_NULL(private_accessors.LookupOrInsert(key)->setter);
2625 private_accessors.LookupOrInsert(key)->setter = property;
2626 break;
2627 }
2628 default:
2629 UNREACHABLE();
2630 }
2631 }
2632
2633 {
2634 RegisterAllocationScope register_scope(this);
2635 RegisterList args = register_allocator()->NewGrowableRegisterList();
2636
2637 Register class_boilerplate = register_allocator()->GrowRegisterList(&args);
2638 Register class_constructor_in_args =
2639 register_allocator()->GrowRegisterList(&args);
2640 Register super_class = register_allocator()->GrowRegisterList(&args);
2641 DCHECK_EQ(ClassBoilerplate::kFirstDynamicArgumentIndex,
2642 args.register_count());
2643
2644 VisitForAccumulatorValueOrTheHole(expr->extends());
2645 builder()->StoreAccumulatorInRegister(super_class);
2646
2647 VisitFunctionLiteral(expr->constructor());
2648 builder()
2649 ->StoreAccumulatorInRegister(class_constructor)
2650 .MoveRegister(class_constructor, class_constructor_in_args)
2651 .LoadConstantPoolEntry(class_boilerplate_entry)
2652 .StoreAccumulatorInRegister(class_boilerplate);
2653
2654 // Create computed names and method values nodes to store into the literal.
2655 for (int i = 0; i < expr->public_members()->length(); i++) {
2656 ClassLiteral::Property* property = expr->public_members()->at(i);
2657 if (property->is_computed_name()) {
2658 Register key = register_allocator()->GrowRegisterList(&args);
2659
2660 builder()->SetExpressionAsStatementPosition(property->key());
2661 BuildLoadPropertyKey(property, key);
2662 if (property->is_static()) {
2663 // The static prototype property is read only. We handle the non
2664 // computed property name case in the parser. Since this is the only
2665 // case where we need to check for an own read only property we
2666 // special case this so we do not need to do this for every property.
2667
2668 FeedbackSlot slot = GetDummyCompareICSlot();
2669 BytecodeLabel done;
2670 builder()
2671 ->LoadLiteral(ast_string_constants()->prototype_string())
2672 .CompareOperation(Token::Value::EQ_STRICT, key,
2673 feedback_index(slot))
2674 .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &done)
2675 .CallRuntime(Runtime::kThrowStaticPrototypeError)
2676 .Bind(&done);
2677 }
2678
2679 if (property->kind() == ClassLiteral::Property::FIELD) {
2680 DCHECK(!property->is_private());
2681 // Initialize field's name variable with the computed name.
2682 DCHECK_NOT_NULL(property->computed_name_var());
2683 builder()->LoadAccumulatorWithRegister(key);
2684 BuildVariableAssignment(property->computed_name_var(), Token::INIT,
2685 HoleCheckMode::kElided);
2686 }
2687 }
2688
2689 DCHECK(!property->is_private());
2690
2691 if (property->kind() == ClassLiteral::Property::FIELD) {
2692 // We don't compute field's value here, but instead do it in the
2693 // initializer function.
2694 continue;
2695 }
2696
2697 Register value = register_allocator()->GrowRegisterList(&args);
2698 VisitForRegisterValue(property->value(), value);
2699 }
2700
2701 builder()->CallRuntime(Runtime::kDefineClass, args);
2702 }
2703 Register prototype = register_allocator()->NewRegister();
2704 builder()->StoreAccumulatorInRegister(prototype);
2705
2706 // Assign to the home object variable. Accumulator already contains the
2707 // prototype.
2708 Variable* home_object_variable = expr->home_object();
2709 if (home_object_variable != nullptr) {
2710 DCHECK(home_object_variable->is_used());
2711 DCHECK(home_object_variable->IsContextSlot());
2712 BuildVariableAssignment(home_object_variable, Token::INIT,
2713 HoleCheckMode::kElided);
2714 }
2715 Variable* static_home_object_variable = expr->static_home_object();
2716 if (static_home_object_variable != nullptr) {
2717 DCHECK(static_home_object_variable->is_used());
2718 DCHECK(static_home_object_variable->IsContextSlot());
2719 builder()->LoadAccumulatorWithRegister(class_constructor);
2720 BuildVariableAssignment(static_home_object_variable, Token::INIT,
2721 HoleCheckMode::kElided);
2722 }
2723
2724 // Assign to class variable.
2725 Variable* class_variable = expr->scope()->class_variable();
2726 if (class_variable != nullptr && class_variable->is_used()) {
2727 DCHECK(class_variable->IsStackLocal() || class_variable->IsContextSlot());
2728 builder()->LoadAccumulatorWithRegister(class_constructor);
2729 BuildVariableAssignment(class_variable, Token::INIT,
2730 HoleCheckMode::kElided);
2731 }
2732
2733 // Define private accessors, using only a single call to the runtime for
2734 // each pair of corresponding getters and setters, in the order the first
2735 // component is declared.
2736 for (auto accessors : private_accessors.ordered_accessors()) {
2737 RegisterAllocationScope inner_register_scope(this);
2738 RegisterList accessors_reg = register_allocator()->NewRegisterList(2);
2739 ClassLiteral::Property* getter = accessors.second->getter;
2740 ClassLiteral::Property* setter = accessors.second->setter;
2741 VisitLiteralAccessor(getter, accessors_reg[0]);
2742 VisitLiteralAccessor(setter, accessors_reg[1]);
2743 builder()->CallRuntime(Runtime::kCreatePrivateAccessors, accessors_reg);
2744 Variable* var = getter != nullptr ? getter->private_name_var()
2745 : setter->private_name_var();
2746 DCHECK_NOT_NULL(var);
2747 BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kElided);
2748 }
2749
2750 if (expr->instance_members_initializer_function() != nullptr) {
2751 Register initializer =
2752 VisitForRegisterValue(expr->instance_members_initializer_function());
2753
2754 FeedbackSlot slot = feedback_spec()->AddStoreICSlot(language_mode());
2755 builder()
2756 ->LoadAccumulatorWithRegister(initializer)
2757 .StoreClassFieldsInitializer(class_constructor, feedback_index(slot))
2758 .LoadAccumulatorWithRegister(class_constructor);
2759 }
2760
2761 if (expr->static_initializer() != nullptr) {
2762 // TODO(gsathya): This can be optimized away to be a part of the
2763 // class boilerplate in the future. The name argument can be
2764 // passed to the DefineClass runtime function and have it set
2765 // there.
2766 if (name.is_valid()) {
2767 Register key = register_allocator()->NewRegister();
2768 builder()
2769 ->LoadLiteral(ast_string_constants()->name_string())
2770 .StoreAccumulatorInRegister(key);
2771
2772 DefineKeyedOwnPropertyInLiteralFlags data_property_flags =
2773 DefineKeyedOwnPropertyInLiteralFlag::kNoFlags;
2774 FeedbackSlot slot =
2775 feedback_spec()->AddDefineKeyedOwnPropertyInLiteralICSlot();
2776 builder()
2777 ->LoadAccumulatorWithRegister(name)
2778 .DefineKeyedOwnPropertyInLiteral(class_constructor, key,
2779 data_property_flags,
2780 feedback_index(slot));
2781 }
2782
2783 RegisterList args = register_allocator()->NewRegisterList(1);
2784 Register initializer = VisitForRegisterValue(expr->static_initializer());
2785
2786 builder()
2787 ->MoveRegister(class_constructor, args[0])
2788 .CallProperty(initializer, args,
2789 feedback_index(feedback_spec()->AddCallICSlot()));
2790 }
2791 builder()->LoadAccumulatorWithRegister(class_constructor);
2792 }
2793
VisitClassLiteral(ClassLiteral * expr)2794 void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
2795 VisitClassLiteral(expr, Register::invalid_value());
2796 }
2797
VisitClassLiteral(ClassLiteral * expr,Register name)2798 void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr, Register name) {
2799 CurrentScope current_scope(this, expr->scope());
2800 DCHECK_NOT_NULL(expr->scope());
2801 if (expr->scope()->NeedsContext()) {
2802 // Make sure to associate the source position for the class
2803 // after the block context is created. Otherwise we have a mismatch
2804 // between the scope and the context, where we already are in a
2805 // block context for the class, but not yet in the class scope.
2806 BytecodeSourceInfo source_info = builder()->PopSourcePosition();
2807 BuildNewLocalBlockContext(expr->scope());
2808 ContextScope scope(this, expr->scope());
2809 builder()->PushSourcePosition(source_info);
2810 BuildClassLiteral(expr, name);
2811 } else {
2812 BuildClassLiteral(expr, name);
2813 }
2814 }
2815
BuildClassProperty(ClassLiteral::Property * property)2816 void BytecodeGenerator::BuildClassProperty(ClassLiteral::Property* property) {
2817 RegisterAllocationScope register_scope(this);
2818 Register key;
2819
2820 // Private methods are not initialized in BuildClassProperty.
2821 DCHECK_IMPLIES(property->is_private(),
2822 property->kind() == ClassLiteral::Property::FIELD);
2823 builder()->SetExpressionPosition(property->key());
2824
2825 bool is_literal_store = property->key()->IsPropertyName() &&
2826 !property->is_computed_name() &&
2827 !property->is_private();
2828
2829 if (!is_literal_store) {
2830 key = register_allocator()->NewRegister();
2831 if (property->is_computed_name()) {
2832 DCHECK_EQ(property->kind(), ClassLiteral::Property::FIELD);
2833 DCHECK(!property->is_private());
2834 Variable* var = property->computed_name_var();
2835 DCHECK_NOT_NULL(var);
2836 // The computed name is already evaluated and stored in a variable at
2837 // class definition time.
2838 BuildVariableLoad(var, HoleCheckMode::kElided);
2839 builder()->StoreAccumulatorInRegister(key);
2840 } else if (property->is_private()) {
2841 Variable* private_name_var = property->private_name_var();
2842 DCHECK_NOT_NULL(private_name_var);
2843 BuildVariableLoad(private_name_var, HoleCheckMode::kElided);
2844 builder()->StoreAccumulatorInRegister(key);
2845 } else {
2846 VisitForRegisterValue(property->key(), key);
2847 }
2848 }
2849
2850 builder()->SetExpressionAsStatementPosition(property->value());
2851 VisitForAccumulatorValue(property->value());
2852
2853 if (is_literal_store) {
2854 FeedbackSlot slot = feedback_spec()->AddDefineNamedOwnICSlot();
2855 builder()->DefineNamedOwnProperty(
2856 builder()->Receiver(),
2857 property->key()->AsLiteral()->AsRawPropertyName(),
2858 feedback_index(slot));
2859 } else {
2860 FeedbackSlot slot = feedback_spec()->AddDefineKeyedOwnICSlot();
2861 builder()->DefineKeyedOwnProperty(builder()->Receiver(), key,
2862 feedback_index(slot));
2863 }
2864 }
2865
VisitInitializeClassMembersStatement(InitializeClassMembersStatement * stmt)2866 void BytecodeGenerator::VisitInitializeClassMembersStatement(
2867 InitializeClassMembersStatement* stmt) {
2868 for (int i = 0; i < stmt->fields()->length(); i++) {
2869 BuildClassProperty(stmt->fields()->at(i));
2870 }
2871 }
2872
VisitInitializeClassStaticElementsStatement(InitializeClassStaticElementsStatement * stmt)2873 void BytecodeGenerator::VisitInitializeClassStaticElementsStatement(
2874 InitializeClassStaticElementsStatement* stmt) {
2875 for (int i = 0; i < stmt->elements()->length(); i++) {
2876 ClassLiteral::StaticElement* element = stmt->elements()->at(i);
2877 switch (element->kind()) {
2878 case ClassLiteral::StaticElement::PROPERTY:
2879 BuildClassProperty(element->property());
2880 break;
2881 case ClassLiteral::StaticElement::STATIC_BLOCK:
2882 VisitBlock(element->static_block());
2883 break;
2884 }
2885 }
2886 }
2887
BuildInvalidPropertyAccess(MessageTemplate tmpl,Property * property)2888 void BytecodeGenerator::BuildInvalidPropertyAccess(MessageTemplate tmpl,
2889 Property* property) {
2890 RegisterAllocationScope register_scope(this);
2891 const AstRawString* name = property->key()->AsVariableProxy()->raw_name();
2892 RegisterList args = register_allocator()->NewRegisterList(2);
2893 builder()
2894 ->LoadLiteral(Smi::FromEnum(tmpl))
2895 .StoreAccumulatorInRegister(args[0])
2896 .LoadLiteral(name)
2897 .StoreAccumulatorInRegister(args[1])
2898 .CallRuntime(Runtime::kNewTypeError, args)
2899 .Throw();
2900 }
2901
BuildPrivateBrandInitialization(Register receiver,Variable * brand)2902 void BytecodeGenerator::BuildPrivateBrandInitialization(Register receiver,
2903 Variable* brand) {
2904 BuildVariableLoad(brand, HoleCheckMode::kElided);
2905 int depth = execution_context()->ContextChainDepth(brand->scope());
2906 ContextScope* class_context = execution_context()->Previous(depth);
2907 if (class_context) {
2908 Register brand_reg = register_allocator()->NewRegister();
2909 FeedbackSlot slot = feedback_spec()->AddDefineKeyedOwnICSlot();
2910 builder()
2911 ->StoreAccumulatorInRegister(brand_reg)
2912 .LoadAccumulatorWithRegister(class_context->reg())
2913 .DefineKeyedOwnProperty(receiver, brand_reg, feedback_index(slot));
2914 } else {
2915 // We are in the slow case where super() is called from a nested
2916 // arrow function or a eval(), so the class scope context isn't
2917 // tracked in a context register in the stack, and we have to
2918 // walk the context chain from the runtime to find it.
2919 DCHECK_NE(info()->literal()->scope()->outer_scope(), brand->scope());
2920 RegisterList brand_args = register_allocator()->NewRegisterList(4);
2921 builder()
2922 ->StoreAccumulatorInRegister(brand_args[1])
2923 .MoveRegister(receiver, brand_args[0])
2924 .MoveRegister(execution_context()->reg(), brand_args[2])
2925 .LoadLiteral(Smi::FromInt(depth))
2926 .StoreAccumulatorInRegister(brand_args[3])
2927 .CallRuntime(Runtime::kAddPrivateBrand, brand_args);
2928 }
2929 }
2930
BuildInstanceMemberInitialization(Register constructor,Register instance)2931 void BytecodeGenerator::BuildInstanceMemberInitialization(Register constructor,
2932 Register instance) {
2933 RegisterList args = register_allocator()->NewRegisterList(1);
2934 Register initializer = register_allocator()->NewRegister();
2935
2936 FeedbackSlot slot = feedback_spec()->AddLoadICSlot();
2937 BytecodeLabel done;
2938
2939 builder()
2940 ->LoadClassFieldsInitializer(constructor, feedback_index(slot))
2941 // TODO(gsathya): This jump can be elided for the base
2942 // constructor and derived constructor. This is only required
2943 // when called from an arrow function.
2944 .JumpIfUndefined(&done)
2945 .StoreAccumulatorInRegister(initializer)
2946 .MoveRegister(instance, args[0])
2947 .CallProperty(initializer, args,
2948 feedback_index(feedback_spec()->AddCallICSlot()))
2949 .Bind(&done);
2950 }
2951
VisitNativeFunctionLiteral(NativeFunctionLiteral * expr)2952 void BytecodeGenerator::VisitNativeFunctionLiteral(
2953 NativeFunctionLiteral* expr) {
2954 size_t entry = builder()->AllocateDeferredConstantPoolEntry();
2955 int index = feedback_spec()->AddCreateClosureSlot();
2956 uint8_t flags = CreateClosureFlags::Encode(false, false, false);
2957 builder()->CreateClosure(entry, index, flags);
2958 native_function_literals_.push_back(std::make_pair(expr, entry));
2959 }
2960
VisitConditional(Conditional * expr)2961 void BytecodeGenerator::VisitConditional(Conditional* expr) {
2962 ConditionalControlFlowBuilder conditional_builder(
2963 builder(), block_coverage_builder_, expr);
2964
2965 if (expr->condition()->ToBooleanIsTrue()) {
2966 // Generate then block unconditionally as always true.
2967 conditional_builder.Then();
2968 VisitForAccumulatorValue(expr->then_expression());
2969 } else if (expr->condition()->ToBooleanIsFalse()) {
2970 // Generate else block unconditionally if it exists.
2971 conditional_builder.Else();
2972 VisitForAccumulatorValue(expr->else_expression());
2973 } else {
2974 VisitForTest(expr->condition(), conditional_builder.then_labels(),
2975 conditional_builder.else_labels(), TestFallthrough::kThen);
2976
2977 conditional_builder.Then();
2978 VisitForAccumulatorValue(expr->then_expression());
2979 conditional_builder.JumpToEnd();
2980
2981 conditional_builder.Else();
2982 VisitForAccumulatorValue(expr->else_expression());
2983 }
2984 }
2985
VisitLiteral(Literal * expr)2986 void BytecodeGenerator::VisitLiteral(Literal* expr) {
2987 if (execution_result()->IsEffect()) return;
2988 switch (expr->type()) {
2989 case Literal::kSmi:
2990 builder()->LoadLiteral(expr->AsSmiLiteral());
2991 break;
2992 case Literal::kHeapNumber:
2993 builder()->LoadLiteral(expr->AsNumber());
2994 break;
2995 case Literal::kUndefined:
2996 builder()->LoadUndefined();
2997 break;
2998 case Literal::kBoolean:
2999 builder()->LoadBoolean(expr->ToBooleanIsTrue());
3000 execution_result()->SetResultIsBoolean();
3001 break;
3002 case Literal::kNull:
3003 builder()->LoadNull();
3004 break;
3005 case Literal::kTheHole:
3006 builder()->LoadTheHole();
3007 break;
3008 case Literal::kString:
3009 builder()->LoadLiteral(expr->AsRawString());
3010 execution_result()->SetResultIsString();
3011 break;
3012 case Literal::kBigInt:
3013 builder()->LoadLiteral(expr->AsBigInt());
3014 break;
3015 }
3016 }
3017
VisitRegExpLiteral(RegExpLiteral * expr)3018 void BytecodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
3019 // Materialize a regular expression literal.
3020 builder()->CreateRegExpLiteral(
3021 expr->raw_pattern(), feedback_index(feedback_spec()->AddLiteralSlot()),
3022 expr->flags());
3023 }
3024
BuildCreateObjectLiteral(Register literal,uint8_t flags,size_t entry)3025 void BytecodeGenerator::BuildCreateObjectLiteral(Register literal,
3026 uint8_t flags, size_t entry) {
3027 // TODO(cbruni): Directly generate runtime call for literals we cannot
3028 // optimize once the CreateShallowObjectLiteral stub is in sync with the TF
3029 // optimizations.
3030 int literal_index = feedback_index(feedback_spec()->AddLiteralSlot());
3031 builder()
3032 ->CreateObjectLiteral(entry, literal_index, flags)
3033 .StoreAccumulatorInRegister(literal);
3034 }
3035
VisitObjectLiteral(ObjectLiteral * expr)3036 void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
3037 expr->builder()->InitDepthAndFlags();
3038
3039 // Fast path for the empty object literal which doesn't need an
3040 // AllocationSite.
3041 if (expr->builder()->IsEmptyObjectLiteral()) {
3042 DCHECK(expr->builder()->IsFastCloningSupported());
3043 builder()->CreateEmptyObjectLiteral();
3044 return;
3045 }
3046
3047 Variable* home_object = expr->home_object();
3048 if (home_object != nullptr) {
3049 DCHECK(home_object->is_used());
3050 DCHECK(home_object->IsContextSlot());
3051 }
3052 MultipleEntryBlockContextScope object_literal_context_scope(
3053 this, home_object ? home_object->scope() : nullptr);
3054
3055 // Deep-copy the literal boilerplate.
3056 uint8_t flags = CreateObjectLiteralFlags::Encode(
3057 expr->builder()->ComputeFlags(),
3058 expr->builder()->IsFastCloningSupported());
3059
3060 Register literal = register_allocator()->NewRegister();
3061
3062 // Create literal object.
3063 int property_index = 0;
3064 bool clone_object_spread =
3065 expr->properties()->first()->kind() == ObjectLiteral::Property::SPREAD;
3066 if (clone_object_spread) {
3067 // Avoid the slow path for spreads in the following common cases:
3068 // 1) `let obj = { ...source }`
3069 // 2) `let obj = { ...source, override: 1 }`
3070 // 3) `let obj = { ...source, ...overrides }`
3071 RegisterAllocationScope register_scope(this);
3072 Expression* property = expr->properties()->first()->value();
3073 Register from_value = VisitForRegisterValue(property);
3074 int clone_index = feedback_index(feedback_spec()->AddCloneObjectSlot());
3075 builder()->CloneObject(from_value, flags, clone_index);
3076 builder()->StoreAccumulatorInRegister(literal);
3077 property_index++;
3078 } else {
3079 size_t entry;
3080 // If constant properties is an empty fixed array, use a cached empty fixed
3081 // array to ensure it's only added to the constant pool once.
3082 if (expr->builder()->properties_count() == 0) {
3083 entry = builder()->EmptyObjectBoilerplateDescriptionConstantPoolEntry();
3084 } else {
3085 entry = builder()->AllocateDeferredConstantPoolEntry();
3086 object_literals_.push_back(std::make_pair(expr->builder(), entry));
3087 }
3088 BuildCreateObjectLiteral(literal, flags, entry);
3089 }
3090
3091 // Store computed values into the literal.
3092 AccessorTable<ObjectLiteral::Property> accessor_table(zone());
3093 for (; property_index < expr->properties()->length(); property_index++) {
3094 ObjectLiteral::Property* property = expr->properties()->at(property_index);
3095 if (property->is_computed_name()) break;
3096 if (!clone_object_spread && property->IsCompileTimeValue()) continue;
3097
3098 RegisterAllocationScope inner_register_scope(this);
3099 Literal* key = property->key()->AsLiteral();
3100 switch (property->kind()) {
3101 case ObjectLiteral::Property::SPREAD:
3102 UNREACHABLE();
3103 case ObjectLiteral::Property::CONSTANT:
3104 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3105 DCHECK(clone_object_spread || !property->value()->IsCompileTimeValue());
3106 V8_FALLTHROUGH;
3107 case ObjectLiteral::Property::COMPUTED: {
3108 // It is safe to use [[Put]] here because the boilerplate already
3109 // contains computed properties with an uninitialized value.
3110 Register key_reg;
3111 if (key->IsStringLiteral()) {
3112 DCHECK(key->IsPropertyName());
3113 } else {
3114 key_reg = register_allocator()->NewRegister();
3115 builder()->SetExpressionPosition(property->key());
3116 VisitForRegisterValue(property->key(), key_reg);
3117 }
3118
3119 object_literal_context_scope.SetEnteredIf(
3120 property->value()->IsConciseMethodDefinition());
3121 builder()->SetExpressionPosition(property->value());
3122
3123 if (property->emit_store()) {
3124 VisitForAccumulatorValue(property->value());
3125 if (key->IsStringLiteral()) {
3126 FeedbackSlot slot = feedback_spec()->AddDefineNamedOwnICSlot();
3127 builder()->DefineNamedOwnProperty(literal, key->AsRawPropertyName(),
3128 feedback_index(slot));
3129 } else {
3130 FeedbackSlot slot = feedback_spec()->AddDefineKeyedOwnICSlot();
3131 builder()->DefineKeyedOwnProperty(literal, key_reg,
3132 feedback_index(slot));
3133 }
3134 } else {
3135 VisitForEffect(property->value());
3136 }
3137 break;
3138 }
3139 case ObjectLiteral::Property::PROTOTYPE: {
3140 // __proto__:null is handled by CreateObjectLiteral.
3141 if (property->IsNullPrototype()) break;
3142 DCHECK(property->emit_store());
3143 DCHECK(!property->NeedsSetFunctionName());
3144 RegisterList args = register_allocator()->NewRegisterList(2);
3145 builder()->MoveRegister(literal, args[0]);
3146 object_literal_context_scope.SetEnteredIf(false);
3147 builder()->SetExpressionPosition(property->value());
3148 VisitForRegisterValue(property->value(), args[1]);
3149 builder()->CallRuntime(Runtime::kInternalSetPrototype, args);
3150 break;
3151 }
3152 case ObjectLiteral::Property::GETTER:
3153 if (property->emit_store()) {
3154 accessor_table.LookupOrInsert(key)->getter = property;
3155 }
3156 break;
3157 case ObjectLiteral::Property::SETTER:
3158 if (property->emit_store()) {
3159 accessor_table.LookupOrInsert(key)->setter = property;
3160 }
3161 break;
3162 }
3163 }
3164
3165 // Define accessors, using only a single call to the runtime for each pair
3166 // of corresponding getters and setters.
3167 object_literal_context_scope.SetEnteredIf(true);
3168 for (auto accessors : accessor_table.ordered_accessors()) {
3169 RegisterAllocationScope inner_register_scope(this);
3170 RegisterList args = register_allocator()->NewRegisterList(5);
3171 builder()->MoveRegister(literal, args[0]);
3172 VisitForRegisterValue(accessors.first, args[1]);
3173 VisitLiteralAccessor(accessors.second->getter, args[2]);
3174 VisitLiteralAccessor(accessors.second->setter, args[3]);
3175 builder()
3176 ->LoadLiteral(Smi::FromInt(NONE))
3177 .StoreAccumulatorInRegister(args[4])
3178 .CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, args);
3179 }
3180
3181 // Object literals have two parts. The "static" part on the left contains no
3182 // computed property names, and so we can compute its map ahead of time; see
3183 // Runtime_CreateObjectLiteralBoilerplate. The second "dynamic" part starts
3184 // with the first computed property name and continues with all properties to
3185 // its right. All the code from above initializes the static component of the
3186 // object literal, and arranges for the map of the result to reflect the
3187 // static order in which the keys appear. For the dynamic properties, we
3188 // compile them into a series of "SetOwnProperty" runtime calls. This will
3189 // preserve insertion order.
3190 for (; property_index < expr->properties()->length(); property_index++) {
3191 ObjectLiteral::Property* property = expr->properties()->at(property_index);
3192 RegisterAllocationScope inner_register_scope(this);
3193
3194 bool should_be_in_object_literal_scope =
3195 (property->value()->IsConciseMethodDefinition() ||
3196 property->value()->IsAccessorFunctionDefinition());
3197
3198 if (property->IsPrototype()) {
3199 // __proto__:null is handled by CreateObjectLiteral.
3200 if (property->IsNullPrototype()) continue;
3201 DCHECK(property->emit_store());
3202 DCHECK(!property->NeedsSetFunctionName());
3203 RegisterList args = register_allocator()->NewRegisterList(2);
3204 builder()->MoveRegister(literal, args[0]);
3205
3206 DCHECK(!should_be_in_object_literal_scope);
3207 object_literal_context_scope.SetEnteredIf(false);
3208 builder()->SetExpressionPosition(property->value());
3209 VisitForRegisterValue(property->value(), args[1]);
3210 builder()->CallRuntime(Runtime::kInternalSetPrototype, args);
3211 continue;
3212 }
3213
3214 switch (property->kind()) {
3215 case ObjectLiteral::Property::CONSTANT:
3216 case ObjectLiteral::Property::COMPUTED:
3217 case ObjectLiteral::Property::MATERIALIZED_LITERAL: {
3218 // Computed property keys don't belong to the object literal scope (even
3219 // if they're syntactically inside it).
3220 if (property->is_computed_name()) {
3221 object_literal_context_scope.SetEnteredIf(false);
3222 }
3223 Register key = register_allocator()->NewRegister();
3224 BuildLoadPropertyKey(property, key);
3225
3226 object_literal_context_scope.SetEnteredIf(
3227 should_be_in_object_literal_scope);
3228 builder()->SetExpressionPosition(property->value());
3229 Register value;
3230
3231 // Static class fields require the name property to be set on
3232 // the class, meaning we can't wait until the
3233 // DefineKeyedOwnPropertyInLiteral call later to set the name.
3234 if (property->value()->IsClassLiteral() &&
3235 property->value()->AsClassLiteral()->static_initializer() !=
3236 nullptr) {
3237 value = register_allocator()->NewRegister();
3238 VisitClassLiteral(property->value()->AsClassLiteral(), key);
3239 builder()->StoreAccumulatorInRegister(value);
3240 } else {
3241 value = VisitForRegisterValue(property->value());
3242 }
3243
3244 DefineKeyedOwnPropertyInLiteralFlags data_property_flags =
3245 DefineKeyedOwnPropertyInLiteralFlag::kNoFlags;
3246 if (property->NeedsSetFunctionName()) {
3247 data_property_flags |=
3248 DefineKeyedOwnPropertyInLiteralFlag::kSetFunctionName;
3249 }
3250
3251 FeedbackSlot slot =
3252 feedback_spec()->AddDefineKeyedOwnPropertyInLiteralICSlot();
3253 builder()
3254 ->LoadAccumulatorWithRegister(value)
3255 .DefineKeyedOwnPropertyInLiteral(literal, key, data_property_flags,
3256 feedback_index(slot));
3257 break;
3258 }
3259 case ObjectLiteral::Property::GETTER:
3260 case ObjectLiteral::Property::SETTER: {
3261 // Computed property keys don't belong to the object literal scope (even
3262 // if they're syntactically inside it).
3263 if (property->is_computed_name()) {
3264 object_literal_context_scope.SetEnteredIf(false);
3265 }
3266 RegisterList args = register_allocator()->NewRegisterList(4);
3267 builder()->MoveRegister(literal, args[0]);
3268 BuildLoadPropertyKey(property, args[1]);
3269
3270 DCHECK(should_be_in_object_literal_scope);
3271 object_literal_context_scope.SetEnteredIf(true);
3272 builder()->SetExpressionPosition(property->value());
3273 VisitForRegisterValue(property->value(), args[2]);
3274 builder()
3275 ->LoadLiteral(Smi::FromInt(NONE))
3276 .StoreAccumulatorInRegister(args[3]);
3277 Runtime::FunctionId function_id =
3278 property->kind() == ObjectLiteral::Property::GETTER
3279 ? Runtime::kDefineGetterPropertyUnchecked
3280 : Runtime::kDefineSetterPropertyUnchecked;
3281 builder()->CallRuntime(function_id, args);
3282 break;
3283 }
3284 case ObjectLiteral::Property::SPREAD: {
3285 RegisterList args = register_allocator()->NewRegisterList(2);
3286 builder()->MoveRegister(literal, args[0]);
3287 builder()->SetExpressionPosition(property->value());
3288 object_literal_context_scope.SetEnteredIf(false);
3289 VisitForRegisterValue(property->value(), args[1]);
3290 builder()->CallRuntime(Runtime::kInlineCopyDataProperties, args);
3291 break;
3292 }
3293 case ObjectLiteral::Property::PROTOTYPE:
3294 UNREACHABLE(); // Handled specially above.
3295 }
3296 }
3297
3298 builder()->LoadAccumulatorWithRegister(literal);
3299 if (home_object != nullptr) {
3300 object_literal_context_scope.SetEnteredIf(true);
3301 BuildVariableAssignment(home_object, Token::INIT, HoleCheckMode::kElided);
3302 }
3303 }
3304
3305 // Fill an array with values from an iterator, starting at a given index. It is
3306 // guaranteed that the loop will only terminate if the iterator is exhausted, or
3307 // if one of iterator.next(), value.done, or value.value fail.
3308 //
3309 // In pseudocode:
3310 //
3311 // loop {
3312 // value = iterator.next()
3313 // if (value.done) break;
3314 // value = value.value
3315 // array[index++] = value
3316 // }
BuildFillArrayWithIterator(IteratorRecord iterator,Register array,Register index,Register value,FeedbackSlot next_value_slot,FeedbackSlot next_done_slot,FeedbackSlot index_slot,FeedbackSlot element_slot)3317 void BytecodeGenerator::BuildFillArrayWithIterator(
3318 IteratorRecord iterator, Register array, Register index, Register value,
3319 FeedbackSlot next_value_slot, FeedbackSlot next_done_slot,
3320 FeedbackSlot index_slot, FeedbackSlot element_slot) {
3321 DCHECK(array.is_valid());
3322 DCHECK(index.is_valid());
3323 DCHECK(value.is_valid());
3324
3325 LoopBuilder loop_builder(builder(), nullptr, nullptr);
3326 LoopScope loop_scope(this, &loop_builder);
3327
3328 // Call the iterator's .next() method. Break from the loop if the `done`
3329 // property is truthy, otherwise load the value from the iterator result and
3330 // append the argument.
3331 BuildIteratorNext(iterator, value);
3332 builder()->LoadNamedProperty(
3333 value, ast_string_constants()->done_string(),
3334 feedback_index(feedback_spec()->AddLoadICSlot()));
3335 loop_builder.BreakIfTrue(ToBooleanMode::kConvertToBoolean);
3336
3337 loop_builder.LoopBody();
3338 builder()
3339 // value = value.value
3340 ->LoadNamedProperty(value, ast_string_constants()->value_string(),
3341 feedback_index(next_value_slot))
3342 // array[index] = value
3343 .StoreInArrayLiteral(array, index, feedback_index(element_slot))
3344 // index++
3345 .LoadAccumulatorWithRegister(index)
3346 .UnaryOperation(Token::INC, feedback_index(index_slot))
3347 .StoreAccumulatorInRegister(index);
3348 loop_builder.BindContinueTarget();
3349 }
3350
BuildCreateArrayLiteral(const ZonePtrList<Expression> * elements,ArrayLiteral * expr)3351 void BytecodeGenerator::BuildCreateArrayLiteral(
3352 const ZonePtrList<Expression>* elements, ArrayLiteral* expr) {
3353 RegisterAllocationScope register_scope(this);
3354 Register index = register_allocator()->NewRegister();
3355 Register array = register_allocator()->NewRegister();
3356 SharedFeedbackSlot element_slot(feedback_spec(),
3357 FeedbackSlotKind::kStoreInArrayLiteral);
3358 ZonePtrList<Expression>::const_iterator current = elements->begin();
3359 ZonePtrList<Expression>::const_iterator end = elements->end();
3360 bool is_empty = elements->is_empty();
3361
3362 if (!is_empty && (*current)->IsSpread()) {
3363 // If we have a leading spread, use CreateArrayFromIterable to create
3364 // an array from it and then add the remaining components to that array.
3365 VisitForAccumulatorValue(*current);
3366 builder()->SetExpressionPosition((*current)->AsSpread()->expression());
3367 builder()->CreateArrayFromIterable().StoreAccumulatorInRegister(array);
3368
3369 if (++current != end) {
3370 // If there are remaning elements, prepare the index register that is
3371 // used for adding those elements. The next index is the length of the
3372 // newly created array.
3373 auto length = ast_string_constants()->length_string();
3374 int length_load_slot = feedback_index(feedback_spec()->AddLoadICSlot());
3375 builder()
3376 ->LoadNamedProperty(array, length, length_load_slot)
3377 .StoreAccumulatorInRegister(index);
3378 }
3379 } else {
3380 // There are some elements before the first (if any) spread, and we can
3381 // use a boilerplate when creating the initial array from those elements.
3382
3383 // First, allocate a constant pool entry for the boilerplate that will
3384 // be created during finalization, and will contain all the constant
3385 // elements before the first spread. This also handle the empty array case
3386 // and one-shot optimization.
3387
3388 ArrayLiteralBoilerplateBuilder* array_literal_builder = nullptr;
3389 if (expr != nullptr) {
3390 array_literal_builder = expr->builder();
3391 } else {
3392 DCHECK(!elements->is_empty());
3393
3394 // get first_spread_index
3395 int first_spread_index = -1;
3396 for (auto iter = elements->begin(); iter != elements->end(); iter++) {
3397 if ((*iter)->IsSpread()) {
3398 first_spread_index = static_cast<int>(iter - elements->begin());
3399 break;
3400 }
3401 }
3402
3403 array_literal_builder = zone()->New<ArrayLiteralBoilerplateBuilder>(
3404 elements, first_spread_index);
3405 array_literal_builder->InitDepthAndFlags();
3406 }
3407
3408 DCHECK(array_literal_builder != nullptr);
3409 uint8_t flags = CreateArrayLiteralFlags::Encode(
3410 array_literal_builder->IsFastCloningSupported(),
3411 array_literal_builder->ComputeFlags());
3412 if (is_empty) {
3413 // Empty array literal fast-path.
3414 int literal_index = feedback_index(feedback_spec()->AddLiteralSlot());
3415 DCHECK(array_literal_builder->IsFastCloningSupported());
3416 builder()->CreateEmptyArrayLiteral(literal_index);
3417 } else {
3418 // Create array literal from boilerplate.
3419 size_t entry = builder()->AllocateDeferredConstantPoolEntry();
3420 array_literals_.push_back(std::make_pair(array_literal_builder, entry));
3421 int literal_index = feedback_index(feedback_spec()->AddLiteralSlot());
3422 builder()->CreateArrayLiteral(entry, literal_index, flags);
3423 }
3424 builder()->StoreAccumulatorInRegister(array);
3425
3426 ZonePtrList<Expression>::const_iterator first_spread_or_end =
3427 array_literal_builder->first_spread_index() >= 0
3428 ? current + array_literal_builder->first_spread_index()
3429 : end;
3430
3431 // Insert the missing non-constant elements, up until the first spread
3432 // index, into the initial array (the remaining elements will be inserted
3433 // below).
3434 DCHECK_EQ(current, elements->begin());
3435 int array_index = 0;
3436 for (; current != first_spread_or_end; ++current, array_index++) {
3437 Expression* subexpr = *current;
3438 DCHECK(!subexpr->IsSpread());
3439 // Skip the constants.
3440 if (subexpr->IsCompileTimeValue()) continue;
3441
3442 builder()
3443 ->LoadLiteral(Smi::FromInt(array_index))
3444 .StoreAccumulatorInRegister(index);
3445 VisitForAccumulatorValue(subexpr);
3446 builder()->StoreInArrayLiteral(array, index,
3447 feedback_index(element_slot.Get()));
3448 }
3449
3450 if (current != end) {
3451 // If there are remaining elements, prepare the index register
3452 // to store the next element, which comes from the first spread.
3453 builder()
3454 ->LoadLiteral(Smi::FromInt(array_index))
3455 .StoreAccumulatorInRegister(index);
3456 }
3457 }
3458
3459 // Now build insertions for the remaining elements from current to end.
3460 SharedFeedbackSlot index_slot(feedback_spec(), FeedbackSlotKind::kBinaryOp);
3461 SharedFeedbackSlot length_slot(
3462 feedback_spec(), feedback_spec()->GetStoreICSlot(LanguageMode::kStrict));
3463 for (; current != end; ++current) {
3464 Expression* subexpr = *current;
3465 if (subexpr->IsSpread()) {
3466 RegisterAllocationScope scope(this);
3467 builder()->SetExpressionAsStatementPosition(
3468 subexpr->AsSpread()->expression());
3469 VisitForAccumulatorValue(subexpr->AsSpread()->expression());
3470 builder()->SetExpressionPosition(subexpr->AsSpread()->expression());
3471 IteratorRecord iterator = BuildGetIteratorRecord(IteratorType::kNormal);
3472
3473 Register value = register_allocator()->NewRegister();
3474 FeedbackSlot next_value_load_slot = feedback_spec()->AddLoadICSlot();
3475 FeedbackSlot next_done_load_slot = feedback_spec()->AddLoadICSlot();
3476 FeedbackSlot real_index_slot = index_slot.Get();
3477 FeedbackSlot real_element_slot = element_slot.Get();
3478 BuildFillArrayWithIterator(iterator, array, index, value,
3479 next_value_load_slot, next_done_load_slot,
3480 real_index_slot, real_element_slot);
3481 } else if (!subexpr->IsTheHoleLiteral()) {
3482 // literal[index++] = subexpr
3483 VisitForAccumulatorValue(subexpr);
3484 builder()
3485 ->StoreInArrayLiteral(array, index,
3486 feedback_index(element_slot.Get()))
3487 .LoadAccumulatorWithRegister(index);
3488 // Only increase the index if we are not the last element.
3489 if (current + 1 != end) {
3490 builder()
3491 ->UnaryOperation(Token::INC, feedback_index(index_slot.Get()))
3492 .StoreAccumulatorInRegister(index);
3493 }
3494 } else {
3495 // literal.length = ++index
3496 // length_slot is only used when there are holes.
3497 auto length = ast_string_constants()->length_string();
3498 builder()
3499 ->LoadAccumulatorWithRegister(index)
3500 .UnaryOperation(Token::INC, feedback_index(index_slot.Get()))
3501 .StoreAccumulatorInRegister(index)
3502 .SetNamedProperty(array, length, feedback_index(length_slot.Get()),
3503 LanguageMode::kStrict);
3504 }
3505 }
3506
3507 builder()->LoadAccumulatorWithRegister(array);
3508 }
3509
VisitArrayLiteral(ArrayLiteral * expr)3510 void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
3511 expr->builder()->InitDepthAndFlags();
3512 BuildCreateArrayLiteral(expr->values(), expr);
3513 }
3514
VisitVariableProxy(VariableProxy * proxy)3515 void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
3516 builder()->SetExpressionPosition(proxy);
3517 BuildVariableLoad(proxy->var(), proxy->hole_check_mode());
3518 }
3519
BuildVariableLoad(Variable * variable,HoleCheckMode hole_check_mode,TypeofMode typeof_mode)3520 void BytecodeGenerator::BuildVariableLoad(Variable* variable,
3521 HoleCheckMode hole_check_mode,
3522 TypeofMode typeof_mode) {
3523 switch (variable->location()) {
3524 case VariableLocation::LOCAL: {
3525 Register source(builder()->Local(variable->index()));
3526 // We need to load the variable into the accumulator, even when in a
3527 // VisitForRegisterScope, in order to avoid register aliasing if
3528 // subsequent expressions assign to the same variable.
3529 builder()->LoadAccumulatorWithRegister(source);
3530 if (hole_check_mode == HoleCheckMode::kRequired) {
3531 BuildThrowIfHole(variable);
3532 }
3533 break;
3534 }
3535 case VariableLocation::PARAMETER: {
3536 Register source;
3537 if (variable->IsReceiver()) {
3538 source = builder()->Receiver();
3539 } else {
3540 source = builder()->Parameter(variable->index());
3541 }
3542 // We need to load the variable into the accumulator, even when in a
3543 // VisitForRegisterScope, in order to avoid register aliasing if
3544 // subsequent expressions assign to the same variable.
3545 builder()->LoadAccumulatorWithRegister(source);
3546 if (hole_check_mode == HoleCheckMode::kRequired) {
3547 BuildThrowIfHole(variable);
3548 }
3549 break;
3550 }
3551 case VariableLocation::UNALLOCATED: {
3552 // The global identifier "undefined" is immutable. Everything
3553 // else could be reassigned. For performance, we do a pointer comparison
3554 // rather than checking if the raw_name is really "undefined".
3555 if (variable->raw_name() == ast_string_constants()->undefined_string()) {
3556 builder()->LoadUndefined();
3557 } else {
3558 FeedbackSlot slot = GetCachedLoadGlobalICSlot(typeof_mode, variable);
3559 builder()->LoadGlobal(variable->raw_name(), feedback_index(slot),
3560 typeof_mode);
3561 }
3562 break;
3563 }
3564 case VariableLocation::CONTEXT: {
3565 int depth = execution_context()->ContextChainDepth(variable->scope());
3566 ContextScope* context = execution_context()->Previous(depth);
3567 Register context_reg;
3568 if (context) {
3569 context_reg = context->reg();
3570 depth = 0;
3571 } else {
3572 context_reg = execution_context()->reg();
3573 }
3574
3575 BytecodeArrayBuilder::ContextSlotMutability immutable =
3576 (variable->maybe_assigned() == kNotAssigned)
3577 ? BytecodeArrayBuilder::kImmutableSlot
3578 : BytecodeArrayBuilder::kMutableSlot;
3579
3580 builder()->LoadContextSlot(context_reg, variable->index(), depth,
3581 immutable);
3582 if (hole_check_mode == HoleCheckMode::kRequired) {
3583 BuildThrowIfHole(variable);
3584 }
3585 break;
3586 }
3587 case VariableLocation::LOOKUP: {
3588 switch (variable->mode()) {
3589 case VariableMode::kDynamicLocal: {
3590 Variable* local_variable = variable->local_if_not_shadowed();
3591 int depth =
3592 execution_context()->ContextChainDepth(local_variable->scope());
3593 builder()->LoadLookupContextSlot(variable->raw_name(), typeof_mode,
3594 local_variable->index(), depth);
3595 if (hole_check_mode == HoleCheckMode::kRequired) {
3596 BuildThrowIfHole(variable);
3597 }
3598 break;
3599 }
3600 case VariableMode::kDynamicGlobal: {
3601 int depth =
3602 current_scope()->ContextChainLengthUntilOutermostSloppyEval();
3603 // TODO(1008414): Add back caching here when bug is fixed properly.
3604 FeedbackSlot slot = feedback_spec()->AddLoadGlobalICSlot(typeof_mode);
3605
3606 builder()->LoadLookupGlobalSlot(variable->raw_name(), typeof_mode,
3607 feedback_index(slot), depth);
3608 break;
3609 }
3610 default:
3611 builder()->LoadLookupSlot(variable->raw_name(), typeof_mode);
3612 }
3613 break;
3614 }
3615 case VariableLocation::MODULE: {
3616 int depth = execution_context()->ContextChainDepth(variable->scope());
3617 builder()->LoadModuleVariable(variable->index(), depth);
3618 if (hole_check_mode == HoleCheckMode::kRequired) {
3619 BuildThrowIfHole(variable);
3620 }
3621 break;
3622 }
3623 case VariableLocation::REPL_GLOBAL: {
3624 DCHECK(variable->IsReplGlobal());
3625 FeedbackSlot slot = GetCachedLoadGlobalICSlot(typeof_mode, variable);
3626 builder()->LoadGlobal(variable->raw_name(), feedback_index(slot),
3627 typeof_mode);
3628 break;
3629 }
3630 }
3631 }
3632
BuildVariableLoadForAccumulatorValue(Variable * variable,HoleCheckMode hole_check_mode,TypeofMode typeof_mode)3633 void BytecodeGenerator::BuildVariableLoadForAccumulatorValue(
3634 Variable* variable, HoleCheckMode hole_check_mode, TypeofMode typeof_mode) {
3635 ValueResultScope accumulator_result(this);
3636 BuildVariableLoad(variable, hole_check_mode, typeof_mode);
3637 }
3638
BuildReturn(int source_position)3639 void BytecodeGenerator::BuildReturn(int source_position) {
3640 if (FLAG_trace) {
3641 RegisterAllocationScope register_scope(this);
3642 Register result = register_allocator()->NewRegister();
3643 // Runtime returns {result} value, preserving accumulator.
3644 builder()->StoreAccumulatorInRegister(result).CallRuntime(
3645 Runtime::kTraceExit, result);
3646 }
3647 if (info()->flags().collect_type_profile()) {
3648 builder()->CollectTypeProfile(info()->literal()->return_position());
3649 }
3650 builder()->SetStatementPosition(source_position);
3651 builder()->Return();
3652 }
3653
BuildAsyncReturn(int source_position)3654 void BytecodeGenerator::BuildAsyncReturn(int source_position) {
3655 RegisterAllocationScope register_scope(this);
3656
3657 if (IsAsyncGeneratorFunction(info()->literal()->kind())) {
3658 RegisterList args = register_allocator()->NewRegisterList(3);
3659 builder()
3660 ->MoveRegister(generator_object(), args[0]) // generator
3661 .StoreAccumulatorInRegister(args[1]) // value
3662 .LoadTrue()
3663 .StoreAccumulatorInRegister(args[2]) // done
3664 .CallRuntime(Runtime::kInlineAsyncGeneratorResolve, args);
3665 } else {
3666 DCHECK(IsAsyncFunction(info()->literal()->kind()) ||
3667 IsAsyncModule(info()->literal()->kind()));
3668 RegisterList args = register_allocator()->NewRegisterList(2);
3669 builder()
3670 ->MoveRegister(generator_object(), args[0]) // generator
3671 .StoreAccumulatorInRegister(args[1]) // value
3672 .CallRuntime(Runtime::kInlineAsyncFunctionResolve, args);
3673 }
3674
3675 BuildReturn(source_position);
3676 }
3677
BuildReThrow()3678 void BytecodeGenerator::BuildReThrow() { builder()->ReThrow(); }
3679
BuildThrowIfHole(Variable * variable)3680 void BytecodeGenerator::BuildThrowIfHole(Variable* variable) {
3681 if (variable->is_this()) {
3682 DCHECK(variable->mode() == VariableMode::kConst);
3683 builder()->ThrowSuperNotCalledIfHole();
3684 } else {
3685 builder()->ThrowReferenceErrorIfHole(variable->raw_name());
3686 }
3687 }
3688
BuildHoleCheckForVariableAssignment(Variable * variable,Token::Value op)3689 void BytecodeGenerator::BuildHoleCheckForVariableAssignment(Variable* variable,
3690 Token::Value op) {
3691 DCHECK(!IsPrivateMethodOrAccessorVariableMode(variable->mode()));
3692 if (variable->is_this() && variable->mode() == VariableMode::kConst &&
3693 op == Token::INIT) {
3694 // Perform an initialization check for 'this'. 'this' variable is the
3695 // only variable able to trigger bind operations outside the TDZ
3696 // via 'super' calls.
3697 builder()->ThrowSuperAlreadyCalledIfNotHole();
3698 } else {
3699 // Perform an initialization check for let/const declared variables.
3700 // E.g. let x = (x = 20); is not allowed.
3701 DCHECK(IsLexicalVariableMode(variable->mode()));
3702 BuildThrowIfHole(variable);
3703 }
3704 }
3705
BuildVariableAssignment(Variable * variable,Token::Value op,HoleCheckMode hole_check_mode,LookupHoistingMode lookup_hoisting_mode)3706 void BytecodeGenerator::BuildVariableAssignment(
3707 Variable* variable, Token::Value op, HoleCheckMode hole_check_mode,
3708 LookupHoistingMode lookup_hoisting_mode) {
3709 VariableMode mode = variable->mode();
3710 RegisterAllocationScope assignment_register_scope(this);
3711 BytecodeLabel end_label;
3712 switch (variable->location()) {
3713 case VariableLocation::PARAMETER:
3714 case VariableLocation::LOCAL: {
3715 Register destination;
3716 if (VariableLocation::PARAMETER == variable->location()) {
3717 if (variable->IsReceiver()) {
3718 destination = builder()->Receiver();
3719 } else {
3720 destination = builder()->Parameter(variable->index());
3721 }
3722 } else {
3723 destination = builder()->Local(variable->index());
3724 }
3725
3726 if (hole_check_mode == HoleCheckMode::kRequired) {
3727 // Load destination to check for hole.
3728 Register value_temp = register_allocator()->NewRegister();
3729 builder()
3730 ->StoreAccumulatorInRegister(value_temp)
3731 .LoadAccumulatorWithRegister(destination);
3732
3733 BuildHoleCheckForVariableAssignment(variable, op);
3734 builder()->LoadAccumulatorWithRegister(value_temp);
3735 }
3736
3737 if (mode != VariableMode::kConst || op == Token::INIT) {
3738 builder()->StoreAccumulatorInRegister(destination);
3739 } else if (variable->throw_on_const_assignment(language_mode())) {
3740 builder()->CallRuntime(Runtime::kThrowConstAssignError);
3741 }
3742 break;
3743 }
3744 case VariableLocation::UNALLOCATED: {
3745 BuildStoreGlobal(variable);
3746 break;
3747 }
3748 case VariableLocation::CONTEXT: {
3749 int depth = execution_context()->ContextChainDepth(variable->scope());
3750 ContextScope* context = execution_context()->Previous(depth);
3751 Register context_reg;
3752
3753 if (context) {
3754 context_reg = context->reg();
3755 depth = 0;
3756 } else {
3757 context_reg = execution_context()->reg();
3758 }
3759
3760 if (hole_check_mode == HoleCheckMode::kRequired) {
3761 // Load destination to check for hole.
3762 Register value_temp = register_allocator()->NewRegister();
3763 builder()
3764 ->StoreAccumulatorInRegister(value_temp)
3765 .LoadContextSlot(context_reg, variable->index(), depth,
3766 BytecodeArrayBuilder::kMutableSlot);
3767
3768 BuildHoleCheckForVariableAssignment(variable, op);
3769 builder()->LoadAccumulatorWithRegister(value_temp);
3770 }
3771
3772 if (mode != VariableMode::kConst || op == Token::INIT) {
3773 builder()->StoreContextSlot(context_reg, variable->index(), depth);
3774 } else if (variable->throw_on_const_assignment(language_mode())) {
3775 builder()->CallRuntime(Runtime::kThrowConstAssignError);
3776 }
3777 break;
3778 }
3779 case VariableLocation::LOOKUP: {
3780 builder()->StoreLookupSlot(variable->raw_name(), language_mode(),
3781 lookup_hoisting_mode);
3782 break;
3783 }
3784 case VariableLocation::MODULE: {
3785 DCHECK(IsDeclaredVariableMode(mode));
3786
3787 if (mode == VariableMode::kConst && op != Token::INIT) {
3788 builder()->CallRuntime(Runtime::kThrowConstAssignError);
3789 break;
3790 }
3791
3792 // If we don't throw above, we know that we're dealing with an
3793 // export because imports are const and we do not generate initializing
3794 // assignments for them.
3795 DCHECK(variable->IsExport());
3796
3797 int depth = execution_context()->ContextChainDepth(variable->scope());
3798 if (hole_check_mode == HoleCheckMode::kRequired) {
3799 Register value_temp = register_allocator()->NewRegister();
3800 builder()
3801 ->StoreAccumulatorInRegister(value_temp)
3802 .LoadModuleVariable(variable->index(), depth);
3803 BuildHoleCheckForVariableAssignment(variable, op);
3804 builder()->LoadAccumulatorWithRegister(value_temp);
3805 }
3806 builder()->StoreModuleVariable(variable->index(), depth);
3807 break;
3808 }
3809 case VariableLocation::REPL_GLOBAL: {
3810 // A let or const declaration like 'let x = 7' is effectively translated
3811 // to:
3812 // <top of the script>:
3813 // ScriptContext.x = TheHole;
3814 // ...
3815 // <where the actual 'let' is>:
3816 // ScriptContextTable.x = 7; // no hole check
3817 //
3818 // The ScriptContext slot for 'x' that we store to here is not
3819 // necessarily the ScriptContext of this script, but rather the
3820 // first ScriptContext that has a slot for name 'x'.
3821 DCHECK(variable->IsReplGlobal());
3822 if (op == Token::INIT) {
3823 RegisterList store_args = register_allocator()->NewRegisterList(2);
3824 builder()
3825 ->StoreAccumulatorInRegister(store_args[1])
3826 .LoadLiteral(variable->raw_name())
3827 .StoreAccumulatorInRegister(store_args[0]);
3828 builder()->CallRuntime(
3829 Runtime::kStoreGlobalNoHoleCheckForReplLetOrConst, store_args);
3830 } else {
3831 if (mode == VariableMode::kConst) {
3832 builder()->CallRuntime(Runtime::kThrowConstAssignError);
3833 } else {
3834 BuildStoreGlobal(variable);
3835 }
3836 }
3837 break;
3838 }
3839 }
3840 }
3841
BuildLoadNamedProperty(const Expression * object_expr,Register object,const AstRawString * name)3842 void BytecodeGenerator::BuildLoadNamedProperty(const Expression* object_expr,
3843 Register object,
3844 const AstRawString* name) {
3845 FeedbackSlot slot = GetCachedLoadICSlot(object_expr, name);
3846 builder()->LoadNamedProperty(object, name, feedback_index(slot));
3847 }
3848
BuildSetNamedProperty(const Expression * object_expr,Register object,const AstRawString * name)3849 void BytecodeGenerator::BuildSetNamedProperty(const Expression* object_expr,
3850 Register object,
3851 const AstRawString* name) {
3852 Register value;
3853 if (!execution_result()->IsEffect()) {
3854 value = register_allocator()->NewRegister();
3855 builder()->StoreAccumulatorInRegister(value);
3856 }
3857
3858 FeedbackSlot slot = GetCachedStoreICSlot(object_expr, name);
3859 builder()->SetNamedProperty(object, name, feedback_index(slot),
3860 language_mode());
3861
3862 if (!execution_result()->IsEffect()) {
3863 builder()->LoadAccumulatorWithRegister(value);
3864 }
3865 }
3866
BuildStoreGlobal(Variable * variable)3867 void BytecodeGenerator::BuildStoreGlobal(Variable* variable) {
3868 Register value;
3869 if (!execution_result()->IsEffect()) {
3870 value = register_allocator()->NewRegister();
3871 builder()->StoreAccumulatorInRegister(value);
3872 }
3873
3874 FeedbackSlot slot = GetCachedStoreGlobalICSlot(language_mode(), variable);
3875 builder()->StoreGlobal(variable->raw_name(), feedback_index(slot));
3876
3877 if (!execution_result()->IsEffect()) {
3878 builder()->LoadAccumulatorWithRegister(value);
3879 }
3880 }
3881
3882 // static
3883 BytecodeGenerator::AssignmentLhsData
NonProperty(Expression * expr)3884 BytecodeGenerator::AssignmentLhsData::NonProperty(Expression* expr) {
3885 return AssignmentLhsData(NON_PROPERTY, expr, RegisterList(), Register(),
3886 Register(), nullptr, nullptr);
3887 }
3888 // static
3889 BytecodeGenerator::AssignmentLhsData
NamedProperty(Expression * object_expr,Register object,const AstRawString * name)3890 BytecodeGenerator::AssignmentLhsData::NamedProperty(Expression* object_expr,
3891 Register object,
3892 const AstRawString* name) {
3893 return AssignmentLhsData(NAMED_PROPERTY, nullptr, RegisterList(), object,
3894 Register(), object_expr, name);
3895 }
3896 // static
3897 BytecodeGenerator::AssignmentLhsData
KeyedProperty(Register object,Register key)3898 BytecodeGenerator::AssignmentLhsData::KeyedProperty(Register object,
3899 Register key) {
3900 return AssignmentLhsData(KEYED_PROPERTY, nullptr, RegisterList(), object, key,
3901 nullptr, nullptr);
3902 }
3903 // static
3904 BytecodeGenerator::AssignmentLhsData
NamedSuperProperty(RegisterList super_property_args)3905 BytecodeGenerator::AssignmentLhsData::NamedSuperProperty(
3906 RegisterList super_property_args) {
3907 return AssignmentLhsData(NAMED_SUPER_PROPERTY, nullptr, super_property_args,
3908 Register(), Register(), nullptr, nullptr);
3909 }
3910 // static
3911 BytecodeGenerator::AssignmentLhsData
PrivateMethodOrAccessor(AssignType type,Property * property,Register object,Register key)3912 BytecodeGenerator::AssignmentLhsData::PrivateMethodOrAccessor(
3913 AssignType type, Property* property, Register object, Register key) {
3914 return AssignmentLhsData(type, property, RegisterList(), object, key, nullptr,
3915 nullptr);
3916 }
3917 // static
3918 BytecodeGenerator::AssignmentLhsData
KeyedSuperProperty(RegisterList super_property_args)3919 BytecodeGenerator::AssignmentLhsData::KeyedSuperProperty(
3920 RegisterList super_property_args) {
3921 return AssignmentLhsData(KEYED_SUPER_PROPERTY, nullptr, super_property_args,
3922 Register(), Register(), nullptr, nullptr);
3923 }
3924
PrepareAssignmentLhs(Expression * lhs,AccumulatorPreservingMode accumulator_preserving_mode)3925 BytecodeGenerator::AssignmentLhsData BytecodeGenerator::PrepareAssignmentLhs(
3926 Expression* lhs, AccumulatorPreservingMode accumulator_preserving_mode) {
3927 // Left-hand side can only be a property, a global or a variable slot.
3928 Property* property = lhs->AsProperty();
3929 AssignType assign_type = Property::GetAssignType(property);
3930
3931 // Evaluate LHS expression.
3932 switch (assign_type) {
3933 case NON_PROPERTY:
3934 return AssignmentLhsData::NonProperty(lhs);
3935 case NAMED_PROPERTY: {
3936 AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
3937 Register object = VisitForRegisterValue(property->obj());
3938 const AstRawString* name =
3939 property->key()->AsLiteral()->AsRawPropertyName();
3940 return AssignmentLhsData::NamedProperty(property->obj(), object, name);
3941 }
3942 case KEYED_PROPERTY: {
3943 AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
3944 Register object = VisitForRegisterValue(property->obj());
3945 Register key = VisitForRegisterValue(property->key());
3946 return AssignmentLhsData::KeyedProperty(object, key);
3947 }
3948 case PRIVATE_METHOD:
3949 case PRIVATE_GETTER_ONLY:
3950 case PRIVATE_SETTER_ONLY:
3951 case PRIVATE_GETTER_AND_SETTER: {
3952 DCHECK(!property->IsSuperAccess());
3953 AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
3954 Register object = VisitForRegisterValue(property->obj());
3955 Register key = VisitForRegisterValue(property->key());
3956 return AssignmentLhsData::PrivateMethodOrAccessor(assign_type, property,
3957 object, key);
3958 }
3959 case NAMED_SUPER_PROPERTY: {
3960 AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
3961 RegisterList super_property_args =
3962 register_allocator()->NewRegisterList(4);
3963 BuildThisVariableLoad();
3964 builder()->StoreAccumulatorInRegister(super_property_args[0]);
3965 BuildVariableLoad(
3966 property->obj()->AsSuperPropertyReference()->home_object()->var(),
3967 HoleCheckMode::kElided);
3968 builder()->StoreAccumulatorInRegister(super_property_args[1]);
3969 builder()
3970 ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
3971 .StoreAccumulatorInRegister(super_property_args[2]);
3972 return AssignmentLhsData::NamedSuperProperty(super_property_args);
3973 }
3974 case KEYED_SUPER_PROPERTY: {
3975 AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
3976 RegisterList super_property_args =
3977 register_allocator()->NewRegisterList(4);
3978 BuildThisVariableLoad();
3979 builder()->StoreAccumulatorInRegister(super_property_args[0]);
3980 BuildVariableLoad(
3981 property->obj()->AsSuperPropertyReference()->home_object()->var(),
3982 HoleCheckMode::kElided);
3983 builder()->StoreAccumulatorInRegister(super_property_args[1]);
3984 VisitForRegisterValue(property->key(), super_property_args[2]);
3985 return AssignmentLhsData::KeyedSuperProperty(super_property_args);
3986 }
3987 }
3988 UNREACHABLE();
3989 }
3990
3991 // Build the iteration finalizer called in the finally block of an iteration
3992 // protocol execution. This closes the iterator if needed, and suppresses any
3993 // exception it throws if necessary, including the exception when the return
3994 // method is not callable.
3995 //
3996 // In pseudo-code, this builds:
3997 //
3998 // if (!done) {
3999 // try {
4000 // let method = iterator.return
4001 // if (method !== null && method !== undefined) {
4002 // let return_val = method.call(iterator)
4003 // if (!%IsObject(return_val)) throw TypeError
4004 // }
4005 // } catch (e) {
4006 // if (iteration_continuation != RETHROW)
4007 // rethrow e
4008 // }
4009 // }
4010 //
4011 // For async iterators, iterator.close() becomes await iterator.close().
BuildFinalizeIteration(IteratorRecord iterator,Register done,Register iteration_continuation_token)4012 void BytecodeGenerator::BuildFinalizeIteration(
4013 IteratorRecord iterator, Register done,
4014 Register iteration_continuation_token) {
4015 RegisterAllocationScope register_scope(this);
4016 BytecodeLabels iterator_is_done(zone());
4017
4018 // if (!done) {
4019 builder()->LoadAccumulatorWithRegister(done).JumpIfTrue(
4020 ToBooleanMode::kConvertToBoolean, iterator_is_done.New());
4021
4022 {
4023 RegisterAllocationScope inner_register_scope(this);
4024 BuildTryCatch(
4025 // try {
4026 // let method = iterator.return
4027 // if (method !== null && method !== undefined) {
4028 // let return_val = method.call(iterator)
4029 // if (!%IsObject(return_val)) throw TypeError
4030 // }
4031 // }
4032 [&]() {
4033 Register method = register_allocator()->NewRegister();
4034 builder()
4035 ->LoadNamedProperty(
4036 iterator.object(), ast_string_constants()->return_string(),
4037 feedback_index(feedback_spec()->AddLoadICSlot()))
4038 .JumpIfUndefinedOrNull(iterator_is_done.New())
4039 .StoreAccumulatorInRegister(method);
4040
4041 RegisterList args(iterator.object());
4042 builder()->CallProperty(
4043 method, args, feedback_index(feedback_spec()->AddCallICSlot()));
4044 if (iterator.type() == IteratorType::kAsync) {
4045 BuildAwait();
4046 }
4047 builder()->JumpIfJSReceiver(iterator_is_done.New());
4048 {
4049 // Throw this exception inside the try block so that it is
4050 // suppressed by the iteration continuation if necessary.
4051 RegisterAllocationScope register_scope(this);
4052 Register return_result = register_allocator()->NewRegister();
4053 builder()
4054 ->StoreAccumulatorInRegister(return_result)
4055 .CallRuntime(Runtime::kThrowIteratorResultNotAnObject,
4056 return_result);
4057 }
4058 },
4059
4060 // catch (e) {
4061 // if (iteration_continuation != RETHROW)
4062 // rethrow e
4063 // }
4064 [&](Register context) {
4065 // Reuse context register to store the exception.
4066 Register close_exception = context;
4067 builder()->StoreAccumulatorInRegister(close_exception);
4068
4069 BytecodeLabel suppress_close_exception;
4070 builder()
4071 ->LoadLiteral(
4072 Smi::FromInt(ControlScope::DeferredCommands::kRethrowToken))
4073 .CompareReference(iteration_continuation_token)
4074 .JumpIfTrue(ToBooleanMode::kAlreadyBoolean,
4075 &suppress_close_exception)
4076 .LoadAccumulatorWithRegister(close_exception)
4077 .ReThrow()
4078 .Bind(&suppress_close_exception);
4079 },
4080 HandlerTable::UNCAUGHT);
4081 }
4082
4083 iterator_is_done.Bind(builder());
4084 }
4085
4086 // Get the default value of a destructuring target. Will mutate the
4087 // destructuring target expression if there is a default value.
4088 //
4089 // For
4090 // a = b
4091 // in
4092 // let {a = b} = c
4093 // returns b and mutates the input into a.
GetDestructuringDefaultValue(Expression ** target)4094 Expression* BytecodeGenerator::GetDestructuringDefaultValue(
4095 Expression** target) {
4096 Expression* default_value = nullptr;
4097 if ((*target)->IsAssignment()) {
4098 Assignment* default_init = (*target)->AsAssignment();
4099 DCHECK_EQ(default_init->op(), Token::ASSIGN);
4100 default_value = default_init->value();
4101 *target = default_init->target();
4102 DCHECK((*target)->IsValidReferenceExpression() || (*target)->IsPattern());
4103 }
4104 return default_value;
4105 }
4106
4107 // Convert a destructuring assignment to an array literal into a sequence of
4108 // iterator accesses into the value being assigned (in the accumulator).
4109 //
4110 // [a().x, ...b] = accumulator
4111 //
4112 // becomes
4113 //
4114 // iterator = %GetIterator(accumulator)
4115 // try {
4116 //
4117 // // Individual assignments read off the value from iterator.next() This gets
4118 // // repeated per destructuring element.
4119 // if (!done) {
4120 // // Make sure we are considered 'done' if .next(), .done or .value fail.
4121 // done = true
4122 // var next_result = iterator.next()
4123 // var tmp_done = next_result.done
4124 // if (!tmp_done) {
4125 // value = next_result.value
4126 // done = false
4127 // }
4128 // }
4129 // if (done)
4130 // value = undefined
4131 // a().x = value
4132 //
4133 // // A spread receives the remaining items in the iterator.
4134 // var array = []
4135 // var index = 0
4136 // %FillArrayWithIterator(iterator, array, index, done)
4137 // done = true
4138 // b = array
4139 //
4140 // } catch(e) {
4141 // iteration_continuation = RETHROW
4142 // } finally {
4143 // %FinalizeIteration(iterator, done, iteration_continuation)
4144 // }
BuildDestructuringArrayAssignment(ArrayLiteral * pattern,Token::Value op,LookupHoistingMode lookup_hoisting_mode)4145 void BytecodeGenerator::BuildDestructuringArrayAssignment(
4146 ArrayLiteral* pattern, Token::Value op,
4147 LookupHoistingMode lookup_hoisting_mode) {
4148 RegisterAllocationScope scope(this);
4149
4150 Register value = register_allocator()->NewRegister();
4151 builder()->StoreAccumulatorInRegister(value);
4152
4153 // Store the iterator in a dedicated register so that it can be closed on
4154 // exit, and the 'done' value in a dedicated register so that it can be
4155 // changed and accessed independently of the iteration result.
4156 IteratorRecord iterator = BuildGetIteratorRecord(IteratorType::kNormal);
4157 Register done = register_allocator()->NewRegister();
4158 builder()->LoadFalse();
4159 builder()->StoreAccumulatorInRegister(done);
4160
4161 BuildTryFinally(
4162 // Try block.
4163 [&]() {
4164 Register next_result = register_allocator()->NewRegister();
4165 FeedbackSlot next_value_load_slot = feedback_spec()->AddLoadICSlot();
4166 FeedbackSlot next_done_load_slot = feedback_spec()->AddLoadICSlot();
4167
4168 Spread* spread = nullptr;
4169 for (Expression* target : *pattern->values()) {
4170 if (target->IsSpread()) {
4171 spread = target->AsSpread();
4172 break;
4173 }
4174
4175 Expression* default_value = GetDestructuringDefaultValue(&target);
4176 if (!target->IsPattern()) {
4177 builder()->SetExpressionAsStatementPosition(target);
4178 }
4179
4180 AssignmentLhsData lhs_data = PrepareAssignmentLhs(target);
4181
4182 // if (!done) {
4183 // // Make sure we are considered done if .next(), .done or .value
4184 // // fail.
4185 // done = true
4186 // var next_result = iterator.next()
4187 // var tmp_done = next_result.done
4188 // if (!tmp_done) {
4189 // value = next_result.value
4190 // done = false
4191 // }
4192 // }
4193 // if (done)
4194 // value = undefined
4195 BytecodeLabels is_done(zone());
4196
4197 builder()->LoadAccumulatorWithRegister(done);
4198 builder()->JumpIfTrue(ToBooleanMode::kConvertToBoolean,
4199 is_done.New());
4200
4201 builder()->LoadTrue().StoreAccumulatorInRegister(done);
4202 BuildIteratorNext(iterator, next_result);
4203 builder()
4204 ->LoadNamedProperty(next_result,
4205 ast_string_constants()->done_string(),
4206 feedback_index(next_done_load_slot))
4207 .JumpIfTrue(ToBooleanMode::kConvertToBoolean, is_done.New());
4208
4209 // Only do the assignment if this is not a hole (i.e. 'elided').
4210 if (!target->IsTheHoleLiteral()) {
4211 builder()
4212 ->LoadNamedProperty(next_result,
4213 ast_string_constants()->value_string(),
4214 feedback_index(next_value_load_slot))
4215 .StoreAccumulatorInRegister(next_result)
4216 .LoadFalse()
4217 .StoreAccumulatorInRegister(done)
4218 .LoadAccumulatorWithRegister(next_result);
4219
4220 // [<pattern> = <init>] = <value>
4221 // becomes (roughly)
4222 // temp = <value>.next();
4223 // <pattern> = temp === undefined ? <init> : temp;
4224 BytecodeLabel do_assignment;
4225 if (default_value) {
4226 builder()->JumpIfNotUndefined(&do_assignment);
4227 // Since done == true => temp == undefined, jump directly to using
4228 // the default value for that case.
4229 is_done.Bind(builder());
4230 VisitForAccumulatorValue(default_value);
4231 } else {
4232 builder()->Jump(&do_assignment);
4233 is_done.Bind(builder());
4234 builder()->LoadUndefined();
4235 }
4236 builder()->Bind(&do_assignment);
4237
4238 BuildAssignment(lhs_data, op, lookup_hoisting_mode);
4239 } else {
4240 builder()->LoadFalse().StoreAccumulatorInRegister(done);
4241 DCHECK_EQ(lhs_data.assign_type(), NON_PROPERTY);
4242 is_done.Bind(builder());
4243 }
4244 }
4245
4246 if (spread) {
4247 RegisterAllocationScope scope(this);
4248 BytecodeLabel is_done;
4249
4250 // A spread is turned into a loop over the remainer of the iterator.
4251 Expression* target = spread->expression();
4252
4253 if (!target->IsPattern()) {
4254 builder()->SetExpressionAsStatementPosition(spread);
4255 }
4256
4257 AssignmentLhsData lhs_data = PrepareAssignmentLhs(target);
4258
4259 // var array = [];
4260 Register array = register_allocator()->NewRegister();
4261 builder()->CreateEmptyArrayLiteral(
4262 feedback_index(feedback_spec()->AddLiteralSlot()));
4263 builder()->StoreAccumulatorInRegister(array);
4264
4265 // If done, jump to assigning empty array
4266 builder()->LoadAccumulatorWithRegister(done);
4267 builder()->JumpIfTrue(ToBooleanMode::kConvertToBoolean, &is_done);
4268
4269 // var index = 0;
4270 Register index = register_allocator()->NewRegister();
4271 builder()->LoadLiteral(Smi::zero());
4272 builder()->StoreAccumulatorInRegister(index);
4273
4274 // Set done to true, since it's guaranteed to be true by the time the
4275 // array fill completes.
4276 builder()->LoadTrue().StoreAccumulatorInRegister(done);
4277
4278 // Fill the array with the iterator.
4279 FeedbackSlot element_slot =
4280 feedback_spec()->AddStoreInArrayLiteralICSlot();
4281 FeedbackSlot index_slot = feedback_spec()->AddBinaryOpICSlot();
4282 BuildFillArrayWithIterator(iterator, array, index, next_result,
4283 next_value_load_slot, next_done_load_slot,
4284 index_slot, element_slot);
4285
4286 builder()->Bind(&is_done);
4287 // Assign the array to the LHS.
4288 builder()->LoadAccumulatorWithRegister(array);
4289 BuildAssignment(lhs_data, op, lookup_hoisting_mode);
4290 }
4291 },
4292 // Finally block.
4293 [&](Register iteration_continuation_token) {
4294 // Finish the iteration in the finally block.
4295 BuildFinalizeIteration(iterator, done, iteration_continuation_token);
4296 },
4297 HandlerTable::UNCAUGHT);
4298
4299 if (!execution_result()->IsEffect()) {
4300 builder()->LoadAccumulatorWithRegister(value);
4301 }
4302 }
4303
4304 // Convert a destructuring assignment to an object literal into a sequence of
4305 // property accesses into the value being assigned (in the accumulator).
4306 //
4307 // { y, [x++]: a(), ...b.c } = value
4308 //
4309 // becomes
4310 //
4311 // var rest_runtime_callargs = new Array(3);
4312 // rest_runtime_callargs[0] = value;
4313 //
4314 // rest_runtime_callargs[1] = "y";
4315 // y = value.y;
4316 //
4317 // var temp1 = %ToName(x++);
4318 // rest_runtime_callargs[2] = temp1;
4319 // a() = value[temp1];
4320 //
4321 // b.c =
4322 // %CopyDataPropertiesWithExcludedPropertiesOnStack.call(rest_runtime_callargs);
BuildDestructuringObjectAssignment(ObjectLiteral * pattern,Token::Value op,LookupHoistingMode lookup_hoisting_mode)4323 void BytecodeGenerator::BuildDestructuringObjectAssignment(
4324 ObjectLiteral* pattern, Token::Value op,
4325 LookupHoistingMode lookup_hoisting_mode) {
4326 RegisterAllocationScope register_scope(this);
4327
4328 // Store the assignment value in a register.
4329 Register value;
4330 RegisterList rest_runtime_callargs;
4331 if (pattern->builder()->has_rest_property()) {
4332 rest_runtime_callargs =
4333 register_allocator()->NewRegisterList(pattern->properties()->length());
4334 value = rest_runtime_callargs[0];
4335 } else {
4336 value = register_allocator()->NewRegister();
4337 }
4338 builder()->StoreAccumulatorInRegister(value);
4339
4340 // if (value === null || value === undefined)
4341 // throw new TypeError(kNonCoercible);
4342 //
4343 // Since the first property access on null/undefined will also trigger a
4344 // TypeError, we can elide this check. The exception is when there are no
4345 // properties and no rest property (this is an empty literal), or when the
4346 // first property is a computed name and accessing it can have side effects.
4347 //
4348 // TODO(leszeks): Also eliminate this check if the value is known to be
4349 // non-null (e.g. an object literal).
4350 if (pattern->properties()->is_empty() ||
4351 (pattern->properties()->at(0)->is_computed_name() &&
4352 pattern->properties()->at(0)->kind() != ObjectLiteralProperty::SPREAD)) {
4353 BytecodeLabel is_null_or_undefined, not_null_or_undefined;
4354 builder()
4355 ->JumpIfUndefinedOrNull(&is_null_or_undefined)
4356 .Jump(¬_null_or_undefined);
4357
4358 {
4359 builder()->Bind(&is_null_or_undefined);
4360 builder()->SetExpressionPosition(pattern);
4361 builder()->CallRuntime(Runtime::kThrowPatternAssignmentNonCoercible,
4362 value);
4363 }
4364 builder()->Bind(¬_null_or_undefined);
4365 }
4366
4367 int i = 0;
4368 for (ObjectLiteralProperty* pattern_property : *pattern->properties()) {
4369 RegisterAllocationScope inner_register_scope(this);
4370
4371 // The key of the pattern becomes the key into the RHS value, and the value
4372 // of the pattern becomes the target of the assignment.
4373 //
4374 // e.g. { a: b } = o becomes b = o.a
4375 Expression* pattern_key = pattern_property->key();
4376 Expression* target = pattern_property->value();
4377 Expression* default_value = GetDestructuringDefaultValue(&target);
4378
4379 if (!target->IsPattern()) {
4380 builder()->SetExpressionAsStatementPosition(target);
4381 }
4382
4383 // Calculate this property's key into the assignment RHS value, additionally
4384 // storing the key for rest_runtime_callargs if needed.
4385 //
4386 // The RHS is accessed using the key either by LoadNamedProperty (if
4387 // value_name is valid) or by LoadKeyedProperty (otherwise).
4388 const AstRawString* value_name = nullptr;
4389 Register value_key;
4390
4391 if (pattern_property->kind() != ObjectLiteralProperty::Kind::SPREAD) {
4392 if (pattern_key->IsPropertyName()) {
4393 value_name = pattern_key->AsLiteral()->AsRawPropertyName();
4394 }
4395 if (pattern->builder()->has_rest_property() || !value_name) {
4396 if (pattern->builder()->has_rest_property()) {
4397 value_key = rest_runtime_callargs[i + 1];
4398 } else {
4399 value_key = register_allocator()->NewRegister();
4400 }
4401 if (pattern_property->is_computed_name()) {
4402 // { [a()]: b().x } = c
4403 // becomes
4404 // var tmp = a()
4405 // b().x = c[tmp]
4406 DCHECK(!pattern_key->IsPropertyName() ||
4407 !pattern_key->IsNumberLiteral());
4408 VisitForAccumulatorValue(pattern_key);
4409 builder()->ToName(value_key);
4410 } else {
4411 // We only need the key for non-computed properties when it is numeric
4412 // or is being saved for the rest_runtime_callargs.
4413 DCHECK(pattern_key->IsNumberLiteral() ||
4414 (pattern->builder()->has_rest_property() &&
4415 pattern_key->IsPropertyName()));
4416 VisitForRegisterValue(pattern_key, value_key);
4417 }
4418 }
4419 }
4420
4421 AssignmentLhsData lhs_data = PrepareAssignmentLhs(target);
4422
4423 // Get the value from the RHS.
4424 if (pattern_property->kind() == ObjectLiteralProperty::Kind::SPREAD) {
4425 DCHECK_EQ(i, pattern->properties()->length() - 1);
4426 DCHECK(!value_key.is_valid());
4427 DCHECK_NULL(value_name);
4428 builder()->CallRuntime(
4429 Runtime::kInlineCopyDataPropertiesWithExcludedPropertiesOnStack,
4430 rest_runtime_callargs);
4431 } else if (value_name) {
4432 builder()->LoadNamedProperty(
4433 value, value_name, feedback_index(feedback_spec()->AddLoadICSlot()));
4434 } else {
4435 DCHECK(value_key.is_valid());
4436 builder()->LoadAccumulatorWithRegister(value_key).LoadKeyedProperty(
4437 value, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
4438 }
4439
4440 // {<pattern> = <init>} = <value>
4441 // becomes
4442 // temp = <value>;
4443 // <pattern> = temp === undefined ? <init> : temp;
4444 if (default_value) {
4445 BytecodeLabel value_not_undefined;
4446 builder()->JumpIfNotUndefined(&value_not_undefined);
4447 VisitForAccumulatorValue(default_value);
4448 builder()->Bind(&value_not_undefined);
4449 }
4450
4451 BuildAssignment(lhs_data, op, lookup_hoisting_mode);
4452
4453 i++;
4454 }
4455
4456 if (!execution_result()->IsEffect()) {
4457 builder()->LoadAccumulatorWithRegister(value);
4458 }
4459 }
4460
BuildAssignment(const AssignmentLhsData & lhs_data,Token::Value op,LookupHoistingMode lookup_hoisting_mode)4461 void BytecodeGenerator::BuildAssignment(
4462 const AssignmentLhsData& lhs_data, Token::Value op,
4463 LookupHoistingMode lookup_hoisting_mode) {
4464 // Assign the value to the LHS.
4465 switch (lhs_data.assign_type()) {
4466 case NON_PROPERTY: {
4467 if (ObjectLiteral* pattern_as_object =
4468 lhs_data.expr()->AsObjectLiteral()) {
4469 // Split object literals into destructuring.
4470 BuildDestructuringObjectAssignment(pattern_as_object, op,
4471 lookup_hoisting_mode);
4472 } else if (ArrayLiteral* pattern_as_array =
4473 lhs_data.expr()->AsArrayLiteral()) {
4474 // Split object literals into destructuring.
4475 BuildDestructuringArrayAssignment(pattern_as_array, op,
4476 lookup_hoisting_mode);
4477 } else {
4478 DCHECK(lhs_data.expr()->IsVariableProxy());
4479 VariableProxy* proxy = lhs_data.expr()->AsVariableProxy();
4480 BuildVariableAssignment(proxy->var(), op, proxy->hole_check_mode(),
4481 lookup_hoisting_mode);
4482 }
4483 break;
4484 }
4485 case NAMED_PROPERTY: {
4486 BuildSetNamedProperty(lhs_data.object_expr(), lhs_data.object(),
4487 lhs_data.name());
4488 break;
4489 }
4490 case KEYED_PROPERTY: {
4491 FeedbackSlot slot = feedback_spec()->AddKeyedStoreICSlot(language_mode());
4492 Register value;
4493 if (!execution_result()->IsEffect()) {
4494 value = register_allocator()->NewRegister();
4495 builder()->StoreAccumulatorInRegister(value);
4496 }
4497 builder()->SetKeyedProperty(lhs_data.object(), lhs_data.key(),
4498 feedback_index(slot), language_mode());
4499 if (!execution_result()->IsEffect()) {
4500 builder()->LoadAccumulatorWithRegister(value);
4501 }
4502 break;
4503 }
4504 case NAMED_SUPER_PROPERTY: {
4505 builder()
4506 ->StoreAccumulatorInRegister(lhs_data.super_property_args()[3])
4507 .CallRuntime(Runtime::kStoreToSuper, lhs_data.super_property_args());
4508 break;
4509 }
4510 case KEYED_SUPER_PROPERTY: {
4511 builder()
4512 ->StoreAccumulatorInRegister(lhs_data.super_property_args()[3])
4513 .CallRuntime(Runtime::kStoreKeyedToSuper,
4514 lhs_data.super_property_args());
4515 break;
4516 }
4517 case PRIVATE_METHOD: {
4518 Property* property = lhs_data.expr()->AsProperty();
4519 BuildPrivateBrandCheck(property, lhs_data.object());
4520 BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
4521 lhs_data.expr()->AsProperty());
4522 break;
4523 }
4524 case PRIVATE_GETTER_ONLY: {
4525 Property* property = lhs_data.expr()->AsProperty();
4526 BuildPrivateBrandCheck(property, lhs_data.object());
4527 BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
4528 lhs_data.expr()->AsProperty());
4529 break;
4530 }
4531 case PRIVATE_SETTER_ONLY:
4532 case PRIVATE_GETTER_AND_SETTER: {
4533 Register value = register_allocator()->NewRegister();
4534 builder()->StoreAccumulatorInRegister(value);
4535 Property* property = lhs_data.expr()->AsProperty();
4536 BuildPrivateBrandCheck(property, lhs_data.object());
4537 BuildPrivateSetterAccess(lhs_data.object(), lhs_data.key(), value);
4538 if (!execution_result()->IsEffect()) {
4539 builder()->LoadAccumulatorWithRegister(value);
4540 }
4541 break;
4542 }
4543 }
4544 }
4545
VisitAssignment(Assignment * expr)4546 void BytecodeGenerator::VisitAssignment(Assignment* expr) {
4547 AssignmentLhsData lhs_data = PrepareAssignmentLhs(expr->target());
4548
4549 VisitForAccumulatorValue(expr->value());
4550
4551 builder()->SetExpressionPosition(expr);
4552 BuildAssignment(lhs_data, expr->op(), expr->lookup_hoisting_mode());
4553 }
4554
VisitCompoundAssignment(CompoundAssignment * expr)4555 void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
4556 AssignmentLhsData lhs_data = PrepareAssignmentLhs(expr->target());
4557
4558 // Evaluate the value and potentially handle compound assignments by loading
4559 // the left-hand side value and performing a binary operation.
4560 switch (lhs_data.assign_type()) {
4561 case NON_PROPERTY: {
4562 VariableProxy* proxy = expr->target()->AsVariableProxy();
4563 BuildVariableLoad(proxy->var(), proxy->hole_check_mode());
4564 break;
4565 }
4566 case NAMED_PROPERTY: {
4567 BuildLoadNamedProperty(lhs_data.object_expr(), lhs_data.object(),
4568 lhs_data.name());
4569 break;
4570 }
4571 case KEYED_PROPERTY: {
4572 FeedbackSlot slot = feedback_spec()->AddKeyedLoadICSlot();
4573 builder()
4574 ->LoadAccumulatorWithRegister(lhs_data.key())
4575 .LoadKeyedProperty(lhs_data.object(), feedback_index(slot));
4576 break;
4577 }
4578 case NAMED_SUPER_PROPERTY: {
4579 builder()->CallRuntime(Runtime::kLoadFromSuper,
4580 lhs_data.super_property_args().Truncate(3));
4581 break;
4582 }
4583 case KEYED_SUPER_PROPERTY: {
4584 builder()->CallRuntime(Runtime::kLoadKeyedFromSuper,
4585 lhs_data.super_property_args().Truncate(3));
4586 break;
4587 }
4588 // BuildAssignment() will throw an error about the private method being
4589 // read-only.
4590 case PRIVATE_METHOD: {
4591 Property* property = lhs_data.expr()->AsProperty();
4592 BuildPrivateBrandCheck(property, lhs_data.object());
4593 builder()->LoadAccumulatorWithRegister(lhs_data.key());
4594 break;
4595 }
4596 // For read-only properties, BuildAssignment() will throw an error about
4597 // the missing setter.
4598 case PRIVATE_GETTER_ONLY:
4599 case PRIVATE_GETTER_AND_SETTER: {
4600 Property* property = lhs_data.expr()->AsProperty();
4601 BuildPrivateBrandCheck(property, lhs_data.object());
4602 BuildPrivateGetterAccess(lhs_data.object(), lhs_data.key());
4603 break;
4604 }
4605 case PRIVATE_SETTER_ONLY: {
4606 // The property access is invalid, but if the brand check fails too, we
4607 // need to return the error from the brand check.
4608 Property* property = lhs_data.expr()->AsProperty();
4609 BuildPrivateBrandCheck(property, lhs_data.object());
4610 BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
4611 lhs_data.expr()->AsProperty());
4612 break;
4613 }
4614 }
4615
4616 BinaryOperation* binop = expr->binary_operation();
4617 FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
4618 BytecodeLabel short_circuit;
4619 if (binop->op() == Token::NULLISH) {
4620 BytecodeLabel nullish;
4621 builder()
4622 ->JumpIfUndefinedOrNull(&nullish)
4623 .Jump(&short_circuit)
4624 .Bind(&nullish);
4625 VisitForAccumulatorValue(expr->value());
4626 } else if (binop->op() == Token::OR) {
4627 builder()->JumpIfTrue(ToBooleanMode::kConvertToBoolean, &short_circuit);
4628 VisitForAccumulatorValue(expr->value());
4629 } else if (binop->op() == Token::AND) {
4630 builder()->JumpIfFalse(ToBooleanMode::kConvertToBoolean, &short_circuit);
4631 VisitForAccumulatorValue(expr->value());
4632 } else if (expr->value()->IsSmiLiteral()) {
4633 builder()->BinaryOperationSmiLiteral(
4634 binop->op(), expr->value()->AsLiteral()->AsSmiLiteral(),
4635 feedback_index(slot));
4636 } else {
4637 Register old_value = register_allocator()->NewRegister();
4638 builder()->StoreAccumulatorInRegister(old_value);
4639 VisitForAccumulatorValue(expr->value());
4640 builder()->BinaryOperation(binop->op(), old_value, feedback_index(slot));
4641 }
4642 builder()->SetExpressionPosition(expr);
4643
4644 BuildAssignment(lhs_data, expr->op(), expr->lookup_hoisting_mode());
4645 builder()->Bind(&short_circuit);
4646 }
4647
4648 // Suspends the generator to resume at the next suspend_id, with output stored
4649 // in the accumulator. When the generator is resumed, the sent value is loaded
4650 // in the accumulator.
BuildSuspendPoint(int position)4651 void BytecodeGenerator::BuildSuspendPoint(int position) {
4652 // Because we eliminate jump targets in dead code, we also eliminate resumes
4653 // when the suspend is not emitted because otherwise the below call to Bind
4654 // would start a new basic block and the code would be considered alive.
4655 if (builder()->RemainderOfBlockIsDead()) {
4656 return;
4657 }
4658 const int suspend_id = suspend_count_++;
4659
4660 RegisterList registers = register_allocator()->AllLiveRegisters();
4661
4662 // Save context, registers, and state. This bytecode then returns the value
4663 // in the accumulator.
4664 builder()->SetExpressionPosition(position);
4665 builder()->SuspendGenerator(generator_object(), registers, suspend_id);
4666
4667 // Upon resume, we continue here.
4668 builder()->Bind(generator_jump_table_, suspend_id);
4669
4670 // Clobbers all registers and sets the accumulator to the
4671 // [[input_or_debug_pos]] slot of the generator object.
4672 builder()->ResumeGenerator(generator_object(), registers);
4673 }
4674
VisitYield(Yield * expr)4675 void BytecodeGenerator::VisitYield(Yield* expr) {
4676 builder()->SetExpressionPosition(expr);
4677 VisitForAccumulatorValue(expr->expression());
4678
4679 // If this is not the first yield
4680 if (suspend_count_ > 0) {
4681 if (IsAsyncGeneratorFunction(function_kind())) {
4682 // AsyncGenerator yields (with the exception of the initial yield)
4683 // delegate work to the AsyncGeneratorYield stub, which Awaits the operand
4684 // and on success, wraps the value in an IteratorResult.
4685 RegisterAllocationScope register_scope(this);
4686 RegisterList args = register_allocator()->NewRegisterList(3);
4687 builder()
4688 ->MoveRegister(generator_object(), args[0]) // generator
4689 .StoreAccumulatorInRegister(args[1]) // value
4690 .LoadBoolean(catch_prediction() != HandlerTable::ASYNC_AWAIT)
4691 .StoreAccumulatorInRegister(args[2]) // is_caught
4692 .CallRuntime(Runtime::kInlineAsyncGeneratorYield, args);
4693 } else {
4694 // Generator yields (with the exception of the initial yield) wrap the
4695 // value into IteratorResult.
4696 RegisterAllocationScope register_scope(this);
4697 RegisterList args = register_allocator()->NewRegisterList(2);
4698 builder()
4699 ->StoreAccumulatorInRegister(args[0]) // value
4700 .LoadFalse()
4701 .StoreAccumulatorInRegister(args[1]) // done
4702 .CallRuntime(Runtime::kInlineCreateIterResultObject, args);
4703 }
4704 }
4705
4706 BuildSuspendPoint(expr->position());
4707 // At this point, the generator has been resumed, with the received value in
4708 // the accumulator.
4709
4710 // TODO(caitp): remove once yield* desugaring for async generators is handled
4711 // in BytecodeGenerator.
4712 if (expr->on_abrupt_resume() == Yield::kNoControl) {
4713 DCHECK(IsAsyncGeneratorFunction(function_kind()));
4714 return;
4715 }
4716
4717 Register input = register_allocator()->NewRegister();
4718 builder()->StoreAccumulatorInRegister(input).CallRuntime(
4719 Runtime::kInlineGeneratorGetResumeMode, generator_object());
4720
4721 // Now dispatch on resume mode.
4722 STATIC_ASSERT(JSGeneratorObject::kNext + 1 == JSGeneratorObject::kReturn);
4723 BytecodeJumpTable* jump_table =
4724 builder()->AllocateJumpTable(2, JSGeneratorObject::kNext);
4725
4726 builder()->SwitchOnSmiNoFeedback(jump_table);
4727
4728 {
4729 // Resume with throw (switch fallthrough).
4730 // TODO(leszeks): Add a debug-only check that the accumulator is
4731 // JSGeneratorObject::kThrow.
4732 builder()->SetExpressionPosition(expr);
4733 builder()->LoadAccumulatorWithRegister(input);
4734 builder()->Throw();
4735 }
4736
4737 {
4738 // Resume with return.
4739 builder()->Bind(jump_table, JSGeneratorObject::kReturn);
4740 builder()->LoadAccumulatorWithRegister(input);
4741 if (IsAsyncGeneratorFunction(function_kind())) {
4742 execution_control()->AsyncReturnAccumulator(kNoSourcePosition);
4743 } else {
4744 execution_control()->ReturnAccumulator(kNoSourcePosition);
4745 }
4746 }
4747
4748 {
4749 // Resume with next.
4750 builder()->Bind(jump_table, JSGeneratorObject::kNext);
4751 BuildIncrementBlockCoverageCounterIfEnabled(expr,
4752 SourceRangeKind::kContinuation);
4753 builder()->LoadAccumulatorWithRegister(input);
4754 }
4755 }
4756
4757 // Desugaring of (yield* iterable)
4758 //
4759 // do {
4760 // const kNext = 0;
4761 // const kReturn = 1;
4762 // const kThrow = 2;
4763 //
4764 // let output; // uninitialized
4765 //
4766 // let iteratorRecord = GetIterator(iterable);
4767 // let iterator = iteratorRecord.[[Iterator]];
4768 // let next = iteratorRecord.[[NextMethod]];
4769 // let input = undefined;
4770 // let resumeMode = kNext;
4771 //
4772 // while (true) {
4773 // // From the generator to the iterator:
4774 // // Forward input according to resumeMode and obtain output.
4775 // switch (resumeMode) {
4776 // case kNext:
4777 // output = next.[[Call]](iterator, « »);;
4778 // break;
4779 // case kReturn:
4780 // let iteratorReturn = iterator.return;
4781 // if (IS_NULL_OR_UNDEFINED(iteratorReturn)) {
4782 // if (IS_ASYNC_GENERATOR) input = await input;
4783 // return input;
4784 // }
4785 // output = iteratorReturn.[[Call]](iterator, «input»);
4786 // break;
4787 // case kThrow:
4788 // let iteratorThrow = iterator.throw;
4789 // if (IS_NULL_OR_UNDEFINED(iteratorThrow)) {
4790 // let iteratorReturn = iterator.return;
4791 // if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) {
4792 // output = iteratorReturn.[[Call]](iterator, « »);
4793 // if (IS_ASYNC_GENERATOR) output = await output;
4794 // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
4795 // }
4796 // throw MakeTypeError(kThrowMethodMissing);
4797 // }
4798 // output = iteratorThrow.[[Call]](iterator, «input»);
4799 // break;
4800 // }
4801 //
4802 // if (IS_ASYNC_GENERATOR) output = await output;
4803 // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
4804 // if (output.done) break;
4805 //
4806 // // From the generator to its user:
4807 // // Forward output, receive new input, and determine resume mode.
4808 // if (IS_ASYNC_GENERATOR) {
4809 // // AsyncGeneratorYield abstract operation awaits the operand before
4810 // // resolving the promise for the current AsyncGeneratorRequest.
4811 // %_AsyncGeneratorYield(output.value)
4812 // }
4813 // input = Suspend(output);
4814 // resumeMode = %GeneratorGetResumeMode();
4815 // }
4816 //
4817 // if (resumeMode === kReturn) {
4818 // return output.value;
4819 // }
4820 // output.value
4821 // }
VisitYieldStar(YieldStar * expr)4822 void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
4823 Register output = register_allocator()->NewRegister();
4824 Register resume_mode = register_allocator()->NewRegister();
4825 IteratorType iterator_type = IsAsyncGeneratorFunction(function_kind())
4826 ? IteratorType::kAsync
4827 : IteratorType::kNormal;
4828
4829 {
4830 RegisterAllocationScope register_scope(this);
4831 RegisterList iterator_and_input = register_allocator()->NewRegisterList(2);
4832 VisitForAccumulatorValue(expr->expression());
4833 IteratorRecord iterator = BuildGetIteratorRecord(
4834 register_allocator()->NewRegister() /* next method */,
4835 iterator_and_input[0], iterator_type);
4836
4837 Register input = iterator_and_input[1];
4838 builder()->LoadUndefined().StoreAccumulatorInRegister(input);
4839 builder()
4840 ->LoadLiteral(Smi::FromInt(JSGeneratorObject::kNext))
4841 .StoreAccumulatorInRegister(resume_mode);
4842
4843 {
4844 // This loop builder does not construct counters as the loop is not
4845 // visible to the user, and we therefore neither pass the block coverage
4846 // builder nor the expression.
4847 //
4848 // In addition to the normal suspend for yield*, a yield* in an async
4849 // generator has 2 additional suspends:
4850 // - One for awaiting the iterator result of closing the generator when
4851 // resumed with a "throw" completion, and a throw method is not
4852 // present on the delegated iterator
4853 // - One for awaiting the iterator result yielded by the delegated
4854 // iterator
4855
4856 LoopBuilder loop_builder(builder(), nullptr, nullptr);
4857 LoopScope loop_scope(this, &loop_builder);
4858
4859 {
4860 BytecodeLabels after_switch(zone());
4861 BytecodeJumpTable* switch_jump_table =
4862 builder()->AllocateJumpTable(2, 1);
4863
4864 builder()
4865 ->LoadAccumulatorWithRegister(resume_mode)
4866 .SwitchOnSmiNoFeedback(switch_jump_table);
4867
4868 // Fallthrough to default case.
4869 // TODO(ignition): Add debug code to check that {resume_mode} really is
4870 // {JSGeneratorObject::kNext} in this case.
4871 STATIC_ASSERT(JSGeneratorObject::kNext == 0);
4872 {
4873 FeedbackSlot slot = feedback_spec()->AddCallICSlot();
4874 builder()->CallProperty(iterator.next(), iterator_and_input,
4875 feedback_index(slot));
4876 builder()->Jump(after_switch.New());
4877 }
4878
4879 STATIC_ASSERT(JSGeneratorObject::kReturn == 1);
4880 builder()->Bind(switch_jump_table, JSGeneratorObject::kReturn);
4881 {
4882 const AstRawString* return_string =
4883 ast_string_constants()->return_string();
4884 BytecodeLabels no_return_method(zone());
4885
4886 BuildCallIteratorMethod(iterator.object(), return_string,
4887 iterator_and_input, after_switch.New(),
4888 &no_return_method);
4889 no_return_method.Bind(builder());
4890 builder()->LoadAccumulatorWithRegister(input);
4891 if (iterator_type == IteratorType::kAsync) {
4892 // Await input.
4893 BuildAwait(expr->position());
4894 execution_control()->AsyncReturnAccumulator(kNoSourcePosition);
4895 } else {
4896 execution_control()->ReturnAccumulator(kNoSourcePosition);
4897 }
4898 }
4899
4900 STATIC_ASSERT(JSGeneratorObject::kThrow == 2);
4901 builder()->Bind(switch_jump_table, JSGeneratorObject::kThrow);
4902 {
4903 const AstRawString* throw_string =
4904 ast_string_constants()->throw_string();
4905 BytecodeLabels no_throw_method(zone());
4906 BuildCallIteratorMethod(iterator.object(), throw_string,
4907 iterator_and_input, after_switch.New(),
4908 &no_throw_method);
4909
4910 // If there is no "throw" method, perform IteratorClose, and finally
4911 // throw a TypeError.
4912 no_throw_method.Bind(builder());
4913 BuildIteratorClose(iterator, expr);
4914 builder()->CallRuntime(Runtime::kThrowThrowMethodMissing);
4915 }
4916
4917 after_switch.Bind(builder());
4918 }
4919
4920 if (iterator_type == IteratorType::kAsync) {
4921 // Await the result of the method invocation.
4922 BuildAwait(expr->position());
4923 }
4924
4925 // Check that output is an object.
4926 BytecodeLabel check_if_done;
4927 builder()
4928 ->StoreAccumulatorInRegister(output)
4929 .JumpIfJSReceiver(&check_if_done)
4930 .CallRuntime(Runtime::kThrowIteratorResultNotAnObject, output);
4931
4932 builder()->Bind(&check_if_done);
4933 // Break once output.done is true.
4934 builder()->LoadNamedProperty(
4935 output, ast_string_constants()->done_string(),
4936 feedback_index(feedback_spec()->AddLoadICSlot()));
4937
4938 loop_builder.BreakIfTrue(ToBooleanMode::kConvertToBoolean);
4939
4940 // Suspend the current generator.
4941 if (iterator_type == IteratorType::kNormal) {
4942 builder()->LoadAccumulatorWithRegister(output);
4943 } else {
4944 RegisterAllocationScope inner_register_scope(this);
4945 DCHECK_EQ(iterator_type, IteratorType::kAsync);
4946 // If generatorKind is async, perform AsyncGeneratorYield(output.value),
4947 // which will await `output.value` before resolving the current
4948 // AsyncGeneratorRequest's promise.
4949 builder()->LoadNamedProperty(
4950 output, ast_string_constants()->value_string(),
4951 feedback_index(feedback_spec()->AddLoadICSlot()));
4952
4953 RegisterList args = register_allocator()->NewRegisterList(3);
4954 builder()
4955 ->MoveRegister(generator_object(), args[0]) // generator
4956 .StoreAccumulatorInRegister(args[1]) // value
4957 .LoadBoolean(catch_prediction() != HandlerTable::ASYNC_AWAIT)
4958 .StoreAccumulatorInRegister(args[2]) // is_caught
4959 .CallRuntime(Runtime::kInlineAsyncGeneratorYield, args);
4960 }
4961
4962 BuildSuspendPoint(expr->position());
4963 builder()->StoreAccumulatorInRegister(input);
4964 builder()
4965 ->CallRuntime(Runtime::kInlineGeneratorGetResumeMode,
4966 generator_object())
4967 .StoreAccumulatorInRegister(resume_mode);
4968
4969 loop_builder.BindContinueTarget();
4970 }
4971 }
4972
4973 // Decide if we trigger a return or if the yield* expression should just
4974 // produce a value.
4975 BytecodeLabel completion_is_output_value;
4976 Register output_value = register_allocator()->NewRegister();
4977 builder()
4978 ->LoadNamedProperty(output, ast_string_constants()->value_string(),
4979 feedback_index(feedback_spec()->AddLoadICSlot()))
4980 .StoreAccumulatorInRegister(output_value)
4981 .LoadLiteral(Smi::FromInt(JSGeneratorObject::kReturn))
4982 .CompareReference(resume_mode)
4983 .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &completion_is_output_value)
4984 .LoadAccumulatorWithRegister(output_value);
4985 if (iterator_type == IteratorType::kAsync) {
4986 execution_control()->AsyncReturnAccumulator(kNoSourcePosition);
4987 } else {
4988 execution_control()->ReturnAccumulator(kNoSourcePosition);
4989 }
4990
4991 builder()->Bind(&completion_is_output_value);
4992 BuildIncrementBlockCoverageCounterIfEnabled(expr,
4993 SourceRangeKind::kContinuation);
4994 builder()->LoadAccumulatorWithRegister(output_value);
4995 }
4996
BuildAwait(int position)4997 void BytecodeGenerator::BuildAwait(int position) {
4998 // Rather than HandlerTable::UNCAUGHT, async functions use
4999 // HandlerTable::ASYNC_AWAIT to communicate that top-level exceptions are
5000 // transformed into promise rejections. This is necessary to prevent emitting
5001 // multiple debug events for the same uncaught exception. There is no point
5002 // in the body of an async function where catch prediction is
5003 // HandlerTable::UNCAUGHT.
5004 DCHECK(catch_prediction() != HandlerTable::UNCAUGHT ||
5005 info()->scope()->is_repl_mode_scope());
5006
5007 {
5008 // Await(operand) and suspend.
5009 RegisterAllocationScope register_scope(this);
5010
5011 Runtime::FunctionId await_intrinsic_id;
5012 if (IsAsyncGeneratorFunction(function_kind())) {
5013 await_intrinsic_id = catch_prediction() == HandlerTable::ASYNC_AWAIT
5014 ? Runtime::kInlineAsyncGeneratorAwaitUncaught
5015 : Runtime::kInlineAsyncGeneratorAwaitCaught;
5016 } else {
5017 await_intrinsic_id = catch_prediction() == HandlerTable::ASYNC_AWAIT
5018 ? Runtime::kInlineAsyncFunctionAwaitUncaught
5019 : Runtime::kInlineAsyncFunctionAwaitCaught;
5020 }
5021 RegisterList args = register_allocator()->NewRegisterList(2);
5022 builder()
5023 ->MoveRegister(generator_object(), args[0])
5024 .StoreAccumulatorInRegister(args[1])
5025 .CallRuntime(await_intrinsic_id, args);
5026 }
5027
5028 BuildSuspendPoint(position);
5029
5030 Register input = register_allocator()->NewRegister();
5031 Register resume_mode = register_allocator()->NewRegister();
5032
5033 // Now dispatch on resume mode.
5034 BytecodeLabel resume_next;
5035 builder()
5036 ->StoreAccumulatorInRegister(input)
5037 .CallRuntime(Runtime::kInlineGeneratorGetResumeMode, generator_object())
5038 .StoreAccumulatorInRegister(resume_mode)
5039 .LoadLiteral(Smi::FromInt(JSGeneratorObject::kNext))
5040 .CompareReference(resume_mode)
5041 .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &resume_next);
5042
5043 // Resume with "throw" completion (rethrow the received value).
5044 // TODO(leszeks): Add a debug-only check that the accumulator is
5045 // JSGeneratorObject::kThrow.
5046 builder()->LoadAccumulatorWithRegister(input).ReThrow();
5047
5048 // Resume with next.
5049 builder()->Bind(&resume_next);
5050 builder()->LoadAccumulatorWithRegister(input);
5051 }
5052
VisitAwait(Await * expr)5053 void BytecodeGenerator::VisitAwait(Await* expr) {
5054 builder()->SetExpressionPosition(expr);
5055 VisitForAccumulatorValue(expr->expression());
5056 BuildAwait(expr->position());
5057 BuildIncrementBlockCoverageCounterIfEnabled(expr,
5058 SourceRangeKind::kContinuation);
5059 }
5060
VisitThrow(Throw * expr)5061 void BytecodeGenerator::VisitThrow(Throw* expr) {
5062 AllocateBlockCoverageSlotIfEnabled(expr, SourceRangeKind::kContinuation);
5063 VisitForAccumulatorValue(expr->exception());
5064 builder()->SetExpressionPosition(expr);
5065 builder()->Throw();
5066 }
5067
VisitPropertyLoad(Register obj,Property * property)5068 void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
5069 if (property->is_optional_chain_link()) {
5070 DCHECK_NOT_NULL(optional_chaining_null_labels_);
5071 int right_range =
5072 AllocateBlockCoverageSlotIfEnabled(property, SourceRangeKind::kRight);
5073 builder()->LoadAccumulatorWithRegister(obj).JumpIfUndefinedOrNull(
5074 optional_chaining_null_labels_->New());
5075 BuildIncrementBlockCoverageCounterIfEnabled(right_range);
5076 }
5077
5078 AssignType property_kind = Property::GetAssignType(property);
5079
5080 switch (property_kind) {
5081 case NON_PROPERTY:
5082 UNREACHABLE();
5083 case NAMED_PROPERTY: {
5084 builder()->SetExpressionPosition(property);
5085 const AstRawString* name =
5086 property->key()->AsLiteral()->AsRawPropertyName();
5087 BuildLoadNamedProperty(property->obj(), obj, name);
5088 break;
5089 }
5090 case KEYED_PROPERTY: {
5091 VisitForAccumulatorValue(property->key());
5092 builder()->SetExpressionPosition(property);
5093 builder()->LoadKeyedProperty(
5094 obj, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
5095 break;
5096 }
5097 case NAMED_SUPER_PROPERTY:
5098 VisitNamedSuperPropertyLoad(property, Register::invalid_value());
5099 break;
5100 case KEYED_SUPER_PROPERTY:
5101 VisitKeyedSuperPropertyLoad(property, Register::invalid_value());
5102 break;
5103 case PRIVATE_SETTER_ONLY: {
5104 BuildPrivateBrandCheck(property, obj);
5105 BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
5106 property);
5107 break;
5108 }
5109 case PRIVATE_GETTER_ONLY:
5110 case PRIVATE_GETTER_AND_SETTER: {
5111 Register key = VisitForRegisterValue(property->key());
5112 BuildPrivateBrandCheck(property, obj);
5113 BuildPrivateGetterAccess(obj, key);
5114 break;
5115 }
5116 case PRIVATE_METHOD: {
5117 BuildPrivateBrandCheck(property, obj);
5118 // In the case of private methods, property->key() is the function to be
5119 // loaded (stored in a context slot), so load this directly.
5120 VisitForAccumulatorValue(property->key());
5121 break;
5122 }
5123 }
5124 }
5125
BuildPrivateGetterAccess(Register object,Register accessor_pair)5126 void BytecodeGenerator::BuildPrivateGetterAccess(Register object,
5127 Register accessor_pair) {
5128 RegisterAllocationScope scope(this);
5129 Register accessor = register_allocator()->NewRegister();
5130 RegisterList args = register_allocator()->NewRegisterList(1);
5131
5132 builder()
5133 ->CallRuntime(Runtime::kLoadPrivateGetter, accessor_pair)
5134 .StoreAccumulatorInRegister(accessor)
5135 .MoveRegister(object, args[0])
5136 .CallProperty(accessor, args,
5137 feedback_index(feedback_spec()->AddCallICSlot()));
5138 }
5139
BuildPrivateSetterAccess(Register object,Register accessor_pair,Register value)5140 void BytecodeGenerator::BuildPrivateSetterAccess(Register object,
5141 Register accessor_pair,
5142 Register value) {
5143 RegisterAllocationScope scope(this);
5144 Register accessor = register_allocator()->NewRegister();
5145 RegisterList args = register_allocator()->NewRegisterList(2);
5146
5147 builder()
5148 ->CallRuntime(Runtime::kLoadPrivateSetter, accessor_pair)
5149 .StoreAccumulatorInRegister(accessor)
5150 .MoveRegister(object, args[0])
5151 .MoveRegister(value, args[1])
5152 .CallProperty(accessor, args,
5153 feedback_index(feedback_spec()->AddCallICSlot()));
5154 }
5155
BuildPrivateMethodIn(Variable * private_name,Expression * object_expression)5156 void BytecodeGenerator::BuildPrivateMethodIn(Variable* private_name,
5157 Expression* object_expression) {
5158 DCHECK(IsPrivateMethodOrAccessorVariableMode(private_name->mode()));
5159 ClassScope* scope = private_name->scope()->AsClassScope();
5160 if (private_name->is_static()) {
5161 // For static private methods, "#privatemethod in ..." only returns true for
5162 // the class constructor.
5163 if (scope->class_variable() == nullptr) {
5164 // Can only happen via the debugger. See comment in
5165 // BuildPrivateBrandCheck.
5166 RegisterAllocationScope register_scope(this);
5167 RegisterList args = register_allocator()->NewRegisterList(2);
5168 builder()
5169 ->LoadLiteral(Smi::FromEnum(
5170 MessageTemplate::
5171 kInvalidUnusedPrivateStaticMethodAccessedByDebugger))
5172 .StoreAccumulatorInRegister(args[0])
5173 .LoadLiteral(private_name->raw_name())
5174 .StoreAccumulatorInRegister(args[1])
5175 .CallRuntime(Runtime::kNewError, args)
5176 .Throw();
5177 } else {
5178 VisitForAccumulatorValue(object_expression);
5179 Register object = register_allocator()->NewRegister();
5180 builder()->StoreAccumulatorInRegister(object);
5181
5182 BytecodeLabel is_object;
5183 builder()->JumpIfJSReceiver(&is_object);
5184
5185 RegisterList args = register_allocator()->NewRegisterList(3);
5186 builder()
5187 ->StoreAccumulatorInRegister(args[2])
5188 .LoadLiteral(Smi::FromEnum(MessageTemplate::kInvalidInOperatorUse))
5189 .StoreAccumulatorInRegister(args[0])
5190 .LoadLiteral(private_name->raw_name())
5191 .StoreAccumulatorInRegister(args[1])
5192 .CallRuntime(Runtime::kNewTypeError, args)
5193 .Throw();
5194
5195 builder()->Bind(&is_object);
5196 BuildVariableLoadForAccumulatorValue(scope->class_variable(),
5197 HoleCheckMode::kElided);
5198 builder()->CompareReference(object);
5199 }
5200 } else {
5201 BuildVariableLoadForAccumulatorValue(scope->brand(),
5202 HoleCheckMode::kElided);
5203 Register brand = register_allocator()->NewRegister();
5204 builder()->StoreAccumulatorInRegister(brand);
5205
5206 VisitForAccumulatorValue(object_expression);
5207 builder()->SetExpressionPosition(object_expression);
5208
5209 FeedbackSlot slot = feedback_spec()->AddKeyedHasICSlot();
5210 builder()->CompareOperation(Token::IN, brand, feedback_index(slot));
5211 execution_result()->SetResultIsBoolean();
5212 }
5213 }
5214
BuildPrivateBrandCheck(Property * property,Register object)5215 void BytecodeGenerator::BuildPrivateBrandCheck(Property* property,
5216 Register object) {
5217 Variable* private_name = property->key()->AsVariableProxy()->var();
5218 DCHECK(IsPrivateMethodOrAccessorVariableMode(private_name->mode()));
5219 ClassScope* scope = private_name->scope()->AsClassScope();
5220 builder()->SetExpressionPosition(property);
5221 if (private_name->is_static()) {
5222 // For static private methods, the only valid receiver is the class.
5223 // Load the class constructor.
5224 if (scope->class_variable() == nullptr) {
5225 // If the static private method has not been used used in source
5226 // code (either explicitly or through the presence of eval), but is
5227 // accessed by the debugger at runtime, reference to the class variable
5228 // is not available since it was not be context-allocated. Therefore we
5229 // can't build a branch check, and throw an ReferenceError as if the
5230 // method was optimized away.
5231 // TODO(joyee): get a reference to the class constructor through
5232 // something other than scope->class_variable() in this scenario.
5233 RegisterAllocationScope register_scope(this);
5234 RegisterList args = register_allocator()->NewRegisterList(2);
5235 builder()
5236 ->LoadLiteral(Smi::FromEnum(
5237 MessageTemplate::
5238 kInvalidUnusedPrivateStaticMethodAccessedByDebugger))
5239 .StoreAccumulatorInRegister(args[0])
5240 .LoadLiteral(private_name->raw_name())
5241 .StoreAccumulatorInRegister(args[1])
5242 .CallRuntime(Runtime::kNewError, args)
5243 .Throw();
5244 } else {
5245 BuildVariableLoadForAccumulatorValue(scope->class_variable(),
5246 HoleCheckMode::kElided);
5247 BytecodeLabel return_check;
5248 builder()->CompareReference(object).JumpIfTrue(
5249 ToBooleanMode::kAlreadyBoolean, &return_check);
5250 const AstRawString* name = scope->class_variable()->raw_name();
5251 RegisterAllocationScope register_scope(this);
5252 RegisterList args = register_allocator()->NewRegisterList(2);
5253 builder()
5254 ->LoadLiteral(
5255 Smi::FromEnum(MessageTemplate::kInvalidPrivateBrandStatic))
5256 .StoreAccumulatorInRegister(args[0])
5257 .LoadLiteral(name)
5258 .StoreAccumulatorInRegister(args[1])
5259 .CallRuntime(Runtime::kNewTypeError, args)
5260 .Throw();
5261 builder()->Bind(&return_check);
5262 }
5263 } else {
5264 BuildVariableLoadForAccumulatorValue(scope->brand(),
5265 HoleCheckMode::kElided);
5266 builder()->LoadKeyedProperty(
5267 object, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
5268 }
5269 }
5270
VisitPropertyLoadForRegister(Register obj,Property * expr,Register destination)5271 void BytecodeGenerator::VisitPropertyLoadForRegister(Register obj,
5272 Property* expr,
5273 Register destination) {
5274 ValueResultScope result_scope(this);
5275 VisitPropertyLoad(obj, expr);
5276 builder()->StoreAccumulatorInRegister(destination);
5277 }
5278
VisitNamedSuperPropertyLoad(Property * property,Register opt_receiver_out)5279 void BytecodeGenerator::VisitNamedSuperPropertyLoad(Property* property,
5280 Register opt_receiver_out) {
5281 RegisterAllocationScope register_scope(this);
5282 if (FLAG_super_ic) {
5283 Register receiver = register_allocator()->NewRegister();
5284 BuildThisVariableLoad();
5285 builder()->StoreAccumulatorInRegister(receiver);
5286 BuildVariableLoad(
5287 property->obj()->AsSuperPropertyReference()->home_object()->var(),
5288 HoleCheckMode::kElided);
5289 builder()->SetExpressionPosition(property);
5290 auto name = property->key()->AsLiteral()->AsRawPropertyName();
5291 FeedbackSlot slot = GetCachedLoadSuperICSlot(name);
5292 builder()->LoadNamedPropertyFromSuper(receiver, name, feedback_index(slot));
5293 if (opt_receiver_out.is_valid()) {
5294 builder()->MoveRegister(receiver, opt_receiver_out);
5295 }
5296 } else {
5297 RegisterList args = register_allocator()->NewRegisterList(3);
5298 BuildThisVariableLoad();
5299 builder()->StoreAccumulatorInRegister(args[0]);
5300 BuildVariableLoad(
5301 property->obj()->AsSuperPropertyReference()->home_object()->var(),
5302 HoleCheckMode::kElided);
5303 builder()->StoreAccumulatorInRegister(args[1]);
5304 builder()->SetExpressionPosition(property);
5305 builder()
5306 ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
5307 .StoreAccumulatorInRegister(args[2])
5308 .CallRuntime(Runtime::kLoadFromSuper, args);
5309
5310 if (opt_receiver_out.is_valid()) {
5311 builder()->MoveRegister(args[0], opt_receiver_out);
5312 }
5313 }
5314 }
5315
VisitKeyedSuperPropertyLoad(Property * property,Register opt_receiver_out)5316 void BytecodeGenerator::VisitKeyedSuperPropertyLoad(Property* property,
5317 Register opt_receiver_out) {
5318 RegisterAllocationScope register_scope(this);
5319 RegisterList args = register_allocator()->NewRegisterList(3);
5320 BuildThisVariableLoad();
5321 builder()->StoreAccumulatorInRegister(args[0]);
5322 BuildVariableLoad(
5323 property->obj()->AsSuperPropertyReference()->home_object()->var(),
5324 HoleCheckMode::kElided);
5325 builder()->StoreAccumulatorInRegister(args[1]);
5326 VisitForRegisterValue(property->key(), args[2]);
5327
5328 builder()->SetExpressionPosition(property);
5329 builder()->CallRuntime(Runtime::kLoadKeyedFromSuper, args);
5330
5331 if (opt_receiver_out.is_valid()) {
5332 builder()->MoveRegister(args[0], opt_receiver_out);
5333 }
5334 }
5335
5336 template <typename ExpressionFunc>
BuildOptionalChain(ExpressionFunc expression_func)5337 void BytecodeGenerator::BuildOptionalChain(ExpressionFunc expression_func) {
5338 BytecodeLabel done;
5339 OptionalChainNullLabelScope label_scope(this);
5340 expression_func();
5341 builder()->Jump(&done);
5342 label_scope.labels()->Bind(builder());
5343 builder()->LoadUndefined();
5344 builder()->Bind(&done);
5345 }
5346
VisitOptionalChain(OptionalChain * expr)5347 void BytecodeGenerator::VisitOptionalChain(OptionalChain* expr) {
5348 BuildOptionalChain([&]() { VisitForAccumulatorValue(expr->expression()); });
5349 }
5350
VisitProperty(Property * expr)5351 void BytecodeGenerator::VisitProperty(Property* expr) {
5352 AssignType property_kind = Property::GetAssignType(expr);
5353 if (property_kind != NAMED_SUPER_PROPERTY &&
5354 property_kind != KEYED_SUPER_PROPERTY) {
5355 Register obj = VisitForRegisterValue(expr->obj());
5356 VisitPropertyLoad(obj, expr);
5357 } else {
5358 VisitPropertyLoad(Register::invalid_value(), expr);
5359 }
5360 }
5361
VisitArguments(const ZonePtrList<Expression> * args,RegisterList * arg_regs)5362 void BytecodeGenerator::VisitArguments(const ZonePtrList<Expression>* args,
5363 RegisterList* arg_regs) {
5364 // Visit arguments.
5365 for (int i = 0; i < static_cast<int>(args->length()); i++) {
5366 VisitAndPushIntoRegisterList(args->at(i), arg_regs);
5367 }
5368 }
5369
VisitCall(Call * expr)5370 void BytecodeGenerator::VisitCall(Call* expr) {
5371 Expression* callee_expr = expr->expression();
5372 Call::CallType call_type = expr->GetCallType();
5373
5374 if (call_type == Call::SUPER_CALL) {
5375 return VisitCallSuper(expr);
5376 }
5377
5378 // We compile the call differently depending on the presence of spreads and
5379 // their positions.
5380 //
5381 // If there is only one spread and it is the final argument, there is a
5382 // special CallWithSpread bytecode.
5383 //
5384 // If there is a non-final spread, we rewrite calls like
5385 // callee(1, ...x, 2)
5386 // to
5387 // %reflect_apply(callee, receiver, [1, ...x, 2])
5388 const Call::SpreadPosition spread_position = expr->spread_position();
5389
5390 // Grow the args list as we visit receiver / arguments to avoid allocating all
5391 // the registers up-front. Otherwise these registers are unavailable during
5392 // receiver / argument visiting and we can end up with memory leaks due to
5393 // registers keeping objects alive.
5394 RegisterList args = register_allocator()->NewGrowableRegisterList();
5395
5396 // The callee is the first register in args for ease of calling %reflect_apply
5397 // if we have a non-final spread. For all other cases it is popped from args
5398 // before emitting the call below.
5399 Register callee = register_allocator()->GrowRegisterList(&args);
5400
5401 bool implicit_undefined_receiver = false;
5402
5403 // TODO(petermarshall): We have a lot of call bytecodes that are very similar,
5404 // see if we can reduce the number by adding a separate argument which
5405 // specifies the call type (e.g., property, spread, tailcall, etc.).
5406
5407 // Prepare the callee and the receiver to the function call. This depends on
5408 // the semantics of the underlying call type.
5409 switch (call_type) {
5410 case Call::NAMED_PROPERTY_CALL:
5411 case Call::KEYED_PROPERTY_CALL:
5412 case Call::PRIVATE_CALL: {
5413 Property* property = callee_expr->AsProperty();
5414 VisitAndPushIntoRegisterList(property->obj(), &args);
5415 VisitPropertyLoadForRegister(args.last_register(), property, callee);
5416 break;
5417 }
5418 case Call::GLOBAL_CALL: {
5419 // Receiver is undefined for global calls.
5420 if (spread_position == Call::kNoSpread) {
5421 implicit_undefined_receiver = true;
5422 } else {
5423 // TODO(leszeks): There's no special bytecode for tail calls or spread
5424 // calls with an undefined receiver, so just push undefined ourselves.
5425 BuildPushUndefinedIntoRegisterList(&args);
5426 }
5427 // Load callee as a global variable.
5428 VariableProxy* proxy = callee_expr->AsVariableProxy();
5429 BuildVariableLoadForAccumulatorValue(proxy->var(),
5430 proxy->hole_check_mode());
5431 builder()->StoreAccumulatorInRegister(callee);
5432 break;
5433 }
5434 case Call::WITH_CALL: {
5435 Register receiver = register_allocator()->GrowRegisterList(&args);
5436 DCHECK(callee_expr->AsVariableProxy()->var()->IsLookupSlot());
5437 {
5438 RegisterAllocationScope inner_register_scope(this);
5439 Register name = register_allocator()->NewRegister();
5440
5441 // Call %LoadLookupSlotForCall to get the callee and receiver.
5442 RegisterList result_pair = register_allocator()->NewRegisterList(2);
5443 Variable* variable = callee_expr->AsVariableProxy()->var();
5444 builder()
5445 ->LoadLiteral(variable->raw_name())
5446 .StoreAccumulatorInRegister(name)
5447 .CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, name,
5448 result_pair)
5449 .MoveRegister(result_pair[0], callee)
5450 .MoveRegister(result_pair[1], receiver);
5451 }
5452 break;
5453 }
5454 case Call::OTHER_CALL: {
5455 // Receiver is undefined for other calls.
5456 if (spread_position == Call::kNoSpread) {
5457 implicit_undefined_receiver = true;
5458 } else {
5459 // TODO(leszeks): There's no special bytecode for tail calls or spread
5460 // calls with an undefined receiver, so just push undefined ourselves.
5461 BuildPushUndefinedIntoRegisterList(&args);
5462 }
5463 VisitForRegisterValue(callee_expr, callee);
5464 break;
5465 }
5466 case Call::NAMED_SUPER_PROPERTY_CALL: {
5467 Register receiver = register_allocator()->GrowRegisterList(&args);
5468 Property* property = callee_expr->AsProperty();
5469 VisitNamedSuperPropertyLoad(property, receiver);
5470 builder()->StoreAccumulatorInRegister(callee);
5471 break;
5472 }
5473 case Call::KEYED_SUPER_PROPERTY_CALL: {
5474 Register receiver = register_allocator()->GrowRegisterList(&args);
5475 Property* property = callee_expr->AsProperty();
5476 VisitKeyedSuperPropertyLoad(property, receiver);
5477 builder()->StoreAccumulatorInRegister(callee);
5478 break;
5479 }
5480 case Call::NAMED_OPTIONAL_CHAIN_PROPERTY_CALL:
5481 case Call::KEYED_OPTIONAL_CHAIN_PROPERTY_CALL:
5482 case Call::PRIVATE_OPTIONAL_CHAIN_CALL: {
5483 OptionalChain* chain = callee_expr->AsOptionalChain();
5484 Property* property = chain->expression()->AsProperty();
5485 BuildOptionalChain([&]() {
5486 VisitAndPushIntoRegisterList(property->obj(), &args);
5487 VisitPropertyLoad(args.last_register(), property);
5488 });
5489 builder()->StoreAccumulatorInRegister(callee);
5490 break;
5491 }
5492 case Call::SUPER_CALL:
5493 UNREACHABLE();
5494 }
5495
5496 if (expr->is_optional_chain_link()) {
5497 DCHECK_NOT_NULL(optional_chaining_null_labels_);
5498 int right_range =
5499 AllocateBlockCoverageSlotIfEnabled(expr, SourceRangeKind::kRight);
5500 builder()->LoadAccumulatorWithRegister(callee).JumpIfUndefinedOrNull(
5501 optional_chaining_null_labels_->New());
5502 BuildIncrementBlockCoverageCounterIfEnabled(right_range);
5503 }
5504
5505 int receiver_arg_count = -1;
5506 if (spread_position == Call::kHasNonFinalSpread) {
5507 // If we're building %reflect_apply, build the array literal and put it in
5508 // the 3rd argument.
5509 DCHECK(!implicit_undefined_receiver);
5510 DCHECK_EQ(args.register_count(), 2);
5511 BuildCreateArrayLiteral(expr->arguments(), nullptr);
5512 builder()->StoreAccumulatorInRegister(
5513 register_allocator()->GrowRegisterList(&args));
5514 } else {
5515 // If we're not building %reflect_apply and don't need to build an array
5516 // literal, pop the callee and evaluate all arguments to the function call
5517 // and store in sequential args registers.
5518 args = args.PopLeft();
5519 VisitArguments(expr->arguments(), &args);
5520 receiver_arg_count = implicit_undefined_receiver ? 0 : 1;
5521 CHECK_EQ(receiver_arg_count + expr->arguments()->length(),
5522 args.register_count());
5523 }
5524
5525 // Resolve callee for a potential direct eval call. This block will mutate the
5526 // callee value.
5527 if (expr->is_possibly_eval() && expr->arguments()->length() > 0) {
5528 RegisterAllocationScope inner_register_scope(this);
5529 RegisterList runtime_call_args = register_allocator()->NewRegisterList(6);
5530 // Set up arguments for ResolvePossiblyDirectEval by copying callee, source
5531 // strings and function closure, and loading language and
5532 // position.
5533
5534 // Move the first arg.
5535 if (spread_position == Call::kHasNonFinalSpread) {
5536 int feedback_slot_index =
5537 feedback_index(feedback_spec()->AddKeyedLoadICSlot());
5538 Register args_array = args[2];
5539 builder()
5540 ->LoadLiteral(Smi::FromInt(0))
5541 .LoadKeyedProperty(args_array, feedback_slot_index)
5542 .StoreAccumulatorInRegister(runtime_call_args[1]);
5543 } else {
5544 // FIXME(v8:5690): Support final spreads for eval.
5545 DCHECK_GE(receiver_arg_count, 0);
5546 builder()->MoveRegister(args[receiver_arg_count], runtime_call_args[1]);
5547 }
5548 builder()
5549 ->MoveRegister(callee, runtime_call_args[0])
5550 .MoveRegister(Register::function_closure(), runtime_call_args[2])
5551 .LoadLiteral(Smi::FromEnum(language_mode()))
5552 .StoreAccumulatorInRegister(runtime_call_args[3])
5553 .LoadLiteral(Smi::FromInt(current_scope()->start_position()))
5554 .StoreAccumulatorInRegister(runtime_call_args[4])
5555 .LoadLiteral(Smi::FromInt(expr->position()))
5556 .StoreAccumulatorInRegister(runtime_call_args[5]);
5557
5558 // Call ResolvePossiblyDirectEval and modify the callee.
5559 builder()
5560 ->CallRuntime(Runtime::kResolvePossiblyDirectEval, runtime_call_args)
5561 .StoreAccumulatorInRegister(callee);
5562 }
5563
5564 builder()->SetExpressionPosition(expr);
5565
5566 if (spread_position == Call::kHasFinalSpread) {
5567 DCHECK(!implicit_undefined_receiver);
5568 builder()->CallWithSpread(callee, args,
5569 feedback_index(feedback_spec()->AddCallICSlot()));
5570 } else if (spread_position == Call::kHasNonFinalSpread) {
5571 builder()->CallJSRuntime(Context::REFLECT_APPLY_INDEX, args);
5572 } else if (call_type == Call::NAMED_PROPERTY_CALL ||
5573 call_type == Call::KEYED_PROPERTY_CALL) {
5574 DCHECK(!implicit_undefined_receiver);
5575 builder()->CallProperty(callee, args,
5576 feedback_index(feedback_spec()->AddCallICSlot()));
5577 } else if (implicit_undefined_receiver) {
5578 builder()->CallUndefinedReceiver(
5579 callee, args, feedback_index(feedback_spec()->AddCallICSlot()));
5580 } else {
5581 builder()->CallAnyReceiver(
5582 callee, args, feedback_index(feedback_spec()->AddCallICSlot()));
5583 }
5584 }
5585
VisitCallSuper(Call * expr)5586 void BytecodeGenerator::VisitCallSuper(Call* expr) {
5587 RegisterAllocationScope register_scope(this);
5588 SuperCallReference* super = expr->expression()->AsSuperCallReference();
5589 const ZonePtrList<Expression>* args = expr->arguments();
5590
5591 // We compile the super call differently depending on the presence of spreads
5592 // and their positions.
5593 //
5594 // If there is only one spread and it is the final argument, there is a
5595 // special ConstructWithSpread bytecode.
5596 //
5597 // It there is a non-final spread, we rewrite something like
5598 // super(1, ...x, 2)
5599 // to
5600 // %reflect_construct(constructor, [1, ...x, 2], new_target)
5601 //
5602 // That is, we implement (non-last-arg) spreads in super calls via our
5603 // mechanism for spreads in array literals.
5604 const Call::SpreadPosition spread_position = expr->spread_position();
5605
5606 // Prepare the constructor to the super call.
5607 Register this_function = VisitForRegisterValue(super->this_function_var());
5608 Register constructor = register_allocator()->NewRegister();
5609 builder()
5610 ->LoadAccumulatorWithRegister(this_function)
5611 .GetSuperConstructor(constructor);
5612
5613 if (spread_position == Call::kHasNonFinalSpread) {
5614 // First generate the array containing all arguments.
5615 BuildCreateArrayLiteral(args, nullptr);
5616
5617 // Check if the constructor is in fact a constructor.
5618 builder()->ThrowIfNotSuperConstructor(constructor);
5619
5620 // Now pass that array to %reflect_construct.
5621 RegisterList construct_args = register_allocator()->NewRegisterList(3);
5622 builder()->StoreAccumulatorInRegister(construct_args[1]);
5623 builder()->MoveRegister(constructor, construct_args[0]);
5624 VisitForRegisterValue(super->new_target_var(), construct_args[2]);
5625 builder()->CallJSRuntime(Context::REFLECT_CONSTRUCT_INDEX, construct_args);
5626 } else {
5627 RegisterList args_regs = register_allocator()->NewGrowableRegisterList();
5628 VisitArguments(args, &args_regs);
5629
5630 // Check if the constructor is in fact a constructor.
5631 builder()->ThrowIfNotSuperConstructor(constructor);
5632
5633 // The new target is loaded into the accumulator from the
5634 // {new.target} variable.
5635 VisitForAccumulatorValue(super->new_target_var());
5636 builder()->SetExpressionPosition(expr);
5637
5638 int feedback_slot_index = feedback_index(feedback_spec()->AddCallICSlot());
5639
5640 if (spread_position == Call::kHasFinalSpread) {
5641 builder()->ConstructWithSpread(constructor, args_regs,
5642 feedback_slot_index);
5643 } else {
5644 DCHECK_EQ(spread_position, Call::kNoSpread);
5645 // Call construct.
5646 // TODO(turbofan): For now we do gather feedback on super constructor
5647 // calls, utilizing the existing machinery to inline the actual call
5648 // target and the JSCreate for the implicit receiver allocation. This
5649 // is not an ideal solution for super constructor calls, but it gets
5650 // the job done for now. In the long run we might want to revisit this
5651 // and come up with a better way.
5652 builder()->Construct(constructor, args_regs, feedback_slot_index);
5653 }
5654 }
5655
5656 // Explicit calls to the super constructor using super() perform an
5657 // implicit binding assignment to the 'this' variable.
5658 //
5659 // Default constructors don't need have to do the assignment because
5660 // 'this' isn't accessed in default constructors.
5661 if (!IsDefaultConstructor(info()->literal()->kind())) {
5662 Variable* var = closure_scope()->GetReceiverScope()->receiver();
5663 BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kRequired);
5664 }
5665
5666 Register instance = register_allocator()->NewRegister();
5667 builder()->StoreAccumulatorInRegister(instance);
5668
5669 // The constructor scope always needs ScopeInfo, so we are certain that
5670 // the first constructor scope found in the outer scope chain is the
5671 // scope that we are looking for for this super() call.
5672 // Note that this doesn't necessarily mean that the constructor needs
5673 // a context, if it doesn't this would get handled specially in
5674 // BuildPrivateBrandInitialization().
5675 DeclarationScope* constructor_scope = info()->scope()->GetConstructorScope();
5676
5677 // We can rely on the class_scope_has_private_brand bit to tell if the
5678 // constructor needs private brand initialization, and if that's
5679 // the case we are certain that its outer class scope requires a context to
5680 // keep the brand variable, so we can just get the brand variable
5681 // from the outer scope.
5682 if (constructor_scope->class_scope_has_private_brand()) {
5683 DCHECK(constructor_scope->outer_scope()->is_class_scope());
5684 ClassScope* class_scope = constructor_scope->outer_scope()->AsClassScope();
5685 DCHECK_NOT_NULL(class_scope->brand());
5686 Variable* brand = class_scope->brand();
5687 BuildPrivateBrandInitialization(instance, brand);
5688 }
5689
5690 // The derived constructor has the correct bit set always, so we
5691 // don't emit code to load and call the initializer if not
5692 // required.
5693 //
5694 // For the arrow function or eval case, we always emit code to load
5695 // and call the initializer.
5696 //
5697 // TODO(gsathya): In the future, we could tag nested arrow functions
5698 // or eval with the correct bit so that we do the load conditionally
5699 // if required.
5700 if (info()->literal()->requires_instance_members_initializer() ||
5701 !IsDerivedConstructor(info()->literal()->kind())) {
5702 BuildInstanceMemberInitialization(this_function, instance);
5703 }
5704
5705 builder()->LoadAccumulatorWithRegister(instance);
5706 }
5707
VisitCallNew(CallNew * expr)5708 void BytecodeGenerator::VisitCallNew(CallNew* expr) {
5709 RegisterList args = register_allocator()->NewGrowableRegisterList();
5710
5711 // Load the constructor. It's in the first register in args for ease of
5712 // calling %reflect_construct if we have a non-final spread. For all other
5713 // cases it is popped before emitting the construct below.
5714 VisitAndPushIntoRegisterList(expr->expression(), &args);
5715
5716 // We compile the new differently depending on the presence of spreads and
5717 // their positions.
5718 //
5719 // If there is only one spread and it is the final argument, there is a
5720 // special ConstructWithSpread bytecode.
5721 //
5722 // If there is a non-final spread, we rewrite calls like
5723 // new ctor(1, ...x, 2)
5724 // to
5725 // %reflect_construct(ctor, [1, ...x, 2])
5726 const CallNew::SpreadPosition spread_position = expr->spread_position();
5727
5728 if (spread_position == CallNew::kHasNonFinalSpread) {
5729 BuildCreateArrayLiteral(expr->arguments(), nullptr);
5730 builder()->SetExpressionPosition(expr);
5731 builder()
5732 ->StoreAccumulatorInRegister(
5733 register_allocator()->GrowRegisterList(&args))
5734 .CallJSRuntime(Context::REFLECT_CONSTRUCT_INDEX, args);
5735 return;
5736 }
5737
5738 Register constructor = args.first_register();
5739 args = args.PopLeft();
5740 VisitArguments(expr->arguments(), &args);
5741
5742 // The accumulator holds new target which is the same as the
5743 // constructor for CallNew.
5744 builder()->SetExpressionPosition(expr);
5745 builder()->LoadAccumulatorWithRegister(constructor);
5746
5747 int feedback_slot_index = feedback_index(feedback_spec()->AddCallICSlot());
5748 if (spread_position == CallNew::kHasFinalSpread) {
5749 builder()->ConstructWithSpread(constructor, args, feedback_slot_index);
5750 } else {
5751 DCHECK_EQ(spread_position, CallNew::kNoSpread);
5752 builder()->Construct(constructor, args, feedback_slot_index);
5753 }
5754 }
5755
VisitCallRuntime(CallRuntime * expr)5756 void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
5757 if (expr->is_jsruntime()) {
5758 RegisterList args = register_allocator()->NewGrowableRegisterList();
5759 VisitArguments(expr->arguments(), &args);
5760 builder()->CallJSRuntime(expr->context_index(), args);
5761 } else {
5762 // Evaluate all arguments to the runtime call.
5763 RegisterList args = register_allocator()->NewGrowableRegisterList();
5764 VisitArguments(expr->arguments(), &args);
5765 Runtime::FunctionId function_id = expr->function()->function_id;
5766 builder()->CallRuntime(function_id, args);
5767 }
5768 }
5769
VisitVoid(UnaryOperation * expr)5770 void BytecodeGenerator::VisitVoid(UnaryOperation* expr) {
5771 VisitForEffect(expr->expression());
5772 builder()->LoadUndefined();
5773 }
5774
VisitForTypeOfValue(Expression * expr)5775 void BytecodeGenerator::VisitForTypeOfValue(Expression* expr) {
5776 if (expr->IsVariableProxy()) {
5777 // Typeof does not throw a reference error on global variables, hence we
5778 // perform a non-contextual load in case the operand is a variable proxy.
5779 VariableProxy* proxy = expr->AsVariableProxy();
5780 BuildVariableLoadForAccumulatorValue(proxy->var(), proxy->hole_check_mode(),
5781 TypeofMode::kInside);
5782 } else {
5783 VisitForAccumulatorValue(expr);
5784 }
5785 }
5786
VisitTypeOf(UnaryOperation * expr)5787 void BytecodeGenerator::VisitTypeOf(UnaryOperation* expr) {
5788 VisitForTypeOfValue(expr->expression());
5789 builder()->TypeOf();
5790 }
5791
VisitNot(UnaryOperation * expr)5792 void BytecodeGenerator::VisitNot(UnaryOperation* expr) {
5793 if (execution_result()->IsEffect()) {
5794 VisitForEffect(expr->expression());
5795 } else if (execution_result()->IsTest()) {
5796 // No actual logical negation happening, we just swap the control flow, by
5797 // swapping the target labels and the fallthrough branch, and visit in the
5798 // same test result context.
5799 TestResultScope* test_result = execution_result()->AsTest();
5800 test_result->InvertControlFlow();
5801 VisitInSameTestExecutionScope(expr->expression());
5802 } else {
5803 TypeHint type_hint = VisitForAccumulatorValue(expr->expression());
5804 builder()->LogicalNot(ToBooleanModeFromTypeHint(type_hint));
5805 // Always returns a boolean value.
5806 execution_result()->SetResultIsBoolean();
5807 }
5808 }
5809
VisitUnaryOperation(UnaryOperation * expr)5810 void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
5811 switch (expr->op()) {
5812 case Token::Value::NOT:
5813 VisitNot(expr);
5814 break;
5815 case Token::Value::TYPEOF:
5816 VisitTypeOf(expr);
5817 break;
5818 case Token::Value::VOID:
5819 VisitVoid(expr);
5820 break;
5821 case Token::Value::DELETE:
5822 VisitDelete(expr);
5823 break;
5824 case Token::Value::ADD:
5825 case Token::Value::SUB:
5826 case Token::Value::BIT_NOT:
5827 VisitForAccumulatorValue(expr->expression());
5828 builder()->SetExpressionPosition(expr);
5829 builder()->UnaryOperation(
5830 expr->op(), feedback_index(feedback_spec()->AddBinaryOpICSlot()));
5831 break;
5832 default:
5833 UNREACHABLE();
5834 }
5835 }
5836
VisitDelete(UnaryOperation * unary)5837 void BytecodeGenerator::VisitDelete(UnaryOperation* unary) {
5838 Expression* expr = unary->expression();
5839 if (expr->IsProperty()) {
5840 // Delete of an object property is allowed both in sloppy
5841 // and strict modes.
5842 Property* property = expr->AsProperty();
5843 DCHECK(!property->IsPrivateReference());
5844 Register object = VisitForRegisterValue(property->obj());
5845 VisitForAccumulatorValue(property->key());
5846 builder()->Delete(object, language_mode());
5847 } else if (expr->IsOptionalChain()) {
5848 Expression* expr_inner = expr->AsOptionalChain()->expression();
5849 if (expr_inner->IsProperty()) {
5850 Property* property = expr_inner->AsProperty();
5851 DCHECK(!property->IsPrivateReference());
5852 BytecodeLabel done;
5853 OptionalChainNullLabelScope label_scope(this);
5854 VisitForAccumulatorValue(property->obj());
5855 if (property->is_optional_chain_link()) {
5856 int right_range = AllocateBlockCoverageSlotIfEnabled(
5857 property, SourceRangeKind::kRight);
5858 builder()->JumpIfUndefinedOrNull(label_scope.labels()->New());
5859 BuildIncrementBlockCoverageCounterIfEnabled(right_range);
5860 }
5861 Register object = register_allocator()->NewRegister();
5862 builder()->StoreAccumulatorInRegister(object);
5863 VisitForAccumulatorValue(property->key());
5864 builder()->Delete(object, language_mode());
5865 builder()->Jump(&done);
5866 label_scope.labels()->Bind(builder());
5867 builder()->LoadTrue();
5868 builder()->Bind(&done);
5869 } else {
5870 VisitForEffect(expr);
5871 builder()->LoadTrue();
5872 }
5873 } else if (expr->IsVariableProxy() &&
5874 !expr->AsVariableProxy()->is_new_target()) {
5875 // Delete of an unqualified identifier is allowed in sloppy mode but is
5876 // not allowed in strict mode.
5877 DCHECK(is_sloppy(language_mode()));
5878 Variable* variable = expr->AsVariableProxy()->var();
5879 switch (variable->location()) {
5880 case VariableLocation::PARAMETER:
5881 case VariableLocation::LOCAL:
5882 case VariableLocation::CONTEXT:
5883 case VariableLocation::REPL_GLOBAL: {
5884 // Deleting local var/let/const, context variables, and arguments
5885 // does not have any effect.
5886 builder()->LoadFalse();
5887 break;
5888 }
5889 case VariableLocation::UNALLOCATED:
5890 // TODO(adamk): Falling through to the runtime results in correct
5891 // behavior, but does unnecessary context-walking (since scope
5892 // analysis has already proven that the variable doesn't exist in
5893 // any non-global scope). Consider adding a DeleteGlobal bytecode
5894 // that knows how to deal with ScriptContexts as well as global
5895 // object properties.
5896 case VariableLocation::LOOKUP: {
5897 Register name_reg = register_allocator()->NewRegister();
5898 builder()
5899 ->LoadLiteral(variable->raw_name())
5900 .StoreAccumulatorInRegister(name_reg)
5901 .CallRuntime(Runtime::kDeleteLookupSlot, name_reg);
5902 break;
5903 }
5904 case VariableLocation::MODULE:
5905 // Modules are always in strict mode and unqualified identifers are not
5906 // allowed in strict mode.
5907 UNREACHABLE();
5908 }
5909 } else {
5910 // Delete of an unresolvable reference, new.target, and this returns true.
5911 VisitForEffect(expr);
5912 builder()->LoadTrue();
5913 }
5914 }
5915
VisitCountOperation(CountOperation * expr)5916 void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
5917 DCHECK(expr->expression()->IsValidReferenceExpression());
5918
5919 // Left-hand side can only be a property, a global or a variable slot.
5920 Property* property = expr->expression()->AsProperty();
5921 AssignType assign_type = Property::GetAssignType(property);
5922
5923 bool is_postfix = expr->is_postfix() && !execution_result()->IsEffect();
5924
5925 // Evaluate LHS expression and get old value.
5926 Register object, key, old_value;
5927 RegisterList super_property_args;
5928 const AstRawString* name;
5929 switch (assign_type) {
5930 case NON_PROPERTY: {
5931 VariableProxy* proxy = expr->expression()->AsVariableProxy();
5932 BuildVariableLoadForAccumulatorValue(proxy->var(),
5933 proxy->hole_check_mode());
5934 break;
5935 }
5936 case NAMED_PROPERTY: {
5937 object = VisitForRegisterValue(property->obj());
5938 name = property->key()->AsLiteral()->AsRawPropertyName();
5939 builder()->LoadNamedProperty(
5940 object, name,
5941 feedback_index(GetCachedLoadICSlot(property->obj(), name)));
5942 break;
5943 }
5944 case KEYED_PROPERTY: {
5945 object = VisitForRegisterValue(property->obj());
5946 // Use visit for accumulator here since we need the key in the accumulator
5947 // for the LoadKeyedProperty.
5948 key = register_allocator()->NewRegister();
5949 VisitForAccumulatorValue(property->key());
5950 builder()->StoreAccumulatorInRegister(key).LoadKeyedProperty(
5951 object, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
5952 break;
5953 }
5954 case NAMED_SUPER_PROPERTY: {
5955 super_property_args = register_allocator()->NewRegisterList(4);
5956 RegisterList load_super_args = super_property_args.Truncate(3);
5957 BuildThisVariableLoad();
5958 builder()->StoreAccumulatorInRegister(load_super_args[0]);
5959 BuildVariableLoad(
5960 property->obj()->AsSuperPropertyReference()->home_object()->var(),
5961 HoleCheckMode::kElided);
5962 builder()->StoreAccumulatorInRegister(load_super_args[1]);
5963 builder()
5964 ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
5965 .StoreAccumulatorInRegister(load_super_args[2])
5966 .CallRuntime(Runtime::kLoadFromSuper, load_super_args);
5967 break;
5968 }
5969 case KEYED_SUPER_PROPERTY: {
5970 super_property_args = register_allocator()->NewRegisterList(4);
5971 RegisterList load_super_args = super_property_args.Truncate(3);
5972 BuildThisVariableLoad();
5973 builder()->StoreAccumulatorInRegister(load_super_args[0]);
5974 BuildVariableLoad(
5975 property->obj()->AsSuperPropertyReference()->home_object()->var(),
5976 HoleCheckMode::kElided);
5977 builder()->StoreAccumulatorInRegister(load_super_args[1]);
5978 VisitForRegisterValue(property->key(), load_super_args[2]);
5979 builder()->CallRuntime(Runtime::kLoadKeyedFromSuper, load_super_args);
5980 break;
5981 }
5982 case PRIVATE_METHOD: {
5983 object = VisitForRegisterValue(property->obj());
5984 BuildPrivateBrandCheck(property, object);
5985 BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
5986 property);
5987 return;
5988 }
5989 case PRIVATE_GETTER_ONLY: {
5990 object = VisitForRegisterValue(property->obj());
5991 BuildPrivateBrandCheck(property, object);
5992 BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
5993 property);
5994 return;
5995 }
5996 case PRIVATE_SETTER_ONLY: {
5997 object = VisitForRegisterValue(property->obj());
5998 BuildPrivateBrandCheck(property, object);
5999 BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
6000 property);
6001 return;
6002 }
6003 case PRIVATE_GETTER_AND_SETTER: {
6004 object = VisitForRegisterValue(property->obj());
6005 key = VisitForRegisterValue(property->key());
6006 BuildPrivateBrandCheck(property, object);
6007 BuildPrivateGetterAccess(object, key);
6008 break;
6009 }
6010 }
6011
6012 // Save result for postfix expressions.
6013 FeedbackSlot count_slot = feedback_spec()->AddBinaryOpICSlot();
6014 if (is_postfix) {
6015 old_value = register_allocator()->NewRegister();
6016 // Convert old value into a number before saving it.
6017 // TODO(ignition): Think about adding proper PostInc/PostDec bytecodes
6018 // instead of this ToNumeric + Inc/Dec dance.
6019 builder()
6020 ->ToNumeric(feedback_index(count_slot))
6021 .StoreAccumulatorInRegister(old_value);
6022 }
6023
6024 // Perform +1/-1 operation.
6025 builder()->UnaryOperation(expr->op(), feedback_index(count_slot));
6026
6027 // Store the value.
6028 builder()->SetExpressionPosition(expr);
6029 switch (assign_type) {
6030 case NON_PROPERTY: {
6031 VariableProxy* proxy = expr->expression()->AsVariableProxy();
6032 BuildVariableAssignment(proxy->var(), expr->op(),
6033 proxy->hole_check_mode());
6034 break;
6035 }
6036 case NAMED_PROPERTY: {
6037 FeedbackSlot slot = GetCachedStoreICSlot(property->obj(), name);
6038 Register value;
6039 if (!execution_result()->IsEffect()) {
6040 value = register_allocator()->NewRegister();
6041 builder()->StoreAccumulatorInRegister(value);
6042 }
6043 builder()->SetNamedProperty(object, name, feedback_index(slot),
6044 language_mode());
6045 if (!execution_result()->IsEffect()) {
6046 builder()->LoadAccumulatorWithRegister(value);
6047 }
6048 break;
6049 }
6050 case KEYED_PROPERTY: {
6051 FeedbackSlot slot = feedback_spec()->AddKeyedStoreICSlot(language_mode());
6052 Register value;
6053 if (!execution_result()->IsEffect()) {
6054 value = register_allocator()->NewRegister();
6055 builder()->StoreAccumulatorInRegister(value);
6056 }
6057 builder()->SetKeyedProperty(object, key, feedback_index(slot),
6058 language_mode());
6059 if (!execution_result()->IsEffect()) {
6060 builder()->LoadAccumulatorWithRegister(value);
6061 }
6062 break;
6063 }
6064 case NAMED_SUPER_PROPERTY: {
6065 builder()
6066 ->StoreAccumulatorInRegister(super_property_args[3])
6067 .CallRuntime(Runtime::kStoreToSuper, super_property_args);
6068 break;
6069 }
6070 case KEYED_SUPER_PROPERTY: {
6071 builder()
6072 ->StoreAccumulatorInRegister(super_property_args[3])
6073 .CallRuntime(Runtime::kStoreKeyedToSuper, super_property_args);
6074 break;
6075 }
6076 case PRIVATE_SETTER_ONLY:
6077 case PRIVATE_GETTER_ONLY:
6078 case PRIVATE_METHOD: {
6079 UNREACHABLE();
6080 }
6081 case PRIVATE_GETTER_AND_SETTER: {
6082 Register value = register_allocator()->NewRegister();
6083 builder()->StoreAccumulatorInRegister(value);
6084 BuildPrivateSetterAccess(object, key, value);
6085 if (!execution_result()->IsEffect()) {
6086 builder()->LoadAccumulatorWithRegister(value);
6087 }
6088 break;
6089 }
6090 }
6091
6092 // Restore old value for postfix expressions.
6093 if (is_postfix) {
6094 builder()->LoadAccumulatorWithRegister(old_value);
6095 }
6096 }
6097
VisitBinaryOperation(BinaryOperation * binop)6098 void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) {
6099 switch (binop->op()) {
6100 case Token::COMMA:
6101 VisitCommaExpression(binop);
6102 break;
6103 case Token::OR:
6104 VisitLogicalOrExpression(binop);
6105 break;
6106 case Token::AND:
6107 VisitLogicalAndExpression(binop);
6108 break;
6109 case Token::NULLISH:
6110 VisitNullishExpression(binop);
6111 break;
6112 default:
6113 VisitArithmeticExpression(binop);
6114 break;
6115 }
6116 }
6117
VisitNaryOperation(NaryOperation * expr)6118 void BytecodeGenerator::VisitNaryOperation(NaryOperation* expr) {
6119 switch (expr->op()) {
6120 case Token::COMMA:
6121 VisitNaryCommaExpression(expr);
6122 break;
6123 case Token::OR:
6124 VisitNaryLogicalOrExpression(expr);
6125 break;
6126 case Token::AND:
6127 VisitNaryLogicalAndExpression(expr);
6128 break;
6129 case Token::NULLISH:
6130 VisitNaryNullishExpression(expr);
6131 break;
6132 default:
6133 VisitNaryArithmeticExpression(expr);
6134 break;
6135 }
6136 }
6137
BuildLiteralCompareNil(Token::Value op,BytecodeArrayBuilder::NilValue nil)6138 void BytecodeGenerator::BuildLiteralCompareNil(
6139 Token::Value op, BytecodeArrayBuilder::NilValue nil) {
6140 if (execution_result()->IsTest()) {
6141 TestResultScope* test_result = execution_result()->AsTest();
6142 switch (test_result->fallthrough()) {
6143 case TestFallthrough::kThen:
6144 builder()->JumpIfNotNil(test_result->NewElseLabel(), op, nil);
6145 break;
6146 case TestFallthrough::kElse:
6147 builder()->JumpIfNil(test_result->NewThenLabel(), op, nil);
6148 break;
6149 case TestFallthrough::kNone:
6150 builder()
6151 ->JumpIfNil(test_result->NewThenLabel(), op, nil)
6152 .Jump(test_result->NewElseLabel());
6153 }
6154 test_result->SetResultConsumedByTest();
6155 } else {
6156 builder()->CompareNil(op, nil);
6157 }
6158 }
6159
VisitCompareOperation(CompareOperation * expr)6160 void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
6161 Expression* sub_expr;
6162 Literal* literal;
6163 if (expr->IsLiteralCompareTypeof(&sub_expr, &literal)) {
6164 // Emit a fast literal comparion for expressions of the form:
6165 // typeof(x) === 'string'.
6166 VisitForTypeOfValue(sub_expr);
6167 builder()->SetExpressionPosition(expr);
6168 TestTypeOfFlags::LiteralFlag literal_flag =
6169 TestTypeOfFlags::GetFlagForLiteral(ast_string_constants(), literal);
6170 if (literal_flag == TestTypeOfFlags::LiteralFlag::kOther) {
6171 builder()->LoadFalse();
6172 } else {
6173 builder()->CompareTypeOf(literal_flag);
6174 }
6175 } else if (expr->IsLiteralCompareUndefined(&sub_expr)) {
6176 VisitForAccumulatorValue(sub_expr);
6177 builder()->SetExpressionPosition(expr);
6178 BuildLiteralCompareNil(expr->op(), BytecodeArrayBuilder::kUndefinedValue);
6179 } else if (expr->IsLiteralCompareNull(&sub_expr)) {
6180 VisitForAccumulatorValue(sub_expr);
6181 builder()->SetExpressionPosition(expr);
6182 BuildLiteralCompareNil(expr->op(), BytecodeArrayBuilder::kNullValue);
6183 } else {
6184 if (expr->op() == Token::IN && expr->left()->IsPrivateName()) {
6185 DCHECK(FLAG_harmony_private_brand_checks);
6186 Variable* var = expr->left()->AsVariableProxy()->var();
6187 if (IsPrivateMethodOrAccessorVariableMode(var->mode())) {
6188 BuildPrivateMethodIn(var, expr->right());
6189 return;
6190 }
6191 // For private fields, the code below does the right thing.
6192 }
6193
6194 Register lhs = VisitForRegisterValue(expr->left());
6195 VisitForAccumulatorValue(expr->right());
6196 builder()->SetExpressionPosition(expr);
6197 FeedbackSlot slot;
6198 if (expr->op() == Token::IN) {
6199 slot = feedback_spec()->AddKeyedHasICSlot();
6200 } else if (expr->op() == Token::INSTANCEOF) {
6201 slot = feedback_spec()->AddInstanceOfSlot();
6202 } else {
6203 slot = feedback_spec()->AddCompareICSlot();
6204 }
6205 builder()->CompareOperation(expr->op(), lhs, feedback_index(slot));
6206 }
6207 // Always returns a boolean value.
6208 execution_result()->SetResultIsBoolean();
6209 }
6210
VisitArithmeticExpression(BinaryOperation * expr)6211 void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
6212 FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
6213 Expression* subexpr;
6214 Smi literal;
6215 if (expr->IsSmiLiteralOperation(&subexpr, &literal)) {
6216 TypeHint type_hint = VisitForAccumulatorValue(subexpr);
6217 builder()->SetExpressionPosition(expr);
6218 builder()->BinaryOperationSmiLiteral(expr->op(), literal,
6219 feedback_index(slot));
6220 if (expr->op() == Token::ADD && type_hint == TypeHint::kString) {
6221 execution_result()->SetResultIsString();
6222 }
6223 } else {
6224 TypeHint lhs_type = VisitForAccumulatorValue(expr->left());
6225 Register lhs = register_allocator()->NewRegister();
6226 builder()->StoreAccumulatorInRegister(lhs);
6227 TypeHint rhs_type = VisitForAccumulatorValue(expr->right());
6228 if (expr->op() == Token::ADD &&
6229 (lhs_type == TypeHint::kString || rhs_type == TypeHint::kString)) {
6230 execution_result()->SetResultIsString();
6231 }
6232
6233 builder()->SetExpressionPosition(expr);
6234 builder()->BinaryOperation(expr->op(), lhs, feedback_index(slot));
6235 }
6236 }
6237
VisitNaryArithmeticExpression(NaryOperation * expr)6238 void BytecodeGenerator::VisitNaryArithmeticExpression(NaryOperation* expr) {
6239 // TODO(leszeks): Add support for lhs smi in commutative ops.
6240 TypeHint type_hint = VisitForAccumulatorValue(expr->first());
6241
6242 for (size_t i = 0; i < expr->subsequent_length(); ++i) {
6243 RegisterAllocationScope register_scope(this);
6244 if (expr->subsequent(i)->IsSmiLiteral()) {
6245 builder()->SetExpressionPosition(expr->subsequent_op_position(i));
6246 builder()->BinaryOperationSmiLiteral(
6247 expr->op(), expr->subsequent(i)->AsLiteral()->AsSmiLiteral(),
6248 feedback_index(feedback_spec()->AddBinaryOpICSlot()));
6249 } else {
6250 Register lhs = register_allocator()->NewRegister();
6251 builder()->StoreAccumulatorInRegister(lhs);
6252 TypeHint rhs_hint = VisitForAccumulatorValue(expr->subsequent(i));
6253 if (rhs_hint == TypeHint::kString) type_hint = TypeHint::kString;
6254 builder()->SetExpressionPosition(expr->subsequent_op_position(i));
6255 builder()->BinaryOperation(
6256 expr->op(), lhs,
6257 feedback_index(feedback_spec()->AddBinaryOpICSlot()));
6258 }
6259 }
6260
6261 if (type_hint == TypeHint::kString && expr->op() == Token::ADD) {
6262 // If any operand of an ADD is a String, a String is produced.
6263 execution_result()->SetResultIsString();
6264 }
6265 }
6266
6267 // Note: the actual spreading is performed by the surrounding expression's
6268 // visitor.
VisitSpread(Spread * expr)6269 void BytecodeGenerator::VisitSpread(Spread* expr) { Visit(expr->expression()); }
6270
VisitEmptyParentheses(EmptyParentheses * expr)6271 void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) {
6272 UNREACHABLE();
6273 }
6274
VisitImportCallExpression(ImportCallExpression * expr)6275 void BytecodeGenerator::VisitImportCallExpression(ImportCallExpression* expr) {
6276 const int register_count = expr->import_assertions() ? 3 : 2;
6277 RegisterList args = register_allocator()->NewRegisterList(register_count);
6278 VisitForRegisterValue(expr->specifier(), args[1]);
6279 if (expr->import_assertions()) {
6280 VisitForRegisterValue(expr->import_assertions(), args[2]);
6281 }
6282 builder()
6283 ->MoveRegister(Register::function_closure(), args[0])
6284 .CallRuntime(Runtime::kDynamicImportCall, args);
6285 }
6286
BuildGetIterator(IteratorType hint)6287 void BytecodeGenerator::BuildGetIterator(IteratorType hint) {
6288 if (hint == IteratorType::kAsync) {
6289 RegisterAllocationScope scope(this);
6290
6291 Register obj = register_allocator()->NewRegister();
6292 Register method = register_allocator()->NewRegister();
6293
6294 // Set method to GetMethod(obj, @@asyncIterator)
6295 builder()->StoreAccumulatorInRegister(obj).LoadAsyncIteratorProperty(
6296 obj, feedback_index(feedback_spec()->AddLoadICSlot()));
6297
6298 BytecodeLabel async_iterator_undefined, done;
6299 builder()->JumpIfUndefinedOrNull(&async_iterator_undefined);
6300
6301 // Let iterator be Call(method, obj)
6302 builder()->StoreAccumulatorInRegister(method).CallProperty(
6303 method, RegisterList(obj),
6304 feedback_index(feedback_spec()->AddCallICSlot()));
6305
6306 // If Type(iterator) is not Object, throw a TypeError exception.
6307 builder()->JumpIfJSReceiver(&done);
6308 builder()->CallRuntime(Runtime::kThrowSymbolAsyncIteratorInvalid);
6309
6310 builder()->Bind(&async_iterator_undefined);
6311 // If method is undefined,
6312 // Let syncMethod be GetMethod(obj, @@iterator)
6313 builder()
6314 ->LoadIteratorProperty(obj,
6315 feedback_index(feedback_spec()->AddLoadICSlot()))
6316 .StoreAccumulatorInRegister(method);
6317
6318 // Let syncIterator be Call(syncMethod, obj)
6319 builder()->CallProperty(method, RegisterList(obj),
6320 feedback_index(feedback_spec()->AddCallICSlot()));
6321
6322 // Return CreateAsyncFromSyncIterator(syncIterator)
6323 // alias `method` register as it's no longer used
6324 Register sync_iter = method;
6325 builder()->StoreAccumulatorInRegister(sync_iter).CallRuntime(
6326 Runtime::kInlineCreateAsyncFromSyncIterator, sync_iter);
6327
6328 builder()->Bind(&done);
6329 } else {
6330 {
6331 RegisterAllocationScope scope(this);
6332
6333 Register obj = register_allocator()->NewRegister();
6334 int load_feedback_index =
6335 feedback_index(feedback_spec()->AddLoadICSlot());
6336 int call_feedback_index =
6337 feedback_index(feedback_spec()->AddCallICSlot());
6338
6339 // Let method be GetMethod(obj, @@iterator) and
6340 // iterator be Call(method, obj).
6341 builder()->StoreAccumulatorInRegister(obj).GetIterator(
6342 obj, load_feedback_index, call_feedback_index);
6343 }
6344
6345 // If Type(iterator) is not Object, throw a TypeError exception.
6346 BytecodeLabel no_type_error;
6347 builder()->JumpIfJSReceiver(&no_type_error);
6348 builder()->CallRuntime(Runtime::kThrowSymbolIteratorInvalid);
6349 builder()->Bind(&no_type_error);
6350 }
6351 }
6352
6353 // Returns an IteratorRecord which is valid for the lifetime of the current
6354 // register_allocation_scope.
BuildGetIteratorRecord(Register next,Register object,IteratorType hint)6355 BytecodeGenerator::IteratorRecord BytecodeGenerator::BuildGetIteratorRecord(
6356 Register next, Register object, IteratorType hint) {
6357 DCHECK(next.is_valid() && object.is_valid());
6358 BuildGetIterator(hint);
6359
6360 builder()
6361 ->StoreAccumulatorInRegister(object)
6362 .LoadNamedProperty(object, ast_string_constants()->next_string(),
6363 feedback_index(feedback_spec()->AddLoadICSlot()))
6364 .StoreAccumulatorInRegister(next);
6365 return IteratorRecord(object, next, hint);
6366 }
6367
BuildGetIteratorRecord(IteratorType hint)6368 BytecodeGenerator::IteratorRecord BytecodeGenerator::BuildGetIteratorRecord(
6369 IteratorType hint) {
6370 Register next = register_allocator()->NewRegister();
6371 Register object = register_allocator()->NewRegister();
6372 return BuildGetIteratorRecord(next, object, hint);
6373 }
6374
BuildIteratorNext(const IteratorRecord & iterator,Register next_result)6375 void BytecodeGenerator::BuildIteratorNext(const IteratorRecord& iterator,
6376 Register next_result) {
6377 DCHECK(next_result.is_valid());
6378 builder()->CallProperty(iterator.next(), RegisterList(iterator.object()),
6379 feedback_index(feedback_spec()->AddCallICSlot()));
6380
6381 if (iterator.type() == IteratorType::kAsync) {
6382 BuildAwait();
6383 }
6384
6385 BytecodeLabel is_object;
6386 builder()
6387 ->StoreAccumulatorInRegister(next_result)
6388 .JumpIfJSReceiver(&is_object)
6389 .CallRuntime(Runtime::kThrowIteratorResultNotAnObject, next_result)
6390 .Bind(&is_object);
6391 }
6392
BuildCallIteratorMethod(Register iterator,const AstRawString * method_name,RegisterList receiver_and_args,BytecodeLabel * if_called,BytecodeLabels * if_notcalled)6393 void BytecodeGenerator::BuildCallIteratorMethod(Register iterator,
6394 const AstRawString* method_name,
6395 RegisterList receiver_and_args,
6396 BytecodeLabel* if_called,
6397 BytecodeLabels* if_notcalled) {
6398 RegisterAllocationScope register_scope(this);
6399
6400 Register method = register_allocator()->NewRegister();
6401 FeedbackSlot slot = feedback_spec()->AddLoadICSlot();
6402 builder()
6403 ->LoadNamedProperty(iterator, method_name, feedback_index(slot))
6404 .JumpIfUndefinedOrNull(if_notcalled->New())
6405 .StoreAccumulatorInRegister(method)
6406 .CallProperty(method, receiver_and_args,
6407 feedback_index(feedback_spec()->AddCallICSlot()))
6408 .Jump(if_called);
6409 }
6410
BuildIteratorClose(const IteratorRecord & iterator,Expression * expr)6411 void BytecodeGenerator::BuildIteratorClose(const IteratorRecord& iterator,
6412 Expression* expr) {
6413 RegisterAllocationScope register_scope(this);
6414 BytecodeLabels done(zone());
6415 BytecodeLabel if_called;
6416 RegisterList args = RegisterList(iterator.object());
6417 BuildCallIteratorMethod(iterator.object(),
6418 ast_string_constants()->return_string(), args,
6419 &if_called, &done);
6420 builder()->Bind(&if_called);
6421
6422 if (iterator.type() == IteratorType::kAsync) {
6423 DCHECK_NOT_NULL(expr);
6424 BuildAwait(expr->position());
6425 }
6426
6427 builder()->JumpIfJSReceiver(done.New());
6428 {
6429 RegisterAllocationScope inner_register_scope(this);
6430 Register return_result = register_allocator()->NewRegister();
6431 builder()
6432 ->StoreAccumulatorInRegister(return_result)
6433 .CallRuntime(Runtime::kThrowIteratorResultNotAnObject, return_result);
6434 }
6435
6436 done.Bind(builder());
6437 }
6438
VisitGetTemplateObject(GetTemplateObject * expr)6439 void BytecodeGenerator::VisitGetTemplateObject(GetTemplateObject* expr) {
6440 builder()->SetExpressionPosition(expr);
6441 size_t entry = builder()->AllocateDeferredConstantPoolEntry();
6442 template_objects_.push_back(std::make_pair(expr, entry));
6443 FeedbackSlot literal_slot = feedback_spec()->AddLiteralSlot();
6444 builder()->GetTemplateObject(entry, feedback_index(literal_slot));
6445 }
6446
VisitTemplateLiteral(TemplateLiteral * expr)6447 void BytecodeGenerator::VisitTemplateLiteral(TemplateLiteral* expr) {
6448 const ZonePtrList<const AstRawString>& parts = *expr->string_parts();
6449 const ZonePtrList<Expression>& substitutions = *expr->substitutions();
6450 // Template strings with no substitutions are turned into StringLiterals.
6451 DCHECK_GT(substitutions.length(), 0);
6452 DCHECK_EQ(parts.length(), substitutions.length() + 1);
6453
6454 // Generate string concatenation
6455 // TODO(caitp): Don't generate feedback slot if it's not used --- introduce
6456 // a simple, concise, reusable mechanism to lazily create reusable slots.
6457 FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
6458 Register last_part = register_allocator()->NewRegister();
6459 bool last_part_valid = false;
6460
6461 builder()->SetExpressionPosition(expr);
6462 for (int i = 0; i < substitutions.length(); ++i) {
6463 if (i != 0) {
6464 builder()->StoreAccumulatorInRegister(last_part);
6465 last_part_valid = true;
6466 }
6467
6468 if (!parts[i]->IsEmpty()) {
6469 builder()->LoadLiteral(parts[i]);
6470 if (last_part_valid) {
6471 builder()->BinaryOperation(Token::ADD, last_part, feedback_index(slot));
6472 }
6473 builder()->StoreAccumulatorInRegister(last_part);
6474 last_part_valid = true;
6475 }
6476
6477 TypeHint type_hint = VisitForAccumulatorValue(substitutions[i]);
6478 if (type_hint != TypeHint::kString) {
6479 builder()->ToString();
6480 }
6481 if (last_part_valid) {
6482 builder()->BinaryOperation(Token::ADD, last_part, feedback_index(slot));
6483 }
6484 last_part_valid = false;
6485 }
6486
6487 if (!parts.last()->IsEmpty()) {
6488 builder()->StoreAccumulatorInRegister(last_part);
6489 builder()->LoadLiteral(parts.last());
6490 builder()->BinaryOperation(Token::ADD, last_part, feedback_index(slot));
6491 }
6492 }
6493
BuildThisVariableLoad()6494 void BytecodeGenerator::BuildThisVariableLoad() {
6495 DeclarationScope* receiver_scope = closure_scope()->GetReceiverScope();
6496 Variable* var = receiver_scope->receiver();
6497 // TODO(littledan): implement 'this' hole check elimination.
6498 HoleCheckMode hole_check_mode =
6499 IsDerivedConstructor(receiver_scope->function_kind())
6500 ? HoleCheckMode::kRequired
6501 : HoleCheckMode::kElided;
6502 BuildVariableLoad(var, hole_check_mode);
6503 }
6504
VisitThisExpression(ThisExpression * expr)6505 void BytecodeGenerator::VisitThisExpression(ThisExpression* expr) {
6506 BuildThisVariableLoad();
6507 }
6508
VisitSuperCallReference(SuperCallReference * expr)6509 void BytecodeGenerator::VisitSuperCallReference(SuperCallReference* expr) {
6510 // Handled by VisitCall().
6511 UNREACHABLE();
6512 }
6513
VisitSuperPropertyReference(SuperPropertyReference * expr)6514 void BytecodeGenerator::VisitSuperPropertyReference(
6515 SuperPropertyReference* expr) {
6516 builder()->CallRuntime(Runtime::kThrowUnsupportedSuperError);
6517 }
6518
VisitCommaExpression(BinaryOperation * binop)6519 void BytecodeGenerator::VisitCommaExpression(BinaryOperation* binop) {
6520 VisitForEffect(binop->left());
6521 Visit(binop->right());
6522 }
6523
VisitNaryCommaExpression(NaryOperation * expr)6524 void BytecodeGenerator::VisitNaryCommaExpression(NaryOperation* expr) {
6525 DCHECK_GT(expr->subsequent_length(), 0);
6526
6527 VisitForEffect(expr->first());
6528 for (size_t i = 0; i < expr->subsequent_length() - 1; ++i) {
6529 VisitForEffect(expr->subsequent(i));
6530 }
6531 Visit(expr->subsequent(expr->subsequent_length() - 1));
6532 }
6533
VisitLogicalTestSubExpression(Token::Value token,Expression * expr,BytecodeLabels * then_labels,BytecodeLabels * else_labels,int coverage_slot)6534 void BytecodeGenerator::VisitLogicalTestSubExpression(
6535 Token::Value token, Expression* expr, BytecodeLabels* then_labels,
6536 BytecodeLabels* else_labels, int coverage_slot) {
6537 DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH);
6538
6539 BytecodeLabels test_next(zone());
6540 if (token == Token::OR) {
6541 VisitForTest(expr, then_labels, &test_next, TestFallthrough::kElse);
6542 } else if (token == Token::AND) {
6543 VisitForTest(expr, &test_next, else_labels, TestFallthrough::kThen);
6544 } else {
6545 DCHECK_EQ(Token::NULLISH, token);
6546 VisitForNullishTest(expr, then_labels, &test_next, else_labels);
6547 }
6548 test_next.Bind(builder());
6549
6550 BuildIncrementBlockCoverageCounterIfEnabled(coverage_slot);
6551 }
6552
VisitLogicalTest(Token::Value token,Expression * left,Expression * right,int right_coverage_slot)6553 void BytecodeGenerator::VisitLogicalTest(Token::Value token, Expression* left,
6554 Expression* right,
6555 int right_coverage_slot) {
6556 DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH);
6557 TestResultScope* test_result = execution_result()->AsTest();
6558 BytecodeLabels* then_labels = test_result->then_labels();
6559 BytecodeLabels* else_labels = test_result->else_labels();
6560 TestFallthrough fallthrough = test_result->fallthrough();
6561
6562 VisitLogicalTestSubExpression(token, left, then_labels, else_labels,
6563 right_coverage_slot);
6564 // The last test has the same then, else and fallthrough as the parent test.
6565 VisitForTest(right, then_labels, else_labels, fallthrough);
6566 }
6567
VisitNaryLogicalTest(Token::Value token,NaryOperation * expr,const NaryCodeCoverageSlots * coverage_slots)6568 void BytecodeGenerator::VisitNaryLogicalTest(
6569 Token::Value token, NaryOperation* expr,
6570 const NaryCodeCoverageSlots* coverage_slots) {
6571 DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH);
6572 DCHECK_GT(expr->subsequent_length(), 0);
6573
6574 TestResultScope* test_result = execution_result()->AsTest();
6575 BytecodeLabels* then_labels = test_result->then_labels();
6576 BytecodeLabels* else_labels = test_result->else_labels();
6577 TestFallthrough fallthrough = test_result->fallthrough();
6578
6579 VisitLogicalTestSubExpression(token, expr->first(), then_labels, else_labels,
6580 coverage_slots->GetSlotFor(0));
6581 for (size_t i = 0; i < expr->subsequent_length() - 1; ++i) {
6582 VisitLogicalTestSubExpression(token, expr->subsequent(i), then_labels,
6583 else_labels,
6584 coverage_slots->GetSlotFor(i + 1));
6585 }
6586 // The last test has the same then, else and fallthrough as the parent test.
6587 VisitForTest(expr->subsequent(expr->subsequent_length() - 1), then_labels,
6588 else_labels, fallthrough);
6589 }
6590
VisitLogicalOrSubExpression(Expression * expr,BytecodeLabels * end_labels,int coverage_slot)6591 bool BytecodeGenerator::VisitLogicalOrSubExpression(Expression* expr,
6592 BytecodeLabels* end_labels,
6593 int coverage_slot) {
6594 if (expr->ToBooleanIsTrue()) {
6595 VisitForAccumulatorValue(expr);
6596 end_labels->Bind(builder());
6597 return true;
6598 } else if (!expr->ToBooleanIsFalse()) {
6599 TypeHint type_hint = VisitForAccumulatorValue(expr);
6600 builder()->JumpIfTrue(ToBooleanModeFromTypeHint(type_hint),
6601 end_labels->New());
6602 }
6603
6604 BuildIncrementBlockCoverageCounterIfEnabled(coverage_slot);
6605
6606 return false;
6607 }
6608
VisitLogicalAndSubExpression(Expression * expr,BytecodeLabels * end_labels,int coverage_slot)6609 bool BytecodeGenerator::VisitLogicalAndSubExpression(Expression* expr,
6610 BytecodeLabels* end_labels,
6611 int coverage_slot) {
6612 if (expr->ToBooleanIsFalse()) {
6613 VisitForAccumulatorValue(expr);
6614 end_labels->Bind(builder());
6615 return true;
6616 } else if (!expr->ToBooleanIsTrue()) {
6617 TypeHint type_hint = VisitForAccumulatorValue(expr);
6618 builder()->JumpIfFalse(ToBooleanModeFromTypeHint(type_hint),
6619 end_labels->New());
6620 }
6621
6622 BuildIncrementBlockCoverageCounterIfEnabled(coverage_slot);
6623
6624 return false;
6625 }
6626
VisitNullishSubExpression(Expression * expr,BytecodeLabels * end_labels,int coverage_slot)6627 bool BytecodeGenerator::VisitNullishSubExpression(Expression* expr,
6628 BytecodeLabels* end_labels,
6629 int coverage_slot) {
6630 if (expr->IsLiteralButNotNullOrUndefined()) {
6631 VisitForAccumulatorValue(expr);
6632 end_labels->Bind(builder());
6633 return true;
6634 } else if (!expr->IsNullOrUndefinedLiteral()) {
6635 VisitForAccumulatorValue(expr);
6636 BytecodeLabel is_null_or_undefined;
6637 builder()
6638 ->JumpIfUndefinedOrNull(&is_null_or_undefined)
6639 .Jump(end_labels->New());
6640 builder()->Bind(&is_null_or_undefined);
6641 }
6642
6643 BuildIncrementBlockCoverageCounterIfEnabled(coverage_slot);
6644
6645 return false;
6646 }
6647
VisitLogicalOrExpression(BinaryOperation * binop)6648 void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) {
6649 Expression* left = binop->left();
6650 Expression* right = binop->right();
6651
6652 int right_coverage_slot =
6653 AllocateBlockCoverageSlotIfEnabled(binop, SourceRangeKind::kRight);
6654
6655 if (execution_result()->IsTest()) {
6656 TestResultScope* test_result = execution_result()->AsTest();
6657 if (left->ToBooleanIsTrue()) {
6658 builder()->Jump(test_result->NewThenLabel());
6659 } else if (left->ToBooleanIsFalse() && right->ToBooleanIsFalse()) {
6660 BuildIncrementBlockCoverageCounterIfEnabled(right_coverage_slot);
6661 builder()->Jump(test_result->NewElseLabel());
6662 } else {
6663 VisitLogicalTest(Token::OR, left, right, right_coverage_slot);
6664 }
6665 test_result->SetResultConsumedByTest();
6666 } else {
6667 BytecodeLabels end_labels(zone());
6668 if (VisitLogicalOrSubExpression(left, &end_labels, right_coverage_slot)) {
6669 return;
6670 }
6671 VisitForAccumulatorValue(right);
6672 end_labels.Bind(builder());
6673 }
6674 }
6675
VisitNaryLogicalOrExpression(NaryOperation * expr)6676 void BytecodeGenerator::VisitNaryLogicalOrExpression(NaryOperation* expr) {
6677 Expression* first = expr->first();
6678 DCHECK_GT(expr->subsequent_length(), 0);
6679
6680 NaryCodeCoverageSlots coverage_slots(this, expr);
6681
6682 if (execution_result()->IsTest()) {
6683 TestResultScope* test_result = execution_result()->AsTest();
6684 if (first->ToBooleanIsTrue()) {
6685 builder()->Jump(test_result->NewThenLabel());
6686 } else {
6687 VisitNaryLogicalTest(Token::OR, expr, &coverage_slots);
6688 }
6689 test_result->SetResultConsumedByTest();
6690 } else {
6691 BytecodeLabels end_labels(zone());
6692 if (VisitLogicalOrSubExpression(first, &end_labels,
6693 coverage_slots.GetSlotFor(0))) {
6694 return;
6695 }
6696 for (size_t i = 0; i < expr->subsequent_length() - 1; ++i) {
6697 if (VisitLogicalOrSubExpression(expr->subsequent(i), &end_labels,
6698 coverage_slots.GetSlotFor(i + 1))) {
6699 return;
6700 }
6701 }
6702 // We have to visit the last value even if it's true, because we need its
6703 // actual value.
6704 VisitForAccumulatorValue(expr->subsequent(expr->subsequent_length() - 1));
6705 end_labels.Bind(builder());
6706 }
6707 }
6708
VisitLogicalAndExpression(BinaryOperation * binop)6709 void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) {
6710 Expression* left = binop->left();
6711 Expression* right = binop->right();
6712
6713 int right_coverage_slot =
6714 AllocateBlockCoverageSlotIfEnabled(binop, SourceRangeKind::kRight);
6715
6716 if (execution_result()->IsTest()) {
6717 TestResultScope* test_result = execution_result()->AsTest();
6718 if (left->ToBooleanIsFalse()) {
6719 builder()->Jump(test_result->NewElseLabel());
6720 } else if (left->ToBooleanIsTrue() && right->ToBooleanIsTrue()) {
6721 BuildIncrementBlockCoverageCounterIfEnabled(right_coverage_slot);
6722 builder()->Jump(test_result->NewThenLabel());
6723 } else {
6724 VisitLogicalTest(Token::AND, left, right, right_coverage_slot);
6725 }
6726 test_result->SetResultConsumedByTest();
6727 } else {
6728 BytecodeLabels end_labels(zone());
6729 if (VisitLogicalAndSubExpression(left, &end_labels, right_coverage_slot)) {
6730 return;
6731 }
6732 VisitForAccumulatorValue(right);
6733 end_labels.Bind(builder());
6734 }
6735 }
6736
VisitNaryLogicalAndExpression(NaryOperation * expr)6737 void BytecodeGenerator::VisitNaryLogicalAndExpression(NaryOperation* expr) {
6738 Expression* first = expr->first();
6739 DCHECK_GT(expr->subsequent_length(), 0);
6740
6741 NaryCodeCoverageSlots coverage_slots(this, expr);
6742
6743 if (execution_result()->IsTest()) {
6744 TestResultScope* test_result = execution_result()->AsTest();
6745 if (first->ToBooleanIsFalse()) {
6746 builder()->Jump(test_result->NewElseLabel());
6747 } else {
6748 VisitNaryLogicalTest(Token::AND, expr, &coverage_slots);
6749 }
6750 test_result->SetResultConsumedByTest();
6751 } else {
6752 BytecodeLabels end_labels(zone());
6753 if (VisitLogicalAndSubExpression(first, &end_labels,
6754 coverage_slots.GetSlotFor(0))) {
6755 return;
6756 }
6757 for (size_t i = 0; i < expr->subsequent_length() - 1; ++i) {
6758 if (VisitLogicalAndSubExpression(expr->subsequent(i), &end_labels,
6759 coverage_slots.GetSlotFor(i + 1))) {
6760 return;
6761 }
6762 }
6763 // We have to visit the last value even if it's false, because we need its
6764 // actual value.
6765 VisitForAccumulatorValue(expr->subsequent(expr->subsequent_length() - 1));
6766 end_labels.Bind(builder());
6767 }
6768 }
6769
VisitNullishExpression(BinaryOperation * binop)6770 void BytecodeGenerator::VisitNullishExpression(BinaryOperation* binop) {
6771 Expression* left = binop->left();
6772 Expression* right = binop->right();
6773
6774 int right_coverage_slot =
6775 AllocateBlockCoverageSlotIfEnabled(binop, SourceRangeKind::kRight);
6776
6777 if (execution_result()->IsTest()) {
6778 TestResultScope* test_result = execution_result()->AsTest();
6779 if (left->IsLiteralButNotNullOrUndefined() && left->ToBooleanIsTrue()) {
6780 builder()->Jump(test_result->NewThenLabel());
6781 } else if (left->IsNullOrUndefinedLiteral() &&
6782 right->IsNullOrUndefinedLiteral()) {
6783 BuildIncrementBlockCoverageCounterIfEnabled(right_coverage_slot);
6784 builder()->Jump(test_result->NewElseLabel());
6785 } else {
6786 VisitLogicalTest(Token::NULLISH, left, right, right_coverage_slot);
6787 }
6788 test_result->SetResultConsumedByTest();
6789 } else {
6790 BytecodeLabels end_labels(zone());
6791 if (VisitNullishSubExpression(left, &end_labels, right_coverage_slot)) {
6792 return;
6793 }
6794 VisitForAccumulatorValue(right);
6795 end_labels.Bind(builder());
6796 }
6797 }
6798
VisitNaryNullishExpression(NaryOperation * expr)6799 void BytecodeGenerator::VisitNaryNullishExpression(NaryOperation* expr) {
6800 Expression* first = expr->first();
6801 DCHECK_GT(expr->subsequent_length(), 0);
6802
6803 NaryCodeCoverageSlots coverage_slots(this, expr);
6804
6805 if (execution_result()->IsTest()) {
6806 TestResultScope* test_result = execution_result()->AsTest();
6807 if (first->IsLiteralButNotNullOrUndefined() && first->ToBooleanIsTrue()) {
6808 builder()->Jump(test_result->NewThenLabel());
6809 } else {
6810 VisitNaryLogicalTest(Token::NULLISH, expr, &coverage_slots);
6811 }
6812 test_result->SetResultConsumedByTest();
6813 } else {
6814 BytecodeLabels end_labels(zone());
6815 if (VisitNullishSubExpression(first, &end_labels,
6816 coverage_slots.GetSlotFor(0))) {
6817 return;
6818 }
6819 for (size_t i = 0; i < expr->subsequent_length() - 1; ++i) {
6820 if (VisitNullishSubExpression(expr->subsequent(i), &end_labels,
6821 coverage_slots.GetSlotFor(i + 1))) {
6822 return;
6823 }
6824 }
6825 // We have to visit the last value even if it's nullish, because we need its
6826 // actual value.
6827 VisitForAccumulatorValue(expr->subsequent(expr->subsequent_length() - 1));
6828 end_labels.Bind(builder());
6829 }
6830 }
6831
BuildNewLocalActivationContext()6832 void BytecodeGenerator::BuildNewLocalActivationContext() {
6833 ValueResultScope value_execution_result(this);
6834 Scope* scope = closure_scope();
6835 DCHECK_EQ(current_scope(), closure_scope());
6836
6837 // Create the appropriate context.
6838 DCHECK(scope->is_function_scope() || scope->is_eval_scope());
6839 int slot_count = scope->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
6840 if (slot_count <= ConstructorBuiltins::MaximumFunctionContextSlots()) {
6841 switch (scope->scope_type()) {
6842 case EVAL_SCOPE:
6843 builder()->CreateEvalContext(scope, slot_count);
6844 break;
6845 case FUNCTION_SCOPE:
6846 builder()->CreateFunctionContext(scope, slot_count);
6847 break;
6848 default:
6849 UNREACHABLE();
6850 }
6851 } else {
6852 Register arg = register_allocator()->NewRegister();
6853 builder()->LoadLiteral(scope).StoreAccumulatorInRegister(arg).CallRuntime(
6854 Runtime::kNewFunctionContext, arg);
6855 }
6856 }
6857
BuildLocalActivationContextInitialization()6858 void BytecodeGenerator::BuildLocalActivationContextInitialization() {
6859 DeclarationScope* scope = closure_scope();
6860
6861 if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) {
6862 Variable* variable = scope->receiver();
6863 Register receiver(builder()->Receiver());
6864 // Context variable (at bottom of the context chain).
6865 DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
6866 builder()->LoadAccumulatorWithRegister(receiver).StoreContextSlot(
6867 execution_context()->reg(), variable->index(), 0);
6868 }
6869
6870 // Copy parameters into context if necessary.
6871 int num_parameters = scope->num_parameters();
6872 for (int i = 0; i < num_parameters; i++) {
6873 Variable* variable = scope->parameter(i);
6874 if (!variable->IsContextSlot()) continue;
6875
6876 Register parameter(builder()->Parameter(i));
6877 // Context variable (at bottom of the context chain).
6878 DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
6879 builder()->LoadAccumulatorWithRegister(parameter).StoreContextSlot(
6880 execution_context()->reg(), variable->index(), 0);
6881 }
6882 }
6883
BuildNewLocalBlockContext(Scope * scope)6884 void BytecodeGenerator::BuildNewLocalBlockContext(Scope* scope) {
6885 ValueResultScope value_execution_result(this);
6886 DCHECK(scope->is_block_scope());
6887
6888 builder()->CreateBlockContext(scope);
6889 }
6890
BuildNewLocalWithContext(Scope * scope)6891 void BytecodeGenerator::BuildNewLocalWithContext(Scope* scope) {
6892 ValueResultScope value_execution_result(this);
6893
6894 Register extension_object = register_allocator()->NewRegister();
6895
6896 builder()->ToObject(extension_object);
6897 builder()->CreateWithContext(extension_object, scope);
6898 }
6899
BuildNewLocalCatchContext(Scope * scope)6900 void BytecodeGenerator::BuildNewLocalCatchContext(Scope* scope) {
6901 ValueResultScope value_execution_result(this);
6902 DCHECK(scope->catch_variable()->IsContextSlot());
6903
6904 Register exception = register_allocator()->NewRegister();
6905 builder()->StoreAccumulatorInRegister(exception);
6906 builder()->CreateCatchContext(exception, scope);
6907 }
6908
VisitLiteralAccessor(LiteralProperty * property,Register value_out)6909 void BytecodeGenerator::VisitLiteralAccessor(LiteralProperty* property,
6910 Register value_out) {
6911 if (property == nullptr) {
6912 builder()->LoadNull().StoreAccumulatorInRegister(value_out);
6913 } else {
6914 VisitForRegisterValue(property->value(), value_out);
6915 }
6916 }
6917
VisitArgumentsObject(Variable * variable)6918 void BytecodeGenerator::VisitArgumentsObject(Variable* variable) {
6919 if (variable == nullptr) return;
6920
6921 DCHECK(variable->IsContextSlot() || variable->IsStackAllocated());
6922
6923 // Allocate and initialize a new arguments object and assign to the
6924 // {arguments} variable.
6925 builder()->CreateArguments(closure_scope()->GetArgumentsType());
6926 BuildVariableAssignment(variable, Token::ASSIGN, HoleCheckMode::kElided);
6927 }
6928
VisitRestArgumentsArray(Variable * rest)6929 void BytecodeGenerator::VisitRestArgumentsArray(Variable* rest) {
6930 if (rest == nullptr) return;
6931
6932 // Allocate and initialize a new rest parameter and assign to the {rest}
6933 // variable.
6934 builder()->CreateArguments(CreateArgumentsType::kRestParameter);
6935 DCHECK(rest->IsContextSlot() || rest->IsStackAllocated());
6936 BuildVariableAssignment(rest, Token::ASSIGN, HoleCheckMode::kElided);
6937 }
6938
VisitThisFunctionVariable(Variable * variable)6939 void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) {
6940 if (variable == nullptr) return;
6941
6942 // Store the closure we were called with in the given variable.
6943 builder()->LoadAccumulatorWithRegister(Register::function_closure());
6944 BuildVariableAssignment(variable, Token::INIT, HoleCheckMode::kElided);
6945 }
6946
VisitNewTargetVariable(Variable * variable)6947 void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
6948 if (variable == nullptr) return;
6949
6950 // The generator resume trampoline abuses the new.target register
6951 // to pass in the generator object. In ordinary calls, new.target is always
6952 // undefined because generator functions are non-constructible, so don't
6953 // assign anything to the new.target variable.
6954 if (IsResumableFunction(info()->literal()->kind())) return;
6955
6956 if (variable->location() == VariableLocation::LOCAL) {
6957 // The new.target register was already assigned by entry trampoline.
6958 DCHECK_EQ(incoming_new_target_or_generator_.index(),
6959 GetRegisterForLocalVariable(variable).index());
6960 return;
6961 }
6962
6963 // Store the new target we were called with in the given variable.
6964 builder()->LoadAccumulatorWithRegister(incoming_new_target_or_generator_);
6965 BuildVariableAssignment(variable, Token::INIT, HoleCheckMode::kElided);
6966 }
6967
BuildGeneratorObjectVariableInitialization()6968 void BytecodeGenerator::BuildGeneratorObjectVariableInitialization() {
6969 DCHECK(IsResumableFunction(info()->literal()->kind()));
6970
6971 Variable* generator_object_var = closure_scope()->generator_object_var();
6972 RegisterAllocationScope register_scope(this);
6973 RegisterList args = register_allocator()->NewRegisterList(2);
6974 Runtime::FunctionId function_id =
6975 ((IsAsyncFunction(info()->literal()->kind()) &&
6976 !IsAsyncGeneratorFunction(info()->literal()->kind())) ||
6977 IsAsyncModule(info()->literal()->kind()))
6978 ? Runtime::kInlineAsyncFunctionEnter
6979 : Runtime::kInlineCreateJSGeneratorObject;
6980 builder()
6981 ->MoveRegister(Register::function_closure(), args[0])
6982 .MoveRegister(builder()->Receiver(), args[1])
6983 .CallRuntime(function_id, args)
6984 .StoreAccumulatorInRegister(generator_object());
6985
6986 if (generator_object_var->location() == VariableLocation::LOCAL) {
6987 // The generator object register is already set to the variable's local
6988 // register.
6989 DCHECK_EQ(generator_object().index(),
6990 GetRegisterForLocalVariable(generator_object_var).index());
6991 } else {
6992 BuildVariableAssignment(generator_object_var, Token::INIT,
6993 HoleCheckMode::kElided);
6994 }
6995 }
6996
BuildPushUndefinedIntoRegisterList(RegisterList * reg_list)6997 void BytecodeGenerator::BuildPushUndefinedIntoRegisterList(
6998 RegisterList* reg_list) {
6999 Register reg = register_allocator()->GrowRegisterList(reg_list);
7000 builder()->LoadUndefined().StoreAccumulatorInRegister(reg);
7001 }
7002
BuildLoadPropertyKey(LiteralProperty * property,Register out_reg)7003 void BytecodeGenerator::BuildLoadPropertyKey(LiteralProperty* property,
7004 Register out_reg) {
7005 if (property->key()->IsStringLiteral()) {
7006 builder()
7007 ->LoadLiteral(property->key()->AsLiteral()->AsRawString())
7008 .StoreAccumulatorInRegister(out_reg);
7009 } else {
7010 VisitForAccumulatorValue(property->key());
7011 builder()->ToName(out_reg);
7012 }
7013 }
7014
AllocateBlockCoverageSlotIfEnabled(AstNode * node,SourceRangeKind kind)7015 int BytecodeGenerator::AllocateBlockCoverageSlotIfEnabled(
7016 AstNode* node, SourceRangeKind kind) {
7017 return (block_coverage_builder_ == nullptr)
7018 ? BlockCoverageBuilder::kNoCoverageArraySlot
7019 : block_coverage_builder_->AllocateBlockCoverageSlot(node, kind);
7020 }
7021
AllocateNaryBlockCoverageSlotIfEnabled(NaryOperation * node,size_t index)7022 int BytecodeGenerator::AllocateNaryBlockCoverageSlotIfEnabled(
7023 NaryOperation* node, size_t index) {
7024 return (block_coverage_builder_ == nullptr)
7025 ? BlockCoverageBuilder::kNoCoverageArraySlot
7026 : block_coverage_builder_->AllocateNaryBlockCoverageSlot(node,
7027 index);
7028 }
7029
BuildIncrementBlockCoverageCounterIfEnabled(AstNode * node,SourceRangeKind kind)7030 void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled(
7031 AstNode* node, SourceRangeKind kind) {
7032 if (block_coverage_builder_ == nullptr) return;
7033 block_coverage_builder_->IncrementBlockCounter(node, kind);
7034 }
7035
BuildIncrementBlockCoverageCounterIfEnabled(int coverage_array_slot)7036 void BytecodeGenerator::BuildIncrementBlockCoverageCounterIfEnabled(
7037 int coverage_array_slot) {
7038 if (block_coverage_builder_ != nullptr) {
7039 block_coverage_builder_->IncrementBlockCounter(coverage_array_slot);
7040 }
7041 }
7042
7043 // Visits the expression |expr| and places the result in the accumulator.
VisitForAccumulatorValue(Expression * expr)7044 BytecodeGenerator::TypeHint BytecodeGenerator::VisitForAccumulatorValue(
7045 Expression* expr) {
7046 ValueResultScope accumulator_scope(this);
7047 Visit(expr);
7048 return accumulator_scope.type_hint();
7049 }
7050
VisitForAccumulatorValueOrTheHole(Expression * expr)7051 void BytecodeGenerator::VisitForAccumulatorValueOrTheHole(Expression* expr) {
7052 if (expr == nullptr) {
7053 builder()->LoadTheHole();
7054 } else {
7055 VisitForAccumulatorValue(expr);
7056 }
7057 }
7058
7059 // Visits the expression |expr| and discards the result.
VisitForEffect(Expression * expr)7060 void BytecodeGenerator::VisitForEffect(Expression* expr) {
7061 EffectResultScope effect_scope(this);
7062 Visit(expr);
7063 }
7064
7065 // Visits the expression |expr| and returns the register containing
7066 // the expression result.
VisitForRegisterValue(Expression * expr)7067 Register BytecodeGenerator::VisitForRegisterValue(Expression* expr) {
7068 VisitForAccumulatorValue(expr);
7069 Register result = register_allocator()->NewRegister();
7070 builder()->StoreAccumulatorInRegister(result);
7071 return result;
7072 }
7073
7074 // Visits the expression |expr| and stores the expression result in
7075 // |destination|.
VisitForRegisterValue(Expression * expr,Register destination)7076 void BytecodeGenerator::VisitForRegisterValue(Expression* expr,
7077 Register destination) {
7078 ValueResultScope register_scope(this);
7079 Visit(expr);
7080 builder()->StoreAccumulatorInRegister(destination);
7081 }
7082
7083 // Visits the expression |expr| and pushes the result into a new register
7084 // added to the end of |reg_list|.
VisitAndPushIntoRegisterList(Expression * expr,RegisterList * reg_list)7085 void BytecodeGenerator::VisitAndPushIntoRegisterList(Expression* expr,
7086 RegisterList* reg_list) {
7087 {
7088 ValueResultScope register_scope(this);
7089 Visit(expr);
7090 }
7091 // Grow the register list after visiting the expression to avoid reserving
7092 // the register across the expression evaluation, which could cause memory
7093 // leaks for deep expressions due to dead objects being kept alive by pointers
7094 // in registers.
7095 Register destination = register_allocator()->GrowRegisterList(reg_list);
7096 builder()->StoreAccumulatorInRegister(destination);
7097 }
7098
BuildTest(ToBooleanMode mode,BytecodeLabels * then_labels,BytecodeLabels * else_labels,TestFallthrough fallthrough)7099 void BytecodeGenerator::BuildTest(ToBooleanMode mode,
7100 BytecodeLabels* then_labels,
7101 BytecodeLabels* else_labels,
7102 TestFallthrough fallthrough) {
7103 switch (fallthrough) {
7104 case TestFallthrough::kThen:
7105 builder()->JumpIfFalse(mode, else_labels->New());
7106 break;
7107 case TestFallthrough::kElse:
7108 builder()->JumpIfTrue(mode, then_labels->New());
7109 break;
7110 case TestFallthrough::kNone:
7111 builder()->JumpIfTrue(mode, then_labels->New());
7112 builder()->Jump(else_labels->New());
7113 break;
7114 }
7115 }
7116
7117 // Visits the expression |expr| for testing its boolean value and jumping to the
7118 // |then| or |other| label depending on value and short-circuit semantics
VisitForTest(Expression * expr,BytecodeLabels * then_labels,BytecodeLabels * else_labels,TestFallthrough fallthrough)7119 void BytecodeGenerator::VisitForTest(Expression* expr,
7120 BytecodeLabels* then_labels,
7121 BytecodeLabels* else_labels,
7122 TestFallthrough fallthrough) {
7123 bool result_consumed;
7124 TypeHint type_hint;
7125 {
7126 // To make sure that all temporary registers are returned before generating
7127 // jumps below, we ensure that the result scope is deleted before doing so.
7128 // Dead registers might be materialized otherwise.
7129 TestResultScope test_result(this, then_labels, else_labels, fallthrough);
7130 Visit(expr);
7131 result_consumed = test_result.result_consumed_by_test();
7132 type_hint = test_result.type_hint();
7133 // Labels and fallthrough might have been mutated, so update based on
7134 // TestResultScope.
7135 then_labels = test_result.then_labels();
7136 else_labels = test_result.else_labels();
7137 fallthrough = test_result.fallthrough();
7138 }
7139 if (!result_consumed) {
7140 BuildTest(ToBooleanModeFromTypeHint(type_hint), then_labels, else_labels,
7141 fallthrough);
7142 }
7143 }
7144
7145 // Visits the expression |expr| for testing its nullish value and jumping to the
7146 // |then| or |other| label depending on value and short-circuit semantics
VisitForNullishTest(Expression * expr,BytecodeLabels * then_labels,BytecodeLabels * test_next_labels,BytecodeLabels * else_labels)7147 void BytecodeGenerator::VisitForNullishTest(Expression* expr,
7148 BytecodeLabels* then_labels,
7149 BytecodeLabels* test_next_labels,
7150 BytecodeLabels* else_labels) {
7151 // Nullish short circuits on undefined or null, otherwise we fall back to
7152 // BuildTest with no fallthrough.
7153 // TODO(joshualitt): We should do this in a TestResultScope.
7154 TypeHint type_hint = VisitForAccumulatorValue(expr);
7155 ToBooleanMode mode = ToBooleanModeFromTypeHint(type_hint);
7156
7157 // Skip the nullish shortcircuit if we already have a boolean.
7158 if (mode != ToBooleanMode::kAlreadyBoolean) {
7159 builder()->JumpIfUndefinedOrNull(test_next_labels->New());
7160 }
7161 BuildTest(mode, then_labels, else_labels, TestFallthrough::kNone);
7162 }
7163
VisitInSameTestExecutionScope(Expression * expr)7164 void BytecodeGenerator::VisitInSameTestExecutionScope(Expression* expr) {
7165 DCHECK(execution_result()->IsTest());
7166 {
7167 RegisterAllocationScope reg_scope(this);
7168 Visit(expr);
7169 }
7170 if (!execution_result()->AsTest()->result_consumed_by_test()) {
7171 TestResultScope* result_scope = execution_result()->AsTest();
7172 BuildTest(ToBooleanModeFromTypeHint(result_scope->type_hint()),
7173 result_scope->then_labels(), result_scope->else_labels(),
7174 result_scope->fallthrough());
7175 result_scope->SetResultConsumedByTest();
7176 }
7177 }
7178
VisitInScope(Statement * stmt,Scope * scope)7179 void BytecodeGenerator::VisitInScope(Statement* stmt, Scope* scope) {
7180 DCHECK(scope->declarations()->is_empty());
7181 CurrentScope current_scope(this, scope);
7182 ContextScope context_scope(this, scope);
7183 Visit(stmt);
7184 }
7185
GetRegisterForLocalVariable(Variable * variable)7186 Register BytecodeGenerator::GetRegisterForLocalVariable(Variable* variable) {
7187 DCHECK_EQ(VariableLocation::LOCAL, variable->location());
7188 return builder()->Local(variable->index());
7189 }
7190
function_kind() const7191 FunctionKind BytecodeGenerator::function_kind() const {
7192 return info()->literal()->kind();
7193 }
7194
language_mode() const7195 LanguageMode BytecodeGenerator::language_mode() const {
7196 return current_scope()->language_mode();
7197 }
7198
generator_object() const7199 Register BytecodeGenerator::generator_object() const {
7200 DCHECK(IsResumableFunction(info()->literal()->kind()));
7201 return incoming_new_target_or_generator_;
7202 }
7203
feedback_spec()7204 FeedbackVectorSpec* BytecodeGenerator::feedback_spec() {
7205 return info()->feedback_vector_spec();
7206 }
7207
feedback_index(FeedbackSlot slot) const7208 int BytecodeGenerator::feedback_index(FeedbackSlot slot) const {
7209 DCHECK(!slot.IsInvalid());
7210 return FeedbackVector::GetIndex(slot);
7211 }
7212
GetCachedLoadGlobalICSlot(TypeofMode typeof_mode,Variable * variable)7213 FeedbackSlot BytecodeGenerator::GetCachedLoadGlobalICSlot(
7214 TypeofMode typeof_mode, Variable* variable) {
7215 FeedbackSlotCache::SlotKind slot_kind =
7216 typeof_mode == TypeofMode::kInside
7217 ? FeedbackSlotCache::SlotKind::kLoadGlobalInsideTypeof
7218 : FeedbackSlotCache::SlotKind::kLoadGlobalNotInsideTypeof;
7219 FeedbackSlot slot(feedback_slot_cache()->Get(slot_kind, variable));
7220 if (!slot.IsInvalid()) {
7221 return slot;
7222 }
7223 slot = feedback_spec()->AddLoadGlobalICSlot(typeof_mode);
7224 feedback_slot_cache()->Put(slot_kind, variable, feedback_index(slot));
7225 return slot;
7226 }
7227
GetCachedStoreGlobalICSlot(LanguageMode language_mode,Variable * variable)7228 FeedbackSlot BytecodeGenerator::GetCachedStoreGlobalICSlot(
7229 LanguageMode language_mode, Variable* variable) {
7230 FeedbackSlotCache::SlotKind slot_kind =
7231 is_strict(language_mode)
7232 ? FeedbackSlotCache::SlotKind::kStoreGlobalStrict
7233 : FeedbackSlotCache::SlotKind::kStoreGlobalSloppy;
7234 FeedbackSlot slot(feedback_slot_cache()->Get(slot_kind, variable));
7235 if (!slot.IsInvalid()) {
7236 return slot;
7237 }
7238 slot = feedback_spec()->AddStoreGlobalICSlot(language_mode);
7239 feedback_slot_cache()->Put(slot_kind, variable, feedback_index(slot));
7240 return slot;
7241 }
7242
GetCachedLoadICSlot(const Expression * expr,const AstRawString * name)7243 FeedbackSlot BytecodeGenerator::GetCachedLoadICSlot(const Expression* expr,
7244 const AstRawString* name) {
7245 DCHECK(!expr->IsSuperPropertyReference());
7246 if (!FLAG_ignition_share_named_property_feedback) {
7247 return feedback_spec()->AddLoadICSlot();
7248 }
7249 FeedbackSlotCache::SlotKind slot_kind =
7250 FeedbackSlotCache::SlotKind::kLoadProperty;
7251 if (!expr->IsVariableProxy()) {
7252 return feedback_spec()->AddLoadICSlot();
7253 }
7254 const VariableProxy* proxy = expr->AsVariableProxy();
7255 FeedbackSlot slot(
7256 feedback_slot_cache()->Get(slot_kind, proxy->var()->index(), name));
7257 if (!slot.IsInvalid()) {
7258 return slot;
7259 }
7260 slot = feedback_spec()->AddLoadICSlot();
7261 feedback_slot_cache()->Put(slot_kind, proxy->var()->index(), name,
7262 feedback_index(slot));
7263 return slot;
7264 }
7265
GetCachedLoadSuperICSlot(const AstRawString * name)7266 FeedbackSlot BytecodeGenerator::GetCachedLoadSuperICSlot(
7267 const AstRawString* name) {
7268 if (!FLAG_ignition_share_named_property_feedback) {
7269 return feedback_spec()->AddLoadICSlot();
7270 }
7271 FeedbackSlotCache::SlotKind slot_kind =
7272 FeedbackSlotCache::SlotKind::kLoadSuperProperty;
7273
7274 FeedbackSlot slot(feedback_slot_cache()->Get(slot_kind, name));
7275 if (!slot.IsInvalid()) {
7276 return slot;
7277 }
7278 slot = feedback_spec()->AddLoadICSlot();
7279 feedback_slot_cache()->Put(slot_kind, name, feedback_index(slot));
7280 return slot;
7281 }
7282
GetCachedStoreICSlot(const Expression * expr,const AstRawString * name)7283 FeedbackSlot BytecodeGenerator::GetCachedStoreICSlot(const Expression* expr,
7284 const AstRawString* name) {
7285 if (!FLAG_ignition_share_named_property_feedback) {
7286 return feedback_spec()->AddStoreICSlot(language_mode());
7287 }
7288 FeedbackSlotCache::SlotKind slot_kind =
7289 is_strict(language_mode()) ? FeedbackSlotCache::SlotKind::kSetNamedStrict
7290 : FeedbackSlotCache::SlotKind::kSetNamedSloppy;
7291 if (!expr->IsVariableProxy()) {
7292 return feedback_spec()->AddStoreICSlot(language_mode());
7293 }
7294 const VariableProxy* proxy = expr->AsVariableProxy();
7295 FeedbackSlot slot(
7296 feedback_slot_cache()->Get(slot_kind, proxy->var()->index(), name));
7297 if (!slot.IsInvalid()) {
7298 return slot;
7299 }
7300 slot = feedback_spec()->AddStoreICSlot(language_mode());
7301 feedback_slot_cache()->Put(slot_kind, proxy->var()->index(), name,
7302 feedback_index(slot));
7303 return slot;
7304 }
7305
GetCachedCreateClosureSlot(FunctionLiteral * literal)7306 int BytecodeGenerator::GetCachedCreateClosureSlot(FunctionLiteral* literal) {
7307 FeedbackSlotCache::SlotKind slot_kind =
7308 FeedbackSlotCache::SlotKind::kClosureFeedbackCell;
7309 int index = feedback_slot_cache()->Get(slot_kind, literal);
7310 if (index != -1) {
7311 return index;
7312 }
7313 index = feedback_spec()->AddCreateClosureSlot();
7314 feedback_slot_cache()->Put(slot_kind, literal, index);
7315 return index;
7316 }
7317
GetDummyCompareICSlot()7318 FeedbackSlot BytecodeGenerator::GetDummyCompareICSlot() {
7319 return dummy_feedback_slot_.Get();
7320 }
7321
7322 } // namespace interpreter
7323 } // namespace internal
7324 } // namespace v8
7325