[es6] Fix prototype chain walk for instanceof.

When walking up the prototype chain during OrdinaryHasInstance, we first
check if the current prototype equals the expected one, and only
afterwards check the current prototype against null. That's obviously
wrong if we check something like Proxy, whose prototype is null.

R=yangguo@chromium.org
BUG=v8:5085

Review-Url: https://codereview.chromium.org/2041103007
Cr-Commit-Position: refs/heads/master@{#36840}
This commit is contained in:
bmeurer 2016-06-08 23:22:07 -07:00 committed by Commit bot
parent f2312019af
commit eb1c9e2723
12 changed files with 42 additions and 28 deletions

View File

@ -1956,8 +1956,8 @@ Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable,
// Check the current {object} prototype.
Node* object_prototype = LoadMapPrototype(object_map);
GotoIf(WordEqual(object_prototype, callable_prototype), &return_true);
GotoIf(WordEqual(object_prototype, NullConstant()), &return_false);
GotoIf(WordEqual(object_prototype, callable_prototype), &return_true);
// Continue with the prototype.
var_object_map.Bind(LoadMap(object_prototype));

View File

@ -1267,6 +1267,17 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) {
simplified()->LoadField(AccessBuilder::ForMapPrototype()),
loop_object_map, loop_effect, control);
// If not, check if object prototype is the null prototype.
Node* null_proto =
graph()->NewNode(simplified()->ReferenceEqual(r.right_type()),
object_prototype, jsgraph()->NullConstant());
Node* branch_null_proto = graph()->NewNode(
common()->Branch(BranchHint::kFalse), null_proto, control);
Node* if_null_proto = graph()->NewNode(common()->IfTrue(), branch_null_proto);
Node* e_null_proto = effect;
control = graph()->NewNode(common()->IfFalse(), branch_null_proto);
// Check if object prototype is equal to function prototype.
Node* eq_proto =
graph()->NewNode(simplified()->ReferenceEqual(r.right_type()),
@ -1278,16 +1289,6 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) {
control = graph()->NewNode(common()->IfFalse(), branch_eq_proto);
// If not, check if object prototype is the null prototype.
Node* null_proto =
graph()->NewNode(simplified()->ReferenceEqual(r.right_type()),
object_prototype, jsgraph()->NullConstant());
Node* branch_null_proto = graph()->NewNode(
common()->Branch(BranchHint::kFalse), null_proto, control);
Node* if_null_proto = graph()->NewNode(common()->IfTrue(), branch_null_proto);
Node* e_null_proto = effect;
control = graph()->NewNode(common()->IfFalse(), branch_null_proto);
Node* load_object_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
object_prototype, effect, control);

View File

@ -2525,10 +2525,10 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
DeoptimizeIf(eq, instr, Deoptimizer::kProxy);
__ ldr(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
__ cmp(object_prototype, prototype);
EmitTrueBranch(instr, eq);
__ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
EmitFalseBranch(instr, eq);
__ cmp(object_prototype, prototype);
EmitTrueBranch(instr, eq);
__ ldr(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset));
__ b(&loop);
}

View File

@ -2832,10 +2832,10 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
DeoptimizeIf(eq, instr, Deoptimizer::kProxy);
__ Ldr(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
__ Cmp(object_prototype, prototype);
__ B(eq, instr->TrueLabel(chunk_));
__ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
__ B(eq, instr->FalseLabel(chunk_));
__ Cmp(object_prototype, prototype);
__ B(eq, instr->TrueLabel(chunk_));
__ Ldr(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset));
__ B(&loop);
}

View File

@ -2316,10 +2316,10 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
DeoptimizeIf(equal, instr, Deoptimizer::kProxy);
__ mov(object_prototype, FieldOperand(object_map, Map::kPrototypeOffset));
__ cmp(object_prototype, prototype);
EmitTrueBranch(instr, equal);
__ cmp(object_prototype, factory()->null_value());
EmitFalseBranch(instr, equal);
__ cmp(object_prototype, prototype);
EmitTrueBranch(instr, equal);
__ mov(object_map, FieldOperand(object_prototype, HeapObject::kMapOffset));
__ jmp(&loop);
}

View File

@ -2415,9 +2415,9 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
Operand(JS_PROXY_TYPE));
__ lw(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
EmitTrueBranch(instr, eq, object_prototype, Operand(prototype));
__ LoadRoot(at, Heap::kNullValueRootIndex);
EmitFalseBranch(instr, eq, object_prototype, Operand(at));
EmitTrueBranch(instr, eq, object_prototype, Operand(prototype));
__ Branch(USE_DELAY_SLOT, &loop);
__ lw(object_map, FieldMemOperand(object_prototype, HeapObject::kMapOffset));
}

View File

@ -2535,9 +2535,9 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
Operand(JS_PROXY_TYPE));
__ ld(object_prototype, FieldMemOperand(object_map, Map::kPrototypeOffset));
EmitTrueBranch(instr, eq, object_prototype, Operand(prototype));
__ LoadRoot(at, Heap::kNullValueRootIndex);
EmitFalseBranch(instr, eq, object_prototype, Operand(at));
EmitTrueBranch(instr, eq, object_prototype, Operand(prototype));
__ Branch(&loop, USE_DELAY_SLOT);
__ ld(object_map, FieldMemOperand(object_prototype,
HeapObject::kMapOffset)); // In delay slot.

View File

@ -2589,10 +2589,10 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
DeoptimizeIf(eq, instr, Deoptimizer::kProxy);
__ LoadP(object_prototype,
FieldMemOperand(object_map, Map::kPrototypeOffset));
__ cmp(object_prototype, prototype);
EmitTrueBranch(instr, eq);
__ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
EmitFalseBranch(instr, eq);
__ cmp(object_prototype, prototype);
EmitTrueBranch(instr, eq);
__ LoadP(object_map,
FieldMemOperand(object_prototype, HeapObject::kMapOffset));
__ b(&loop);

View File

@ -2567,10 +2567,10 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
DeoptimizeIf(eq, instr, Deoptimizer::kProxy);
__ LoadP(object_prototype,
FieldMemOperand(object_map, Map::kPrototypeOffset));
__ CmpP(object_prototype, prototype);
EmitTrueBranch(instr, eq);
__ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
EmitFalseBranch(instr, eq);
__ CmpP(object_prototype, prototype);
EmitTrueBranch(instr, eq);
__ LoadP(object_map,
FieldMemOperand(object_prototype, HeapObject::kMapOffset));
__ b(&loop);

View File

@ -2456,7 +2456,6 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
Label loop;
__ bind(&loop);
// Deoptimize if the object needs to be access checked.
__ testb(FieldOperand(object_map, Map::kBitFieldOffset),
Immediate(1 << Map::kIsAccessCheckNeeded));
@ -2466,10 +2465,10 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
DeoptimizeIf(equal, instr, Deoptimizer::kProxy);
__ movp(object_prototype, FieldOperand(object_map, Map::kPrototypeOffset));
__ cmpp(object_prototype, prototype);
EmitTrueBranch(instr, equal);
__ CompareRoot(object_prototype, Heap::kNullValueRootIndex);
EmitFalseBranch(instr, equal);
__ cmpp(object_prototype, prototype);
EmitTrueBranch(instr, equal);
__ movp(object_map, FieldOperand(object_prototype, HeapObject::kMapOffset));
__ jmp(&loop);
}

View File

@ -2602,10 +2602,10 @@ void LCodeGen::DoHasInPrototypeChainAndBranch(
DeoptimizeIf(equal, instr, Deoptimizer::kProxy);
__ mov(object_prototype, FieldOperand(object_map, Map::kPrototypeOffset));
__ cmp(object_prototype, prototype);
EmitTrueBranch(instr, equal);
__ cmp(object_prototype, factory()->null_value());
EmitFalseBranch(instr, equal);
__ cmp(object_prototype, prototype);
EmitTrueBranch(instr, equal);
__ mov(object_map, FieldOperand(object_prototype, HeapObject::kMapOffset));
__ jmp(&loop);
}

View File

@ -0,0 +1,14 @@
// Copyright 2016 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.
// Flags: --allow-natives-syntax
function foo(x) {
return x instanceof Proxy;
}
assertFalse(foo({}));
assertFalse(foo({}));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo({}));