#include "node_test_fixture.h" #include "node_api.h" void InitializeBinding(v8::Local exports, v8::Local module, v8::Local context, void* priv) { v8::Isolate* isolate = context->GetIsolate(); exports->Set( context, v8::String::NewFromOneByte(isolate, reinterpret_cast("key")) .ToLocalChecked(), v8::String::NewFromOneByte(isolate, reinterpret_cast("value")) .ToLocalChecked()) .FromJust(); } NODE_MODULE_LINKED(cctest_linkedbinding, InitializeBinding) class LinkedBindingTest : public EnvironmentTestFixture {}; TEST_F(LinkedBindingTest, SimpleTest) { const v8::HandleScope handle_scope(isolate_); const Argv argv; Env test_env {handle_scope, argv}; v8::Local context = isolate_->GetCurrentContext(); const char* run_script = "process._linkedBinding('cctest_linkedbinding').key"; v8::Local script = v8::Script::Compile( context, v8::String::NewFromOneByte(isolate_, reinterpret_cast(run_script)) .ToLocalChecked()) .ToLocalChecked(); v8::Local completion_value = script->Run(context).ToLocalChecked(); v8::String::Utf8Value utf8val(isolate_, completion_value); CHECK_NOT_NULL(*utf8val); CHECK_EQ(strcmp(*utf8val, "value"), 0); } void InitializeLocalBinding(v8::Local exports, v8::Local module, v8::Local context, void* priv) { ++*static_cast(priv); v8::Isolate* isolate = context->GetIsolate(); exports->Set( context, v8::String::NewFromOneByte(isolate, reinterpret_cast("key")) .ToLocalChecked(), v8::String::NewFromOneByte(isolate, reinterpret_cast("value")) .ToLocalChecked()) .FromJust(); } TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingTest) { const v8::HandleScope handle_scope(isolate_); const Argv argv; Env test_env {handle_scope, argv}; int calls = 0; AddLinkedBinding(*test_env, "local_linked", InitializeLocalBinding, &calls); v8::Local context = isolate_->GetCurrentContext(); const char* run_script = "process._linkedBinding('local_linked').key"; v8::Local script = v8::Script::Compile( context, v8::String::NewFromOneByte(isolate_, reinterpret_cast(run_script)) .ToLocalChecked()) .ToLocalChecked(); v8::Local completion_value = script->Run(context).ToLocalChecked(); v8::String::Utf8Value utf8val(isolate_, completion_value); CHECK_NOT_NULL(*utf8val); CHECK_EQ(strcmp(*utf8val, "value"), 0); CHECK_EQ(calls, 1); } napi_value InitializeLocalNapiBinding(napi_env env, napi_value exports) { napi_value key, value; CHECK_EQ( napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &key), napi_ok); CHECK_EQ( napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &value), napi_ok); CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok); return nullptr; } static napi_module local_linked_napi = { NAPI_MODULE_VERSION, node::ModuleFlags::kLinked, nullptr, InitializeLocalNapiBinding, "local_linked_napi", nullptr, {0}, }; TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiTest) { const v8::HandleScope handle_scope(isolate_); const Argv argv; Env test_env {handle_scope, argv}; AddLinkedBinding(*test_env, local_linked_napi); v8::Local context = isolate_->GetCurrentContext(); const char* run_script = "process._linkedBinding('local_linked_napi').hello"; v8::Local script = v8::Script::Compile( context, v8::String::NewFromOneByte(isolate_, reinterpret_cast(run_script)) .ToLocalChecked()) .ToLocalChecked(); v8::Local completion_value = script->Run(context).ToLocalChecked(); v8::String::Utf8Value utf8val(isolate_, completion_value); CHECK_NOT_NULL(*utf8val); CHECK_EQ(strcmp(*utf8val, "world"), 0); } napi_value NapiLinkedWithInstanceData(napi_env env, napi_value exports) { int* instance_data = new int(0); CHECK_EQ( napi_set_instance_data( env, instance_data, [](napi_env env, void* data, void* hint) { ++*static_cast(data); }, nullptr), napi_ok); napi_value key, value; CHECK_EQ( napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &key), napi_ok); CHECK_EQ( napi_create_external(env, instance_data, nullptr, nullptr, &value), napi_ok); CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok); return nullptr; } static napi_module local_linked_napi_id = { NAPI_MODULE_VERSION, node::ModuleFlags::kLinked, nullptr, NapiLinkedWithInstanceData, "local_linked_napi_id", nullptr, {0}, }; TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiInstanceDataTest) { const v8::HandleScope handle_scope(isolate_); int* instance_data = nullptr; { const Argv argv; Env test_env {handle_scope, argv}; AddLinkedBinding(*test_env, local_linked_napi_id); v8::Local context = isolate_->GetCurrentContext(); const char* run_script = "process._linkedBinding('local_linked_napi_id').hello"; v8::Local script = v8::Script::Compile( context, v8::String::NewFromOneByte(isolate_, reinterpret_cast(run_script)) .ToLocalChecked()) .ToLocalChecked(); v8::Local completion_value = script->Run(context).ToLocalChecked(); CHECK(completion_value->IsExternal()); instance_data = static_cast( completion_value.As()->Value()); CHECK_NE(instance_data, nullptr); CHECK_EQ(*instance_data, 0); } CHECK_EQ(*instance_data, 1); delete instance_data; } TEST_F(LinkedBindingTest, ManyBindingsTest) { const v8::HandleScope handle_scope(isolate_); const Argv argv; Env test_env {handle_scope, argv}; int calls = 0; AddLinkedBinding(*test_env, "local_linked1", InitializeLocalBinding, &calls); AddLinkedBinding(*test_env, "local_linked2", InitializeLocalBinding, &calls); AddLinkedBinding(*test_env, "local_linked3", InitializeLocalBinding, &calls); AddLinkedBinding(*test_env, local_linked_napi); // Add a N-API addon as well. AddLinkedBinding(*test_env, "local_linked4", InitializeLocalBinding, &calls); AddLinkedBinding(*test_env, "local_linked5", InitializeLocalBinding, &calls); v8::Local context = isolate_->GetCurrentContext(); const char* run_script = "for (let i = 1; i <= 5; i++)process._linkedBinding(`local_linked${i}`);" "process._linkedBinding('local_linked_napi').hello"; v8::Local script = v8::Script::Compile( context, v8::String::NewFromOneByte(isolate_, reinterpret_cast(run_script)) .ToLocalChecked()) .ToLocalChecked(); v8::Local completion_value = script->Run(context).ToLocalChecked(); v8::String::Utf8Value utf8val(isolate_, completion_value); CHECK_NOT_NULL(*utf8val); CHECK_EQ(strcmp(*utf8val, "world"), 0); CHECK_EQ(calls, 5); }