]> git.kaiwu.me - njs.git/commitdiff
QuickJS: added native module support in CLI.
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 8 Jan 2026 02:09:20 +0000 (18:09 -0800)
committerDmitry Volyntsev <xeioexception@gmail.com>
Mon, 12 Jan 2026 23:05:41 +0000 (15:05 -0800)
auto/feature
auto/make
auto/options
external/njs_shell.c

index 064c3fda00b3a2972029cc2dd153a3f3ad6cd268..536df529087fa5925f2d9a732e67845cfc54289e 100644 (file)
@@ -37,7 +37,7 @@ END
 
 njs_test="$CC $CFLAGS $NJS_CFLAGS $NJS_CC_OPT $NJS_TEST_CFLAGS \
           $njs_feature_inc_path -o $NJS_AUTOTEST $NJS_AUTOTEST.c \
-          $NJS_LD_OPT $NJS_TEST_LIBS $njs_feature_libs"
+          $NJS_LD_OPT $NJS_LINK $NJS_TEST_LIBS $njs_feature_libs"
 
 # /bin/sh -c "(...)" is to intercept "Killed", "Abort trap",
 # "Segmentation fault", or other shell messages.
index 296ff5a8d4a7145385346ddad4d3829195b8a3cb..2a07001d794729fb450b6f3be1345ca9b998fdd8 100644 (file)
--- a/auto/make
+++ b/auto/make
@@ -88,7 +88,7 @@ cat << END > $NJS_MAKEFILE
 
 NJS_CC = ${CC}
 NJS_STATIC_LINK = ${AR} -r -c
-NJS_LINK = ${CC} ${NJS_LD_OPT}
+NJS_LINK = ${CC} ${NJS_LD_OPT} ${NJS_LINK}
 NJS_PIC = ${NJS_PIC}
 CFLAGS = ${NJS_CC_OPT} ${CFLAGS:-${NJS_CFLAGS}}
 NJS_LIB_AUX_CFLAGS = ${NJS_LIB_AUX_CFLAGS}
index 49d980192e6f33aa9cbaf369b988cc77d59dcb57..e99f9b97b84558dc6b17bfa5990a2e568ad6e2e5 100644 (file)
@@ -5,6 +5,7 @@
 
 NJS_CC_OPT=
 NJS_LD_OPT=
+NJS_LINK="-Wl,-E"
 NJS_PIC=-fPIC
 
 NJS_DEBUG=NO
index d72a3d98769ec8244c14c4dfa6ccc04fcbf35ee2..d0f9e25f5c3123ec96a306df127e9f8706d5d4f1 100644 (file)
@@ -14,6 +14,7 @@
 #if (NJS_HAVE_QUICKJS)
 #include <qjs.h>
 #include <pthread.h>
+#include <dlfcn.h>
 #endif
 
 #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE)
@@ -2558,6 +2559,71 @@ njs_qjs_rejection_tracker(JSContext *ctx, JSValueConst promise,
 }
 
 
+static njs_int_t
+njs_has_suffix(const char *str, const char *suffix)
+{
+    size_t  len, slen;
+
+    len = njs_strlen(str);
+    slen = njs_strlen(suffix);
+
+    return (len >= slen && memcmp(str + len - slen, suffix, slen) == 0);
+}
+
+
+static JSModuleDef *
+njs_qjs_module_loader_so(JSContext *ctx, const char *module_name)
+{
+    void                *handle;
+    char                *filename;
+    JSModuleDef         *m;
+    qjs_addon_init_pt   init;
+
+    if (!strchr(module_name, '/')) {
+        filename = js_malloc(ctx, njs_strlen(module_name) + 3);
+        if (filename == NULL) {
+            JS_ThrowOutOfMemory(ctx);
+            return NULL;
+        }
+
+        strcpy(filename, "./");
+        strcpy(filename + 2, module_name);
+
+    } else {
+        filename = (char *) module_name;
+    }
+
+    handle = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
+    if (filename != module_name) {
+        js_free(ctx, filename);
+    }
+
+    if (handle == NULL) {
+        JS_ThrowReferenceError(ctx, "could not load module filename '%s' as "
+                               "shared library: %s", module_name, dlerror());
+        return NULL;
+    }
+
+    init = dlsym(handle, "js_init_module");
+    if (init == NULL) {
+        JS_ThrowReferenceError(ctx, "could not load module filename '%s': "
+                               "js_init_module not found", module_name);
+        dlclose(handle);
+        return NULL;
+    }
+
+    m = init(ctx, module_name);
+    if (m == NULL) {
+        JS_ThrowReferenceError(ctx, "could not load module filename '%s': "
+                               "initialization error", module_name);
+        dlclose(handle);
+        return NULL;
+    }
+
+    return m;
+}
+
+
 static JSModuleDef *
 njs_qjs_module_loader(JSContext *ctx, const char *module_name, void *opaque)
 {
@@ -2572,6 +2638,10 @@ njs_qjs_module_loader(JSContext *ctx, const char *module_name, void *opaque)
     opts = opaque;
     console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
 
+    if (njs_has_suffix(module_name, ".so")) {
+        return njs_qjs_module_loader_so(ctx, module_name);
+    }
+
     njs_memzero(&info, sizeof(njs_module_info_t));
 
     info.name.start = (u_char *) module_name;