|  | // 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 |