123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- // Copyright 2013 The Chromium 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 "gin/wrappable.h"
- #include "base/check.h"
- #include "gin/arguments.h"
- #include "gin/handle.h"
- #include "gin/object_template_builder.h"
- #include "gin/per_isolate_data.h"
- #include "gin/public/isolate_holder.h"
- #include "gin/test/v8_test.h"
- #include "gin/try_catch.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "v8/include/v8-function.h"
- #include "v8/include/v8-message.h"
- #include "v8/include/v8-script.h"
- namespace gin {
- namespace {
- // A non-member function to be bound to an ObjectTemplateBuilder.
- void NonMemberMethod() {}
- // This useless base class ensures that the value of a pointer to a MyObject
- // (below) is not the same as the value of that pointer cast to the object's
- // WrappableBase base.
- class BaseClass {
- public:
- BaseClass() : value_(23) {}
- BaseClass(const BaseClass&) = delete;
- BaseClass& operator=(const BaseClass&) = delete;
- virtual ~BaseClass() = default;
- // So the compiler doesn't complain that |value_| is unused.
- int value() const { return value_; }
- private:
- int value_;
- };
- class MyObject : public BaseClass,
- public Wrappable<MyObject> {
- public:
- MyObject(const MyObject&) = delete;
- MyObject& operator=(const MyObject&) = delete;
- static WrapperInfo kWrapperInfo;
- static gin::Handle<MyObject> Create(v8::Isolate* isolate) {
- return CreateHandle(isolate, new MyObject());
- }
- int value() const { return value_; }
- void set_value(int value) { value_ = value; }
- void Method() {}
- protected:
- MyObject() : value_(0) {}
- ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) final {
- return Wrappable<MyObject>::GetObjectTemplateBuilder(isolate)
- .SetProperty("value", &MyObject::value, &MyObject::set_value)
- .SetMethod("memberMethod", &MyObject::Method)
- .SetMethod("nonMemberMethod", &NonMemberMethod);
- }
- ~MyObject() override = default;
- private:
- int value_;
- };
- class MyObject2 : public Wrappable<MyObject2> {
- public:
- static WrapperInfo kWrapperInfo;
- };
- class MyNamedObject : public Wrappable<MyNamedObject> {
- public:
- MyNamedObject(const MyNamedObject&) = delete;
- MyNamedObject& operator=(const MyNamedObject&) = delete;
- static WrapperInfo kWrapperInfo;
- static gin::Handle<MyNamedObject> Create(v8::Isolate* isolate) {
- return CreateHandle(isolate, new MyNamedObject());
- }
- void Method() {}
- protected:
- MyNamedObject() = default;
- ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) final {
- return Wrappable<MyNamedObject>::GetObjectTemplateBuilder(isolate)
- .SetMethod("memberMethod", &MyNamedObject::Method)
- .SetMethod("nonMemberMethod", &NonMemberMethod);
- }
- const char* GetTypeName() final { return "MyNamedObject"; }
- ~MyNamedObject() override = default;
- };
- WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin };
- WrapperInfo MyObject2::kWrapperInfo = { kEmbedderNativeGin };
- WrapperInfo MyNamedObject::kWrapperInfo = {kEmbedderNativeGin};
- } // namespace
- typedef V8Test WrappableTest;
- TEST_F(WrappableTest, WrapAndUnwrap) {
- v8::Isolate* isolate = instance_->isolate();
- v8::HandleScope handle_scope(isolate);
- Handle<MyObject> obj = MyObject::Create(isolate);
- v8::Local<v8::Value> wrapper =
- ConvertToV8(isolate, obj.get()).ToLocalChecked();
- EXPECT_FALSE(wrapper.IsEmpty());
- MyObject* unwrapped = NULL;
- EXPECT_TRUE(ConvertFromV8(isolate, wrapper, &unwrapped));
- EXPECT_EQ(obj.get(), unwrapped);
- }
- TEST_F(WrappableTest, UnwrapFailures) {
- v8::Isolate* isolate = instance_->isolate();
- v8::HandleScope handle_scope(isolate);
- // Something that isn't an object.
- v8::Local<v8::Value> thing = v8::Number::New(isolate, 42);
- MyObject* unwrapped = NULL;
- EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
- EXPECT_FALSE(unwrapped);
- // An object that's not wrapping anything.
- thing = v8::Object::New(isolate);
- EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
- EXPECT_FALSE(unwrapped);
- // An object that's wrapping a C++ object of the wrong type.
- thing.Clear();
- thing = ConvertToV8(isolate, new MyObject2()).ToLocalChecked();
- EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped));
- EXPECT_FALSE(unwrapped);
- }
- TEST_F(WrappableTest, GetAndSetProperty) {
- v8::Isolate* isolate = instance_->isolate();
- v8::HandleScope handle_scope(isolate);
- gin::Handle<MyObject> obj = MyObject::Create(isolate);
- obj->set_value(42);
- EXPECT_EQ(42, obj->value());
- v8::Local<v8::String> source = StringToV8(isolate,
- "(function (obj) {"
- " if (obj.value !== 42) throw 'FAIL';"
- " else obj.value = 191; })");
- EXPECT_FALSE(source.IsEmpty());
- gin::TryCatch try_catch(isolate);
- v8::Local<v8::Script> script =
- v8::Script::Compile(context_.Get(isolate), source).ToLocalChecked();
- v8::Local<v8::Value> val =
- script->Run(context_.Get(isolate)).ToLocalChecked();
- v8::Local<v8::Function> func;
- EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
- v8::Local<v8::Value> argv[] = {
- ConvertToV8(isolate, obj.get()).ToLocalChecked(),
- };
- func->Call(context_.Get(isolate), v8::Undefined(isolate), 1, argv)
- .ToLocalChecked();
- EXPECT_FALSE(try_catch.HasCaught());
- EXPECT_EQ("", try_catch.GetStackTrace());
- EXPECT_EQ(191, obj->value());
- }
- TEST_F(WrappableTest, MethodInvocationErrorsOnUnnamedObject) {
- v8::Isolate* isolate = instance_->isolate();
- v8::HandleScope handle_scope(isolate);
- v8::Local<v8::Context> context = context_.Get(isolate);
- gin::Handle<MyObject> obj = MyObject::Create(isolate);
- v8::Local<v8::Object> v8_object =
- ConvertToV8(isolate, obj.get()).ToLocalChecked().As<v8::Object>();
- v8::Local<v8::Value> member_method =
- v8_object->Get(context, StringToV8(isolate, "memberMethod"))
- .ToLocalChecked();
- ASSERT_TRUE(member_method->IsFunction());
- v8::Local<v8::Value> non_member_method =
- v8_object->Get(context, StringToV8(isolate, "nonMemberMethod"))
- .ToLocalChecked();
- ASSERT_TRUE(non_member_method->IsFunction());
- auto get_error = [isolate, context](v8::Local<v8::Value> function_to_run,
- v8::Local<v8::Value> context_object) {
- constexpr char kScript[] =
- "(function(toRun, contextObject) { toRun.apply(contextObject, []); })";
- v8::Local<v8::String> source = StringToV8(isolate, kScript);
- EXPECT_FALSE(source.IsEmpty());
- v8::TryCatch try_catch(isolate);
- v8::Local<v8::Script> script =
- v8::Script::Compile(context, source).ToLocalChecked();
- v8::Local<v8::Value> val = script->Run(context).ToLocalChecked();
- v8::Local<v8::Function> func;
- EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
- v8::Local<v8::Value> argv[] = {function_to_run, context_object};
- func->Call(context, v8::Undefined(isolate), std::size(argv), argv)
- .FromMaybe(v8::Local<v8::Value>());
- if (!try_catch.HasCaught())
- return std::string();
- return V8ToString(isolate, try_catch.Message()->Get());
- };
- EXPECT_EQ(std::string(), get_error(member_method, v8_object));
- EXPECT_EQ(std::string(), get_error(non_member_method, v8_object));
- EXPECT_EQ("Uncaught TypeError: Illegal invocation",
- get_error(member_method, v8::Null(isolate)));
- // A non-member function shouldn't throw errors for being applied on a
- // null (or invalid) object.
- EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
- v8::Local<v8::Object> wrong_object = v8::Object::New(isolate);
- // We should get an error for passing the wrong object.
- EXPECT_EQ("Uncaught TypeError: Illegal invocation",
- get_error(member_method, wrong_object));
- // But again, not for a "static" method.
- EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
- }
- TEST_F(WrappableTest, MethodInvocationErrorsOnNamedObject) {
- v8::Isolate* isolate = instance_->isolate();
- v8::HandleScope handle_scope(isolate);
- v8::Local<v8::Context> context = context_.Get(isolate);
- gin::Handle<MyNamedObject> obj = MyNamedObject::Create(isolate);
- v8::Local<v8::Object> v8_object =
- ConvertToV8(isolate, obj.get()).ToLocalChecked().As<v8::Object>();
- v8::Local<v8::Value> member_method =
- v8_object->Get(context, StringToV8(isolate, "memberMethod"))
- .ToLocalChecked();
- ASSERT_TRUE(member_method->IsFunction());
- v8::Local<v8::Value> non_member_method =
- v8_object->Get(context, StringToV8(isolate, "nonMemberMethod"))
- .ToLocalChecked();
- ASSERT_TRUE(non_member_method->IsFunction());
- auto get_error = [isolate, context](v8::Local<v8::Value> function_to_run,
- v8::Local<v8::Value> context_object) {
- constexpr char kScript[] =
- "(function(toRun, contextObject) { toRun.apply(contextObject, []); })";
- v8::Local<v8::String> source = StringToV8(isolate, kScript);
- EXPECT_FALSE(source.IsEmpty());
- v8::TryCatch try_catch(isolate);
- v8::Local<v8::Script> script =
- v8::Script::Compile(context, source).ToLocalChecked();
- v8::Local<v8::Value> val = script->Run(context).ToLocalChecked();
- v8::Local<v8::Function> func;
- EXPECT_TRUE(ConvertFromV8(isolate, val, &func));
- v8::Local<v8::Value> argv[] = {function_to_run, context_object};
- func->Call(context, v8::Undefined(isolate), std::size(argv), argv)
- .FromMaybe(v8::Local<v8::Value>());
- if (!try_catch.HasCaught())
- return std::string();
- return V8ToString(isolate, try_catch.Message()->Get());
- };
- EXPECT_EQ(std::string(), get_error(member_method, v8_object));
- EXPECT_EQ(std::string(), get_error(non_member_method, v8_object));
- EXPECT_EQ(
- "Uncaught TypeError: Illegal invocation: Function must be called on "
- "an object of type MyNamedObject",
- get_error(member_method, v8::Null(isolate)));
- // A non-member function shouldn't throw errors for being applied on a
- // null (or invalid) object.
- EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
- v8::Local<v8::Object> wrong_object = v8::Object::New(isolate);
- // We should get an error for passing the wrong object.
- EXPECT_EQ(
- "Uncaught TypeError: Illegal invocation: Function must be called on "
- "an object of type MyNamedObject",
- get_error(member_method, wrong_object));
- // But again, not for a "static" method.
- EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
- }
- class MyObjectWithLazyProperties
- : public Wrappable<MyObjectWithLazyProperties> {
- public:
- MyObjectWithLazyProperties(const MyObjectWithLazyProperties&) = delete;
- MyObjectWithLazyProperties& operator=(const MyObjectWithLazyProperties&) =
- delete;
- static WrapperInfo kWrapperInfo;
- static gin::Handle<MyObjectWithLazyProperties> Create(v8::Isolate* isolate) {
- return CreateHandle(isolate, new MyObjectWithLazyProperties());
- }
- int access_count() const { return access_count_; }
- private:
- MyObjectWithLazyProperties() = default;
- ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) final {
- return Wrappable::GetObjectTemplateBuilder(isolate)
- .SetLazyDataProperty("fortyTwo", &MyObjectWithLazyProperties::FortyTwo)
- .SetLazyDataProperty("self",
- base::BindRepeating([](gin::Arguments* arguments) {
- v8::Local<v8::Value> holder;
- CHECK(arguments->GetHolder(&holder));
- return holder;
- }));
- }
- int FortyTwo() {
- access_count_++;
- return 42;
- }
- int access_count_ = 0;
- };
- WrapperInfo MyObjectWithLazyProperties::kWrapperInfo = {kEmbedderNativeGin};
- TEST_F(WrappableTest, LazyPropertyGetterIsCalledOnce) {
- v8::Isolate* isolate = instance_->isolate();
- v8::HandleScope handle_scope(isolate);
- v8::Local<v8::Context> context = context_.Get(isolate);
- auto handle = MyObjectWithLazyProperties::Create(isolate);
- v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
- v8::Local<v8::String> key = StringToSymbol(isolate, "fortyTwo");
- v8::Local<v8::Value> value;
- bool has_own_property = false;
- ASSERT_TRUE(v8_object->HasOwnProperty(context, key).To(&has_own_property));
- EXPECT_TRUE(has_own_property);
- EXPECT_EQ(0, handle->access_count());
- ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
- EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 42)));
- EXPECT_EQ(1, handle->access_count());
- ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
- EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 42)));
- EXPECT_EQ(1, handle->access_count());
- }
- TEST_F(WrappableTest, LazyPropertyGetterCanBeSetFirst) {
- v8::Isolate* isolate = instance_->isolate();
- v8::HandleScope handle_scope(isolate);
- v8::Local<v8::Context> context = context_.Get(isolate);
- auto handle = MyObjectWithLazyProperties::Create(isolate);
- v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
- v8::Local<v8::String> key = StringToSymbol(isolate, "fortyTwo");
- v8::Local<v8::Value> value;
- EXPECT_EQ(0, handle->access_count());
- bool set_ok = false;
- ASSERT_TRUE(
- v8_object->Set(context, key, v8::Int32::New(isolate, 1701)).To(&set_ok));
- ASSERT_TRUE(set_ok);
- ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
- EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 1701)));
- EXPECT_EQ(0, handle->access_count());
- }
- TEST_F(WrappableTest, LazyPropertyGetterCanBindSpecialArguments) {
- v8::Isolate* isolate = instance_->isolate();
- v8::HandleScope handle_scope(isolate);
- v8::Local<v8::Context> context = context_.Get(isolate);
- auto handle = MyObjectWithLazyProperties::Create(isolate);
- v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
- v8::Local<v8::Value> value;
- ASSERT_TRUE(
- v8_object->Get(context, StringToSymbol(isolate, "self")).ToLocal(&value));
- EXPECT_TRUE(v8_object == value);
- }
- } // namespace gin
|