v8/test/unittests/api/v8-object-unittest.cc
Yuki Shiino 25decc66ae Set the current context to the function's context when entering to LAP.
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}
2017-08-24 12:04:19 +00:00

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