wrappable_unittest.cc 14 KB

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