diff --git a/node.gyp b/node.gyp index 64d0721d249..96e9805a362 100644 --- a/node.gyp +++ b/node.gyp @@ -1038,6 +1038,7 @@ 'test/cctest/test_base64.cc', 'test/cctest/test_node_postmortem_metadata.cc', 'test/cctest/test_environment.cc', + 'test/cctest/test_linked_binding.cc', 'test/cctest/test_platform.cc', 'test/cctest/test_report_util.cc', 'test/cctest/test_traced_value.cc', diff --git a/src/node.h b/src/node.h index 3ba5cf92f34..dffeb2b9cd7 100644 --- a/src/node.h +++ b/src/node.h @@ -445,6 +445,10 @@ typedef void (*addon_context_register_func)( v8::Local context, void* priv); +enum ModuleFlags { + kLinked = 0x02 +}; + struct node_module { int nm_version; unsigned int nm_flags; @@ -532,6 +536,14 @@ extern "C" NODE_EXTERN void node_module_register(void* mod); /* NOLINTNEXTLINE (readability/null_usage) */ \ NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, 0) +// Embedders can use this type of binding for statically linked native bindings. +// It is used the same way addon bindings are used, except that linked bindings +// can be accessed through `process._linkedBinding(modname)`. +#define NODE_MODULE_LINKED(modname, regfunc) \ + /* NOLINTNEXTLINE (readability/null_usage) */ \ + NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, \ + node::ModuleFlags::kLinked) + /* * For backward compatibility in add-on modules. */ diff --git a/src/node_binding.h b/src/node_binding.h index fcd7f34ac6a..e2f4d320c80 100644 --- a/src/node_binding.h +++ b/src/node_binding.h @@ -21,6 +21,11 @@ enum { NM_F_DELETEME = 1 << 3, }; +// Make sure our internal values match the public API's values. +static_assert(static_cast(NM_F_LINKED) == + static_cast(node::ModuleFlags::kLinked), + "NM_F_LINKED != node::ModuleFlags::kLinked"); + #define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \ static node::node_module _module = { \ NODE_MODULE_VERSION, \ diff --git a/test/cctest/test_linked_binding.cc b/test/cctest/test_linked_binding.cc new file mode 100644 index 00000000000..e0b46d38227 --- /dev/null +++ b/test/cctest/test_linked_binding.cc @@ -0,0 +1,42 @@ +#include "node_test_fixture.h" +#include "node_internals.h" // RunBootstrapping() + +void InitializeBinding(v8::Local exports, + v8::Local module, + v8::Local context) { + v8::Isolate* isolate = context->GetIsolate(); + exports->Set( + context, + v8::String::NewFromOneByte(isolate, + reinterpret_cast("key"), + v8::NewStringType::kNormal).ToLocalChecked(), + v8::String::NewFromOneByte(isolate, + reinterpret_cast("value"), + v8::NewStringType::kNormal).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), + v8::NewStringType::kNormal).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); +}