Line data Source code
1 : // Copyright 2017 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "include/v8.h"
6 : #include "src/api.h"
7 : #include "src/objects-inl.h"
8 : #include "test/unittests/test-utils.h"
9 : #include "testing/gtest/include/gtest/gtest.h"
10 :
11 : namespace v8 {
12 : namespace {
13 :
14 : using ObjectTest = TestWithContext;
15 :
16 0 : void accessor_name_getter_callback(Local<Name>,
17 0 : const PropertyCallbackInfo<Value>&) {}
18 :
19 15444 : TEST_F(ObjectTest, SetAccessorWhenUnconfigurablePropAlreadyDefined) {
20 2 : TryCatch try_catch(isolate());
21 :
22 1 : Local<Object> global = context()->Global();
23 : Local<String> property_name =
24 1 : String::NewFromUtf8(isolate(), "foo", NewStringType::kNormal)
25 : .ToLocalChecked();
26 :
27 2 : PropertyDescriptor prop_desc;
28 1 : prop_desc.set_configurable(false);
29 2 : global->DefineProperty(context(), property_name, prop_desc).ToChecked();
30 :
31 : Maybe<bool> result = global->SetAccessor(context(), property_name,
32 1 : accessor_name_getter_callback);
33 1 : ASSERT_TRUE(result.IsJust());
34 2 : ASSERT_FALSE(result.FromJust());
35 2 : ASSERT_FALSE(try_catch.HasCaught());
36 : }
37 :
38 : using LapContextTest = TestWithIsolate;
39 :
40 15443 : TEST_F(LapContextTest, CurrentContextInLazyAccessorOnPrototype) {
41 : // The receiver object is created in |receiver_context|, but its prototype
42 : // object is created in |prototype_context|, and the property is accessed
43 : // from |caller_context|.
44 1 : Local<Context> receiver_context = Context::New(isolate());
45 1 : Local<Context> prototype_context = Context::New(isolate());
46 1 : Local<Context> caller_context = Context::New(isolate());
47 :
48 : static int call_count; // The number of calls of the accessor callback.
49 1 : call_count = 0;
50 :
51 1 : Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate());
52 1 : Local<Signature> signature = Signature::New(isolate(), function_template);
53 : Local<String> property_key =
54 1 : String::NewFromUtf8(isolate(), "property", NewStringType::kNormal)
55 : .ToLocalChecked();
56 : Local<FunctionTemplate> get_or_set = FunctionTemplate::New(
57 : isolate(),
58 16 : [](const FunctionCallbackInfo<Value>& info) {
59 8 : ++call_count;
60 : Local<Context> prototype_context = *reinterpret_cast<Local<Context>*>(
61 8 : info.Data().As<External>()->Value());
62 16 : EXPECT_EQ(prototype_context, info.GetIsolate()->GetCurrentContext());
63 16 : },
64 2 : External::New(isolate(), &prototype_context), signature);
65 2 : function_template->PrototypeTemplate()->SetAccessorProperty(
66 1 : property_key, get_or_set, get_or_set);
67 :
68 : // |object| is created in |receiver_context|, and |prototype| is created
69 : // in |prototype_context|. And then, object.__proto__ = prototype.
70 : Local<Function> interface_for_receiver =
71 1 : function_template->GetFunction(receiver_context).ToLocalChecked();
72 : Local<Function> interface_for_prototype =
73 1 : function_template->GetFunction(prototype_context).ToLocalChecked();
74 : Local<String> prototype_key =
75 1 : String::NewFromUtf8(isolate(), "prototype", NewStringType::kNormal)
76 : .ToLocalChecked();
77 : Local<Object> prototype =
78 2 : interface_for_prototype->Get(caller_context, prototype_key)
79 : .ToLocalChecked()
80 : .As<Object>();
81 : Local<Object> object =
82 : interface_for_receiver->NewInstance(receiver_context).ToLocalChecked();
83 2 : object->SetPrototype(caller_context, prototype).ToChecked();
84 2 : EXPECT_EQ(receiver_context, object->CreationContext());
85 2 : EXPECT_EQ(prototype_context, prototype->CreationContext());
86 :
87 2 : EXPECT_EQ(0, call_count);
88 2 : object->Get(caller_context, property_key).ToLocalChecked();
89 2 : EXPECT_EQ(1, call_count);
90 2 : object->Set(caller_context, property_key, Null(isolate())).ToChecked();
91 2 : EXPECT_EQ(2, call_count);
92 :
93 : // Test with a compiled version.
94 : Local<String> object_key =
95 1 : String::NewFromUtf8(isolate(), "object", NewStringType::kNormal)
96 : .ToLocalChecked();
97 3 : caller_context->Global()->Set(caller_context, object_key, object).ToChecked();
98 : const char script[] =
99 : "function f() { object.property; object.property = 0; } "
100 : "f(); f(); "
101 : "%OptimizeFunctionOnNextCall(f); "
102 1 : "f();";
103 : Context::Scope scope(caller_context);
104 1 : internal::FLAG_allow_natives_syntax = true;
105 1 : Script::Compile(
106 : caller_context,
107 1 : String::NewFromUtf8(isolate(), script, v8::NewStringType::kNormal)
108 : .ToLocalChecked())
109 : .ToLocalChecked()
110 1 : ->Run(caller_context)
111 : .ToLocalChecked();
112 2 : EXPECT_EQ(8, call_count);
113 1 : }
114 :
115 15443 : TEST_F(LapContextTest, CurrentContextInLazyAccessorOnPlatformObject) {
116 1 : Local<Context> receiver_context = Context::New(isolate());
117 1 : Local<Context> caller_context = Context::New(isolate());
118 :
119 : static int call_count; // The number of calls of the accessor callback.
120 1 : call_count = 0;
121 :
122 1 : Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate());
123 1 : Local<Signature> signature = Signature::New(isolate(), function_template);
124 : Local<String> property_key =
125 1 : String::NewFromUtf8(isolate(), "property", NewStringType::kNormal)
126 : .ToLocalChecked();
127 : Local<FunctionTemplate> get_or_set = FunctionTemplate::New(
128 : isolate(),
129 16 : [](const FunctionCallbackInfo<Value>& info) {
130 8 : ++call_count;
131 : Local<Context> receiver_context = *reinterpret_cast<Local<Context>*>(
132 8 : info.Data().As<External>()->Value());
133 16 : EXPECT_EQ(receiver_context, info.GetIsolate()->GetCurrentContext());
134 16 : },
135 2 : External::New(isolate(), &receiver_context), signature);
136 2 : function_template->InstanceTemplate()->SetAccessorProperty(
137 1 : property_key, get_or_set, get_or_set);
138 :
139 : Local<Function> interface =
140 1 : function_template->GetFunction(receiver_context).ToLocalChecked();
141 : Local<Object> object =
142 : interface->NewInstance(receiver_context).ToLocalChecked();
143 :
144 2 : EXPECT_EQ(0, call_count);
145 2 : object->Get(caller_context, property_key).ToLocalChecked();
146 2 : EXPECT_EQ(1, call_count);
147 2 : object->Set(caller_context, property_key, Null(isolate())).ToChecked();
148 2 : EXPECT_EQ(2, call_count);
149 :
150 : // Test with a compiled version.
151 : Local<String> object_key =
152 1 : String::NewFromUtf8(isolate(), "object", NewStringType::kNormal)
153 : .ToLocalChecked();
154 3 : caller_context->Global()->Set(caller_context, object_key, object).ToChecked();
155 : const char script[] =
156 : "function f() { object.property; object.property = 0; } "
157 : "f(); f(); "
158 : "%OptimizeFunctionOnNextCall(f); "
159 1 : "f();";
160 : Context::Scope scope(caller_context);
161 1 : internal::FLAG_allow_natives_syntax = true;
162 1 : Script::Compile(
163 : caller_context,
164 1 : String::NewFromUtf8(isolate(), script, v8::NewStringType::kNormal)
165 : .ToLocalChecked())
166 : .ToLocalChecked()
167 1 : ->Run(caller_context)
168 : .ToLocalChecked();
169 2 : EXPECT_EQ(8, call_count);
170 1 : }
171 :
172 15443 : TEST_F(LapContextTest, CurrentContextInLazyAccessorOnInterface) {
173 1 : Local<Context> interface_context = Context::New(isolate());
174 1 : Local<Context> caller_context = Context::New(isolate());
175 :
176 : static int call_count; // The number of calls of the accessor callback.
177 1 : call_count = 0;
178 :
179 1 : Local<FunctionTemplate> function_template = FunctionTemplate::New(isolate());
180 : Local<String> property_key =
181 1 : String::NewFromUtf8(isolate(), "property", NewStringType::kNormal)
182 : .ToLocalChecked();
183 : Local<FunctionTemplate> get_or_set = FunctionTemplate::New(
184 : isolate(),
185 16 : [](const FunctionCallbackInfo<Value>& info) {
186 8 : ++call_count;
187 : Local<Context> interface_context = *reinterpret_cast<Local<Context>*>(
188 8 : info.Data().As<External>()->Value());
189 16 : EXPECT_EQ(interface_context, info.GetIsolate()->GetCurrentContext());
190 16 : },
191 2 : External::New(isolate(), &interface_context), Local<Signature>());
192 1 : function_template->SetAccessorProperty(property_key, get_or_set, get_or_set);
193 :
194 : Local<Function> interface =
195 1 : function_template->GetFunction(interface_context).ToLocalChecked();
196 :
197 2 : EXPECT_EQ(0, call_count);
198 2 : interface->Get(caller_context, property_key).ToLocalChecked();
199 2 : EXPECT_EQ(1, call_count);
200 2 : interface->Set(caller_context, property_key, Null(isolate())).ToChecked();
201 2 : EXPECT_EQ(2, call_count);
202 :
203 : // Test with a compiled version.
204 : Local<String> interface_key =
205 1 : String::NewFromUtf8(isolate(), "Interface", NewStringType::kNormal)
206 : .ToLocalChecked();
207 1 : caller_context->Global()
208 3 : ->Set(caller_context, interface_key, interface)
209 : .ToChecked();
210 : const char script[] =
211 : "function f() { Interface.property; Interface.property = 0; } "
212 : "f(); f(); "
213 : "%OptimizeFunctionOnNextCall(f); "
214 1 : "f();";
215 : Context::Scope scope(caller_context);
216 1 : internal::FLAG_allow_natives_syntax = true;
217 1 : Script::Compile(
218 : caller_context,
219 1 : String::NewFromUtf8(isolate(), script, v8::NewStringType::kNormal)
220 : .ToLocalChecked())
221 : .ToLocalChecked()
222 1 : ->Run(caller_context)
223 : .ToLocalChecked();
224 2 : EXPECT_EQ(8, call_count);
225 1 : }
226 :
227 : } // namespace
228 9264 : } // namespace v8
|