// 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_BYTECODE_ARRAY_BUILDER_H_ #define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_ #include "src/ast/ast.h" #include "src/base/compiler-specific.h" #include "src/globals.h" #include "src/interpreter/bytecode-array-writer.h" #include "src/interpreter/bytecode-flags.h" #include "src/interpreter/bytecode-register-allocator.h" #include "src/interpreter/bytecode-register.h" #include "src/interpreter/bytecode-source-info.h" #include "src/interpreter/bytecodes.h" #include "src/interpreter/constant-array-builder.h" #include "src/interpreter/handler-table-builder.h" #include "src/zone/zone-containers.h" namespace v8 { namespace internal { class FeedbackVectorSpec; class Isolate; namespace interpreter { class BytecodeLabel; class BytecodeLoopHeader; class BytecodeNode; class BytecodeRegisterOptimizer; class BytecodeJumpTable; class Register; class V8_EXPORT_PRIVATE BytecodeArrayBuilder final { public: BytecodeArrayBuilder( Zone* zone, int parameter_count, int locals_count, FeedbackVectorSpec* feedback_vector_spec = nullptr, SourcePositionTableBuilder::RecordingMode source_position_mode = SourcePositionTableBuilder::RECORD_SOURCE_POSITIONS); Handle<BytecodeArray> ToBytecodeArray(Isolate* isolate); // Get the number of parameters expected by function. int parameter_count() const { DCHECK_GE(parameter_count_, 0); return parameter_count_; } // Get the number of locals required for bytecode array. int locals_count() const { DCHECK_GE(local_register_count_, 0); return local_register_count_; } // Returns the number of fixed (non-temporary) registers. int fixed_register_count() const { return locals_count(); } // Returns the number of fixed and temporary registers. int total_register_count() const { DCHECK_LE(fixed_register_count(), register_allocator()->maximum_register_count()); return register_allocator()->maximum_register_count(); } Register Local(int index) const; Register Parameter(int parameter_index) const; Register Receiver() const; // Constant loads to accumulator. BytecodeArrayBuilder& LoadConstantPoolEntry(size_t entry); BytecodeArrayBuilder& LoadLiteral(Smi value); BytecodeArrayBuilder& LoadLiteral(double value); BytecodeArrayBuilder& LoadLiteral(const AstRawString* raw_string); BytecodeArrayBuilder& LoadLiteral(const Scope* scope); BytecodeArrayBuilder& LoadLiteral(AstBigInt bigint); BytecodeArrayBuilder& LoadLiteral(AstSymbol symbol); BytecodeArrayBuilder& LoadUndefined(); BytecodeArrayBuilder& LoadNull(); BytecodeArrayBuilder& LoadTheHole(); BytecodeArrayBuilder& LoadTrue(); BytecodeArrayBuilder& LoadFalse(); BytecodeArrayBuilder& LoadBoolean(bool value); // Global loads to the accumulator and stores from the accumulator. BytecodeArrayBuilder& LoadGlobal(const AstRawString* name, int feedback_slot, TypeofMode typeof_mode); BytecodeArrayBuilder& StoreGlobal(const AstRawString* name, int feedback_slot); // Load the object at |slot_index| at |depth| in the context chain starting // with |context| into the accumulator. enum ContextSlotMutability { kImmutableSlot, kMutableSlot }; BytecodeArrayBuilder& LoadContextSlot(Register context, int slot_index, int depth, ContextSlotMutability immutable); // Stores the object in the accumulator into |slot_index| at |depth| in the // context chain starting with |context|. BytecodeArrayBuilder& StoreContextSlot(Register context, int slot_index, int depth); // Load from a module variable into the accumulator. |depth| is the depth of // the current context relative to the module context. BytecodeArrayBuilder& LoadModuleVariable(int cell_index, int depth); // Store from the accumulator into a module variable. |depth| is the depth of // the current context relative to the module context. BytecodeArrayBuilder& StoreModuleVariable(int cell_index, int depth); // Register-accumulator transfers. BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg); BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg); // Register-register transfer. BytecodeArrayBuilder& MoveRegister(Register from, Register to); // Named load property. BytecodeArrayBuilder& LoadNamedProperty(Register object, const AstRawString* name, int feedback_slot); // Named load property without feedback BytecodeArrayBuilder& LoadNamedPropertyNoFeedback(Register object, const AstRawString* name); // Keyed load property. The key should be in the accumulator. BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot); // Named load property of the @@iterator symbol. BytecodeArrayBuilder& LoadIteratorProperty(Register object, int feedback_slot); // Named load property of the @@asyncIterator symbol. BytecodeArrayBuilder& LoadAsyncIteratorProperty(Register object, int feedback_slot); // Store properties. Flag for NeedsSetFunctionName() should // be in the accumulator. BytecodeArrayBuilder& StoreDataPropertyInLiteral( Register object, Register name, DataPropertyInLiteralFlags flags, int feedback_slot); // Collect type information for developer tools. The value for which we // record the type is stored in the accumulator. BytecodeArrayBuilder& CollectTypeProfile(int position); // Store a property named by a property name. The value to be stored should be // in the accumulator. BytecodeArrayBuilder& StoreNamedProperty(Register object, const AstRawString* name, int feedback_slot, LanguageMode language_mode); // Store a property named by a property name without feedback slot. The value // to be stored should be in the accumulator. BytecodeArrayBuilder& StoreNamedPropertyNoFeedback( Register object, const AstRawString* name, LanguageMode language_mode); // Store a property named by a constant from the constant pool. The value to // be stored should be in the accumulator. BytecodeArrayBuilder& StoreNamedProperty(Register object, size_t constant_pool_entry, int feedback_slot, LanguageMode language_mode); // Store an own property named by a constant from the constant pool. The // value to be stored should be in the accumulator. BytecodeArrayBuilder& StoreNamedOwnProperty(Register object, const AstRawString* name, int feedback_slot); // Store a property keyed by a value in a register. The value to be stored // should be in the accumulator. BytecodeArrayBuilder& StoreKeyedProperty(Register object, Register key, int feedback_slot, LanguageMode language_mode); // Store an own element in an array literal. The value to be stored should be // in the accumulator. BytecodeArrayBuilder& StoreInArrayLiteral(Register array, Register index, int feedback_slot); // Store the home object property. The value to be stored should be in the // accumulator. BytecodeArrayBuilder& StoreHomeObjectProperty(Register object, int feedback_slot, LanguageMode language_mode); // Store the class fields property. The initializer to be stored should // be in the accumulator. BytecodeArrayBuilder& StoreClassFieldsInitializer(Register constructor, int feedback_slot); // Load class fields property. BytecodeArrayBuilder& LoadClassFieldsInitializer(Register constructor, int feedback_slot); // Lookup the variable with |name|. BytecodeArrayBuilder& LoadLookupSlot(const AstRawString* name, TypeofMode typeof_mode); // Lookup the variable with |name|, which is known to be at |slot_index| at // |depth| in the context chain if not shadowed by a context extension // somewhere in that context chain. BytecodeArrayBuilder& LoadLookupContextSlot(const AstRawString* name, TypeofMode typeof_mode, int slot_index, int depth); // Lookup the variable with |name|, which has its feedback in |feedback_slot| // and is known to be global if not shadowed by a context extension somewhere // up to |depth| in that context chain. BytecodeArrayBuilder& LoadLookupGlobalSlot(const AstRawString* name, TypeofMode typeof_mode, int feedback_slot, int depth); // Store value in the accumulator into the variable with |name|. BytecodeArrayBuilder& StoreLookupSlot( const AstRawString* name, LanguageMode language_mode, LookupHoistingMode lookup_hoisting_mode); // Create a new closure for a SharedFunctionInfo which will be inserted at // constant pool index |shared_function_info_entry|. BytecodeArrayBuilder& CreateClosure(size_t shared_function_info_entry, int slot, int flags); // Create a new local context for a |scope|. BytecodeArrayBuilder& CreateBlockContext(const Scope* scope); // Create a new context for a catch block with |exception| and |scope|. BytecodeArrayBuilder& CreateCatchContext(Register exception, const Scope* scope); // Create a new context with the given |scope| and size |slots|. BytecodeArrayBuilder& CreateFunctionContext(const Scope* scope, int slots); // Create a new eval context with the given |scope| and size |slots|. BytecodeArrayBuilder& CreateEvalContext(const Scope* scope, int slots); // Creates a new context with the given |scope| for a with-statement // with the |object| in a register. BytecodeArrayBuilder& CreateWithContext(Register object, const Scope* scope); // Create a new arguments object in the accumulator. BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type); // Literals creation. Constant elements should be in the accumulator. BytecodeArrayBuilder& CreateRegExpLiteral(const AstRawString* pattern, int literal_index, int flags); BytecodeArrayBuilder& CreateArrayLiteral(size_t constant_elements_entry, int literal_index, int flags); BytecodeArrayBuilder& CreateEmptyArrayLiteral(int literal_index); BytecodeArrayBuilder& CreateArrayFromIterable(); BytecodeArrayBuilder& CreateObjectLiteral(size_t constant_properties_entry, int literal_index, int flags); BytecodeArrayBuilder& CreateEmptyObjectLiteral(); BytecodeArrayBuilder& CloneObject(Register source, int flags, int feedback_slot); // Gets or creates the template for a TemplateObjectDescription which will // be inserted at constant pool index |template_object_description_entry|. BytecodeArrayBuilder& GetTemplateObject( size_t template_object_description_entry, int feedback_slot); // Push the context in accumulator as the new context, and store in register // |context|. BytecodeArrayBuilder& PushContext(Register context); // Pop the current context and replace with |context|. BytecodeArrayBuilder& PopContext(Register context); // Call a JS function which is known to be a property of a JS object. The // JSFunction or Callable to be called should be in |callable|. The arguments // should be in |args|, with the receiver in |args[0]|. The call type of the // expression is in |call_type|. Type feedback is recorded in the // |feedback_slot| in the type feedback vector. BytecodeArrayBuilder& CallProperty(Register callable, RegisterList args, int feedback_slot); // Call a JS function with an known undefined receiver. The JSFunction or // Callable to be called should be in |callable|. The arguments should be in // |args|, with no receiver as it is implicitly set to undefined. Type // feedback is recorded in the |feedback_slot| in the type feedback vector. BytecodeArrayBuilder& CallUndefinedReceiver(Register callable, RegisterList args, int feedback_slot); // Call a JS function with an any receiver, possibly (but not necessarily) // undefined. The JSFunction or Callable to be called should be in |callable|. // The arguments should be in |args|, with the receiver in |args[0]|. Type // feedback is recorded in the |feedback_slot| in the type feedback vector. BytecodeArrayBuilder& CallAnyReceiver(Register callable, RegisterList args, int feedback_slot); // Call a JS function with an any receiver, possibly (but not necessarily) // undefined. The JSFunction or Callable to be called should be in |callable|. // The arguments should be in |args|, with the receiver in |args[0]|. BytecodeArrayBuilder& CallNoFeedback(Register callable, RegisterList args); // Tail call into a JS function. The JSFunction or Callable to be called // should be in |callable|. The arguments should be in |args|, with the // receiver in |args[0]|. Type feedback is recorded in the |feedback_slot| in // the type feedback vector. BytecodeArrayBuilder& TailCall(Register callable, RegisterList args, int feedback_slot); // Call a JS function. The JSFunction or Callable to be called should be in // |callable|, the receiver in |args[0]| and the arguments in |args[1]| // onwards. The final argument must be a spread. BytecodeArrayBuilder& CallWithSpread(Register callable, RegisterList args, int feedback_slot); // Call the Construct operator. The accumulator holds the |new_target|. // The |constructor| is in a register and arguments are in |args|. BytecodeArrayBuilder& Construct(Register constructor, RegisterList args, int feedback_slot); // Call the Construct operator for use with a spread. The accumulator holds // the |new_target|. The |constructor| is in a register and arguments are in // |args|. The final argument must be a spread. BytecodeArrayBuilder& ConstructWithSpread(Register constructor, RegisterList args, int feedback_slot); // Call the runtime function with |function_id| and arguments |args|. BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id, RegisterList args); // Call the runtime function with |function_id| with single argument |arg|. BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id, Register arg); // Call the runtime function with |function_id| with no arguments. BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id); // Call the runtime function with |function_id| and arguments |args|, that // returns a pair of values. The return values will be returned in // |return_pair|. BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id, RegisterList args, RegisterList return_pair); // Call the runtime function with |function_id| with single argument |arg| // that returns a pair of values. The return values will be returned in // |return_pair|. BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id, Register arg, RegisterList return_pair); // Call the JS runtime function with |context_index| and arguments |args|, // with no receiver as it is implicitly set to undefined. BytecodeArrayBuilder& CallJSRuntime(int context_index, RegisterList args); // Operators (register holds the lhs value, accumulator holds the rhs value). // Type feedback will be recorded in the |feedback_slot| BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg, int feedback_slot); // Same as above, but lhs in the accumulator and rhs in |literal|. BytecodeArrayBuilder& BinaryOperationSmiLiteral(Token::Value binop, Smi literal, int feedback_slot); // Unary and Count Operators (value stored in accumulator). // Type feedback will be recorded in the |feedback_slot| BytecodeArrayBuilder& UnaryOperation(Token::Value op, int feedback_slot); enum class ToBooleanMode { kConvertToBoolean, // Perform ToBoolean conversion on accumulator. kAlreadyBoolean, // Accumulator is already a Boolean. }; // Unary Operators. BytecodeArrayBuilder& LogicalNot(ToBooleanMode mode); BytecodeArrayBuilder& TypeOf(); // Expects a heap object in the accumulator. Returns its super constructor in // the register |out| if it passes the IsConstructor test. Otherwise, it // throws a TypeError exception. BytecodeArrayBuilder& GetSuperConstructor(Register out); // Deletes property from an object. This expects that accumulator contains // the key to be deleted and the register contains a reference to the object. BytecodeArrayBuilder& Delete(Register object, LanguageMode language_mode); // JavaScript defines two kinds of 'nil'. enum NilValue { kNullValue, kUndefinedValue }; // Tests. BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg, int feedback_slot); BytecodeArrayBuilder& CompareReference(Register reg); BytecodeArrayBuilder& CompareUndetectable(); BytecodeArrayBuilder& CompareUndefined(); BytecodeArrayBuilder& CompareNull(); BytecodeArrayBuilder& CompareNil(Token::Value op, NilValue nil); BytecodeArrayBuilder& CompareTypeOf( TestTypeOfFlags::LiteralFlag literal_flag); // Converts accumulator and stores result in register |out|. BytecodeArrayBuilder& ToObject(Register out); BytecodeArrayBuilder& ToName(Register out); BytecodeArrayBuilder& ToString(); // Converts accumulator and stores result back in accumulator. BytecodeArrayBuilder& ToNumber(int feedback_slot); BytecodeArrayBuilder& ToNumeric(int feedback_slot); // Exception handling. BytecodeArrayBuilder& MarkHandler(int handler_id, HandlerTable::CatchPrediction will_catch); BytecodeArrayBuilder& MarkTryBegin(int handler_id, Register context); BytecodeArrayBuilder& MarkTryEnd(int handler_id); // Flow Control. BytecodeArrayBuilder& Bind(BytecodeLabel* label); BytecodeArrayBuilder& Bind(BytecodeLoopHeader* label); BytecodeArrayBuilder& Bind(BytecodeJumpTable* jump_table, int case_value); BytecodeArrayBuilder& Jump(BytecodeLabel* label); BytecodeArrayBuilder& JumpLoop(BytecodeLoopHeader* loop_header, int loop_depth); BytecodeArrayBuilder& JumpIfTrue(ToBooleanMode mode, BytecodeLabel* label); BytecodeArrayBuilder& JumpIfFalse(ToBooleanMode mode, BytecodeLabel* label); BytecodeArrayBuilder& JumpIfJSReceiver(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfNotNull(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfNotUndefined(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfNil(BytecodeLabel* label, Token::Value op, NilValue nil); BytecodeArrayBuilder& JumpIfNotNil(BytecodeLabel* label, Token::Value op, NilValue nil); BytecodeArrayBuilder& SwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table); BytecodeArrayBuilder& StackCheck(int position); // Sets the pending message to the value in the accumulator, and returns the // previous pending message in the accumulator. BytecodeArrayBuilder& SetPendingMessage(); BytecodeArrayBuilder& Throw(); BytecodeArrayBuilder& ReThrow(); BytecodeArrayBuilder& Abort(AbortReason reason); BytecodeArrayBuilder& Return(); BytecodeArrayBuilder& ThrowReferenceErrorIfHole(const AstRawString* name); BytecodeArrayBuilder& ThrowSuperNotCalledIfHole(); BytecodeArrayBuilder& ThrowSuperAlreadyCalledIfNotHole(); // Debugger. BytecodeArrayBuilder& Debugger(); // Increment the block counter at the given slot (block code coverage). BytecodeArrayBuilder& IncBlockCounter(int slot); // Complex flow control. BytecodeArrayBuilder& ForInEnumerate(Register receiver); BytecodeArrayBuilder& ForInPrepare(RegisterList cache_info_triple, int feedback_slot); BytecodeArrayBuilder& ForInContinue(Register index, Register cache_length); BytecodeArrayBuilder& ForInNext(Register receiver, Register index, RegisterList cache_type_array_pair, int feedback_slot); BytecodeArrayBuilder& ForInStep(Register index); // Generators. BytecodeArrayBuilder& SuspendGenerator(Register generator, RegisterList registers, int suspend_id); BytecodeArrayBuilder& SwitchOnGeneratorState(Register generator, BytecodeJumpTable* jump_table); BytecodeArrayBuilder& ResumeGenerator(Register generator, RegisterList registers); // Creates a new handler table entry and returns a {hander_id} identifying the // entry, so that it can be referenced by above exception handling support. int NewHandlerEntry() { return handler_table_builder()->NewHandlerEntry(); } // Allocates a new jump table of given |size| and |case_value_base| in the // constant pool. BytecodeJumpTable* AllocateJumpTable(int size, int case_value_base); // Gets a constant pool entry. size_t GetConstantPoolEntry(const AstRawString* raw_string); size_t GetConstantPoolEntry(AstBigInt bigint); size_t GetConstantPoolEntry(const Scope* scope); size_t GetConstantPoolEntry(double number); #define ENTRY_GETTER(NAME, ...) size_t NAME##ConstantPoolEntry(); SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_GETTER) #undef ENTRY_GETTER // Allocates a slot in the constant pool which can later be set. size_t AllocateDeferredConstantPoolEntry(); // Sets the deferred value into an allocated constant pool entry. void SetDeferredConstantPoolEntry(size_t entry, Handle<Object> object); void InitializeReturnPosition(FunctionLiteral* literal); void SetStatementPosition(Statement* stmt) { if (stmt->position() == kNoSourcePosition) return; latest_source_info_.MakeStatementPosition(stmt->position()); } void SetExpressionPosition(Expression* expr) { SetExpressionPosition(expr->position()); } void SetExpressionPosition(int position) { if (position == kNoSourcePosition) return; if (!latest_source_info_.is_statement()) { // Ensure the current expression position is overwritten with the // latest value. latest_source_info_.MakeExpressionPosition(position); } } void SetExpressionAsStatementPosition(Expression* expr) { if (expr->position() == kNoSourcePosition) return; latest_source_info_.MakeStatementPosition(expr->position()); } void SetReturnPosition(int source_position, FunctionLiteral* literal) { if (source_position != kNoSourcePosition) { latest_source_info_.MakeStatementPosition(source_position); } else if (literal->return_position() != kNoSourcePosition) { latest_source_info_.MakeStatementPosition(literal->return_position()); } } bool RemainderOfBlockIsDead() const { return bytecode_array_writer_.RemainderOfBlockIsDead(); } // Returns the raw operand value for the given register or register list. uint32_t GetInputRegisterOperand(Register reg); uint32_t GetOutputRegisterOperand(Register reg); uint32_t GetInputRegisterListOperand(RegisterList reg_list); uint32_t GetOutputRegisterListOperand(RegisterList reg_list); // Outputs raw register transfer bytecodes without going through the register // optimizer. void OutputLdarRaw(Register reg); void OutputStarRaw(Register reg); void OutputMovRaw(Register src, Register dest); // Accessors BytecodeRegisterAllocator* register_allocator() { return ®ister_allocator_; } const BytecodeRegisterAllocator* register_allocator() const { return ®ister_allocator_; } Zone* zone() const { return zone_; } private: friend class BytecodeRegisterAllocator; template <Bytecode bytecode, AccumulatorUse accumulator_use, OperandType... operand_types> friend class BytecodeNodeBuilder; const FeedbackVectorSpec* feedback_vector_spec() const { return feedback_vector_spec_; } // Returns the current source position for the given |bytecode|. V8_INLINE BytecodeSourceInfo CurrentSourcePosition(Bytecode bytecode); #define DECLARE_BYTECODE_OUTPUT(Name, ...) \ template <typename... Operands> \ V8_INLINE BytecodeNode Create##Name##Node(Operands... operands); \ template <typename... Operands> \ V8_INLINE void Output##Name(Operands... operands); \ template <typename... Operands> \ V8_INLINE void Output##Name(BytecodeLabel* label, Operands... operands); BYTECODE_LIST(DECLARE_BYTECODE_OUTPUT) #undef DECLARE_OPERAND_TYPE_INFO V8_INLINE void OutputJumpLoop(BytecodeLoopHeader* loop_header, int loop_depth); V8_INLINE void OutputSwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table); bool RegisterIsValid(Register reg) const; bool RegisterListIsValid(RegisterList reg_list) const; // Sets a deferred source info which should be emitted before any future // source info (either attached to a following bytecode or as a nop). void SetDeferredSourceInfo(BytecodeSourceInfo source_info); // Either attach deferred source info to node, or emit it as a nop bytecode // if node already have valid source info. void AttachOrEmitDeferredSourceInfo(BytecodeNode* node); // Write bytecode to bytecode array. void Write(BytecodeNode* node); void WriteJump(BytecodeNode* node, BytecodeLabel* label); void WriteJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header); void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* label); // Not implemented as the illegal bytecode is used inside internally // to indicate a bytecode field is not valid or an error has occurred // during bytecode generation. BytecodeArrayBuilder& Illegal(); template <Bytecode bytecode, AccumulatorUse accumulator_use> void PrepareToOutputBytecode(); BytecodeArrayWriter* bytecode_array_writer() { return &bytecode_array_writer_; } ConstantArrayBuilder* constant_array_builder() { return &constant_array_builder_; } const ConstantArrayBuilder* constant_array_builder() const { return &constant_array_builder_; } HandlerTableBuilder* handler_table_builder() { return &handler_table_builder_; } Zone* zone_; FeedbackVectorSpec* feedback_vector_spec_; bool bytecode_generated_; ConstantArrayBuilder constant_array_builder_; HandlerTableBuilder handler_table_builder_; int parameter_count_; int local_register_count_; BytecodeRegisterAllocator register_allocator_; BytecodeArrayWriter bytecode_array_writer_; BytecodeRegisterOptimizer* register_optimizer_; BytecodeSourceInfo latest_source_info_; BytecodeSourceInfo deferred_source_info_; DISALLOW_COPY_AND_ASSIGN(BytecodeArrayBuilder); }; V8_EXPORT_PRIVATE std::ostream& operator<<( std::ostream& os, const BytecodeArrayBuilder::ToBooleanMode& mode); } // namespace interpreter } // namespace internal } // namespace v8 #endif // V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_