This patch combine three patch which is related to "--gcov" flag.
[platform/framework/web/chromium-efl.git] / gin / wrappable_unittest.cc
1 // Copyright 2013 The Chromium Authors
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 "gin/wrappable.h"
6
7 #include "base/check.h"
8 #include "gin/arguments.h"
9 #include "gin/handle.h"
10 #include "gin/object_template_builder.h"
11 #include "gin/per_isolate_data.h"
12 #include "gin/public/isolate_holder.h"
13 #include "gin/test/v8_test.h"
14 #include "gin/try_catch.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "v8/include/v8-function.h"
17 #include "v8/include/v8-message.h"
18 #include "v8/include/v8-script.h"
19
20 namespace gin {
21
22 namespace {
23
24 // A non-member function to be bound to an ObjectTemplateBuilder.
25 void NonMemberMethod() {}
26
27 // This useless base class ensures that the value of a pointer to a MyObject
28 // (below) is not the same as the value of that pointer cast to the object's
29 // WrappableBase base.
30 class BaseClass {
31  public:
32   BaseClass() : value_(23) {}
33   BaseClass(const BaseClass&) = delete;
34   BaseClass& operator=(const BaseClass&) = delete;
35   virtual ~BaseClass() = default;
36
37   // So the compiler doesn't complain that |value_| is unused.
38   int value() const { return value_; }
39
40  private:
41   int value_;
42 };
43
44 class MyObject : public BaseClass,
45                  public Wrappable<MyObject> {
46  public:
47   MyObject(const MyObject&) = delete;
48   MyObject& operator=(const MyObject&) = delete;
49
50   static WrapperInfo kWrapperInfo;
51
52   static gin::Handle<MyObject> Create(v8::Isolate* isolate) {
53     return CreateHandle(isolate, new MyObject());
54   }
55
56   int value() const { return value_; }
57   void set_value(int value) { value_ = value; }
58
59   void Method() {}
60
61  protected:
62   MyObject() : value_(0) {}
63   ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) final {
64     return Wrappable<MyObject>::GetObjectTemplateBuilder(isolate)
65         .SetProperty("value", &MyObject::value, &MyObject::set_value)
66         .SetMethod("memberMethod", &MyObject::Method)
67         .SetMethod("nonMemberMethod", &NonMemberMethod);
68   }
69   ~MyObject() override = default;
70
71  private:
72   int value_;
73 };
74
75 class MyObject2 : public Wrappable<MyObject2> {
76  public:
77   static WrapperInfo kWrapperInfo;
78 };
79
80 class MyNamedObject : public Wrappable<MyNamedObject> {
81  public:
82   MyNamedObject(const MyNamedObject&) = delete;
83   MyNamedObject& operator=(const MyNamedObject&) = delete;
84
85   static WrapperInfo kWrapperInfo;
86
87   static gin::Handle<MyNamedObject> Create(v8::Isolate* isolate) {
88     return CreateHandle(isolate, new MyNamedObject());
89   }
90
91   void Method() {}
92
93  protected:
94   MyNamedObject() = default;
95   ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) final {
96     return Wrappable<MyNamedObject>::GetObjectTemplateBuilder(isolate)
97         .SetMethod("memberMethod", &MyNamedObject::Method)
98         .SetMethod("nonMemberMethod", &NonMemberMethod);
99   }
100   const char* GetTypeName() final { return "MyNamedObject"; }
101   ~MyNamedObject() override = default;
102 };
103
104 WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin };
105 WrapperInfo MyObject2::kWrapperInfo = { kEmbedderNativeGin };
106 WrapperInfo MyNamedObject::kWrapperInfo = {kEmbedderNativeGin};
107
108 }  // namespace
109
110 typedef V8Test WrappableTest;
111
112 TEST_F(WrappableTest, WrapAndUnwrap) {
113   v8::Isolate* isolate = instance_->isolate();
114   v8::HandleScope handle_scope(isolate);
115
116   Handle<MyObject> obj = MyObject::Create(isolate);
117
118   v8::Local<v8::Value> wrapper =
119       ConvertToV8(isolate, obj.get()).ToLocalChecked();
120   EXPECT_FALSE(wrapper.IsEmpty());
121
122   MyObject* unwrapped = NULL;
123   EXPECT_TRUE(ConvertFromV8(isolate, wrapper, &unwrapped));
124   EXPECT_EQ(obj.get(), unwrapped);
125 }
126
127 TEST_F(WrappableTest, UnwrapFailures) {
128   v8::Isolate* isolate = instance_->isolate();
129   v8::HandleScope handle_scope(isolate);
130
131   // Something that isn't an object.
132   v8::Local<v8::Value> thing = v8::Number::New(isolate, 42);
133   MyObject* unwrapped = NULL;
134   EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
135   EXPECT_FALSE(unwrapped);
136
137   // An object that's not wrapping anything.
138   thing = v8::Object::New(isolate);
139   EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
140   EXPECT_FALSE(unwrapped);
141
142   // An object that's wrapping a C++ object of the wrong type.
143   thing.Clear();
144   thing = ConvertToV8(isolate, new MyObject2()).ToLocalChecked();
145   EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
146   EXPECT_FALSE(unwrapped);
147 }
148
149 TEST_F(WrappableTest, GetAndSetProperty) {
150   v8::Isolate* isolate = instance_->isolate();
151   v8::HandleScope handle_scope(isolate);
152
153   gin::Handle<MyObject> obj = MyObject::Create(isolate);
154
155   obj->set_value(42);
156   EXPECT_EQ(42, obj->value());
157
158   v8::Local<v8::String> source = StringToV8(isolate,
159       "(function (obj) {"
160       "   if (obj.value !== 42) throw 'FAIL';"
161       "   else obj.value = 191; })");
162   EXPECT_FALSE(source.IsEmpty());
163
164   gin::TryCatch try_catch(isolate);
165   v8::Local<v8::Script> script =
166       v8::Script::Compile(context_.Get(isolate), source).ToLocalChecked();
167   v8::Local<v8::Value> val =
168       script->Run(context_.Get(isolate)).ToLocalChecked();
169   v8::Local<v8::Function> func;
170   EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
171   v8::Local<v8::Value> argv[] = {
172       ConvertToV8(isolate, obj.get()).ToLocalChecked(),
173   };
174   func->Call(context_.Get(isolate), v8::Undefined(isolate), 1, argv)
175       .ToLocalChecked();
176   EXPECT_FALSE(try_catch.HasCaught());
177   EXPECT_EQ("", try_catch.GetStackTrace());
178
179   EXPECT_EQ(191, obj->value());
180 }
181
182 TEST_F(WrappableTest, MethodInvocationErrorsOnUnnamedObject) {
183   v8::Isolate* isolate = instance_->isolate();
184   v8::HandleScope handle_scope(isolate);
185   v8::Local<v8::Context> context = context_.Get(isolate);
186
187   gin::Handle<MyObject> obj = MyObject::Create(isolate);
188
189   v8::Local<v8::Object> v8_object =
190       ConvertToV8(isolate, obj.get()).ToLocalChecked().As<v8::Object>();
191   v8::Local<v8::Value> member_method =
192       v8_object->Get(context, StringToV8(isolate, "memberMethod"))
193           .ToLocalChecked();
194   ASSERT_TRUE(member_method->IsFunction());
195   v8::Local<v8::Value> non_member_method =
196       v8_object->Get(context, StringToV8(isolate, "nonMemberMethod"))
197           .ToLocalChecked();
198   ASSERT_TRUE(non_member_method->IsFunction());
199
200   auto get_error = [isolate, context](v8::Local<v8::Value> function_to_run,
201                                       v8::Local<v8::Value> context_object) {
202     constexpr char kScript[] =
203         "(function(toRun, contextObject) { toRun.apply(contextObject, []); })";
204     v8::Local<v8::String> source = StringToV8(isolate, kScript);
205     EXPECT_FALSE(source.IsEmpty());
206
207     v8::TryCatch try_catch(isolate);
208     v8::Local<v8::Script> script =
209         v8::Script::Compile(context, source).ToLocalChecked();
210     v8::Local<v8::Value> val = script->Run(context).ToLocalChecked();
211     v8::Local<v8::Function> func;
212     EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
213     v8::Local<v8::Value> argv[] = {function_to_run, context_object};
214     func->Call(context, v8::Undefined(isolate), std::size(argv), argv)
215         .FromMaybe(v8::Local<v8::Value>());
216     if (!try_catch.HasCaught())
217       return std::string();
218     return V8ToString(isolate, try_catch.Message()->Get());
219   };
220
221   EXPECT_EQ(std::string(), get_error(member_method, v8_object));
222   EXPECT_EQ(std::string(), get_error(non_member_method, v8_object));
223
224   EXPECT_EQ("Uncaught TypeError: Illegal invocation",
225             get_error(member_method, v8::Null(isolate)));
226   // A non-member function shouldn't throw errors for being applied on a
227   // null (or invalid) object.
228   EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
229
230   v8::Local<v8::Object> wrong_object = v8::Object::New(isolate);
231   // We should get an error for passing the wrong object.
232   EXPECT_EQ("Uncaught TypeError: Illegal invocation",
233             get_error(member_method, wrong_object));
234   // But again, not for a "static" method.
235   EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
236 }
237
238 TEST_F(WrappableTest, MethodInvocationErrorsOnNamedObject) {
239   v8::Isolate* isolate = instance_->isolate();
240   v8::HandleScope handle_scope(isolate);
241   v8::Local<v8::Context> context = context_.Get(isolate);
242
243   gin::Handle<MyNamedObject> obj = MyNamedObject::Create(isolate);
244
245   v8::Local<v8::Object> v8_object =
246       ConvertToV8(isolate, obj.get()).ToLocalChecked().As<v8::Object>();
247   v8::Local<v8::Value> member_method =
248       v8_object->Get(context, StringToV8(isolate, "memberMethod"))
249           .ToLocalChecked();
250   ASSERT_TRUE(member_method->IsFunction());
251   v8::Local<v8::Value> non_member_method =
252       v8_object->Get(context, StringToV8(isolate, "nonMemberMethod"))
253           .ToLocalChecked();
254   ASSERT_TRUE(non_member_method->IsFunction());
255
256   auto get_error = [isolate, context](v8::Local<v8::Value> function_to_run,
257                                       v8::Local<v8::Value> context_object) {
258     constexpr char kScript[] =
259         "(function(toRun, contextObject) { toRun.apply(contextObject, []); })";
260     v8::Local<v8::String> source = StringToV8(isolate, kScript);
261     EXPECT_FALSE(source.IsEmpty());
262
263     v8::TryCatch try_catch(isolate);
264     v8::Local<v8::Script> script =
265         v8::Script::Compile(context, source).ToLocalChecked();
266     v8::Local<v8::Value> val = script->Run(context).ToLocalChecked();
267     v8::Local<v8::Function> func;
268     EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
269     v8::Local<v8::Value> argv[] = {function_to_run, context_object};
270     func->Call(context, v8::Undefined(isolate), std::size(argv), argv)
271         .FromMaybe(v8::Local<v8::Value>());
272     if (!try_catch.HasCaught())
273       return std::string();
274     return V8ToString(isolate, try_catch.Message()->Get());
275   };
276
277   EXPECT_EQ(std::string(), get_error(member_method, v8_object));
278   EXPECT_EQ(std::string(), get_error(non_member_method, v8_object));
279
280   EXPECT_EQ(
281       "Uncaught TypeError: Illegal invocation: Function must be called on "
282       "an object of type MyNamedObject",
283       get_error(member_method, v8::Null(isolate)));
284   // A non-member function shouldn't throw errors for being applied on a
285   // null (or invalid) object.
286   EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
287
288   v8::Local<v8::Object> wrong_object = v8::Object::New(isolate);
289   // We should get an error for passing the wrong object.
290   EXPECT_EQ(
291       "Uncaught TypeError: Illegal invocation: Function must be called on "
292       "an object of type MyNamedObject",
293       get_error(member_method, wrong_object));
294   // But again, not for a "static" method.
295   EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
296 }
297
298 class MyObjectWithLazyProperties
299     : public Wrappable<MyObjectWithLazyProperties> {
300  public:
301   MyObjectWithLazyProperties(const MyObjectWithLazyProperties&) = delete;
302   MyObjectWithLazyProperties& operator=(const MyObjectWithLazyProperties&) =
303       delete;
304
305   static WrapperInfo kWrapperInfo;
306
307   static gin::Handle<MyObjectWithLazyProperties> Create(v8::Isolate* isolate) {
308     return CreateHandle(isolate, new MyObjectWithLazyProperties());
309   }
310
311   int access_count() const { return access_count_; }
312
313  private:
314   MyObjectWithLazyProperties() = default;
315
316   ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) final {
317     return Wrappable::GetObjectTemplateBuilder(isolate)
318         .SetLazyDataProperty("fortyTwo", &MyObjectWithLazyProperties::FortyTwo)
319         .SetLazyDataProperty("self",
320                              base::BindRepeating([](gin::Arguments* arguments) {
321                                v8::Local<v8::Value> holder;
322                                CHECK(arguments->GetHolder(&holder));
323                                return holder;
324                              }));
325   }
326
327   int FortyTwo() {
328     access_count_++;
329     return 42;
330   }
331
332   int access_count_ = 0;
333 };
334
335 WrapperInfo MyObjectWithLazyProperties::kWrapperInfo = {kEmbedderNativeGin};
336
337 TEST_F(WrappableTest, LazyPropertyGetterIsCalledOnce) {
338   v8::Isolate* isolate = instance_->isolate();
339   v8::HandleScope handle_scope(isolate);
340   v8::Local<v8::Context> context = context_.Get(isolate);
341
342   auto handle = MyObjectWithLazyProperties::Create(isolate);
343   v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
344   v8::Local<v8::String> key = StringToSymbol(isolate, "fortyTwo");
345   v8::Local<v8::Value> value;
346
347   bool has_own_property = false;
348   ASSERT_TRUE(v8_object->HasOwnProperty(context, key).To(&has_own_property));
349   EXPECT_TRUE(has_own_property);
350
351   EXPECT_EQ(0, handle->access_count());
352
353   ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
354   EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 42)));
355   EXPECT_EQ(1, handle->access_count());
356
357   ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
358   EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 42)));
359   EXPECT_EQ(1, handle->access_count());
360 }
361
362 TEST_F(WrappableTest, LazyPropertyGetterCanBeSetFirst) {
363   v8::Isolate* isolate = instance_->isolate();
364   v8::HandleScope handle_scope(isolate);
365   v8::Local<v8::Context> context = context_.Get(isolate);
366
367   auto handle = MyObjectWithLazyProperties::Create(isolate);
368   v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
369   v8::Local<v8::String> key = StringToSymbol(isolate, "fortyTwo");
370   v8::Local<v8::Value> value;
371
372   EXPECT_EQ(0, handle->access_count());
373
374   bool set_ok = false;
375   ASSERT_TRUE(
376       v8_object->Set(context, key, v8::Int32::New(isolate, 1701)).To(&set_ok));
377   ASSERT_TRUE(set_ok);
378   ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
379   EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 1701)));
380   EXPECT_EQ(0, handle->access_count());
381 }
382
383 TEST_F(WrappableTest, LazyPropertyGetterCanBindSpecialArguments) {
384   v8::Isolate* isolate = instance_->isolate();
385   v8::HandleScope handle_scope(isolate);
386   v8::Local<v8::Context> context = context_.Get(isolate);
387
388   auto handle = MyObjectWithLazyProperties::Create(isolate);
389   v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
390   v8::Local<v8::Value> value;
391   ASSERT_TRUE(
392       v8_object->Get(context, StringToSymbol(isolate, "self")).ToLocal(&value));
393   EXPECT_TRUE(v8_object == value);
394 }
395
396 TEST_F(WrappableTest, CannotConstruct) {
397   v8::Isolate* isolate = instance_->isolate();
398   v8::HandleScope handle_scope(isolate);
399   v8::Local<v8::Context> context = context_.Get(isolate);
400
401   Handle<MyObject> obj = MyObject::Create(isolate);
402   v8::Local<v8::Value> wrapper =
403       ConvertToV8(isolate, obj.get()).ToLocalChecked();
404   ASSERT_FALSE(wrapper.IsEmpty());
405
406   v8::Local<v8::String> source =
407       StringToV8(isolate, "(obj => new obj.constructor())");
408   v8::Local<v8::Script> script =
409       v8::Script::Compile(context, source).ToLocalChecked();
410   v8::Local<v8::Function> function =
411       script->Run(context).ToLocalChecked().As<v8::Function>();
412
413   {
414     TryCatch try_catch(isolate);
415     EXPECT_TRUE(function
416                     ->Call(context, v8::Undefined(isolate), 1,
417                            (v8::Local<v8::Value>[]){wrapper})
418                     .IsEmpty());
419     EXPECT_TRUE(try_catch.HasCaught());
420   }
421 }
422
423 }  // namespace gin