25decc66ae
In case of LAP(lazy accessor pair), the function's creation context must be equal to the accessor holder's creation context, so this CL changes the current context to the accessor holder's creation context. Note that this is the second attempt after https://crrev.com/2770003002 The change from the previous attempt is to skip looking for the object's constructor if the object itself is a function. Also some of Blink's LAP-context-sensitive tests got updated at https://crrev.com/c/597990 and the rest of the tests will get temporarily disabled at https://crrev.com/c/605408 . TBR=verwaest@chromium.org Bug: v8:6156 Change-Id: I09709a90995d82a03996d0347e5a1d8425b5db9c Reviewed-on: https://chromium-review.googlesource.com/563152 Commit-Queue: Yuki Shiino <yukishiino@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/master@{#47572}
232 lines
8.9 KiB
C++
232 lines
8.9 KiB
C++
// Copyright 2017 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 "include/v8.h"
|
|
#include "src/api.h"
|
|
#include "src/objects-inl.h"
|
|
#include "test/unittests/test-utils.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace v8 {
|
|
namespace {
|
|
|
|
using ObjectTest = TestWithContext;
|
|
|
|
void accessor_name_getter_callback(Local<Name>,
|
|
const PropertyCallbackInfo<Value>&) {}
|
|
|
|
TEST_F(ObjectTest, SetAccessorWhenUnconfigurablePropAlreadyDefined) {
|
|
TryCatch try_catch(isolate());
|
|
|
|
Local<Object> global = context()->Global();
|
|
Local<String> property_name =
|
|
String::NewFromUtf8(isolate(), "foo", NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
|
|
PropertyDescriptor prop_desc;
|
|
prop_desc.set_configurable(false);
|
|
global->DefineProperty(context(), property_name, prop_desc).ToChecked();
|
|
|
|
Maybe<bool> result = global->SetAccessor(context(), property_name,
|
|
accessor_name_getter_callback);
|
|
ASSERT_TRUE(result.IsJust());
|
|
ASSERT_FALSE(result.FromJust());
|
|
ASSERT_FALSE(try_catch.HasCaught());
|
|
}
|
|
|
|
using LapContextTest = TestWithIsolate;
|
|
|
|
// TODO(yukishiino): Enable this unittest once
|
|
// PropertyAccessInfo::accessor_holder() gets supported. Currently we're using
|
|
// PropertyAccessInfo::holder(), which doesn't return the accessor holder.
|
|
TEST_F(LapContextTest, DISABLED_CurrentContextInLazyAccessorOnPrototype) {
|
|
// The receiver object is created in |receiver_context|, but its prototype
|
|
// object is created in |prototype_context|, and the property is accessed
|
|
// from |caller_context|.
|
|
Local<Context> receiver_context = Context::New(isolate());
|
|
Local<Context> prototype_context = Context::New(isolate());
|
|
Local<Context> caller_context = Context::New(isolate());
|
|
|
|
static int call_count; // The number of calls of the accessor callback.
|
|
call_count = 0;
|
|
|
|
Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate());
|
|
Local<Signature> signature = Signature::New(isolate(), function_template);
|
|
Local<String> property_key =
|
|
String::NewFromUtf8(isolate(), "property", NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
Local<FunctionTemplate> get_or_set = FunctionTemplate::New(
|
|
isolate(),
|
|
[](const FunctionCallbackInfo<Value>& info) {
|
|
++call_count;
|
|
Local<Context> prototype_context = *reinterpret_cast<Local<Context>*>(
|
|
info.Data().As<External>()->Value());
|
|
EXPECT_EQ(prototype_context, info.GetIsolate()->GetCurrentContext());
|
|
},
|
|
External::New(isolate(), &prototype_context), signature);
|
|
function_template->PrototypeTemplate()->SetAccessorProperty(
|
|
property_key, get_or_set, get_or_set);
|
|
|
|
// |object| is created in |receiver_context|, and |prototype| is created
|
|
// in |prototype_context|. And then, object.__proto__ = prototype.
|
|
Local<Function> interface_for_receiver =
|
|
function_template->GetFunction(receiver_context).ToLocalChecked();
|
|
Local<Function> interface_for_prototype =
|
|
function_template->GetFunction(prototype_context).ToLocalChecked();
|
|
Local<String> prototype_key =
|
|
String::NewFromUtf8(isolate(), "prototype", NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
Local<Object> prototype =
|
|
interface_for_prototype->Get(caller_context, prototype_key)
|
|
.ToLocalChecked()
|
|
.As<Object>();
|
|
Local<Object> object =
|
|
interface_for_receiver->NewInstance(receiver_context).ToLocalChecked();
|
|
object->SetPrototype(caller_context, prototype).ToChecked();
|
|
EXPECT_EQ(receiver_context, object->CreationContext());
|
|
EXPECT_EQ(prototype_context, prototype->CreationContext());
|
|
|
|
EXPECT_EQ(0, call_count);
|
|
object->Get(caller_context, property_key).ToLocalChecked();
|
|
EXPECT_EQ(1, call_count);
|
|
object->Set(caller_context, property_key, Null(isolate())).ToChecked();
|
|
EXPECT_EQ(2, call_count);
|
|
|
|
// Test with a compiled version.
|
|
Local<String> object_key =
|
|
String::NewFromUtf8(isolate(), "object", NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
caller_context->Global()->Set(caller_context, object_key, object).ToChecked();
|
|
const char script[] =
|
|
"function f() { object.property; object.property = 0; } "
|
|
"f(); f(); "
|
|
"%OptimizeFunctionOnNextCall(f); "
|
|
"f();";
|
|
Context::Scope scope(caller_context);
|
|
internal::FLAG_allow_natives_syntax = true;
|
|
Script::Compile(
|
|
caller_context,
|
|
String::NewFromUtf8(isolate(), script, v8::NewStringType::kNormal)
|
|
.ToLocalChecked())
|
|
.ToLocalChecked()
|
|
->Run(caller_context)
|
|
.ToLocalChecked();
|
|
EXPECT_EQ(8, call_count);
|
|
}
|
|
|
|
TEST_F(LapContextTest, CurrentContextInLazyAccessorOnPlatformObject) {
|
|
Local<Context> receiver_context = Context::New(isolate());
|
|
Local<Context> caller_context = Context::New(isolate());
|
|
|
|
static int call_count; // The number of calls of the accessor callback.
|
|
call_count = 0;
|
|
|
|
Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate());
|
|
Local<Signature> signature = Signature::New(isolate(), function_template);
|
|
Local<String> property_key =
|
|
String::NewFromUtf8(isolate(), "property", NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
Local<FunctionTemplate> get_or_set = FunctionTemplate::New(
|
|
isolate(),
|
|
[](const FunctionCallbackInfo<Value>& info) {
|
|
++call_count;
|
|
Local<Context> receiver_context = *reinterpret_cast<Local<Context>*>(
|
|
info.Data().As<External>()->Value());
|
|
EXPECT_EQ(receiver_context, info.GetIsolate()->GetCurrentContext());
|
|
},
|
|
External::New(isolate(), &receiver_context), signature);
|
|
function_template->InstanceTemplate()->SetAccessorProperty(
|
|
property_key, get_or_set, get_or_set);
|
|
|
|
Local<Function> interface =
|
|
function_template->GetFunction(receiver_context).ToLocalChecked();
|
|
Local<Object> object =
|
|
interface->NewInstance(receiver_context).ToLocalChecked();
|
|
|
|
EXPECT_EQ(0, call_count);
|
|
object->Get(caller_context, property_key).ToLocalChecked();
|
|
EXPECT_EQ(1, call_count);
|
|
object->Set(caller_context, property_key, Null(isolate())).ToChecked();
|
|
EXPECT_EQ(2, call_count);
|
|
|
|
// Test with a compiled version.
|
|
Local<String> object_key =
|
|
String::NewFromUtf8(isolate(), "object", NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
caller_context->Global()->Set(caller_context, object_key, object).ToChecked();
|
|
const char script[] =
|
|
"function f() { object.property; object.property = 0; } "
|
|
"f(); f(); "
|
|
"%OptimizeFunctionOnNextCall(f); "
|
|
"f();";
|
|
Context::Scope scope(caller_context);
|
|
internal::FLAG_allow_natives_syntax = true;
|
|
Script::Compile(
|
|
caller_context,
|
|
String::NewFromUtf8(isolate(), script, v8::NewStringType::kNormal)
|
|
.ToLocalChecked())
|
|
.ToLocalChecked()
|
|
->Run(caller_context)
|
|
.ToLocalChecked();
|
|
EXPECT_EQ(8, call_count);
|
|
}
|
|
|
|
TEST_F(LapContextTest, CurrentContextInLazyAccessorOnInterface) {
|
|
Local<Context> interface_context = Context::New(isolate());
|
|
Local<Context> caller_context = Context::New(isolate());
|
|
|
|
static int call_count; // The number of calls of the accessor callback.
|
|
call_count = 0;
|
|
|
|
Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate());
|
|
Local<String> property_key =
|
|
String::NewFromUtf8(isolate(), "property", NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
Local<FunctionTemplate> get_or_set = FunctionTemplate::New(
|
|
isolate(),
|
|
[](const FunctionCallbackInfo<Value>& info) {
|
|
++call_count;
|
|
Local<Context> interface_context = *reinterpret_cast<Local<Context>*>(
|
|
info.Data().As<External>()->Value());
|
|
EXPECT_EQ(interface_context, info.GetIsolate()->GetCurrentContext());
|
|
},
|
|
External::New(isolate(), &interface_context), Local<Signature>());
|
|
function_template->SetAccessorProperty(property_key, get_or_set, get_or_set);
|
|
|
|
Local<Function> interface =
|
|
function_template->GetFunction(interface_context).ToLocalChecked();
|
|
|
|
EXPECT_EQ(0, call_count);
|
|
interface->Get(caller_context, property_key).ToLocalChecked();
|
|
EXPECT_EQ(1, call_count);
|
|
interface->Set(caller_context, property_key, Null(isolate())).ToChecked();
|
|
EXPECT_EQ(2, call_count);
|
|
|
|
// Test with a compiled version.
|
|
Local<String> interface_key =
|
|
String::NewFromUtf8(isolate(), "Interface", NewStringType::kNormal)
|
|
.ToLocalChecked();
|
|
caller_context->Global()
|
|
->Set(caller_context, interface_key, interface)
|
|
.ToChecked();
|
|
const char script[] =
|
|
"function f() { Interface.property; Interface.property = 0; } "
|
|
"f(); f(); "
|
|
"%OptimizeFunctionOnNextCall(f); "
|
|
"f();";
|
|
Context::Scope scope(caller_context);
|
|
internal::FLAG_allow_natives_syntax = true;
|
|
Script::Compile(
|
|
caller_context,
|
|
String::NewFromUtf8(isolate(), script, v8::NewStringType::kNormal)
|
|
.ToLocalChecked())
|
|
.ToLocalChecked()
|
|
->Run(caller_context)
|
|
.ToLocalChecked();
|
|
EXPECT_EQ(8, call_count);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace v8
|