// 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/interpreter/bytecode-label.h" #include "src/zone-containers.h" namespace v8 { namespace internal { namespace interpreter { class ControlFlowBuilder BASE_EMBEDDED { public: explicit ControlFlowBuilder(BytecodeArrayBuilder* builder) : builder_(builder) {} virtual ~ControlFlowBuilder() {} protected: BytecodeArrayBuilder* builder() const { return builder_; } private: BytecodeArrayBuilder* builder_; DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder); }; class BreakableControlFlowBuilder : public ControlFlowBuilder { public: explicit BreakableControlFlowBuilder(BytecodeArrayBuilder* builder) : ControlFlowBuilder(builder), break_sites_(builder->zone()) {} virtual ~BreakableControlFlowBuilder(); // This method should be called by the control flow owner before // destruction to update sites that emit jumps for break. void SetBreakTarget(const BytecodeLabel& break_target); // This method is called when visiting break statements in the AST. // Inserts a jump to a unbound label that is patched when the corresponding // SetBreakTarget is called. void Break() { EmitJump(&break_sites_); } void BreakIfTrue() { EmitJumpIfTrue(&break_sites_); } void BreakIfFalse() { EmitJumpIfFalse(&break_sites_); } void BreakIfUndefined() { EmitJumpIfUndefined(&break_sites_); } void BreakIfNull() { EmitJumpIfNull(&break_sites_); } protected: void EmitJump(ZoneVector<BytecodeLabel>* labels); void EmitJump(ZoneVector<BytecodeLabel>* labels, int index); void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels); void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels, int index); void EmitJumpIfFalse(ZoneVector<BytecodeLabel>* labels); void EmitJumpIfFalse(ZoneVector<BytecodeLabel>* labels, int index); void EmitJumpIfUndefined(ZoneVector<BytecodeLabel>* labels); void EmitJumpIfNull(ZoneVector<BytecodeLabel>* labels); void BindLabels(const BytecodeLabel& target, ZoneVector<BytecodeLabel>* site); // Unbound labels that identify jumps for break statements in the code. ZoneVector<BytecodeLabel> break_sites_; }; // Class to track control flow for block statements (which can break in JS). class BlockBuilder final : public BreakableControlFlowBuilder { public: explicit BlockBuilder(BytecodeArrayBuilder* builder) : BreakableControlFlowBuilder(builder) {} void EndBlock(); private: BytecodeLabel block_end_; }; // A class to help with co-ordinating break and continue statements with // their loop. class LoopBuilder final : public BreakableControlFlowBuilder { public: explicit LoopBuilder(BytecodeArrayBuilder* builder) : BreakableControlFlowBuilder(builder), continue_sites_(builder->zone()) {} ~LoopBuilder(); void LoopHeader(ZoneVector<BytecodeLabel>* additional_labels); void JumpToHeader() { builder()->Jump(&loop_header_); } void JumpToHeaderIfTrue() { builder()->JumpIfTrue(&loop_header_); } void SetContinueTarget(); void EndLoop(); // This method is called when visiting continue statements in the AST. // Inserts a jump to an unbound label that is patched when SetContinueTarget // is called. void Continue() { EmitJump(&continue_sites_); } void ContinueIfTrue() { EmitJumpIfTrue(&continue_sites_); } void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_sites_); } void ContinueIfNull() { EmitJumpIfNull(&continue_sites_); } private: BytecodeLabel loop_header_; BytecodeLabel loop_end_; // Unbound labels that identify jumps for continue statements in the code. ZoneVector<BytecodeLabel> continue_sites_; }; // A class to help with co-ordinating break statements with their switch. class SwitchBuilder final : public BreakableControlFlowBuilder { public: explicit SwitchBuilder(BytecodeArrayBuilder* builder, int number_of_cases) : BreakableControlFlowBuilder(builder), case_sites_(builder->zone()) { case_sites_.resize(number_of_cases); } ~SwitchBuilder(); // 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); // This method is called when visiting case comparison operation for |index|. // Inserts a JumpIfTrue to a unbound label that is patched when the // corresponding SetCaseTarget is called. void Case(int index) { EmitJumpIfTrue(&case_sites_, 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) { EmitJump(&case_sites_, 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 TryCatchBuilder final : public ControlFlowBuilder { public: explicit TryCatchBuilder(BytecodeArrayBuilder* builder) : ControlFlowBuilder(builder), handler_id_(builder->NewHandlerEntry()) {} void BeginTry(Register context); void EndTry(); void EndCatch(); private: int handler_id_; BytecodeLabel handler_; BytecodeLabel exit_; }; // A class to help with co-ordinating control flow in try-finally statements. class TryFinallyBuilder final : public ControlFlowBuilder { public: explicit TryFinallyBuilder(BytecodeArrayBuilder* builder, bool will_catch) : ControlFlowBuilder(builder), handler_id_(builder->NewHandlerEntry()), finalization_sites_(builder->zone()), will_catch_(will_catch) {} void BeginTry(Register context); void LeaveTry(); void EndTry(); void BeginHandler(); void BeginFinally(); void EndFinally(); private: int handler_id_; BytecodeLabel handler_; // Unbound labels that identify jumps to the finally block in the code. ZoneVector<BytecodeLabel> finalization_sites_; // Conservative prediction of whether exceptions thrown into the handler for // this finally block will be caught. Note that such a prediction depends on // whether this try-finally is nested inside a surrounding try-catch. bool will_catch_; }; } // namespace interpreter } // namespace internal } // namespace v8 #endif // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_