Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /gin /wrappable_unittest.cc
blob: de3749ed1e49618664f6c720db2089f06a4725b9 [file] [log] [blame] [edit]
// Copyright 2013 The Chromium Authors
// 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/cppgc/allocation.h"
#include"v8/include/v8-cppgc.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.
voidNonMemberMethod(){}
// 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.
classBaseClass{
public:
BaseClass()=default;
BaseClass(constBaseClass&)=delete;
BaseClass&operator=(constBaseClass&)=delete;
virtual~BaseClass()=default;
// So the compiler doesn't complain that |value_| is unused.
int value()const{return value_;}
private:
int value_=23;
};
classMyObject:publicWrappable<MyObject>,publicBaseClass{
public:
MyObject(constMyObject&)=delete;
MyObject&operator=(constMyObject&)=delete;
MyObject()=default;
staticMyObject*Create(v8::Isolate* isolate){
return cppgc::MakeGarbageCollected<MyObject>(
isolate->GetCppHeap()->GetAllocationHandle());
}
int value()const{return value_;}
void set_value(int value){ value_= value;}
voidMethod(){}
staticconstexprWrapperInfo kWrapperInfo={{kEmbedderNativeGin},
kTestObject};
constWrapperInfo* wrapper_info()const override{return&kWrapperInfo;}
constchar*GetHumanReadableName()const final{return"MyObject";}
protected:
ObjectTemplateBuilderGetObjectTemplateBuilder(v8::Isolate* isolate) final{
returnWrappable<MyObject>::GetObjectTemplateBuilder(isolate)
.SetProperty("value",&MyObject::value,&MyObject::set_value)
.SetMethod("memberMethod",&MyObject::Method)
.SetMethod("nonMemberMethod",&NonMemberMethod);
}
private:
int value_=0;
};
classMyObject2:publicWrappable<MyObject2>{
public:
MyObject2()=default;
staticconstexprWrapperInfo kWrapperInfo={{kEmbedderNativeGin},
kTestObject2};
constWrapperInfo* wrapper_info()const override{return&kWrapperInfo;}
constchar*GetHumanReadableName()const final{return"MyObject2";}
staticMyObject2*Create(v8::Isolate* isolate){
return cppgc::MakeGarbageCollected<MyObject2>(
isolate->GetCppHeap()->GetAllocationHandle());
}
};
classMyNamedObject:publicWrappable<MyNamedObject>{
public:
MyNamedObject(constMyNamedObject&)=delete;
MyNamedObject&operator=(constMyNamedObject&)=delete;
MyNamedObject()=default;
staticconstexprWrapperInfo kWrapperInfo={{kEmbedderNativeGin},
kTestObject2};
constWrapperInfo* wrapper_info()const override{return&kWrapperInfo;}
staticMyNamedObject*Create(v8::Isolate* isolate){
return cppgc::MakeGarbageCollected<MyNamedObject>(
isolate->GetCppHeap()->GetAllocationHandle());
}
voidMethod(){}
protected:
ObjectTemplateBuilderGetObjectTemplateBuilder(v8::Isolate* isolate) final{
returnWrappable<MyNamedObject>::GetObjectTemplateBuilder(isolate)
.SetMethod("memberMethod",&MyNamedObject::Method)
.SetMethod("nonMemberMethod",&NonMemberMethod);
}
constchar*GetHumanReadableName()const final{return"MyNamedObject";}
};
}// namespace
typedef V8TestWrappableTest;
TEST_F(WrappableTest,WrapAndUnwrap){
v8::Isolate* isolate= instance_->isolate();
v8::HandleScope handle_scope(isolate);
MyObject* obj=MyObject::Create(isolate);
v8::Local<v8::Value> wrapper=ConvertToV8(isolate, obj).ToLocalChecked();
EXPECT_FALSE(wrapper.IsEmpty());
MyObject* unwrapped=nullptr;
EXPECT_TRUE(ConvertFromV8(isolate, wrapper,&unwrapped));
EXPECT_EQ(obj, unwrapped);
}
TEST_F(WrappableTest,UnwrapNull){
v8::Isolate* isolate= instance_->isolate();
v8::HandleScope handle_scope(isolate);
MyObject* obj=nullptr;
v8::Local<v8::Value> wrapper=ConvertToV8(isolate, obj).ToLocalChecked();
EXPECT_FALSE(wrapper.IsEmpty());
MyObject* unwrapped=nullptr;
ConvertFromV8(isolate, wrapper,&unwrapped);
EXPECT_EQ(obj, unwrapped);
}
TEST_F(WrappableTest,GetAndSetProperty){
v8::Isolate* isolate= instance_->isolate();
v8::HandleScope handle_scope(isolate);
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).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);
MyObject* obj=MyObject::Create(isolate);
v8::Local<v8::Object> v8_object=
ConvertToV8(isolate, obj).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){
constexprchar 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_TRUE(get_error(member_method, v8::Null(isolate))
.starts_with("Uncaught TypeError: Illegal invocation"));
// 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_TRUE(get_error(member_method, wrong_object)
.starts_with("Uncaught TypeError: Illegal invocation"));
// 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);
MyNamedObject* obj=MyNamedObject::Create(isolate);
v8::Local<v8::Object> v8_object=
ConvertToV8(isolate, obj).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){
constexprchar 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)));
}
classMyObjectWithLazyProperties
:publicWrappable<MyObjectWithLazyProperties>{
public:
MyObjectWithLazyProperties(constMyObjectWithLazyProperties&)=delete;
MyObjectWithLazyProperties&operator=(constMyObjectWithLazyProperties&)=
delete;
MyObjectWithLazyProperties()=default;
staticconstexprWrapperInfo kWrapperInfo={{kEmbedderNativeGin},
kTestObject};
constWrapperInfo* wrapper_info()const override{return&kWrapperInfo;}
constchar*GetHumanReadableName()const final{
return"MyObjectWithLazyProperties";
}
staticMyObjectWithLazyProperties*Create(v8::Isolate* isolate){
return cppgc::MakeGarbageCollected<MyObjectWithLazyProperties>(
isolate->GetCppHeap()->GetAllocationHandle());
}
int access_count()const{return access_count_;}
private:
ObjectTemplateBuilderGetObjectTemplateBuilder(v8::Isolate* isolate) final{
returnWrappable::GetObjectTemplateBuilder(isolate)
.SetLazyDataProperty("fortyTwo",&MyObjectWithLazyProperties::FortyTwo)
.SetLazyDataProperty("self",
base::BindRepeating([](gin::Arguments* arguments){
v8::Local<v8::Value> holder;
CHECK(arguments->GetHolder(&holder));
return holder;
}));
}
intFortyTwo(){
access_count_++;
return42;
}
int access_count_=0;
};
TEST_F(WrappableTest,LazyPropertyGetterIsCalledOnce){
v8::Isolate* isolate= instance_->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context= context_.Get(isolate);
MyObjectWithLazyProperties* obj=MyObjectWithLazyProperties::Create(isolate);
v8::Local<v8::Object> v8_object=
ConvertToV8(isolate, obj).ToLocalChecked().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, obj->access_count());
ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate,42)));
EXPECT_EQ(1, obj->access_count());
ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate,42)));
EXPECT_EQ(1, obj->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* obj=MyObjectWithLazyProperties::Create(isolate);
v8::Local<v8::Object> v8_object=
ConvertToV8(isolate, obj).ToLocalChecked().As<v8::Object>();
v8::Local<v8::String> key=StringToSymbol(isolate,"fortyTwo");
v8::Local<v8::Value> value;
EXPECT_EQ(0, obj->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, obj->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* obj=MyObjectWithLazyProperties::Create(isolate);
v8::Local<v8::Object> v8_object=
ConvertToV8(isolate, obj).ToLocalChecked().As<v8::Object>();
v8::Local<v8::Value> value;
ASSERT_TRUE(
v8_object->Get(context,StringToSymbol(isolate,"self")).ToLocal(&value));
EXPECT_TRUE(v8_object== value);
}
TEST_F(WrappableTest,CannotConstruct){
v8::Isolate* isolate= instance_->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context= context_.Get(isolate);
MyObject* obj=MyObject::Create(isolate);
v8::Local<v8::Value> wrapper=ConvertToV8(isolate, obj).ToLocalChecked();
ASSERT_FALSE(wrapper.IsEmpty());
v8::Local<v8::String> source=
StringToV8(isolate,"(obj => new obj.constructor())");
v8::Local<v8::Script> script=
v8::Script::Compile(context, source).ToLocalChecked();
v8::Local<v8::Function> function=
script->Run(context).ToLocalChecked().As<v8::Function>();
{
TryCatch try_catch(isolate);
EXPECT_TRUE(function
->Call(context, v8::Undefined(isolate),1,
(v8::Local<v8::Value>[]){wrapper})
.IsEmpty());
EXPECT_TRUE(try_catch.HasCaught());
}
}
}// namespace gin

[8]ページ先頭

©2009-2025 Movatter.jp