blob: ac68947e1a1ffaa9df32a82f209c982d779fb852 [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_
#define V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_
#include "src/interpreter/bytecode-array-builder.h"
#include "src/ast/ast-source-ranges.h"
#include "src/interpreter/block-coverage-builder.h"
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/bytecode-label.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
namespace interpreter {
class V8_EXPORT_PRIVATE ControlFlowBuilder {
public:
explicit ControlFlowBuilder(BytecodeArrayBuilder* builder)
: builder_(builder) {}
virtual ~ControlFlowBuilder() = default;
protected:
BytecodeArrayBuilder* builder() const { return builder_; }
private:
BytecodeArrayBuilder* builder_;
DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder);
};
class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
: public ControlFlowBuilder {
public:
BreakableControlFlowBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
AstNode* node)
: ControlFlowBuilder(builder),
break_labels_(builder->zone()),
node_(node),
block_coverage_builder_(block_coverage_builder) {}
~BreakableControlFlowBuilder() override;
// This method is called when visiting break statements in the AST.
// Inserts a jump to an unbound label that is patched when the corresponding
// BindBreakTarget is called.
void Break() { EmitJump(&break_labels_); }
void BreakIfTrue(BytecodeArrayBuilder::ToBooleanMode mode) {
EmitJumpIfTrue(mode, &break_labels_);
}
void BreakIfFalse(BytecodeArrayBuilder::ToBooleanMode mode) {
EmitJumpIfFalse(mode, &break_labels_);
}
void BreakIfUndefined() { EmitJumpIfUndefined(&break_labels_); }
void BreakIfNull() { EmitJumpIfNull(&break_labels_); }
BytecodeLabels* break_labels() { return &break_labels_; }
protected:
void EmitJump(BytecodeLabels* labels);
void EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,
BytecodeLabels* labels);
void EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode mode,
BytecodeLabels* labels);
void EmitJumpIfUndefined(BytecodeLabels* labels);
void EmitJumpIfNull(BytecodeLabels* labels);
// Called from the destructor to update sites that emit jumps for break.
void BindBreakTarget();
// Unbound labels that identify jumps for break statements in the code.
BytecodeLabels break_labels_;
// A continuation counter (for block coverage) is needed e.g. when
// encountering a break statement.
AstNode* node_;
BlockCoverageBuilder* block_coverage_builder_;
};
// Class to track control flow for block statements (which can break in JS).
class V8_EXPORT_PRIVATE BlockBuilder final
: public BreakableControlFlowBuilder {
public:
BlockBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
BreakableStatement* statement)
: BreakableControlFlowBuilder(builder, block_coverage_builder,
statement) {}
};
// A class to help with co-ordinating break and continue statements with
// their loop.
class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
public:
LoopBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder, AstNode* node)
: BreakableControlFlowBuilder(builder, block_coverage_builder, node),
continue_labels_(builder->zone()),
end_labels_(builder->zone()) {
if (block_coverage_builder_ != nullptr) {
block_coverage_body_slot_ =
block_coverage_builder_->AllocateBlockCoverageSlot(
node, SourceRangeKind::kBody);
}
source_position_ = node ? node->position() : kNoSourcePosition;
}
~LoopBuilder() override;
void LoopHeader();
void LoopBody();
void JumpToHeader(int loop_depth, LoopBuilder* const parent_loop);
void BindContinueTarget();
// This method is called when visiting continue statements in the AST.
// Inserts a jump to an unbound label that is patched when BindContinueTarget
// is called.
void Continue() { EmitJump(&continue_labels_); }
void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_labels_); }
void ContinueIfNull() { EmitJumpIfNull(&continue_labels_); }
private:
// Emit a Jump to our parent_loop_'s end label which could be a JumpLoop or,
// iff they are a nested inner loop with the same loop header bytecode offset
// as their parent's, a Jump to its parent's end label.
void JumpToLoopEnd() { EmitJump(&end_labels_); }
void BindLoopEnd();
BytecodeLoopHeader loop_header_;
// Unbound labels that identify jumps for continue statements in the code and
// jumps from checking the loop condition to the header for do-while loops.
BytecodeLabels continue_labels_;
// Unbound labels that identify jumps for nested inner loops which share the
// same header offset as this loop. Said inner loops will Jump to our end
// label, which could be a JumpLoop or, iff we are a nested inner loop too, a
// Jump to our parent's end label.
BytecodeLabels end_labels_;
int block_coverage_body_slot_;
int source_position_;
};
// A class to help with co-ordinating break statements with their switch.
class V8_EXPORT_PRIVATE SwitchBuilder final
: public BreakableControlFlowBuilder {
public:
SwitchBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
SwitchStatement* statement, int number_of_cases)
: BreakableControlFlowBuilder(builder, block_coverage_builder, statement),
case_sites_(builder->zone()) {
case_sites_.resize(number_of_cases);
}
~SwitchBuilder() override; // NOLINT (modernize-use-equals-default)
// This method should be called by the SwitchBuilder owner when the case
// statement with |index| is emitted to update the case jump site.
void SetCaseTarget(int index, CaseClause* clause);
// This method is called when visiting case comparison operation for |index|.
// Inserts a JumpIfTrue with ToBooleanMode |mode| to a unbound label that is
// patched when the corresponding SetCaseTarget is called.
void Case(BytecodeArrayBuilder::ToBooleanMode mode, int index) {
builder()->JumpIfTrue(mode, &case_sites_.at(index));
}
// This method is called when all cases comparisons have been emitted if there
// is a default case statement. Inserts a Jump to a unbound label that is
// patched when the corresponding SetCaseTarget is called.
void DefaultAt(int index) { builder()->Jump(&case_sites_.at(index)); }
private:
// Unbound labels that identify jumps for case statements in the code.
ZoneVector<BytecodeLabel> case_sites_;
};
// A class to help with co-ordinating control flow in try-catch statements.
class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder {
public:
TryCatchBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
TryCatchStatement* statement,
HandlerTable::CatchPrediction catch_prediction)
: ControlFlowBuilder(builder),
handler_id_(builder->NewHandlerEntry()),
catch_prediction_(catch_prediction),
block_coverage_builder_(block_coverage_builder),
statement_(statement) {}
~TryCatchBuilder() override;
void BeginTry(Register context);
void EndTry();
void EndCatch();
private:
int handler_id_;
HandlerTable::CatchPrediction catch_prediction_;
BytecodeLabel exit_;
BlockCoverageBuilder* block_coverage_builder_;
TryCatchStatement* statement_;
};
// A class to help with co-ordinating control flow in try-finally statements.
class V8_EXPORT_PRIVATE TryFinallyBuilder final : public ControlFlowBuilder {
public:
TryFinallyBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
TryFinallyStatement* statement,
HandlerTable::CatchPrediction catch_prediction)
: ControlFlowBuilder(builder),
handler_id_(builder->NewHandlerEntry()),
catch_prediction_(catch_prediction),
finalization_sites_(builder->zone()),
block_coverage_builder_(block_coverage_builder),
statement_(statement) {}
~TryFinallyBuilder() override;
void BeginTry(Register context);
void LeaveTry();
void EndTry();
void BeginHandler();
void BeginFinally();
void EndFinally();
private:
int handler_id_;
HandlerTable::CatchPrediction catch_prediction_;
BytecodeLabel handler_;
// Unbound labels that identify jumps to the finally block in the code.
BytecodeLabels finalization_sites_;
BlockCoverageBuilder* block_coverage_builder_;
TryFinallyStatement* statement_;
};
class V8_EXPORT_PRIVATE ConditionalControlFlowBuilder final
: public ControlFlowBuilder {
public:
ConditionalControlFlowBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
AstNode* node)
: ControlFlowBuilder(builder),
end_labels_(builder->zone()),
then_labels_(builder->zone()),
else_labels_(builder->zone()),
node_(node),
block_coverage_builder_(block_coverage_builder) {
DCHECK(node->IsIfStatement() || node->IsConditional());
if (block_coverage_builder != nullptr) {
block_coverage_then_slot_ =
block_coverage_builder->AllocateBlockCoverageSlot(
node, SourceRangeKind::kThen);
block_coverage_else_slot_ =
block_coverage_builder->AllocateBlockCoverageSlot(
node, SourceRangeKind::kElse);
}
}
~ConditionalControlFlowBuilder() override;
BytecodeLabels* then_labels() { return &then_labels_; }
BytecodeLabels* else_labels() { return &else_labels_; }
void Then();
void Else();
void JumpToEnd();
private:
BytecodeLabels end_labels_;
BytecodeLabels then_labels_;
BytecodeLabels else_labels_;
AstNode* node_;
int block_coverage_then_slot_;
int block_coverage_else_slot_;
BlockCoverageBuilder* block_coverage_builder_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_