[Interpreter] Add support for for..in.
For..in introduces 3 new bytecodes ForInPrepare, ForInNext, and ForInDone to start a for..in loop, get the next element, and check if the loop is done. For..in builds upon new LoopBuilder constructs for conditionally breaking and continuing during iteration: BreakIf{Null|Undefined} and ContinueIf{Null|Undefined}. New conditional jump bytecodes support this succinctly: JumpIfNull and JumpIfUndefined. Add missing check to BytecodeLabel that could allow multiple forward referencess to the same label which is not supported. BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1422033002 Cr-Commit-Position: refs/heads/master@{#31651}
This commit is contained in:
parent
76d730b9b2
commit
dcf757a16f
1
.gitignore
vendored
1
.gitignore
vendored
@ -20,6 +20,7 @@
|
||||
*.xcodeproj
|
||||
#*#
|
||||
*~
|
||||
.#*
|
||||
.cpplint-cache
|
||||
.cproject
|
||||
.d8_history
|
||||
|
@ -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_;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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; }
|
||||
|
||||
|
||||
|
@ -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_; }
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -31,6 +31,24 @@ void LoopBuilder::EmitJump(ZoneVector<BytecodeLabel>* sites) {
|
||||
}
|
||||
|
||||
|
||||
void LoopBuilder::EmitJumpIfTrue(ZoneVector<BytecodeLabel>* sites) {
|
||||
sites->push_back(BytecodeLabel());
|
||||
builder()->JumpIfTrue(&sites->back());
|
||||
}
|
||||
|
||||
|
||||
void LoopBuilder::EmitJumpIfUndefined(ZoneVector<BytecodeLabel>* sites) {
|
||||
sites->push_back(BytecodeLabel());
|
||||
builder()->JumpIfUndefined(&sites->back());
|
||||
}
|
||||
|
||||
|
||||
void LoopBuilder::EmitJumpIfNull(ZoneVector<BytecodeLabel>* sites) {
|
||||
sites->push_back(BytecodeLabel());
|
||||
builder()->JumpIfNull(&sites->back());
|
||||
}
|
||||
|
||||
|
||||
void LoopBuilder::BindLabels(const BytecodeLabel& target,
|
||||
ZoneVector<BytecodeLabel>* sites) {
|
||||
for (size_t i = 0; i < sites->size(); i++) {
|
||||
|
@ -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<BytecodeLabel>* site);
|
||||
void EmitJump(ZoneVector<BytecodeLabel>* labels);
|
||||
void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels);
|
||||
void EmitJumpIfUndefined(ZoneVector<BytecodeLabel>* labels);
|
||||
void EmitJumpIfNull(ZoneVector<BytecodeLabel>* labels);
|
||||
|
||||
// Unbound labels that identify jumps for continue/break statements
|
||||
// in the code.
|
||||
|
@ -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 <imm8>
|
||||
//
|
||||
// Jump by number of bytes represented by the immediate operand |imm8|.
|
||||
@ -987,6 +998,62 @@ void Interpreter::DoJumpIfToBooleanFalseConstant(
|
||||
}
|
||||
|
||||
|
||||
// JumpIfNull <imm8>
|
||||
//
|
||||
// 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 <idx>
|
||||
//
|
||||
// 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 <imm8>
|
||||
//
|
||||
// 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 <idx>
|
||||
//
|
||||
// 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 <idx> <flags_reg>
|
||||
//
|
||||
// Creates a regular expression literal for literal index <idx> with flags held
|
||||
@ -1105,6 +1172,57 @@ void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) {
|
||||
}
|
||||
|
||||
|
||||
// ForInPrepare <receiver>
|
||||
//
|
||||
// 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 <for_in_state> <index>
|
||||
//
|
||||
// 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 <for_in_state>
|
||||
//
|
||||
// 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
|
||||
|
@ -147,5 +147,53 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) {
|
||||
shared, context, static_cast<PretenureFlag>(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<Object> cache_type = property_names;
|
||||
Handle<Map> cache_type_map = handle(property_names->map(), isolate);
|
||||
Handle<Map> receiver_map = handle(receiver->map(), isolate);
|
||||
|
||||
Handle<FixedArray> 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<FixedArray>::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<Object>(Smi::FromInt(0), isolate);
|
||||
} else {
|
||||
// One entails slow check
|
||||
cache_type = Handle<Object>(Smi::FromInt(1), isolate);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<FixedArray> 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
|
||||
|
@ -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) \
|
||||
|
@ -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<i::TypeFeedbackVector> vector =
|
||||
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
|
||||
|
||||
ExpectedSnippet<InstanceType> 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<BytecodeArray> bytecode_array =
|
||||
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
|
||||
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Conditional) {
|
||||
InitializedHandleScope handle_scope;
|
||||
BytecodeGeneratorHelper helper;
|
||||
|
@ -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<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
|
||||
InterpreterTester tester(handles.main_isolate(), bytecode_array);
|
||||
auto callable = tester.GetCallable<>();
|
||||
Handle<Object> 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<const char*, Handle<Object>> 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<Object>(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<const char*, int> 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<Object> return_val = callable().ToHandleChecked();
|
||||
CHECK_EQ(Handle<Smi>::cast(return_val)->value(), for_in_samples[i].second);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user