diff --git a/.gitignore b/.gitignore index 0c6cbb1f84..0cb2181102 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ *.xcodeproj #*# *~ +.#* .cpplint-cache .cproject .d8_history diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc index be40706321..dc3c93067a 100644 --- a/src/compiler/bytecode-graph-builder.cc +++ b/src/compiler/bytecode-graph-builder.cc @@ -602,6 +602,12 @@ void BytecodeGraphBuilder::VisitToNumber( } +void BytecodeGraphBuilder::VisitToObject( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + void BytecodeGraphBuilder::VisitJump( const interpreter::BytecodeArrayIterator& iterator) { UNIMPLEMENTED(); @@ -662,6 +668,30 @@ void BytecodeGraphBuilder::VisitJumpIfToBooleanFalseConstant( } +void BytecodeGraphBuilder::VisitJumpIfNull( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitJumpIfNullConstant( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitJumpIfUndefined( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitJumpIfUndefinedConstant( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + void BytecodeGraphBuilder::VisitReturn( const interpreter::BytecodeArrayIterator& iterator) { Node* control = @@ -670,6 +700,24 @@ void BytecodeGraphBuilder::VisitReturn( } +void BytecodeGraphBuilder::VisitForInPrepare( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitForInNext( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitForInDone( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + Node** BytecodeGraphBuilder::EnsureInputBufferSize(int size) { if (size > input_buffer_size_) { size = size + kInputBufferSizeIncrement + input_buffer_size_; diff --git a/src/compiler/interpreter-assembler.cc b/src/compiler/interpreter-assembler.cc index 4faad82467..9fa284c009 100644 --- a/src/compiler/interpreter-assembler.cc +++ b/src/compiler/interpreter-assembler.cc @@ -292,6 +292,15 @@ Node* InterpreterAssembler::LoadConstantPoolEntry(Node* index) { } +Node* InterpreterAssembler::LoadFixedArrayElement(Node* fixed_array, + int index) { + Node* entry_offset = + IntPtrAdd(IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag), + WordShl(Int32Constant(index), kPointerSizeLog2)); + return raw_assembler_->Load(kMachAnyTagged, fixed_array, entry_offset); +} + + Node* InterpreterAssembler::LoadObjectField(Node* object, int offset) { return raw_assembler_->Load(kMachAnyTagged, object, IntPtrConstant(offset - kHeapObjectTag)); diff --git a/src/compiler/interpreter-assembler.h b/src/compiler/interpreter-assembler.h index b546fb21f0..abe98055c5 100644 --- a/src/compiler/interpreter-assembler.h +++ b/src/compiler/interpreter-assembler.h @@ -92,6 +92,9 @@ class InterpreterAssembler { // Load constant at |index| in the constant pool. Node* LoadConstantPoolEntry(Node* index); + // Load an element from a fixed array on the heap. + Node* LoadFixedArrayElement(Node* fixed_array, int index); + // Load a field from an object on the heap. Node* LoadObjectField(Node* object, int offset); diff --git a/src/compiler/raw-machine-assembler.h b/src/compiler/raw-machine-assembler.h index be6bfb8f27..cfb88ef7b3 100644 --- a/src/compiler/raw-machine-assembler.h +++ b/src/compiler/raw-machine-assembler.h @@ -75,6 +75,10 @@ class RawMachineAssembler { // place them into the current basic block. They don't perform control flow, // hence will not switch the current basic block. + Node* NullConstant() { + return HeapConstant(isolate()->factory()->null_value()); + } + Node* UndefinedConstant() { return HeapConstant(isolate()->factory()->undefined_value()); } diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc index 2ce9d51377..7575663e0f 100644 --- a/src/interpreter/bytecode-array-builder.cc +++ b/src/interpreter/bytecode-array-builder.cc @@ -263,6 +263,8 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadFalse() { BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister( Register reg) { + // TODO(oth): Avoid loading the accumulator with the register if the + // previous bytecode stored the accumulator with the same register. Output(Bytecode::kLdar, reg.ToOperand()); return *this; } @@ -270,6 +272,8 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister( BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( Register reg) { + // TODO(oth): Avoid storing the accumulator in the register if the + // previous bytecode loaded the accumulator with the same register. Output(Bytecode::kStar, reg.ToOperand()); return *this; } @@ -481,6 +485,12 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() { } +BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToJSObject() { + Output(Bytecode::kToObject); + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() { Output(Bytecode::kToName); return *this; @@ -531,6 +541,10 @@ Bytecode BytecodeArrayBuilder::GetJumpWithConstantOperand( return Bytecode::kJumpIfToBooleanTrueConstant; case Bytecode::kJumpIfToBooleanFalse: return Bytecode::kJumpIfToBooleanFalseConstant; + case Bytecode::kJumpIfNull: + return Bytecode::kJumpIfNullConstant; + case Bytecode::kJumpIfUndefined: + return Bytecode::kJumpIfUndefinedConstant; default: UNREACHABLE(); return Bytecode::kJumpConstant; @@ -631,6 +645,17 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfToBooleanFalse( } +BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) { + return OutputJump(Bytecode::kJumpIfNull, label); +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined( + BytecodeLabel* label) { + return OutputJump(Bytecode::kJumpIfUndefined, label); +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() { Output(Bytecode::kThrow); exit_seen_in_block_ = true; @@ -645,6 +670,25 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Return() { } +BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare(Register receiver) { + Output(Bytecode::kForInPrepare, receiver.ToOperand()); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(Register for_in_state, + Register index) { + Output(Bytecode::kForInNext, for_in_state.ToOperand(), index.ToOperand()); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register for_in_state) { + Output(Bytecode::kForInDone, for_in_state.ToOperand()); + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::EnterBlock() { return *this; } diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h index 4ec50bb623..ac3692ba29 100644 --- a/src/interpreter/bytecode-array-builder.h +++ b/src/interpreter/bytecode-array-builder.h @@ -170,6 +170,7 @@ class BytecodeArrayBuilder { // Casts. BytecodeArrayBuilder& CastAccumulatorToBoolean(); + BytecodeArrayBuilder& CastAccumulatorToJSObject(); BytecodeArrayBuilder& CastAccumulatorToName(); BytecodeArrayBuilder& CastAccumulatorToNumber(); @@ -180,6 +181,8 @@ class BytecodeArrayBuilder { BytecodeArrayBuilder& Jump(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfTrue(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfFalse(BytecodeLabel* label); + BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label); + BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label); // TODO(mythria) The following two functions should be merged into // JumpIfTrue/False. These bytecodes should be automatically chosen rather // than explicitly using them. @@ -189,6 +192,11 @@ class BytecodeArrayBuilder { BytecodeArrayBuilder& Throw(); BytecodeArrayBuilder& Return(); + // Complex flow control. + BytecodeArrayBuilder& ForInPrepare(Register receiver); + BytecodeArrayBuilder& ForInNext(Register for_in_state, Register index); + BytecodeArrayBuilder& ForInDone(Register for_in_state); + BytecodeArrayBuilder& EnterBlock(); BytecodeArrayBuilder& LeaveBlock(); @@ -287,7 +295,7 @@ class BytecodeLabel final { bound_ = true; } INLINE(void set_referrer(size_t offset)) { - DCHECK(!bound_ && offset != kInvalidOffset); + DCHECK(!bound_ && offset != kInvalidOffset && offset_ == kInvalidOffset); offset_ = offset; } INLINE(size_t offset() const) { return offset_; } diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index a8407294a7..568476e3bc 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -613,8 +613,117 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { } +void BytecodeGenerator::VisitForInAssignment(Expression* expr, + FeedbackVectorSlot slot) { + DCHECK(expr->IsValidReferenceExpression()); + + // Evaluate assignment starting with the value to be stored in the + // accumulator. + Property* property = expr->AsProperty(); + LhsKind assign_type = Property::GetAssignType(property); + switch (assign_type) { + case VARIABLE: { + Variable* variable = expr->AsVariableProxy()->var(); + VisitVariableAssignment(variable, slot); + break; + } + case NAMED_PROPERTY: { + TemporaryRegisterScope temporary_register_scope(builder()); + Register value = temporary_register_scope.NewRegister(); + builder()->StoreAccumulatorInRegister(value); + Register object = VisitForRegisterValue(property->obj()); + size_t name_index = builder()->GetConstantPoolEntry( + property->key()->AsLiteral()->AsPropertyName()); + builder()->StoreNamedProperty(object, name_index, feedback_index(slot), + language_mode()); + break; + } + case KEYED_PROPERTY: { + TemporaryRegisterScope temporary_register_scope(builder()); + Register value = temporary_register_scope.NewRegister(); + builder()->StoreAccumulatorInRegister(value); + Register object = VisitForRegisterValue(property->obj()); + Register key = VisitForRegisterValue(property->key()); + builder()->LoadAccumulatorWithRegister(value); + builder()->StoreKeyedProperty(object, key, feedback_index(slot), + language_mode()); + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNIMPLEMENTED(); + } +} + + void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) { - UNIMPLEMENTED(); + // TODO(oth): For now we need a parent scope for paths that end up + // in VisitLiteral which can allocate in the parent scope. A future + // CL in preparation will add a StatementResultScope that will + // remove the need for this EffectResultScope. + EffectResultScope result_scope(this); + + if (stmt->subject()->IsNullLiteral() || + stmt->subject()->IsUndefinedLiteral(isolate())) { + // ForIn generates lots of code, skip if it wouldn't produce any effects. + return; + } + + LoopBuilder loop_builder(builder()); + ControlScopeForIteration control_scope(this, stmt, &loop_builder); + + // Prepare the state for executing ForIn. + builder()->EnterBlock(); + VisitForAccumulatorValue(stmt->subject()); + loop_builder.BreakIfUndefined(); + loop_builder.BreakIfNull(); + + Register receiver = execution_result()->NewRegister(); + builder()->CastAccumulatorToJSObject(); + builder()->StoreAccumulatorInRegister(receiver); + builder()->CallRuntime(Runtime::kGetPropertyNamesFast, receiver, 1); + builder()->ForInPrepare(receiver); + loop_builder.BreakIfUndefined(); + + Register for_in_state = execution_result()->NewRegister(); + builder()->StoreAccumulatorInRegister(for_in_state); + + // The loop. + BytecodeLabel condition_label, break_label, continue_label; + Register index = receiver; // Re-using register as receiver no longer used. + builder()->LoadLiteral(Smi::FromInt(0)); + + // Check loop termination (accumulator holds index). + builder() + ->Bind(&condition_label) + .StoreAccumulatorInRegister(index) + .ForInDone(for_in_state); + loop_builder.BreakIfTrue(); + + // Get the next item. + builder()->ForInNext(for_in_state, index); + + // Start again if the item, currently in the accumulator, is undefined. + loop_builder.ContinueIfUndefined(); + + // Store the value in the each variable. + VisitForInAssignment(stmt->each(), stmt->EachFeedbackSlot()); + // NB the user's loop variable will be assigned the value of each so + // even an empty body will have this assignment. + Visit(stmt->body()); + + // Increment the index and start loop again. + builder() + ->Bind(&continue_label) + .LoadAccumulatorWithRegister(index) + .CountOperation(Token::Value::ADD, language_mode_strength()) + .Jump(&condition_label); + + // End of loop + builder()->Bind(&break_label).LeaveBlock(); + + loop_builder.SetBreakTarget(break_label); + loop_builder.SetContinueTarget(continue_label); } diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h index c153188ef0..b42311b194 100644 --- a/src/interpreter/bytecode-generator.h +++ b/src/interpreter/bytecode-generator.h @@ -78,6 +78,7 @@ class BytecodeGenerator : public AstVisitor { void VisitObjectLiteralAccessor(Register home_object, ObjectLiteralProperty* property, Register value_out); + void VisitForInAssignment(Expression* expr, FeedbackVectorSlot slot); // Visitors for obtaining expression result in the accumulator, in a diff --git a/src/interpreter/bytecodes.cc b/src/interpreter/bytecodes.cc index 53c4128fdd..8b99f4fb1c 100644 --- a/src/interpreter/bytecodes.cc +++ b/src/interpreter/bytecodes.cc @@ -163,7 +163,9 @@ bool Bytecodes::IsJump(Bytecode bytecode) { return bytecode == Bytecode::kJump || bytecode == Bytecode::kJumpIfTrue || bytecode == Bytecode::kJumpIfFalse || bytecode == Bytecode::kJumpIfToBooleanTrue || - bytecode == Bytecode::kJumpIfToBooleanFalse; + bytecode == Bytecode::kJumpIfToBooleanFalse || + bytecode == Bytecode::kJumpIfNull || + bytecode == Bytecode::kJumpIfUndefined; } @@ -173,7 +175,9 @@ bool Bytecodes::IsJumpConstant(Bytecode bytecode) { bytecode == Bytecode::kJumpIfTrueConstant || bytecode == Bytecode::kJumpIfFalseConstant || bytecode == Bytecode::kJumpIfToBooleanTrueConstant || - bytecode == Bytecode::kJumpIfToBooleanFalseConstant; + bytecode == Bytecode::kJumpIfToBooleanFalseConstant || + bytecode == Bytecode::kJumpIfNull || + bytecode == Bytecode::kJumpIfUndefinedConstant; } diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index f21f17c0da..baf7550c04 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -118,6 +118,7 @@ namespace interpreter { V(ToBoolean, OperandType::kNone) \ V(ToName, OperandType::kNone) \ V(ToNumber, OperandType::kNone) \ + V(ToObject, OperandType::kNone) \ \ /* Literals */ \ V(CreateRegExpLiteral, OperandType::kIdx8, OperandType::kReg8) \ @@ -142,6 +143,17 @@ namespace interpreter { V(JumpIfToBooleanTrueConstant, OperandType::kIdx8) \ V(JumpIfToBooleanFalse, OperandType::kImm8) \ V(JumpIfToBooleanFalseConstant, OperandType::kIdx8) \ + V(JumpIfNull, OperandType::kImm8) \ + V(JumpIfNullConstant, OperandType::kIdx8) \ + V(JumpIfUndefined, OperandType::kImm8) \ + V(JumpIfUndefinedConstant, OperandType::kIdx8) \ + \ + /* Complex flow control For..in */ \ + V(ForInPrepare, OperandType::kReg8) \ + V(ForInNext, OperandType::kReg8, OperandType::kReg8) \ + V(ForInDone, OperandType::kReg8) \ + \ + /* Non-local flow control */ \ V(Throw, OperandType::kNone) \ V(Return, OperandType::kNone) diff --git a/src/interpreter/control-flow-builders.cc b/src/interpreter/control-flow-builders.cc index 6c634bbe84..3d9b1c14f4 100644 --- a/src/interpreter/control-flow-builders.cc +++ b/src/interpreter/control-flow-builders.cc @@ -31,6 +31,24 @@ void LoopBuilder::EmitJump(ZoneVector* sites) { } +void LoopBuilder::EmitJumpIfTrue(ZoneVector* sites) { + sites->push_back(BytecodeLabel()); + builder()->JumpIfTrue(&sites->back()); +} + + +void LoopBuilder::EmitJumpIfUndefined(ZoneVector* sites) { + sites->push_back(BytecodeLabel()); + builder()->JumpIfUndefined(&sites->back()); +} + + +void LoopBuilder::EmitJumpIfNull(ZoneVector* sites) { + sites->push_back(BytecodeLabel()); + builder()->JumpIfNull(&sites->back()); +} + + void LoopBuilder::BindLabels(const BytecodeLabel& target, ZoneVector* sites) { for (size_t i = 0; i < sites->size(); i++) { diff --git a/src/interpreter/control-flow-builders.h b/src/interpreter/control-flow-builders.h index c92fab2099..3ef800c1cf 100644 --- a/src/interpreter/control-flow-builders.h +++ b/src/interpreter/control-flow-builders.h @@ -49,11 +49,20 @@ class LoopBuilder : public ControlFlowBuilder { // patched when the corresponding SetContinueTarget/SetBreakTarget // is called. void Break() { EmitJump(&break_sites_); } + void BreakIfTrue() { EmitJumpIfTrue(&break_sites_); } + void BreakIfUndefined() { EmitJumpIfUndefined(&break_sites_); } + void BreakIfNull() { EmitJumpIfNull(&break_sites_); } void Continue() { EmitJump(&continue_sites_); } + void ContinueIfTrue() { EmitJumpIfTrue(&continue_sites_); } + void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_sites_); } + void ContinueIfNull() { EmitJumpIfNull(&continue_sites_); } private: void BindLabels(const BytecodeLabel& target, ZoneVector* site); void EmitJump(ZoneVector* labels); + void EmitJumpIfTrue(ZoneVector* labels); + void EmitJumpIfUndefined(ZoneVector* labels); + void EmitJumpIfNull(ZoneVector* labels); // Unbound labels that identify jumps for continue/break statements // in the code. diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 5d45fe3b25..5929047cbb 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -847,6 +847,17 @@ void Interpreter::DoToNumber(compiler::InterpreterAssembler* assembler) { } +// ToObject +// +// Cast the object referenced by the accumulator to a JSObject. +void Interpreter::DoToObject(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kToObject, accumulator); + __ SetAccumulator(result); + __ Dispatch(); +} + + // Jump // // Jump by number of bytes represented by the immediate operand |imm8|. @@ -987,6 +998,62 @@ void Interpreter::DoJumpIfToBooleanFalseConstant( } +// JumpIfNull +// +// Jump by number of bytes represented by an immediate operand if the object +// referenced by the accumulator is the null constant. +void Interpreter::DoJumpIfNull(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* null_value = __ HeapConstant(isolate_->factory()->null_value()); + Node* relative_jump = __ BytecodeOperandImm8(0); + __ JumpIfWordEqual(accumulator, null_value, relative_jump); +} + + +// JumpIfNullConstant +// +// Jump by number of bytes in the Smi in the |idx| entry in the constant pool +// if the object referenced by the accumulator is the null constant. +void Interpreter::DoJumpIfNullConstant( + compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* null_value = __ HeapConstant(isolate_->factory()->null_value()); + Node* index = __ BytecodeOperandIdx8(0); + Node* constant = __ LoadConstantPoolEntry(index); + Node* relative_jump = __ SmiUntag(constant); + __ JumpIfWordEqual(accumulator, null_value, relative_jump); +} + + +// JumpIfUndefined +// +// Jump by number of bytes represented by an immediate operand if the object +// referenced by the accumulator is the undefined constant. +void Interpreter::DoJumpIfUndefined(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* undefined_value = + __ HeapConstant(isolate_->factory()->undefined_value()); + Node* relative_jump = __ BytecodeOperandImm8(0); + __ JumpIfWordEqual(accumulator, undefined_value, relative_jump); +} + + +// JumpIfUndefinedConstant +// +// Jump by number of bytes in the Smi in the |idx| entry in the constant pool +// if the object referenced by the accumulator is the undefined constant. +void Interpreter::DoJumpIfUndefinedConstant( + compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* undefined_value = + __ HeapConstant(isolate_->factory()->undefined_value()); + Node* index = __ BytecodeOperandIdx8(0); + Node* constant = __ LoadConstantPoolEntry(index); + Node* relative_jump = __ SmiUntag(constant); + __ JumpIfWordEqual(accumulator, undefined_value, relative_jump); +} + + // CreateRegExpLiteral // // Creates a regular expression literal for literal index with flags held @@ -1105,6 +1172,57 @@ void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) { } +// ForInPrepare +// +// Returns state for for..in loop execution based on the |receiver| and +// the property names in the accumulator. +void Interpreter::DoForInPrepare(compiler::InterpreterAssembler* assembler) { + Node* receiver_reg = __ BytecodeOperandReg8(0); + Node* receiver = __ LoadRegister(receiver_reg); + Node* property_names = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kInterpreterForInPrepare, receiver, + property_names); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// ForInNext +// +// Returns the next key in a for..in loop. The state associated with the +// iteration is contained in |for_in_state| and |index| is the current +// zero-based iteration count. +void Interpreter::DoForInNext(compiler::InterpreterAssembler* assembler) { + Node* for_in_state_reg = __ BytecodeOperandReg8(0); + Node* for_in_state = __ LoadRegister(for_in_state_reg); + Node* receiver = __ LoadFixedArrayElement(for_in_state, 0); + Node* cache_array = __ LoadFixedArrayElement(for_in_state, 1); + Node* cache_type = __ LoadFixedArrayElement(for_in_state, 2); + Node* index_reg = __ BytecodeOperandReg8(1); + Node* index = __ LoadRegister(index_reg); + Node* result = __ CallRuntime(Runtime::kForInNext, receiver, cache_array, + cache_type, index); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// ForInDone +// +// Returns the next key in a for..in loop. The accumulator contains the current +// zero-based iteration count and |for_in_state| is the state returned by an +// earlier invocation of ForInPrepare. +void Interpreter::DoForInDone(compiler::InterpreterAssembler* assembler) { + Node* index = __ GetAccumulator(); + Node* for_in_state_reg = __ BytecodeOperandReg8(0); + Node* for_in_state = __ LoadRegister(for_in_state_reg); + Node* cache_length = __ LoadFixedArrayElement(for_in_state, 3); + Node* result = __ CallRuntime(Runtime::kForInDone, index, cache_length); + __ SetAccumulator(result); + __ Dispatch(); +} + + } // namespace interpreter } // namespace internal } // namespace v8 diff --git a/src/runtime/runtime-interpreter.cc b/src/runtime/runtime-interpreter.cc index 96ebe5d4d3..ef86869ccc 100644 --- a/src/runtime/runtime-interpreter.cc +++ b/src/runtime/runtime-interpreter.cc @@ -147,5 +147,53 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) { shared, context, static_cast(pretenured_flag)); } + +RUNTIME_FUNCTION(Runtime_InterpreterForInPrepare) { + HandleScope scope(isolate); + DCHECK_EQ(2, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0); + CONVERT_ARG_HANDLE_CHECKED(HeapObject, property_names, 1); + + Handle cache_type = property_names; + Handle cache_type_map = handle(property_names->map(), isolate); + Handle receiver_map = handle(receiver->map(), isolate); + + Handle cache_array; + int cache_length; + + if (cache_type_map.is_identical_to(isolate->factory()->meta_map())) { + int enum_length = cache_type_map->EnumLength(); + DescriptorArray* descriptors = receiver_map->instance_descriptors(); + if (enum_length > 0 && descriptors->HasEnumCache()) { + cache_array = handle(descriptors->GetEnumCache(), isolate); + cache_length = cache_array->length(); + } else { + cache_array = isolate->factory()->empty_fixed_array(); + cache_length = 0; + } + } else { + cache_array = Handle::cast(cache_type); + cache_length = cache_array->length(); + + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + if (receiver_map->instance_type() <= LAST_JS_PROXY_TYPE) { + DCHECK_GE(receiver_map->instance_type(), LAST_JS_PROXY_TYPE); + // Zero indicates proxy + cache_type = Handle(Smi::FromInt(0), isolate); + } else { + // One entails slow check + cache_type = Handle(Smi::FromInt(1), isolate); + } + } + + Handle result = isolate->factory()->NewFixedArray(4); + result->set(0, *receiver); + result->set(1, *cache_array); + result->set(2, *cache_type); + result->set(3, Smi::FromInt(cache_length)); + return *result; +} + + } // namespace internal } // namespace v8 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index fce8bb531c..5312affa19 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -228,7 +228,8 @@ namespace internal { F(InterpreterToBoolean, 1, 1) \ F(InterpreterLogicalNot, 1, 1) \ F(InterpreterTypeOf, 1, 1) \ - F(InterpreterNewClosure, 2, 1) + F(InterpreterNewClosure, 2, 1) \ + F(InterpreterForInPrepare, 1, 1) #define FOR_EACH_INTRINSIC_FUNCTION(F) \ diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc index 8325b5b7db..5f8ce63a2d 100644 --- a/test/cctest/interpreter/test-bytecode-generator.cc +++ b/test/cctest/interpreter/test-bytecode-generator.cc @@ -3817,6 +3817,213 @@ TEST(IllegalRedeclaration) { } +TEST(ForIn) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + int simple_flags = + ArrayLiteral::kDisableMementos | ArrayLiteral::kShallowElements; + int deep_elements_flags = + ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos; + + FeedbackVectorSpec feedback_spec(&zone); + feedback_spec.AddStoreICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); + FeedbackVectorSlot slot3 = feedback_spec.AddStoreICSlot(); + FeedbackVectorSlot slot4 = feedback_spec.AddStoreICSlot(); + Handle vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet snippets[] = { + {"for (var p in null) {}", + 2 * kPointerSize, + 1, + 2, + {B(LdaUndefined), B(Return)}, + 0}, + {"for (var p in undefined) {}", + 2 * kPointerSize, + 1, + 2, + {B(LdaUndefined), B(Return)}, + 0}, + {"var x = 'potatoes';\n" + "for (var p in x) { return p; }", + 5 * kPointerSize, + 1, + 52, + { + B(LdaConstant), U8(0), // + B(Star), R(1), // + B(Ldar), R(1), // + B(JumpIfUndefined), U8(44), // + B(JumpIfNull), U8(42), // + B(ToObject), // + B(Star), R(3), // + B(CallRuntime), U16(Runtime::kGetPropertyNamesFast), R(3), U8(1), // + B(ForInPrepare), R(3), // + B(JumpIfUndefined), U8(30), // + B(Star), R(4), // + B(LdaZero), // + B(Star), R(3), // + B(ForInDone), R(4), // + B(JumpIfTrue), U8(21), // + B(ForInNext), R(4), R(3), // + B(JumpIfUndefined), U8(11), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Star), R(2), // + B(Ldar), R(2), // + B(Return), // + B(Ldar), R(3), // + B(Inc), // + B(Jump), U8(-23), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var x = 0;\n" + "for (var p in [1,2,3]) { x += p; }", + 5 * kPointerSize, + 1, + 57, + { + B(LdaZero), // + B(Star), R(1), // + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(0), U8(simple_flags), // + B(JumpIfUndefined), U8(47), // + B(JumpIfNull), U8(45), // + B(ToObject), // + B(Star), R(3), // + B(CallRuntime), U16(Runtime::kGetPropertyNamesFast), R(3), U8(1), // + B(ForInPrepare), R(3), // + B(JumpIfUndefined), U8(33), // + B(Star), R(4), // + B(LdaZero), // + B(Star), R(3), // + B(ForInDone), R(4), // + B(JumpIfTrue), U8(24), // + B(ForInNext), R(4), R(3), // + B(JumpIfUndefined), U8(14), // + B(Star), R(0), // + B(Ldar), R(0), // + B(Star), R(2), // + B(Ldar), R(2), // + B(Add), R(1), // + B(Star), R(1), // + B(Ldar), R(3), // + B(Inc), // + B(Jump), U8(-26), // + B(LdaUndefined), // + B(Return), // + }, + 1, + {InstanceType::FIXED_ARRAY_TYPE}}, + {"var x = { 'a': 1, 'b': 2 };\n" + "for (x['a'] in [10, 20, 30]) {\n" + " if (x['a'] == 10) continue;\n" + " if (x['a'] == 20) break;\n" + "}", + 4 * kPointerSize, + 1, + 83, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateArrayLiteral), U8(1), U8(simple_flags), // + B(JumpIfUndefined), U8(69), // + B(JumpIfNull), U8(67), // + B(ToObject), // + B(Star), R(1), // + B(CallRuntime), U16(Runtime::kGetPropertyNamesFast), R(1), U8(1), // + B(ForInPrepare), R(1), // + B(JumpIfUndefined), U8(55), // + B(Star), R(2), // + B(LdaZero), // + B(Star), R(1), // + B(ForInDone), R(2), // + B(JumpIfTrue), U8(46), // + B(ForInNext), R(2), R(1), // + B(JumpIfUndefined), U8(36), // + B(Star), R(3), // + B(StoreICSloppy), R(0), U8(2), U8(vector->GetIndex(slot4)), // + B(LoadICSloppy), R(0), U8(2), U8(vector->GetIndex(slot2)), // + B(Star), R(3), // + B(LdaSmi8), U8(10), // + B(TestEqual), R(3), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(16), // + B(LoadICSloppy), R(0), U8(2), U8(vector->GetIndex(slot3)), // + B(Star), R(3), // + B(LdaSmi8), U8(20), // + B(TestEqual), R(3), // + B(JumpIfFalse), U8(4), // + B(Jump), U8(7), // + B(Ldar), R(1), // + B(Inc), // + B(Jump), U8(-48), // + B(LdaUndefined), // + B(Return), // + }, + 3, + {InstanceType::FIXED_ARRAY_TYPE, InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var x = [ 10, 11, 12 ] ;\n" + "for (x[0] in [1,2,3]) { return x[3]; }", + 5 * kPointerSize, + 1, + 66, + { + B(LdaConstant), U8(0), // + B(CreateArrayLiteral), U8(0), U8(simple_flags), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateArrayLiteral), U8(1), U8(simple_flags), // + B(JumpIfUndefined), U8(52), // + B(JumpIfNull), U8(50), // + B(ToObject), // + B(Star), R(1), // + B(CallRuntime), U16(Runtime::kGetPropertyNamesFast), R(1), U8(1), // + B(ForInPrepare), R(1), // + B(JumpIfUndefined), U8(38), // + B(Star), R(2), // + B(LdaZero), // + B(Star), R(1), // + B(ForInDone), R(2), // + B(JumpIfTrue), U8(29), // + B(ForInNext), R(2), R(1), // + B(JumpIfUndefined), U8(19), // + B(Star), R(3), // + B(LdaZero), // + B(Star), R(4), // + B(Ldar), R(3), // + B(KeyedStoreICSloppy), R(0), R(4), U8(vector->GetIndex(slot3)), // + B(LdaSmi8), U8(3), // + B(KeyedLoadICSloppy), R(0), U8(vector->GetIndex(slot2)), // + B(Return), // + B(Ldar), R(1), // + B(Inc), // + B(Jump), U8(-31), // + B(LdaUndefined), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, InstanceType::FIXED_ARRAY_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + TEST(Conditional) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; diff --git a/test/cctest/interpreter/test-interpreter.cc b/test/cctest/interpreter/test-interpreter.cc index 164959019a..22bf575e66 100644 --- a/test/cctest/interpreter/test-interpreter.cc +++ b/test/cctest/interpreter/test-interpreter.cc @@ -1214,6 +1214,42 @@ TEST(InterpreterConditionalJumps) { } +TEST(InterpreterConditionalJumps2) { + // TODO(oth): Add tests for all conditional jumps near and far. + HandleAndZoneScope handles; + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + builder.set_locals_count(2); + builder.set_context_count(0); + builder.set_parameter_count(0); + Register reg(0), scratch(1); + BytecodeLabel label[2]; + BytecodeLabel done, done1; + + builder.LoadLiteral(Smi::FromInt(0)) + .StoreAccumulatorInRegister(reg) + .LoadFalse() + .JumpIfFalse(&label[0]); + IncrementRegister(builder, reg, 1024, scratch) + .Bind(&label[0]) + .LoadTrue() + .JumpIfFalse(&done); + IncrementRegister(builder, reg, 1, scratch).LoadTrue().JumpIfTrue(&label[1]); + IncrementRegister(builder, reg, 2048, scratch).Bind(&label[1]); + IncrementRegister(builder, reg, 2, scratch).LoadFalse().JumpIfTrue(&done1); + IncrementRegister(builder, reg, 4, scratch) + .LoadAccumulatorWithRegister(reg) + .Bind(&done) + .Bind(&done1) + .Return(); + + Handle bytecode_array = builder.ToBytecodeArray(); + InterpreterTester tester(handles.main_isolate(), bytecode_array); + auto callable = tester.GetCallable<>(); + Handle return_value = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_value)->value(), 7); +} + + static const Token::Value kComparisonTypes[] = { Token::Value::EQ, Token::Value::NE, Token::Value::EQ_STRICT, Token::Value::NE_STRICT, Token::Value::LTE, Token::Value::LTE, @@ -2401,27 +2437,26 @@ TEST(InterpreterDeleteSloppyUnqualifiedIdentifier) { // These tests generate a syntax error for strict mode. We don't // test for it here. std::pair> test_delete[] = { - std::make_pair("var a = { x:10, y:'abc'};\n" - "var b = delete a;\n" - "if (delete a) {\n" + std::make_pair("var sloppy_a = { x:10, y:'abc'};\n" + "var sloppy_b = delete sloppy_a;\n" + "if (delete sloppy_a) {\n" " return undefined;\n" "} else {\n" - " return a.x;\n" + " return sloppy_a.x;\n" "}\n", Handle(Smi::FromInt(10), isolate)), // TODO(mythria) When try-catch is implemented change the tests to check // if delete actually deletes - std::make_pair("a = { x:10, y:'abc'};\n" - "var b = delete a;\n" + std::make_pair("sloppy_a = { x:10, y:'abc'};\n" + "var sloppy_b = delete sloppy_a;\n" // "try{return a.x;} catch(e) {return b;}\n" - "return b;", + "return sloppy_b;", factory->ToBoolean(true)), - std::make_pair("a = { x:10, y:'abc'};\n" - "var b = delete c;\n" - "return b;", + std::make_pair("sloppy_a = { x:10, y:'abc'};\n" + "var sloppy_b = delete sloppy_c;\n" + "return sloppy_b;", factory->ToBoolean(true))}; - for (size_t i = 0; i < arraysize(test_delete); i++) { std::string source(InterpreterTester::SourceForBody(test_delete[i].first)); InterpreterTester tester(handles.main_isolate(), source.c_str()); @@ -2498,3 +2533,194 @@ TEST(InterpreterGlobalDelete) { CHECK(return_value->SameValue(*test_global_delete[i].second)); } } + + +TEST(InterpreterForIn) { + HandleAndZoneScope handles; + + // TODO(oth): Add a test here for delete mid-loop when delete is ready. + std::pair for_in_samples[] = { + {"function f() {\n" + " var r = -1;\n" + " for (var a in null) { r = a; }\n" + " return r;\n" + "}", + -1}, + {"function f() {\n" + " var r = -1;\n" + " for (var a in undefined) { r = a; }\n" + " return r;\n" + "}", + -1}, + {"function f() {\n" + " var r = 0;\n" + " for (var a in [0,6,7,9]) { r = r + (1 << a); }\n" + " return r;\n" + "}", + 0xf}, + {"function f() {\n" + " var r = 0;\n" + " for (var a in [0,6,7,9]) { r = r + (1 << a); }\n" + " var r = 0;\n" + " for (var a in [0,6,7,9]) { r = r + (1 << a); }\n" + " return r;\n" + "}", + 0xf}, + {"function f() {\n" + " var r = 0;\n" + " for (var a in 'foobar') { r = r + (1 << a); }\n" + " return r;\n" + "}", + 0x3f}, + {"function f() {\n" + " var r = 0;\n" + " for (var a in {1:0, 10:1, 100:2, 1000:3}) {\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1111}, + {"function f() {\n" + " var r = 0;\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (var a in data) {\n" + " if (a == 1) delete data[1];\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1111}, + {"function f() {\n" + " var r = 0;\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (var a in data) {\n" + " if (a == 10) delete data[100];\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1011}, + {"function f() {\n" + " var r = 0;\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (var a in data) {\n" + " if (a == 10) data[10000] = 4;\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1111}, + {"function f() {\n" + " var r = 0;\n" + " var input = 'foobar';\n" + " for (var a in input) {\n" + " if (input[a] == 'b') break;\n" + " r = r + (1 << a);\n" + " }\n" + " return r;\n" + "}", + 0x7}, + {"function f() {\n" + "var r = 0;\n" + "var input = 'foobar';\n" + "for (var a in input) {\n" + " if (input[a] == 'b') continue;\n" + " r = r + (1 << a);\n" + "}\n" + "return r;\n" + "}", + 0x37}, + {"function f() {\n" + " var r = 0;\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (var a in data) {\n" + " if (a == 10) {\n" + " data[10000] = 4;\n" + " }\n" + " r = r + Number(a);\n" + " }\n" + " return r;\n" + "}", + 1111}, + {"function f() {\n" + " var r = [ 3 ];\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (r[10] in data) {\n" + " }\n" + " return Number(r[10]);\n" + "}", + 1000}, + {"function f() {\n" + " var r = [ 3 ];\n" + " var data = {1:0, 10:1, 100:2, 1000:3};\n" + " for (r['100'] in data) {\n" + " }\n" + " return Number(r['100']);\n" + "}", + 1000}, + {"function f() {\n" + " var obj = {}\n" + " var descObj = new Boolean(false);\n" + " var accessed = 0;\n" + " descObj.enumerable = true;\n" + " Object.defineProperties(obj, { prop:descObj });\n" + " for (var p in obj) {\n" + " if (p === 'prop') { accessed = 1; }\n" + " }\n" + " return accessed;" + "}", + 1}, + {"function f() {\n" + " var appointment = {};\n" + " Object.defineProperty(appointment, 'startTime', {\n" + " value: 1001,\n" + " writable: false,\n" + " enumerable: false,\n" + " configurable: true\n" + " });\n" + " Object.defineProperty(appointment, 'name', {\n" + " value: 'NAME',\n" + " writable: false,\n" + " enumerable: false,\n" + " configurable: true\n" + " });\n" + " var meeting = Object.create(appointment);\n" + " Object.defineProperty(meeting, 'conferenceCall', {\n" + " value: 'In-person meeting',\n" + " writable: false,\n" + " enumerable: false,\n" + " configurable: true\n" + " });\n" + "\n" + " var teamMeeting = Object.create(meeting);\n" + "\n" + " var flags = 0;\n" + " for (var p in teamMeeting) {\n" + " if (p === 'startTime') {\n" + " flags |= 1;\n" + " }\n" + " if (p === 'name') {\n" + " flags |= 2;\n" + " }\n" + " if (p === 'conferenceCall') {\n" + " flags |= 4;\n" + " }\n" + " }\n" + "\n" + " var hasOwnProperty = !teamMeeting.hasOwnProperty('name') &&\n" + " !teamMeeting.hasOwnProperty('startTime') &&\n" + " !teamMeeting.hasOwnProperty('conferenceCall');\n" + " if (!hasOwnProperty) {\n" + " flags |= 8;\n" + " }\n" + " return flags;\n" + " }", + 0}}; + + for (size_t i = 0; i < arraysize(for_in_samples); i++) { + InterpreterTester tester(handles.main_isolate(), for_in_samples[i].first); + auto callable = tester.GetCallable<>(); + Handle return_val = callable().ToHandleChecked(); + CHECK_EQ(Handle::cast(return_val)->value(), for_in_samples[i].second); + } +} diff --git a/test/unittests/compiler/interpreter-assembler-unittest.cc b/test/unittests/compiler/interpreter-assembler-unittest.cc index 82ec7120e6..2a844f3215 100644 --- a/test/unittests/compiler/interpreter-assembler-unittest.cc +++ b/test/unittests/compiler/interpreter-assembler-unittest.cc @@ -471,6 +471,36 @@ TARGET_TEST_F(InterpreterAssemblerTest, LoadConstantPoolEntry) { } +TARGET_TEST_F(InterpreterAssemblerTest, LoadFixedArrayElement) { + TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { + InterpreterAssemblerForTest m(this, bytecode); + int index = 3; + Node* fixed_array = m.IntPtrConstant(0xdeadbeef); + Node* load_element = m.LoadFixedArrayElement(fixed_array, index); + EXPECT_THAT( + load_element, + m.IsLoad(kMachAnyTagged, fixed_array, + IsIntPtrAdd( + IsIntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag), + IsWordShl(IsInt32Constant(index), + IsInt32Constant(kPointerSizeLog2))))); + } +} + + +TARGET_TEST_F(InterpreterAssemblerTest, LoadObjectField) { + TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { + InterpreterAssemblerForTest m(this, bytecode); + Node* object = m.IntPtrConstant(0xdeadbeef); + int offset = 16; + Node* load_field = m.LoadObjectField(object, offset); + EXPECT_THAT(load_field, + m.IsLoad(kMachAnyTagged, object, + IsIntPtrConstant(offset - kHeapObjectTag))); + } +} + + TARGET_TEST_F(InterpreterAssemblerTest, LoadContextSlot) { TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { InterpreterAssemblerForTest m(this, bytecode); @@ -505,19 +535,6 @@ TARGET_TEST_F(InterpreterAssemblerTest, StoreContextSlot) { } -TARGET_TEST_F(InterpreterAssemblerTest, LoadObjectField) { - TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { - InterpreterAssemblerForTest m(this, bytecode); - Node* object = m.IntPtrConstant(0xdeadbeef); - int offset = 16; - Node* load_field = m.LoadObjectField(object, offset); - EXPECT_THAT(load_field, - m.IsLoad(kMachAnyTagged, object, - IsIntPtrConstant(offset - kHeapObjectTag))); - } -} - - TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime2) { TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { InterpreterAssemblerForTest m(this, bytecode); diff --git a/test/unittests/interpreter/bytecode-array-builder-unittest.cc b/test/unittests/interpreter/bytecode-array-builder-unittest.cc index 629e4bc135..cd6270ec6f 100644 --- a/test/unittests/interpreter/bytecode-array-builder-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-builder-unittest.cc @@ -126,6 +126,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { // Emit cast operator invocations. builder.CastAccumulatorToNumber() .CastAccumulatorToBoolean() + .CastAccumulatorToJSObject() .CastAccumulatorToName(); // Emit control flow. Return must be the last instruction. @@ -136,7 +137,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { .JumpIfTrue(&start) .JumpIfFalse(&start) .JumpIfToBooleanTrue(&start) - .JumpIfToBooleanFalse(&start); + .JumpIfToBooleanFalse(&start) + .JumpIfNull(&start) + .JumpIfUndefined(&start); + // Insert dummy ops to force longer jumps for (int i = 0; i < 128; i++) { builder.LoadTrue(); @@ -146,12 +150,16 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { .JumpIfTrue(&start) .JumpIfFalse(&start) .JumpIfToBooleanTrue(&start) - .JumpIfToBooleanFalse(&start); + .JumpIfToBooleanFalse(&start) + .JumpIfNull(&start) + .JumpIfUndefined(&start); builder.EnterBlock() .Throw() .LeaveBlock(); + builder.ForInPrepare(reg).ForInDone(reg).ForInNext(reg, reg); + builder.Return(); // Generate BytecodeArray.