Commit 71129d5b authored by epertoso's avatar epertoso Committed by Commit bot

Fix the receiver check in the HandleFastApiCall builtin.

CompatibleReceiverCheck used by the HandleFastApiCall builtin was terminating with failure upon encountering a hidden prototype.

It should actually stop iterating on the first non-hidden prototype.

BUG=

Review URL: https://codereview.chromium.org/1576423003

Cr-Commit-Position: refs/heads/master@{#33294}
parent 7ad13bf8
......@@ -1306,17 +1306,10 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
__ b(eq, &receiver_check_passed);
// Walk the prototype chain.
__ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
Label prototype_loop_start;
__ bind(&prototype_loop_start);
// End if the receiver is null or if it's a hidden type.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ b(eq, receiver_check_failed);
__ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ ldr(ip, FieldMemOperand(map, Map::kBitField3Offset));
__ tst(ip, Operand(Map::IsHiddenPrototype::kMask));
__ b(ne, receiver_check_failed);
// Get the constructor, if any.
__ GetMapConstructor(constructor, map, ip, ip);
__ cmp(ip, Operand(JS_FUNCTION_TYPE));
......@@ -1346,9 +1339,17 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
eq);
__ b(&function_template_loop, eq);
// Load the next prototype and iterate.
// Load the next prototype.
__ bind(&next_prototype);
__ ldr(receiver, FieldMemOperand(map, Map::kPrototypeOffset));
// End if the prototype is null or not hidden.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ b(eq, receiver_check_failed);
__ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ ldr(ip, FieldMemOperand(map, Map::kBitField3Offset));
__ tst(ip, Operand(Map::IsHiddenPrototype::kMask));
__ b(eq, receiver_check_failed);
// Iterate.
__ b(&prototype_loop_start);
__ bind(&receiver_check_passed);
......
......@@ -1286,17 +1286,10 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
__ B(eq, &receiver_check_passed);
// Walk the prototype chain.
__ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
Label prototype_loop_start;
__ Bind(&prototype_loop_start);
// End if the receiver is null or if it's a hidden type.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ B(eq, receiver_check_failed);
__ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ Ldr(x16, FieldMemOperand(map, Map::kBitField3Offset));
__ Tst(x16, Operand(Map::IsHiddenPrototype::kMask));
__ B(ne, receiver_check_failed);
// Get the constructor, if any
__ GetMapConstructor(constructor, map, x16, x16);
__ cmp(x16, Operand(JS_FUNCTION_TYPE));
......@@ -1326,9 +1319,17 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
FieldMemOperand(type, FunctionTemplateInfo::kParentTemplateOffset));
__ B(&function_template_loop);
// Load the next prototype and iterate.
// Load the next prototype.
__ Bind(&next_prototype);
__ Ldr(receiver, FieldMemOperand(map, Map::kPrototypeOffset));
// End if the prototype is null or not hidden.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ B(eq, receiver_check_failed);
__ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ Ldr(x16, FieldMemOperand(map, Map::kBitField3Offset));
__ Tst(x16, Operand(Map::IsHiddenPrototype::kMask));
__ B(eq, receiver_check_failed);
// Iterate.
__ B(&prototype_loop_start);
__ Bind(&receiver_check_passed);
......
......@@ -2348,17 +2348,10 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
__ j(equal, &receiver_check_passed, Label::kNear);
// Walk the prototype chain.
__ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
Label prototype_loop_start;
__ bind(&prototype_loop_start);
// End if receiver is null or if it's a hidden prototype.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ j(equal, receiver_check_failed, Label::kNear);
__ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
__ test(FieldOperand(scratch0, Map::kBitField3Offset),
Immediate(Map::IsHiddenPrototype::kMask));
__ j(not_zero, receiver_check_failed, Label::kNear);
// Get the constructor, if any.
__ GetMapConstructor(scratch0, scratch0, scratch1);
__ CmpInstanceType(scratch1, JS_FUNCTION_TYPE);
......@@ -2391,10 +2384,18 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
FieldOperand(scratch0, FunctionTemplateInfo::kParentTemplateOffset));
__ jmp(&function_template_loop, Label::kNear);
// Load the next prototype and iterate.
// Load the next prototype.
__ bind(&next_prototype);
__ mov(receiver, FieldOperand(receiver, HeapObject::kMapOffset));
__ mov(receiver, FieldOperand(receiver, Map::kPrototypeOffset));
// End if the prototype is null or not hidden.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ j(equal, receiver_check_failed);
__ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
__ test(FieldOperand(scratch0, Map::kBitField3Offset),
Immediate(Map::IsHiddenPrototype::kMask));
__ j(zero, receiver_check_failed);
// Iterate.
__ jmp(&prototype_loop_start, Label::kNear);
__ bind(&receiver_check_passed);
......
......@@ -1322,16 +1322,10 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
&receiver_check_passed);
// Walk the prototype chain.
__ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
Label prototype_loop_start;
__ bind(&prototype_loop_start);
// End if the receiver is null or if it's a hidden type.
__ JumpIfRoot(receiver, Heap::kNullValueRootIndex, receiver_check_failed);
__ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ lw(scratch, FieldMemOperand(map, Map::kBitField3Offset));
__ DecodeField<Map::IsHiddenPrototype>(scratch);
__ Branch(receiver_check_failed, ne, scratch, Operand(zero_reg));
// Get the constructor, if any.
__ GetMapConstructor(constructor, map, scratch, scratch);
Label next_prototype;
......@@ -1363,6 +1357,13 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
// Load the next prototype and iterate.
__ bind(&next_prototype);
__ lw(receiver, FieldMemOperand(map, Map::kPrototypeOffset));
// End if the prototype is null or not hidden.
__ JumpIfRoot(receiver, Heap::kNullValueRootIndex, receiver_check_failed);
__ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ lw(scratch, FieldMemOperand(map, Map::kBitField3Offset));
__ DecodeField<Map::IsHiddenPrototype>(scratch);
__ Branch(receiver_check_failed, eq, scratch, Operand(zero_reg));
__ Branch(&prototype_loop_start);
__ bind(&receiver_check_passed);
......
......@@ -1312,16 +1312,10 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
&receiver_check_passed);
// Walk the prototype chain.
__ ld(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
Label prototype_loop_start;
__ bind(&prototype_loop_start);
// End if the receiver is null or if it's a hidden type.
__ JumpIfRoot(receiver, Heap::kNullValueRootIndex, receiver_check_failed);
__ ld(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ lwu(scratch, FieldMemOperand(map, Map::kBitField3Offset));
__ DecodeField<Map::IsHiddenPrototype>(scratch);
__ Branch(receiver_check_failed, ne, scratch, Operand(zero_reg));
// Get the constructor, if any.
__ GetMapConstructor(constructor, map, scratch, scratch);
Label next_prototype;
......@@ -1350,9 +1344,16 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
FieldMemOperand(type, FunctionTemplateInfo::kParentTemplateOffset));
__ Branch(&function_template_loop);
// Load the next prototype and iterate.
// Load the next prototype.
__ bind(&next_prototype);
__ ld(receiver, FieldMemOperand(map, Map::kPrototypeOffset));
// End if the prototype is null or not hidden.
__ JumpIfRoot(receiver, Heap::kNullValueRootIndex, receiver_check_failed);
__ ld(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ lwu(scratch, FieldMemOperand(map, Map::kBitField3Offset));
__ DecodeField<Map::IsHiddenPrototype>(scratch);
__ Branch(receiver_check_failed, eq, scratch, Operand(zero_reg));
// Iterate.
__ Branch(&prototype_loop_start);
__ bind(&receiver_check_passed);
......
......@@ -1309,17 +1309,10 @@ void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
&receiver_check_passed);
// Walk the prototype chain.
__ LoadP(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
Label prototype_loop_start;
__ bind(&prototype_loop_start);
// End if the receiver is null or if it's a hidden type.
__ JumpIfRoot(receiver, Heap::kNullValueRootIndex, receiver_check_failed);
__ LoadP(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ LoadP(scratch, FieldMemOperand(map, Map::kBitField3Offset));
__ DecodeField<Map::IsHiddenPrototype>(scratch, SetRC);
__ bne(receiver_check_failed, cr0);
// Get the constructor, if any.
__ GetMapConstructor(constructor, map, scratch, scratch);
__ cmpi(scratch, Operand(JS_FUNCTION_TYPE));
......@@ -1350,9 +1343,16 @@ void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
FieldMemOperand(type, FunctionTemplateInfo::kParentTemplateOffset));
__ b(&function_template_loop);
// Load the next prototype and iterate.
// Load the next prototype.
__ bind(&next_prototype);
__ LoadP(receiver, FieldMemOperand(map, Map::kPrototypeOffset));
// End if the prototype is null or not hidden.
__ JumpIfRoot(receiver, Heap::kNullValueRootIndex, receiver_check_failed);
__ LoadP(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ LoadP(scratch, FieldMemOperand(map, Map::kBitField3Offset));
__ DecodeField<Map::IsHiddenPrototype>(scratch, SetRC);
__ beq(receiver_check_failed, cr0);
// Iterate.
__ b(&prototype_loop_start);
__ bind(&receiver_check_passed);
......
......@@ -2436,17 +2436,10 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
__ j(equal, &receiver_check_passed, Label::kNear);
// Walk the prototype chain.
__ movp(map, FieldOperand(receiver, HeapObject::kMapOffset));
Label prototype_loop_start;
__ bind(&prototype_loop_start);
// End if the receiver is null or if it's a hidden prototype.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ j(equal, receiver_check_failed, Label::kNear);
__ movp(map, FieldOperand(receiver, HeapObject::kMapOffset));
__ testq(FieldOperand(map, Map::kBitField3Offset),
Immediate(Map::IsHiddenPrototype::kMask));
__ j(not_zero, receiver_check_failed, Label::kNear);
// Get the constructor, if any.
__ GetMapConstructor(constructor, map, kScratchRegister);
__ CmpInstanceType(kScratchRegister, JS_FUNCTION_TYPE);
......@@ -2478,9 +2471,17 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
FieldOperand(type, FunctionTemplateInfo::kParentTemplateOffset));
__ jmp(&function_template_loop, Label::kNear);
// Load the next prototype and iterate.
// Load the next prototype.
__ bind(&next_prototype);
__ movp(receiver, FieldOperand(map, Map::kPrototypeOffset));
// End if the prototype is null or not hidden.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ j(equal, receiver_check_failed);
__ movp(map, FieldOperand(receiver, HeapObject::kMapOffset));
__ testq(FieldOperand(map, Map::kBitField3Offset),
Immediate(Map::IsHiddenPrototype::kMask));
__ j(zero, receiver_check_failed);
// Iterate.
__ jmp(&prototype_loop_start, Label::kNear);
__ bind(&receiver_check_passed);
......
......@@ -2355,17 +2355,10 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
__ j(equal, &receiver_check_passed, Label::kNear);
// Walk the prototype chain.
__ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
Label prototype_loop_start;
__ bind(&prototype_loop_start);
// End if receiver is null or if it's a hidden prototype.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ j(equal, receiver_check_failed, Label::kNear);
__ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
__ test(FieldOperand(scratch0, Map::kBitField3Offset),
Immediate(Map::IsHiddenPrototype::kMask));
__ j(not_zero, receiver_check_failed, Label::kNear);
// Get the constructor, if any.
__ GetMapConstructor(scratch0, scratch0, scratch1);
__ CmpInstanceType(scratch1, JS_FUNCTION_TYPE);
......@@ -2398,10 +2391,18 @@ static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver,
FieldOperand(scratch0, FunctionTemplateInfo::kParentTemplateOffset));
__ jmp(&function_template_loop, Label::kNear);
// Load the next prototype and iterate.
// Load the next prototype.
__ bind(&next_prototype);
__ mov(receiver, FieldOperand(receiver, HeapObject::kMapOffset));
__ mov(receiver, FieldOperand(receiver, Map::kPrototypeOffset));
// End if the prototype is null or not hidden.
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
__ j(equal, receiver_check_failed);
__ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
__ test(FieldOperand(scratch0, Map::kBitField3Offset),
Immediate(Map::IsHiddenPrototype::kMask));
__ j(zero, receiver_check_failed);
// Iterate.
__ jmp(&prototype_loop_start, Label::kNear);
__ bind(&receiver_check_passed);
......
......@@ -159,6 +159,7 @@
'test-platform.cc',
'test-profile-generator.cc',
'test-random-number-generator.cc',
'test-receiver-check-hidden-prototype.cc',
'test-regexp.cc',
'test-reloc-info.cc',
'test-representation.cc',
......
// Copyright 2014 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.
#include <stdlib.h>
#include "include/v8-experimental.h"
#include "src/v8.h"
#include "test/cctest/cctest.h"
namespace {
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(41);
}
TEST(CompatibleReceiverBuiltin) {
// Check that the HandleFastApiCall builtin visits the hidden prototypes
// during the compatible receiver check.
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
v8::Local<v8::FunctionTemplate> constructor_template =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::FunctionTemplate> prototype_template =
v8::FunctionTemplate::New(isolate);
prototype_template->SetHiddenPrototype(true);
v8::Local<v8::ObjectTemplate> proto_instance_template =
prototype_template->InstanceTemplate();
v8::experimental::FastAccessorBuilder* fast_accessor_builder =
v8::experimental::FastAccessorBuilder::New(isolate);
fast_accessor_builder->ReturnValue(
fast_accessor_builder->IntegerConstant(42));
v8::Local<v8::FunctionTemplate> accessor_template =
v8::FunctionTemplate::NewWithFastHandler(
isolate, SlowCallback, fast_accessor_builder, v8::Local<v8::Value>(),
v8::Signature::New(isolate, prototype_template));
proto_instance_template->SetAccessorProperty(
v8_str("bar"), accessor_template, v8::Local<v8::FunctionTemplate>(),
v8::ReadOnly);
v8::Local<v8::Object> object =
constructor_template->GetFunction(current_context)
.ToLocalChecked()
->NewInstance(current_context)
.ToLocalChecked();
v8::Local<v8::Object> hidden_prototype =
prototype_template->GetFunction(current_context)
.ToLocalChecked()
->NewInstance(current_context)
.ToLocalChecked();
CHECK(object->SetPrototype(current_context, hidden_prototype).FromJust());
context->Global()
->Set(current_context, v8_str("object"), object)
.FromMaybe(false);
CHECK_EQ(42, CompileRun("var getter = object.__lookupGetter__('bar');"
"getter.call(object)")
->Int32Value(current_context)
.FromJust());
}
} // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment