Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: kickstart addon by calling its constructor #20114

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2225,6 +2225,13 @@ inline InitializerCallback GetInitializerCallback(DLib* dlib) {
return reinterpret_cast<InitializerCallback>(dlib->GetSymbolAddress(name));
}

using KickstartCallback = void (*)();

inline KickstartCallback GetKickstartCallback(DLib* dlib) {
const char* name = "node_kickstart_module";
return reinterpret_cast<KickstartCallback>(dlib->GetSymbolAddress(name));
}

// DLOpen is process.dlopen(module, filename, flags).
// Used to load 'module.node' dynamically shared objects.
//
Expand Down Expand Up @@ -2263,7 +2270,7 @@ static void DLOpen(const FunctionCallbackInfo<Value>& args) {
// Objects containing v14 or later modules will have registered themselves
// on the pending list. Activate all of them now. At present, only one
// module per object is supported.
node_module* const mp = modpending;
node_module* mp = modpending;
modpending = nullptr;

if (!is_opened) {
Expand All @@ -2281,15 +2288,26 @@ static void DLOpen(const FunctionCallbackInfo<Value>& args) {
if (mp == nullptr) {
if (auto callback = GetInitializerCallback(&dlib)) {
callback(exports, module, context);
} else {
return;
} else if (auto kickstart = GetKickstartCallback(&dlib)) {
kickstart();
mp = modpending;
modpending = nullptr;
}
if (mp == nullptr) {
dlib.Close();
env->ThrowError("Module did not self-register.");
return;
}
return;
}

// -1 is used for N-API modules
if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) {
if (auto callback = GetInitializerCallback(&dlib)) {
callback(exports, module, context);
return;
}

char errmsg[1024];
snprintf(errmsg,
sizeof(errmsg),
Expand Down
10 changes: 10 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,16 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
/* NOLINTNEXTLINE (readability/null_usage) */ \
NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, 0)

#define NODE_MODULE_FALLBACK_X(modname) \
extern "C" { \
void node_kickstart_module() { \
_register_ ## modname(); \
} \
}

#define NODE_MODULE_FALLBACK(modname) \
NODE_MODULE_FALLBACK_X(modname)

/*
* For backward compatibility in add-on modules.
*/
Expand Down
10 changes: 10 additions & 0 deletions src/node_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ typedef struct {
#define NAPI_MODULE(modname, regfunc) \
NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)

#define NAPI_MODULE_FALLBACK_X(modname) \
EXTERN_C_START \
void node_kickstart_module() { \
_register_ ## modname(); \
} \
EXTERN_C_END

#define NAPI_MODULE_FALLBACK(modname) \
NAPI_MODULE_FALLBACK_X(modname)

#define NAPI_AUTO_LENGTH SIZE_MAX

EXTERN_C_START
Expand Down
14 changes: 14 additions & 0 deletions test/addons-napi/addon-fallback/binding.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "node_api.h"
#include "../common.h"

static napi_value Init(napi_env env, napi_value exports) {
napi_value result, answer;

NAPI_CALL(env, napi_create_object(env, &result));
NAPI_CALL(env, napi_create_int64(env, 42, &answer));
NAPI_CALL(env, napi_set_named_property(env, result, "answer", answer));
return result;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
NAPI_MODULE_FALLBACK(NODE_GYP_MODULE_NAME)
8 changes: 8 additions & 0 deletions test/addons-napi/addon-fallback/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
'targets': [
{
'target_name': 'binding',
'sources': [ 'binding.c' ]
}
]
}
12 changes: 12 additions & 0 deletions test/addons-napi/addon-fallback/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const bindingPath = require.resolve(`./build/${common.buildType}/binding`);
const binding = require(bindingPath);
assert.strictEqual(binding.answer, 42);

// Test multiple loading of the same module.
delete require.cache[bindingPath];
const rebinding = require(bindingPath);
assert.strictEqual(rebinding.answer, 42);
assert.notStrictEqual(binding, rebinding);
33 changes: 33 additions & 0 deletions test/addons/addon-fallback/binding.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "node.h"

void Init(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context) {
const char *error = nullptr;

v8::Isolate *isolate = context->GetIsolate();
v8::Local<v8::Object> result = v8::Object::New(isolate);
v8::Local<v8::String> prop_name;
auto answer = v8::Number::New(isolate, 42.0).As<v8::Value>();

prop_name = v8::String::NewFromUtf8(isolate, "answer",
v8::NewStringType::kNormal).ToLocalChecked();
if (result->Set(context, prop_name, answer).FromJust()) {
prop_name = v8::String::NewFromUtf8(isolate, "exports",
v8::NewStringType::kNormal).ToLocalChecked();
if (module.As<v8::Object>()->Set(context, prop_name, result).FromJust()) {
return;
} else {
error = "Failed to set exports";
}
} else {
error = "Failed to set property";
}

isolate->ThrowException(
v8::String::NewFromUtf8(isolate, error,
v8::NewStringType::kNormal).ToLocalChecked());
}

NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, Init)
NODE_MODULE_FALLBACK(NODE_GYP_MODULE_NAME)
8 changes: 8 additions & 0 deletions test/addons/addon-fallback/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
'targets': [
{
'target_name': 'binding',
'sources': [ 'binding.cc' ]
}
]
}
12 changes: 12 additions & 0 deletions test/addons/addon-fallback/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const bindingPath = require.resolve(`./build/${common.buildType}/binding`);
const binding = require(bindingPath);
assert.strictEqual(binding.answer, 42);

// Test multiple loading of the same module.
delete require.cache[bindingPath];
const rebinding = require(bindingPath);
assert.strictEqual(rebinding.answer, 42);
assert.notStrictEqual(binding, rebinding);