// 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/api.h" #include "src/objects/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, const PropertyCallbackInfo&) {} TEST_F(ObjectTest, SetAccessorWhenUnconfigurablePropAlreadyDefined) { TryCatch try_catch(isolate()); Local global = context()->Global(); Local 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 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; TEST_F(LapContextTest, 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 receiver_context = Context::New(isolate()); Local prototype_context = Context::New(isolate()); Local caller_context = Context::New(isolate()); static int call_count; // The number of calls of the accessor callback. call_count = 0; Local function_template = FunctionTemplate::New(isolate()); Local signature = Signature::New(isolate(), function_template); Local property_key = String::NewFromUtf8(isolate(), "property", NewStringType::kNormal) .ToLocalChecked(); Local get_or_set = FunctionTemplate::New( isolate(), [](const FunctionCallbackInfo& info) { ++call_count; Local prototype_context = *reinterpret_cast*>( info.Data().As()->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 interface_for_receiver = function_template->GetFunction(receiver_context).ToLocalChecked(); Local interface_for_prototype = function_template->GetFunction(prototype_context).ToLocalChecked(); Local prototype_key = String::NewFromUtf8(isolate(), "prototype", NewStringType::kNormal) .ToLocalChecked(); Local prototype = interface_for_prototype->Get(caller_context, prototype_key) .ToLocalChecked() .As(); Local 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 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; } " "%PrepareFunctionForOptimization(f); " "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 receiver_context = Context::New(isolate()); Local caller_context = Context::New(isolate()); static int call_count; // The number of calls of the accessor callback. call_count = 0; Local function_template = FunctionTemplate::New(isolate()); Local signature = Signature::New(isolate(), function_template); Local property_key = String::NewFromUtf8(isolate(), "property", NewStringType::kNormal) .ToLocalChecked(); Local get_or_set = FunctionTemplate::New( isolate(), [](const FunctionCallbackInfo& info) { ++call_count; Local receiver_context = *reinterpret_cast*>( info.Data().As()->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 interface = function_template->GetFunction(receiver_context).ToLocalChecked(); Local 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 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 interface_context = Context::New(isolate()); Local caller_context = Context::New(isolate()); static int call_count; // The number of calls of the accessor callback. call_count = 0; Local function_template = FunctionTemplate::New(isolate()); Local property_key = String::NewFromUtf8(isolate(), "property", NewStringType::kNormal) .ToLocalChecked(); Local get_or_set = FunctionTemplate::New( isolate(), [](const FunctionCallbackInfo& info) { ++call_count; Local interface_context = *reinterpret_cast*>( info.Data().As()->Value()); EXPECT_EQ(interface_context, info.GetIsolate()->GetCurrentContext()); }, External::New(isolate(), &interface_context), Local()); function_template->SetAccessorProperty(property_key, get_or_set, get_or_set); Local 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 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