Skip to content

Commit

Permalink
node: register modules from DSO constructors
Browse files Browse the repository at this point in the history
Built-in modules should be automatically registered, replacing the
static module list.  Add-on modules should also be automatically
registered via DSO constructors.  This improves flexibility in adding
built-in modules and is also a prerequisite to pure-C addon modules.
  • Loading branch information
Keith M Wesolowski authored and tjfontaine committed Jan 27, 2014
1 parent f4c8020 commit 76b9846
Show file tree
Hide file tree
Showing 25 changed files with 159 additions and 236 deletions.
2 changes: 0 additions & 2 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@
'src/node_buffer.cc',
'src/node_constants.cc',
'src/node_contextify.cc',
'src/node_extensions.cc',
'src/node_file.cc',
'src/node_http_parser.cc',
'src/node_javascript.cc',
Expand Down Expand Up @@ -123,7 +122,6 @@
'src/node_buffer.h',
'src/node_constants.h',
'src/node_contextify.h',
'src/node_extensions.h',
'src/node_file.h',
'src/node_http_parser.h',
'src/node_internals.h',
Expand Down
2 changes: 1 addition & 1 deletion src/cares_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1177,4 +1177,4 @@ static void Initialize(Handle<Object> target,
} // namespace cares_wrap
} // namespace node

NODE_MODULE_CONTEXT_AWARE(node_cares_wrap, node::cares_wrap::Initialize)
NODE_MODULE_CONTEXT_AWARE_BUILTIN(cares_wrap, node::cares_wrap::Initialize)
2 changes: 1 addition & 1 deletion src/fs_event_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,4 @@ void FSEventWrap::Close(const FunctionCallbackInfo<Value>& args) {

} // namespace node

NODE_MODULE_CONTEXT_AWARE(node_fs_event_wrap, node::FSEventWrap::Initialize)
NODE_MODULE_CONTEXT_AWARE_BUILTIN(fs_event_wrap, node::FSEventWrap::Initialize)
120 changes: 61 additions & 59 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ static bool use_debug_agent = false;
static bool debug_wait_connect = false;
static int debug_port = 5858;
static bool v8_is_profiling = false;
static node_module* modpending;
static node_module* modlist_builtin;
static node_module* modlist_addon;

// used by C++ modules as well
bool no_deprecation = false;
Expand Down Expand Up @@ -1882,6 +1885,29 @@ void Hrtime(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(tuple);
}

extern "C" void node_module_register(void* m) {
struct node_module* mp = reinterpret_cast<struct node_module*>(m);

if (mp->nm_flags & NM_F_BUILTIN) {
mp->nm_link = modlist_builtin;
modlist_builtin = mp;
} else {
assert(modpending == NULL);
modpending = mp;
}
}

struct node_module* get_builtin_module(const char* name) {
struct node_module* mp;

for (mp = modlist_builtin; mp != NULL; mp = mp->nm_link) {
if (strcmp(mp->nm_modname, name) == 0)
break;
}

assert(mp == NULL || (mp->nm_flags & NM_F_BUILTIN) != 0);
return (mp);
}

typedef void (UV_DYNAMIC* extInit)(Handle<Object> exports);

Expand All @@ -1894,12 +1920,12 @@ typedef void (UV_DYNAMIC* extInit)(Handle<Object> exports);
void DLOpen(const FunctionCallbackInfo<Value>& args) {
HandleScope handle_scope(args.GetIsolate());
Environment* env = Environment::GetCurrent(args.GetIsolate());
char symbol[1024], *base, *pos;
struct node_module* mp;
uv_lib_t lib;
int r;

if (args.Length() < 2) {
return ThrowError("process.dlopen takes exactly 2 arguments.");
ThrowError("process.dlopen takes exactly 2 arguments.");
return;
}

Local<Object> module = args[0]->ToObject(); // Cast
Expand All @@ -1918,68 +1944,43 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
return;
}

String::Utf8Value path(args[1]);
base = *path;

/* Find the shared library filename within the full path. */
#ifdef __POSIX__
pos = strrchr(base, '/');
if (pos != NULL) {
base = pos + 1;
}
#else // Windows
for (;;) {
pos = strpbrk(base, "\\/:");
if (pos == NULL) {
break;
}
base = pos + 1;
}
#endif // __POSIX__

/* Strip the .node extension. */
pos = strrchr(base, '.');
if (pos != NULL) {
*pos = '\0';
}

/* Add the `_module` suffix to the extension name. */
r = snprintf(symbol, sizeof symbol, "%s_module", base);
if (r <= 0 || static_cast<size_t>(r) >= sizeof symbol) {
return ThrowError("Out of memory.");
}

/* Replace dashes with underscores. When loading foo-bar.node,
* look for foo_bar_module, not foo-bar_module.
/*
* 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.
*/
for (pos = symbol; *pos != '\0'; ++pos) {
if (*pos == '-')
*pos = '_';
}
mp = modpending;
modpending = NULL;

node_module_struct *mod;
if (uv_dlsym(&lib, symbol, reinterpret_cast<void**>(&mod))) {
char errmsg[1024];
snprintf(errmsg, sizeof(errmsg), "Symbol %s not found.", symbol);
return ThrowError(errmsg);
if (mp == NULL) {
ThrowError("Module did not self-register.");
return;
}

if (mod->version != NODE_MODULE_VERSION) {
if (mp->nm_version != NODE_MODULE_VERSION) {
char errmsg[1024];
snprintf(errmsg,
sizeof(errmsg),
"Module version mismatch. Expected %d, got %d.",
NODE_MODULE_VERSION, mod->version);
return ThrowError(errmsg);
NODE_MODULE_VERSION, mp->nm_version);
ThrowError(errmsg);
return;
}
if (mp->nm_flags & NM_F_BUILTIN) {
ThrowError("Built-in module self-registered.");
return;
}

// Execute the C++ module
if (mod->register_context_func != NULL) {
mod->register_context_func(exports, module, env->context());
} else if (mod->register_func != NULL) {
mod->register_func(exports, module);
mp->nm_dso_handle = lib.handle;
mp->nm_link = modlist_addon;
modlist_addon = mp;

if (mp->nm_context_register_func != NULL) {
mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv);
} else if (mp->nm_register_func != NULL) {
mp->nm_register_func(exports, module, mp->nm_priv);
} else {
return ThrowError("Module has no declared entry point.");
ThrowError("Module has no declared entry point.");
return;
}

// Tell coverity that 'handle' should not be freed when we return.
Expand Down Expand Up @@ -2082,14 +2083,15 @@ static void Binding(const FunctionCallbackInfo<Value>& args) {
uint32_t l = modules->Length();
modules->Set(l, OneByteString(node_isolate, buf));

node_module_struct* mod = get_builtin_module(*module_v);
node_module* mod = get_builtin_module(*module_v);
if (mod != NULL) {
exports = Object::New();
// Internal bindings don't have a "module" object, only exports.
assert(mod->register_func == NULL);
assert(mod->register_context_func != NULL);
assert(mod->nm_register_func == NULL);
assert(mod->nm_context_register_func != NULL);
Local<Value> unused = Undefined(env->isolate());
mod->register_context_func(exports, unused, env->context());
mod->nm_context_register_func(exports, unused,
env->context(), mod->nm_priv);
cache->Set(module, exports);
} else if (!strcmp(*module_v, "constants")) {
exports = Object::New();
Expand Down
96 changes: 70 additions & 26 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,59 +205,103 @@ const char *signo_string(int errorno);

typedef void (*addon_register_func)(
v8::Handle<v8::Object> exports,
v8::Handle<v8::Value> module);
v8::Handle<v8::Value> module,
void* priv);

typedef void (*addon_context_register_func)(
v8::Handle<v8::Object> exports,
v8::Handle<v8::Value> module,
v8::Handle<v8::Context> context);

struct node_module_struct {
int version;
void *dso_handle;
const char *filename;
node::addon_register_func register_func;
node::addon_context_register_func register_context_func;
const char *modname;
v8::Handle<v8::Context> context,
void* priv);

#define NM_F_BUILTIN 0x01

struct node_module {
int nm_version;
unsigned int nm_flags;
void* nm_dso_handle;
const char* nm_filename;
node::addon_register_func nm_register_func;
node::addon_context_register_func nm_context_register_func;
const char* nm_modname;
void* nm_priv;
struct node_module* nm_link;
};

node_module_struct* get_builtin_module(const char *name);
node_module* get_builtin_module(const char *name);

#define NODE_STANDARD_MODULE_STUFF \
NODE_MODULE_VERSION, \
NULL, \
__FILE__
extern "C" NODE_EXTERN void node_module_register(void* mod);

#ifdef _WIN32
# define NODE_MODULE_EXPORT __declspec(dllexport)
#else
# define NODE_MODULE_EXPORT /* empty */
#endif

#define NODE_MODULE(modname, regfunc) \
#if defined(_MSC_VER)
#pragma section(".CRT$XCU", read)
#define NODE_C_CTOR(fn) \
static void __cdecl fn(void); \
__declspec(allocate(".CRT$XCU")) static void (__cdecl*fn ## _)(void) = fn; \
static void __cdecl fn(void)
#else
#define NODE_C_CTOR(fn) \
static void fn(void) __attribute__((constructor)); \
static void fn(void)
#endif

#define NODE_MODULE_X(modstr, regfunc, priv, flags) \
extern "C" { \
NODE_MODULE_EXPORT node::node_module_struct modname ## _module = \
static node::node_module _module = \
{ \
NODE_STANDARD_MODULE_STUFF, \
NODE_MODULE_VERSION, \
flags, \
NULL, \
__FILE__, \
(node::addon_register_func) (regfunc), \
NULL, \
NODE_STRINGIFY(modname) \
modstr, \
priv, \
NULL \
}; \
NODE_C_CTOR(_register) { \
node_module_register(&_module); \
} \
}

#define NODE_MODULE_CONTEXT_AWARE(modname, regfunc) \
#define NODE_MODULE_CONTEXT_AWARE_X(modstr, regfunc, priv, flags) \
extern "C" { \
NODE_MODULE_EXPORT node::node_module_struct modname ## _module = \
static node::node_module _module = \
{ \
NODE_STANDARD_MODULE_STUFF, \
NODE_MODULE_VERSION, \
flags, \
NULL, \
(regfunc), \
NODE_STRINGIFY(modname) \
__FILE__, \
NULL, \
(node::addon_context_register_func) (regfunc), \
modstr, \
priv, \
NULL \
}; \
NODE_C_CTOR(_register) { \
node_module_register(&_module); \
} \
}

#define NODE_MODULE_DECL(modname) \
extern "C" node::node_module_struct modname ## _module;
#define NODE_MODULE(modname, regfunc) \
NODE_MODULE_X(NODE_STRINGIFY(modname), regfunc, NULL, 0)

#define NODE_MODULE_CONTEXT_AWARE(modname, regfunc) \
NODE_MODULE_CONTEXT_AWARE_X(NODE_STRINGIFY(modname), regfunc, NULL, 0)

#define NODE_MODULE_CONTEXT_AWARE_BUILTIN(modname, regfunc) \
NODE_MODULE_CONTEXT_AWARE_X(NODE_STRINGIFY(modname), \
regfunc, NULL, NM_F_BUILTIN)

/*
* For backward compatibility in add-on modules.
*/
#define NODE_MODULE_DECL /* nothing */

/* Called after the event loop exits but before the VM is disposed.
* Callbacks are run in reverse order of registration, i.e. newest first.
Expand Down
2 changes: 1 addition & 1 deletion src/node_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -665,4 +665,4 @@ void Initialize(Handle<Object> target,
} // namespace Buffer
} // namespace node

NODE_MODULE_CONTEXT_AWARE(node_buffer, node::Buffer::Initialize)
NODE_MODULE_CONTEXT_AWARE_BUILTIN(buffer, node::Buffer::Initialize)
2 changes: 1 addition & 1 deletion src/node_contextify.cc
Original file line number Diff line number Diff line change
Expand Up @@ -628,4 +628,4 @@ void InitContextify(Handle<Object> target,

} // namespace node

NODE_MODULE_CONTEXT_AWARE(node_contextify, node::InitContextify);
NODE_MODULE_CONTEXT_AWARE_BUILTIN(contextify, node::InitContextify);
5 changes: 3 additions & 2 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4210,7 +4210,8 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) {
// FIXME(bnoordhuis) Handle global init correctly.
void InitCrypto(Handle<Object> target,
Handle<Value> unused,
Handle<Context> context) {
Handle<Context> context,
void* priv) {
static uv_once_t init_once = UV_ONCE_INIT;
uv_once(&init_once, InitCryptoOnce);

Expand Down Expand Up @@ -4239,4 +4240,4 @@ void InitCrypto(Handle<Object> target,
} // namespace crypto
} // namespace node

NODE_MODULE_CONTEXT_AWARE(node_crypto, node::crypto::InitCrypto)
NODE_MODULE_CONTEXT_AWARE_BUILTIN(crypto, node::crypto::InitCrypto)
Loading

0 comments on commit 76b9846

Please sign in to comment.