[builtins] Fix binding function with native data property accessors

When the function is created with FunctionTemplate and set name with
native data property accessors, binding the function should throw
immediately if the native accessor throws.

Bug: v8:11989
Change-Id: Ief282202aa5b8515f581fd5478886ed5f001fd4f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3492966
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Commit-Queue: Chengzhong Wu <legendecas@gmail.com>
Cr-Commit-Position: refs/heads/main@{#79356}
This commit is contained in:
legendecas 2022-03-04 00:59:14 +08:00 committed by V8 LUCI CQ
parent a0b25ebd75
commit 8e18ea3913
2 changed files with 51 additions and 7 deletions

View File

@ -207,16 +207,18 @@ Object DoFunctionBind(Isolate* isolate, BuiltinArguments args) {
isolate, function,
isolate->factory()->NewJSBoundFunction(target, this_arg, argv));
LookupIterator length_lookup(isolate, target,
isolate->factory()->length_string(), target,
LookupIterator::OWN);
// Setup the "length" property based on the "length" of the {target}.
// If the targets length is the default JSFunction accessor, we can keep the
// accessor that's installed by default on the JSBoundFunction. It lazily
// computes the value from the underlying internal length.
Handle<AccessorInfo> function_length_accessor =
isolate->factory()->function_length_accessor();
LookupIterator length_lookup(isolate, target,
isolate->factory()->length_string(), target,
LookupIterator::OWN);
if (!target->IsJSFunction() ||
length_lookup.state() != LookupIterator::ACCESSOR ||
!length_lookup.GetAccessors()->IsAccessorInfo()) {
!length_lookup.GetAccessors().is_identical_to(function_length_accessor)) {
Handle<Object> length(Smi::zero(), isolate);
Maybe<PropertyAttributes> attributes =
JSReceiver::GetPropertyAttributes(&length_lookup);
@ -242,11 +244,13 @@ Object DoFunctionBind(Isolate* isolate, BuiltinArguments args) {
// If the target's name is the default JSFunction accessor, we can keep the
// accessor that's installed by default on the JSBoundFunction. It lazily
// computes the value from the underlying internal name.
Handle<AccessorInfo> function_name_accessor =
isolate->factory()->function_name_accessor();
LookupIterator name_lookup(isolate, target, isolate->factory()->name_string(),
target);
if (!target->IsJSFunction() ||
name_lookup.state() != LookupIterator::ACCESSOR ||
!name_lookup.GetAccessors()->IsAccessorInfo() ||
!name_lookup.GetAccessors().is_identical_to(function_name_accessor) ||
(name_lookup.IsFound() && !name_lookup.HolderIsReceiver())) {
Handle<Object> target_name;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, target_name,
@ -257,8 +261,9 @@ Object DoFunctionBind(Isolate* isolate, BuiltinArguments args) {
isolate, name,
Name::ToFunctionName(isolate, Handle<String>::cast(target_name)));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, name, isolate->factory()->NewConsString(
isolate->factory()->bound__string(), name));
isolate, name,
isolate->factory()->NewConsString(isolate->factory()->bound__string(),
name));
} else {
name = isolate->factory()->bound__string();
}

View File

@ -778,3 +778,42 @@ TEST(ObjectTemplateSetLazyPropertyHasNoSideEffect) {
->Int32Value(env.local())
.FromJust());
}
namespace {
void FunctionNativeGetter(v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetIsolate()->ThrowError(v8_str("side effect in getter"));
}
} // namespace
TEST(BindFunctionTemplateSetNativeDataProperty) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
// Check that getter is called on Function.prototype.bind.
{
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->SetNativeDataProperty(v8_str("name"), FunctionNativeGetter);
v8::Local<v8::Function> func =
templ->GetFunction(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("func"), func).FromJust());
v8::TryCatch try_catch(isolate);
CHECK(CompileRun("func.bind()").IsEmpty());
CHECK(try_catch.HasCaught());
}
// Check that getter is called on Function.prototype.bind.
{
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
templ->SetNativeDataProperty(v8_str("length"), FunctionNativeGetter);
v8::Local<v8::Function> func =
templ->GetFunction(env.local()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("func"), func).FromJust());
v8::TryCatch try_catch(isolate);
CHECK(CompileRun("func.bind()").IsEmpty());
CHECK(try_catch.HasCaught());
}
}