From 902cc2cf0eb1fdda5095c9b5a1b6a7127f35e30d Mon Sep 17 00:00:00 2001 From: zeromake Date: Wed, 11 Sep 2024 04:47:40 +0800 Subject: [PATCH] Add native module support on Windows --- .github/workflows/ci.yml | 24 ++++++++++++++-- CMakeLists.txt | 46 +++++++++++++++++-------------- examples/fib.c | 10 ++++++- examples/point.c | 10 ++++++- examples/test_fib.js | 4 ++- examples/test_point.js | 5 +++- gen/test_fib.c | Bin 1954 -> 2711 bytes quickjs-libc.c | 58 +++++++++++++++++++++++++++++++++++++-- 8 files changed, 127 insertions(+), 30 deletions(-) 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 151bd4d0ec17e97ca776332d00394ec0aa417b8d..4cbe0cf58aa19c182b7f39acb9b88c8f7cbe3982 100644 GIT binary patch literal 2711 zcmaJ@O>f&q5WVwPY~70@C<#SMlmnxu6g~)UfL7?KCOM2NRM}{{Q)cQSYnmsOKPhw6>i4lOzU~k2+gnw1 zr+Y#4AJp<=5d2)XMKhLKU3{}4cf_8e6J zY+l-T8vwR&Ckfo2@Qmy;(l}@KxkI9mfQ0#If;$0>LE9Z*Or%jEWbRiL)**7^>1eM3 z5N5U$0$2@{5{f}eQynx7t}!UZ2n`_vd!PVwhX8mjI(n>0_3)H-**XBA5mFP>p$rKu zY~5K3&H-a`xtWzhgZCP}ZKi>GgY!ZoDB}e6WpEY1k0Q>^S>4zha>rPZn5z+CD6zLu zIf6ot5}`T76^o@5f`EI%&2vS4JWI&IIn@EHmpY2qyey7IGo?2gdTFz z@L>G6i500@{Cx+^mNWnxU!0{z0>1ildL@yuv(U5hvd)rFJ_?OM@ImOJv13*?O zDnb8K6LUB(I%jpQQ?ZHaj|#l8xp1VEQ#b5^Mtva$04L2Ha8O)cWH0`%06@@)FpBF* zOkyF3t$7(oD7vPJG{_qZ62r2lo!3lADIsgbnGnE#ALq?WjjOT2wnT6fz&px;nFxR+ zN7KBpsLX~mW)Yv|T{#&0ZdUul=dLyS(WvGAkbcn*pT|Mo+s?SyAO09yQ=hb2_U2t6 zZ%-dlm`8b~Ivmq1BPjts68}3nE7fZ=_O1Hz=gp0E+B8Z@Q7y*(;kT|F8~v-U z)begf>$W!O38u6gguzTu(yipP!F~-C$E;V&HalrlQ|dJk(HGrtfrw`*=~#&4DtHb8 z`JhmztZoUL$M>mOuYXJZoQ}2hX4b&PE>KYz4W>*7ZPJIX|El}6)eouA zw`!-=*cQ^;E5&^%lsIN>DPKv$B&=W#+EB8aE-Q6^ZBzAT1$kFi38!8IH|@wPozGm) z=j^8DIA>*<9(8kX%R)w~16gd#E=+&Vnl!K5tbbzI*{9j+jW0F14*Uk2uDiEL*rc)R cUh8S|CbO$mi7S0W3k#WKcTKo4hjz&S0T)2k761SM delta 686 zcmZ8f%}T>S7$ml~r3Zf~Dm}CW71~3rn@zf@h~mL_upm;KG=WO3N+JY2^x|2RJ%}EC z1@RqxB5#7b^EDWH`La9n&Fp-;dENL}eVz$hc!$+$a5Ie)^L{){dV|EgyNZKB62{3O z8vEy}d--}?v6-bI9%_ioHKS4S9K9BioEVK610m)Rh!Bdb3s8XC z)s=}H5@GE@V1sab|E+>#RME@Z9IEHzIt zA`pM5afa{=*7FLM)_#C=3r(_LXqL!G?iIJrXtbQn;Ut5r@>o2F5Tpwaz>D_&iF+d-0A=60A4uWu(|ysw~6eDnEU H`8ECkA2X`J 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;