diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abb05f8..21162c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -195,14 +195,20 @@ jobs: - uses: actions/checkout@v4 - name: build run: | - cmake -B build -G "Visual Studio 17 2022" -A ${{matrix.arch}} + cmake -B build -DBUILD_EXAMPLES=ON -G "Visual Studio 17 2022" -A ${{matrix.arch}} cmake --build build --config ${{matrix.buildType}} --target qjs_exe cmake --build build --config ${{matrix.buildType}} --target function_source + cmake --build build --config ${{matrix.buildType}} --target fib + cmake --build build --config ${{matrix.buildType}} --target point - name: stats run: | build\${{matrix.buildType}}\qjs.exe -qd - name: test run: | + cp build\${{matrix.buildType}}\fib.dll examples\ + cp build\${{matrix.buildType}}\point.dll examples\ + build\${{matrix.buildType}}\qjs.exe examples\test_fib.js + build\${{matrix.buildType}}\qjs.exe examples\test_point.js build\${{matrix.buildType}}\qjs.exe tests\test_bigint.js build\${{matrix.buildType}}\qjs.exe tests\test_bjson.js build\${{matrix.buildType}}\qjs.exe tests\test_closure.js @@ -224,14 +230,20 @@ jobs: - uses: actions/checkout@v4 - name: build run: | - cmake -B build -G "Visual Studio 17 2022" -T ClangCL + cmake -B build -DBUILD_EXAMPLES=ON -G "Visual Studio 17 2022" -T ClangCL cmake --build build --config ${{matrix.buildType}} --target qjs_exe cmake --build build --config ${{matrix.buildType}} --target function_source + cmake --build build --config ${{matrix.buildType}} --target fib + cmake --build build --config ${{matrix.buildType}} --target point - name: stats run: | build\${{matrix.buildType}}\qjs.exe -qd - name: test run: | + cp build\${{matrix.buildType}}\fib.dll examples\ + cp build\${{matrix.buildType}}\point.dll examples\ + build\${{matrix.buildType}}\qjs.exe examples\test_fib.js + build\${{matrix.buildType}}\qjs.exe examples\test_point.js build\${{matrix.buildType}}\qjs.exe tests\test_bigint.js build\${{matrix.buildType}}\qjs.exe tests\test_bjson.js build\${{matrix.buildType}}\qjs.exe tests\test_closure.js @@ -257,14 +269,20 @@ jobs: ninja.exe --version - name: build run: | - cmake -B build -DCMAKE_BUILD_TYPE=${{matrix.buildType}} -G "Ninja" + cmake -B build -DBUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=${{matrix.buildType}} -G "Ninja" cmake --build build --target qjs_exe cmake --build build --target function_source + cmake --build build --target fib + cmake --build build --target point - name: stats run: | build\qjs.exe -qd - name: test run: | + cp build\fib.dll examples\ + cp build\point.dll examples\ + build\qjs.exe examples\test_fib.js + build\qjs.exe examples\test_point.js build\qjs.exe tests\test_bigint.js build\qjs.exe tests\test_bjson.js build\qjs.exe tests\test_closure.js diff --git a/CMakeLists.txt b/CMakeLists.txt index 5988d74..671b9b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,7 +273,7 @@ target_link_libraries(function_source ${qjs_libs}) # Examples # -if(BUILD_EXAMPLES AND NOT WIN32) +if(BUILD_EXAMPLES) add_executable(hello gen/hello.c ) @@ -290,27 +290,31 @@ if(BUILD_EXAMPLES AND NOT WIN32) target_compile_definitions(hello_module PRIVATE ${qjs_defines}) target_link_libraries(hello_module ${qjs_libs}) - if(NOT WIN32) - add_library(fib MODULE examples/fib.c) - set_target_properties(fib PROPERTIES - PREFIX "" - C_VISIBILITY_PRESET default - ) - target_compile_definitions(fib PRIVATE JS_SHARED_LIBRARY) - if(APPLE) - target_link_options(fib PRIVATE -undefined dynamic_lookup) - endif() - - add_library(point MODULE examples/point.c) - set_target_properties(point PROPERTIES - PREFIX "" - C_VISIBILITY_PRESET default - ) - target_compile_definitions(point PRIVATE JS_SHARED_LIBRARY) - if(APPLE) - target_link_options(point PRIVATE -undefined dynamic_lookup) - endif() + add_library(fib MODULE examples/fib.c) + set_target_properties(fib PROPERTIES + PREFIX "" + C_VISIBILITY_PRESET default + ) + target_compile_definitions(fib PRIVATE JS_SHARED_LIBRARY) + if(WIN32) + target_link_libraries(fib ${qjs_libs}) endif() + if(APPLE) + target_link_options(fib PRIVATE -undefined dynamic_lookup) + endif() + + add_library(point MODULE examples/point.c) + set_target_properties(point PROPERTIES + PREFIX "" + C_VISIBILITY_PRESET default + ) + target_compile_definitions(point PRIVATE JS_SHARED_LIBRARY) + if(WIN32) + target_link_libraries(point ${qjs_libs}) + endif() + if(APPLE) + target_link_options(point PRIVATE -undefined dynamic_lookup) + endif() add_executable(test_fib examples/fib.c diff --git a/examples/fib.c b/examples/fib.c index 0786378..b965acc 100644 --- a/examples/fib.c +++ b/examples/fib.c @@ -61,7 +61,15 @@ static int js_fib_init(JSContext *ctx, JSModuleDef *m) #define JS_INIT_MODULE js_init_module_fib #endif -JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) +#ifndef JS_EXTERN +#ifdef _WIN32 +#define JS_EXTERN __declspec(dllexport) +#else +#define JS_EXTERN +#endif +#endif + +JS_EXTERN JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) { JSModuleDef *m; m = JS_NewCModule(ctx, module_name, js_fib_init); diff --git a/examples/point.c b/examples/point.c index da22d6f..a82df15 100644 --- a/examples/point.c +++ b/examples/point.c @@ -141,7 +141,15 @@ static int js_point_init(JSContext *ctx, JSModuleDef *m) return 0; } -JSModuleDef *js_init_module(JSContext *ctx, const char *module_name) +#ifndef JS_EXTERN +#ifdef _WIN32 +#define JS_EXTERN __declspec(dllexport) +#else +#define JS_EXTERN +#endif +#endif + +JS_EXTERN JSModuleDef *js_init_module(JSContext *ctx, const char *module_name) { JSModuleDef *m; m = JS_NewCModule(ctx, module_name, js_point_init); diff --git a/examples/test_fib.js b/examples/test_fib.js index 70d26bd..4bdf9dc 100644 --- a/examples/test_fib.js +++ b/examples/test_fib.js @@ -1,6 +1,8 @@ /* example of JS module importing a C module */ +import * as os from "os"; -import { fib } from "./fib.so"; +const isWin = os.platform === 'win32'; +const { fib } = await import(`./fib.${isWin ? 'dll' : 'so'}`); console.log("Hello World"); console.log("fib(10)=", fib(10)); diff --git a/examples/test_point.js b/examples/test_point.js index 0659bc3..7400420 100644 --- a/examples/test_point.js +++ b/examples/test_point.js @@ -1,5 +1,8 @@ /* example of JS module importing a C module */ -import { Point } from "./point.so"; +import * as os from "os"; + +const isWin = os.platform === 'win32'; +const { Point } = await import(`./point.${isWin ? 'dll' : 'so'}`); function assert(b, str) { diff --git a/gen/test_fib.c b/gen/test_fib.c index 151bd4d..4cbe0cf 100644 Binary files a/gen/test_fib.c and b/gen/test_fib.c differ diff --git a/quickjs-libc.c b/quickjs-libc.c index b56029f..2955e4e 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -96,6 +96,14 @@ extern char **environ; #define MAX_SAFE_INTEGER (((int64_t) 1 << 53) - 1) +#ifndef QJS_NATIVE_MODULE_SUFFIX +#ifdef _WIN32 +#define QJS_NATIVE_MODULE_SUFFIX ".dll" +#else +#define QJS_NATIVE_MODULE_SUFFIX ".so" +#endif +#endif + /* TODO: - add socket calls */ @@ -483,7 +491,53 @@ typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, const char *module_name); -#if defined(_WIN32) || defined(__wasi__) +#if defined(_WIN32) +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JSModuleDef *m; + HINSTANCE hd; + JSInitModuleFunc *init; + char *filename = NULL; + size_t len = strlen(module_name); + JS_BOOL is_absolute = len > 2 && ((module_name[0] >= 'A' && module_name[0] <= 'Z') || + (module_name[0] >= 'a' && module_name[0] <= 'z')) && module_name[1] == ':'; + JS_BOOL is_relative = len > 2 && module_name[0] == '.' && (module_name[1] == '/' || module_name[1] == '\\'); + if (is_absolute || is_relative) { + filename = (char *)module_name; + } else { + filename = js_malloc(ctx, len + 2 + 1); + if (!filename) + return NULL; + strcpy(filename, "./"); + strcpy(filename + 2, module_name); + } + hd = LoadLibraryA(filename); + if (filename != module_name) + js_free(ctx, filename); + if (hd == NULL) { + JS_ThrowReferenceError(ctx, "js_load_module '%s' error: %lu", + module_name, GetLastError()); + goto fail; + } + init = (JSInitModuleFunc *)(uintptr_t)GetProcAddress(hd, "js_init_module"); + if (!init) { + JS_ThrowReferenceError(ctx, "js_init_module '%s' not found: %lu", + module_name, GetLastError()); + goto fail; + } + m = init(ctx, module_name); + if (!m) { + JS_ThrowReferenceError(ctx, "js_call_module '%s' initialization error", + module_name); + fail: + if (hd != NULL) + FreeLibrary(hd); + return NULL; + } + return m; +} +#elif defined(__wasi__) static JSModuleDef *js_module_loader_so(JSContext *ctx, const char *module_name) { @@ -599,7 +653,7 @@ JSModuleDef *js_module_loader(JSContext *ctx, { JSModuleDef *m; - if (has_suffix(module_name, ".so")) { + if (has_suffix(module_name, QJS_NATIVE_MODULE_SUFFIX)) { m = js_module_loader_so(ctx, module_name); } else { size_t buf_len;