summaryrefslogtreecommitdiff
path: root/src/node_binding.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/node_binding.cc')
-rw-r--r--src/node_binding.cc471
1 files changed, 471 insertions, 0 deletions
diff --git a/src/node_binding.cc b/src/node_binding.cc
new file mode 100644
index 0000000000..a2e5134bfa
--- /dev/null
+++ b/src/node_binding.cc
@@ -0,0 +1,471 @@
+#include "node_binding.h"
+#include "node_internals.h"
+#include "node_native_module.h"
+
+#if defined(__POSIX__)
+#include <dlfcn.h>
+#endif
+
+#if HAVE_OPENSSL
+#define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap)
+#else
+#define NODE_BUILTIN_OPENSSL_MODULES(V)
+#endif
+
+#if NODE_HAVE_I18N_SUPPORT
+#define NODE_BUILTIN_ICU_MODULES(V) V(icu)
+#else
+#define NODE_BUILTIN_ICU_MODULES(V)
+#endif
+
+// A list of built-in modules. In order to do module registration
+// in node::Init(), need to add built-in modules in the following list.
+// Then in binding::RegisterBuiltinModules(), it calls modules' registration
+// function. This helps the built-in modules are loaded properly when
+// node is built as static library. No need to depend on the
+// __attribute__((constructor)) like mechanism in GCC.
+#define NODE_BUILTIN_STANDARD_MODULES(V) \
+ V(async_wrap) \
+ V(buffer) \
+ V(cares_wrap) \
+ V(config) \
+ V(contextify) \
+ V(domain) \
+ V(fs) \
+ V(fs_event_wrap) \
+ V(heap_utils) \
+ V(http2) \
+ V(http_parser) \
+ V(inspector) \
+ V(js_stream) \
+ V(messaging) \
+ V(module_wrap) \
+ V(native_module) \
+ V(options) \
+ V(os) \
+ V(performance) \
+ V(pipe_wrap) \
+ V(process_wrap) \
+ V(serdes) \
+ V(signal_wrap) \
+ V(spawn_sync) \
+ V(stream_pipe) \
+ V(stream_wrap) \
+ V(string_decoder) \
+ V(symbols) \
+ V(tcp_wrap) \
+ V(timers) \
+ V(trace_events) \
+ V(tty_wrap) \
+ V(types) \
+ V(udp_wrap) \
+ V(url) \
+ V(util) \
+ V(uv) \
+ V(v8) \
+ V(worker) \
+ V(zlib)
+
+#define NODE_BUILTIN_MODULES(V) \
+ NODE_BUILTIN_STANDARD_MODULES(V) \
+ NODE_BUILTIN_OPENSSL_MODULES(V) \
+ NODE_BUILTIN_ICU_MODULES(V)
+
+// This is used to load built-in modules. Instead of using
+// __attribute__((constructor)), we call the _register_<modname>
+// function for each built-in modules explicitly in
+// binding::RegisterBuiltinModules(). This is only forward declaration.
+// The definitions are in each module's implementation when calling
+// the NODE_BUILTIN_MODULE_CONTEXT_AWARE.
+#define V(modname) void _register_##modname();
+NODE_BUILTIN_MODULES(V)
+#undef V
+
+namespace node {
+
+using v8::Context;
+using v8::Exception;
+using v8::FunctionCallbackInfo;
+using v8::Local;
+using v8::NewStringType;
+using v8::Object;
+using v8::String;
+using v8::Value;
+
+// Globals per process
+static node_module* modlist_builtin;
+static node_module* modlist_internal;
+static node_module* modlist_linked;
+static node_module* modlist_addon;
+static uv_once_t init_modpending_once = UV_ONCE_INIT;
+static uv_key_t thread_local_modpending;
+
+// This is set by node::Init() which is used by embedders
+bool node_is_initialized = false;
+
+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 if (mp->nm_flags & NM_F_INTERNAL) {
+ mp->nm_link = modlist_internal;
+ modlist_internal = mp;
+ } else if (!node_is_initialized) {
+ // "Linked" modules are included as part of the node project.
+ // Like builtins they are registered *before* node::Init runs.
+ mp->nm_flags = NM_F_LINKED;
+ mp->nm_link = modlist_linked;
+ modlist_linked = mp;
+ } else {
+ uv_key_set(&thread_local_modpending, mp);
+ }
+}
+
+namespace binding {
+
+class DLib {
+ public:
+#ifdef __POSIX__
+ static const int kDefaultFlags = RTLD_LAZY;
+#else
+ static const int kDefaultFlags = 0;
+#endif
+
+ inline DLib(const char* filename, int flags)
+ : filename_(filename), flags_(flags), handle_(nullptr) {}
+
+ inline bool Open();
+ inline void Close();
+ inline void* GetSymbolAddress(const char* name);
+
+ const std::string filename_;
+ const int flags_;
+ std::string errmsg_;
+ void* handle_;
+#ifndef __POSIX__
+ uv_lib_t lib_;
+#endif
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DLib);
+};
+
+#ifdef __POSIX__
+bool DLib::Open() {
+ handle_ = dlopen(filename_.c_str(), flags_);
+ if (handle_ != nullptr) return true;
+ errmsg_ = dlerror();
+ return false;
+}
+
+void DLib::Close() {
+ if (handle_ == nullptr) return;
+ dlclose(handle_);
+ handle_ = nullptr;
+}
+
+void* DLib::GetSymbolAddress(const char* name) {
+ return dlsym(handle_, name);
+}
+#else // !__POSIX__
+bool DLib::Open() {
+ int ret = uv_dlopen(filename_.c_str(), &lib_);
+ if (ret == 0) {
+ handle_ = static_cast<void*>(lib_.handle);
+ return true;
+ }
+ errmsg_ = uv_dlerror(&lib_);
+ uv_dlclose(&lib_);
+ return false;
+}
+
+void DLib::Close() {
+ if (handle_ == nullptr) return;
+ uv_dlclose(&lib_);
+ handle_ = nullptr;
+}
+
+void* DLib::GetSymbolAddress(const char* name) {
+ void* address;
+ if (0 == uv_dlsym(&lib_, name, &address)) return address;
+ return nullptr;
+}
+#endif // !__POSIX__
+
+using InitializerCallback = void (*)(Local<Object> exports,
+ Local<Value> module,
+ Local<Context> context);
+
+inline InitializerCallback GetInitializerCallback(DLib* dlib) {
+ const char* name = "node_register_module_v" STRINGIFY(NODE_MODULE_VERSION);
+ return reinterpret_cast<InitializerCallback>(dlib->GetSymbolAddress(name));
+}
+
+inline napi_addon_register_func GetNapiInitializerCallback(DLib* dlib) {
+ const char* name =
+ STRINGIFY(NAPI_MODULE_INITIALIZER_BASE) STRINGIFY(NAPI_MODULE_VERSION);
+ return reinterpret_cast<napi_addon_register_func>(
+ dlib->GetSymbolAddress(name));
+}
+
+void InitModpendingOnce() {
+ CHECK_EQ(0, uv_key_create(&thread_local_modpending));
+}
+
+// DLOpen is process.dlopen(module, filename, flags).
+// Used to load 'module.node' dynamically shared objects.
+//
+// FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict
+// when two contexts try to load the same shared object. Maybe have a shadow
+// cache that's a plain C list or hash table that's shared across contexts?
+void DLOpen(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ auto context = env->context();
+
+ uv_once(&init_modpending_once, InitModpendingOnce);
+ CHECK_NULL(uv_key_get(&thread_local_modpending));
+
+ if (args.Length() < 2) {
+ env->ThrowError("process.dlopen needs at least 2 arguments.");
+ return;
+ }
+
+ int32_t flags = DLib::kDefaultFlags;
+ if (args.Length() > 2 && !args[2]->Int32Value(context).To(&flags)) {
+ return env->ThrowTypeError("flag argument must be an integer.");
+ }
+
+ Local<Object> module;
+ Local<Object> exports;
+ Local<Value> exports_v;
+ if (!args[0]->ToObject(context).ToLocal(&module) ||
+ !module->Get(context, env->exports_string()).ToLocal(&exports_v) ||
+ !exports_v->ToObject(context).ToLocal(&exports)) {
+ return; // Exception pending.
+ }
+
+ node::Utf8Value filename(env->isolate(), args[1]); // Cast
+ DLib dlib(*filename, flags);
+ bool is_opened = dlib.Open();
+
+ // 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 =
+ static_cast<node_module*>(uv_key_get(&thread_local_modpending));
+ uv_key_set(&thread_local_modpending, nullptr);
+
+ if (!is_opened) {
+ Local<String> errmsg = OneByteString(env->isolate(), dlib.errmsg_.c_str());
+ dlib.Close();
+#ifdef _WIN32
+ // Windows needs to add the filename into the error message
+ errmsg = String::Concat(
+ env->isolate(), errmsg, args[1]->ToString(context).ToLocalChecked());
+#endif // _WIN32
+ env->isolate()->ThrowException(Exception::Error(errmsg));
+ return;
+ }
+
+ if (mp == nullptr) {
+ if (auto callback = GetInitializerCallback(&dlib)) {
+ callback(exports, module, context);
+ } else if (auto napi_callback = GetNapiInitializerCallback(&dlib)) {
+ napi_module_register_by_symbol(exports, module, context, napi_callback);
+ } else {
+ dlib.Close();
+ env->ThrowError("Module did not self-register.");
+ }
+ return;
+ }
+
+ // -1 is used for N-API modules
+ if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) {
+ // Even if the module did self-register, it may have done so with the wrong
+ // version. We must only give up after having checked to see if it has an
+ // appropriate initializer callback.
+ if (auto callback = GetInitializerCallback(&dlib)) {
+ callback(exports, module, context);
+ return;
+ }
+ char errmsg[1024];
+ snprintf(errmsg,
+ sizeof(errmsg),
+ "The module '%s'"
+ "\nwas compiled against a different Node.js version using"
+ "\nNODE_MODULE_VERSION %d. This version of Node.js requires"
+ "\nNODE_MODULE_VERSION %d. Please try re-compiling or "
+ "re-installing\nthe module (for instance, using `npm rebuild` "
+ "or `npm install`).",
+ *filename,
+ mp->nm_version,
+ NODE_MODULE_VERSION);
+
+ // NOTE: `mp` is allocated inside of the shared library's memory, calling
+ // `dlclose` will deallocate it
+ dlib.Close();
+ env->ThrowError(errmsg);
+ return;
+ }
+ if (mp->nm_flags & NM_F_BUILTIN) {
+ dlib.Close();
+ env->ThrowError("Built-in module self-registered.");
+ return;
+ }
+
+ mp->nm_dso_handle = dlib.handle_;
+ mp->nm_link = modlist_addon;
+ modlist_addon = mp;
+
+ if (mp->nm_context_register_func != nullptr) {
+ mp->nm_context_register_func(exports, module, context, mp->nm_priv);
+ } else if (mp->nm_register_func != nullptr) {
+ mp->nm_register_func(exports, module, mp->nm_priv);
+ } else {
+ dlib.Close();
+ env->ThrowError("Module has no declared entry point.");
+ return;
+ }
+
+ // Tell coverity that 'handle' should not be freed when we return.
+ // coverity[leaked_storage]
+}
+
+inline struct node_module* FindModule(struct node_module* list,
+ const char* name,
+ int flag) {
+ struct node_module* mp;
+
+ for (mp = list; mp != nullptr; mp = mp->nm_link) {
+ if (strcmp(mp->nm_modname, name) == 0) break;
+ }
+
+ CHECK(mp == nullptr || (mp->nm_flags & flag) != 0);
+ return mp;
+}
+
+node_module* get_builtin_module(const char* name) {
+ return FindModule(modlist_builtin, name, NM_F_BUILTIN);
+}
+node_module* get_internal_module(const char* name) {
+ return FindModule(modlist_internal, name, NM_F_INTERNAL);
+}
+node_module* get_linked_module(const char* name) {
+ return FindModule(modlist_linked, name, NM_F_LINKED);
+}
+
+static Local<Object> InitModule(Environment* env,
+ node_module* mod,
+ Local<String> module) {
+ Local<Object> exports = Object::New(env->isolate());
+ // Internal bindings don't have a "module" object, only exports.
+ CHECK_NULL(mod->nm_register_func);
+ CHECK_NOT_NULL(mod->nm_context_register_func);
+ Local<Value> unused = Undefined(env->isolate());
+ mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv);
+ return exports;
+}
+
+static void ThrowIfNoSuchModule(Environment* env, const char* module_v) {
+ char errmsg[1024];
+ snprintf(errmsg, sizeof(errmsg), "No such module: %s", module_v);
+ env->ThrowError(errmsg);
+}
+
+void GetBinding(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+
+ CHECK(args[0]->IsString());
+
+ Local<String> module = args[0].As<String>();
+ node::Utf8Value module_v(env->isolate(), module);
+
+ node_module* mod = get_builtin_module(*module_v);
+ Local<Object> exports;
+ if (mod != nullptr) {
+ exports = InitModule(env, mod, module);
+ } else {
+ return ThrowIfNoSuchModule(env, *module_v);
+ }
+
+ args.GetReturnValue().Set(exports);
+}
+
+void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+
+ CHECK(args[0]->IsString());
+
+ Local<String> module = args[0].As<String>();
+ node::Utf8Value module_v(env->isolate(), module);
+ Local<Object> exports;
+
+ node_module* mod = get_internal_module(*module_v);
+ if (mod != nullptr) {
+ exports = InitModule(env, mod, module);
+ } else if (!strcmp(*module_v, "constants")) {
+ exports = Object::New(env->isolate());
+ CHECK(
+ exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
+ DefineConstants(env->isolate(), exports);
+ } else if (!strcmp(*module_v, "natives")) {
+ exports = per_process_loader.GetSourceObject(env->context());
+ } else {
+ return ThrowIfNoSuchModule(env, *module_v);
+ }
+
+ args.GetReturnValue().Set(exports);
+}
+
+void GetLinkedBinding(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+
+ CHECK(args[0]->IsString());
+
+ Local<String> module_name = args[0].As<String>();
+
+ node::Utf8Value module_name_v(env->isolate(), module_name);
+ node_module* mod = get_linked_module(*module_name_v);
+
+ if (mod == nullptr) {
+ char errmsg[1024];
+ snprintf(errmsg,
+ sizeof(errmsg),
+ "No such module was linked: %s",
+ *module_name_v);
+ return env->ThrowError(errmsg);
+ }
+
+ Local<Object> module = Object::New(env->isolate());
+ Local<Object> exports = Object::New(env->isolate());
+ Local<String> exports_prop =
+ String::NewFromUtf8(env->isolate(), "exports", NewStringType::kNormal)
+ .ToLocalChecked();
+ module->Set(env->context(), exports_prop, exports).FromJust();
+
+ if (mod->nm_context_register_func != nullptr) {
+ mod->nm_context_register_func(
+ exports, module, env->context(), mod->nm_priv);
+ } else if (mod->nm_register_func != nullptr) {
+ mod->nm_register_func(exports, module, mod->nm_priv);
+ } else {
+ return env->ThrowError("Linked module has no declared entry point.");
+ }
+
+ auto effective_exports =
+ module->Get(env->context(), exports_prop).ToLocalChecked();
+
+ args.GetReturnValue().Set(effective_exports);
+}
+
+// Call built-in modules' _register_<module name> function to
+// do module registration explicitly.
+void RegisterBuiltinModules() {
+#define V(modname) _register_##modname();
+ NODE_BUILTIN_MODULES(V)
+#undef V
+}
+
+} // namespace binding
+} // namespace node