From e7d8403f3d03269976a07bab8dd0edd5c7cb5a4c Mon Sep 17 00:00:00 2001 From: Zurab Kvachadze Date: Fri, 26 Dec 2025 13:04:33 +0100 Subject: [PATCH 1/6] CMake/meson: Unbundle cutils from libqjs as a static lib Functions declared by cutils.h are used by most of the source files in the project. However, they are not part of libqjs API and, thus, the functions are not marked as API. Apart from the fact that cutils do not belong in libqjs, since they do not constitute a public API and are just helpers, they break shared qjs.dll builds on Windows where DLLs must explicitly mark their interfaces. As cutils.h does not mark its functions with JS_EXTERN or alike, the Windows linker is unable to resolve references to the cutils functions. This commit makes cutils a separate static library, which is linked with the dependent executables/libraries. Signed-off-by: Zurab Kvachadze --- CMakeLists.txt | 17 +++++++++++------ meson.build | 24 ++++++++++++++++++++---- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf6bf1754..a060254cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,6 +233,7 @@ xoption(QJS_BUILD_LIBC "Build standard library modules as part of the library" O macro(add_qjs_libc_if_needed target) if(NOT QJS_BUILD_LIBC) target_sources(${target} PRIVATE quickjs-libc.c) + target_link_libraries(${target} PRIVATE cutils) endif() endmacro() macro(add_static_if_needed target) @@ -245,7 +246,6 @@ macro(add_static_if_needed target) endmacro() set(qjs_sources - cutils.c dtoa.c libregexp.c libunicode.c @@ -278,6 +278,10 @@ if(M_LIBRARIES OR CMAKE_C_COMPILER_ID STREQUAL "TinyCC") list(APPEND qjs_libs m) endif() +add_library(cutils STATIC cutils.c) +target_compile_definitions(cutils PRIVATE ${qjs_defines}) +target_link_libraries(cutils PRIVATE ${qjs_libs}) + add_library(qjs ${qjs_sources}) target_compile_definitions(qjs PRIVATE ${qjs_defines}) target_include_directories(qjs PUBLIC @@ -285,6 +289,7 @@ target_include_directories(qjs PUBLIC $ ) target_link_libraries(qjs PUBLIC ${qjs_libs}) +target_link_libraries(qjs PRIVATE $) if(EMSCRIPTEN) add_executable(qjs_wasm ${qjs_sources}) @@ -314,7 +319,7 @@ add_executable(qjsc add_qjs_libc_if_needed(qjsc) add_static_if_needed(qjsc) target_compile_definitions(qjsc PRIVATE ${qjs_defines}) -target_link_libraries(qjsc qjs) +target_link_libraries(qjsc qjs cutils) # QuickJS CLI @@ -331,7 +336,7 @@ set_target_properties(qjs_exe PROPERTIES OUTPUT_NAME "qjs" ) target_compile_definitions(qjs_exe PRIVATE ${qjs_defines}) -target_link_libraries(qjs_exe qjs) +target_link_libraries(qjs_exe qjs cutils) if(NOT WIN32) set_target_properties(qjs_exe PROPERTIES ENABLE_EXPORTS TRUE) endif() @@ -354,7 +359,7 @@ if(NOT EMSCRIPTEN) ) add_qjs_libc_if_needed(run-test262) target_compile_definitions(run-test262 PRIVATE ${qjs_defines}) - target_link_libraries(run-test262 qjs) + target_link_libraries(run-test262 qjs cutils) endif() # Interrupt test @@ -364,17 +369,17 @@ add_executable(api-test api-test.c ) target_compile_definitions(api-test PRIVATE ${qjs_defines}) -target_link_libraries(api-test qjs) +target_link_libraries(api-test qjs cutils) # Unicode generator # add_executable(unicode_gen EXCLUDE_FROM_ALL - cutils.c libunicode.c unicode_gen.c ) target_compile_definitions(unicode_gen PRIVATE ${qjs_defines}) +target_link_libraries(unicode_gen PRIVATE cutils) add_executable(function_source gen/function_source.c diff --git a/meson.build b/meson.build index cf79060bc..05e74025d 100644 --- a/meson.build +++ b/meson.build @@ -133,7 +133,6 @@ qjs_sys_deps += dependency('threads', required: false) qjs_sys_deps += dependency('dl', required: false) qjs_srcs = files( - 'cutils.c', 'dtoa.c', 'libregexp.c', 'libunicode.c', @@ -172,6 +171,14 @@ qjs_libc_lib = static_library( gnu_symbol_visibility: 'hidden', ) +cutils_lib = static_library( + 'cutils', + 'cutils.c', + + dependencies: qjs_sys_deps, + c_args: qjs_c_args +) + qjs_lib = library( 'qjs', qjs_srcs, @@ -185,6 +192,7 @@ qjs_lib = library( dependencies: qjs_sys_deps, link_whole: qjs_libc ? qjs_libc_lib : [], + link_with: cutils_lib, c_args: qjs_c_args, gnu_symbol_visibility: 'hidden', @@ -274,7 +282,10 @@ qjsc_exe = executable( qjsc_srcs, c_args: qjs_c_args, - link_with: qjs_libc ? [] : qjs_libc_lib, + link_with: [ + qjs_libc ? [] : qjs_libc_lib, + cutils_lib + ], dependencies: qjs_dep, install: true, @@ -300,7 +311,10 @@ qjs_exe = executable( qjs_exe_srcs, c_args: qjs_c_args, - link_with: qjs_libc ? [] : qjs_libc_lib, + link_with: [ + qjs_libc ? [] : qjs_libc_lib, + cutils_lib + ], dependencies: [qjs_dep, mimalloc_dep], export_dynamic: true, @@ -383,6 +397,7 @@ if tests.allowed() 'run-test262.c', qjs_libc_srcs, + link_with: cutils_lib, c_args: qjs_c_args, dependencies: qjs_dep, ) @@ -484,6 +499,7 @@ if tests.allowed() 'api-test', 'api-test.c', + link_with: cutils_lib, c_args: qjs_c_args, dependencies: qjs_dep, build_by_default: false, @@ -508,9 +524,9 @@ endif # Unicode generator unicode_gen = executable( 'unicode_gen', - 'cutils.c', 'unicode_gen.c', + link_with: cutils_lib, c_args: qjs_c_args, build_by_default: false, ) From 71cf78c94fb4b415f931af25161dc6a06d45baf8 Mon Sep 17 00:00:00 2001 From: Zurab Kvachadze Date: Sun, 4 Jan 2026 23:39:41 +0100 Subject: [PATCH 2/6] meson: Do not build quickjs-libc as static library if -Dlibc=true Previously, irrespective of the setting of -Dlibc, quickjs-libc would be built as a static library. In case -Dlibc=true is set, the static library is linked in libqjs only. In case -Dlibc=false is set, the archive is linked to every quickjs-libc consumer. In the former case (-Dlibc=true), building quickjs-libc as static library introduces useless indirection, since quickjs-libc is going to be linked only to libqjs anyway. CMake just adds the libc's sources to qjs's and calls it a day. This commit changes meson.build. If -Dlibc=true is set, quickjs-libc's sources are compiled as a part of libqjs. If not, it compiled as a static library, as was done before. In both cases, a direct dependency of quickjs-libc has been changed into a `dependency` on qjs_libc_dep which conveniently abstracts both configuration. So, if quickjs-libc needs to be linked to, just add `dependencies: qjs_libc_dep`. Signed-off-by: Zurab Kvachadze --- meson.build | 61 ++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/meson.build b/meson.build index 05e74025d..f69dcf6db 100644 --- a/meson.build +++ b/meson.build @@ -162,15 +162,6 @@ if not qjs_parser qjs_c_args += ['-DQJS_DISABLE_PARSER'] endif -qjs_libc_lib = static_library( - 'quickjs-libc', - qjs_libc_srcs, - - dependencies: qjs_sys_deps, - c_args: qjs_c_args, - gnu_symbol_visibility: 'hidden', -) - cutils_lib = static_library( 'cutils', 'cutils.c', @@ -182,6 +173,7 @@ cutils_lib = static_library( qjs_lib = library( 'qjs', qjs_srcs, + qjs_libc ? qjs_libc_srcs : [], # export public headers generator( @@ -191,7 +183,6 @@ qjs_lib = library( ).process(qjs_hdrs), dependencies: qjs_sys_deps, - link_whole: qjs_libc ? qjs_libc_lib : [], link_with: cutils_lib, c_args: qjs_c_args, gnu_symbol_visibility: 'hidden', @@ -211,6 +202,26 @@ qjs_dep = declare_dependency( variables: qjs_export_variables, ) + +if qjs_libc + qjs_libc_dep = declare_dependency( + dependencies: qjs_dep + ) +else + qjs_libc_lib = static_library( + 'quickjs-libc', + qjs_libc_srcs, + + c_args: qjs_c_args, + link_with: cutils_lib, + dependencies: [qjs_sys_deps, qjs_dep], + gnu_symbol_visibility: 'hidden', + ) + qjs_libc_dep = declare_dependency( + link_with: qjs_libc_lib + ) +endif + if host_system == 'emscripten' qjs_wasm_export_name = 'getQuickJs' executable( @@ -282,12 +293,8 @@ qjsc_exe = executable( qjsc_srcs, c_args: qjs_c_args, - link_with: [ - qjs_libc ? [] : qjs_libc_lib, - cutils_lib - ], - dependencies: qjs_dep, - + link_with: [cutils_lib], + dependencies: [qjs_dep, qjs_libc_dep], install: true, ) @@ -311,11 +318,8 @@ qjs_exe = executable( qjs_exe_srcs, c_args: qjs_c_args, - link_with: [ - qjs_libc ? [] : qjs_libc_lib, - cutils_lib - ], - dependencies: [qjs_dep, mimalloc_dep], + link_with: [cutils_lib], + dependencies: [qjs_dep, qjs_libc_dep, mimalloc_dep], export_dynamic: true, install: true, @@ -395,11 +399,10 @@ if tests.allowed() run262_exe = executable( 'run-test262', 'run-test262.c', - qjs_libc_srcs, link_with: cutils_lib, c_args: qjs_c_args, - dependencies: qjs_dep, + dependencies: [qjs_dep, qjs_libc_dep] ) test( @@ -512,10 +515,9 @@ if tests.allowed() executable( 'function_source', 'gen/function_source.c', - qjs_libc_srcs, c_args: qjs_c_args, - dependencies: qjs_dep, + dependencies: [qjs_dep, qjs_libc_dep], build_by_default: false, ), ) @@ -623,19 +625,17 @@ if examples.allowed() executable( 'hello', 'gen/hello.c', - qjs_libc_srcs, c_args: qjs_c_args, - dependencies: qjs_dep, + dependencies: [qjs_dep, qjs_libc_dep] ) executable( 'hello_module', 'gen/hello_module.c', - qjs_libc_srcs, c_args: qjs_c_args, - dependencies: qjs_dep, + dependencies: [qjs_dep, qjs_libc_dep] ) subdir('examples') @@ -644,10 +644,9 @@ if examples.allowed() 'test_fib', 'examples/fib.c', 'gen/test_fib.c', - qjs_libc_srcs, c_args: qjs_c_args, - dependencies: qjs_dep, + dependencies: [qjs_dep, qjs_libc_dep], export_dynamic: true, ) endif From 8143aa9565b3920624a75d5a17020563133a708a Mon Sep 17 00:00:00 2001 From: Zurab Kvachadze Date: Mon, 5 Jan 2026 15:00:17 +0100 Subject: [PATCH 3/6] CMake: Build quickjs-libc as a static lib if -DQJS_BUILD_LIBC=false Previously, if QJS_BUILD_LIBC was false, quickjs-libc would be built with every consumer. This made it harder to specify and trace the dependencies/compiler definitions of quickjs-libc itself. With this change, if -DQJS_BUILD_LIBC=false is passed to CMake, quickjs-libc is built as a normal static library, with its own compiler definitions and dependencies. This changes CMake to match the Meson behaviour. Additionally, since CMake does not allow mixing keyword and non-keyword declarations, this commit adds explicit `PRIVATE` to all `target_link_libraries` declarations. Signed-off-by: Zurab Kvachadze --- CMakeLists.txt | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a060254cc..410d4674c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,8 +232,7 @@ endif() xoption(QJS_BUILD_LIBC "Build standard library modules as part of the library" OFF) macro(add_qjs_libc_if_needed target) if(NOT QJS_BUILD_LIBC) - target_sources(${target} PRIVATE quickjs-libc.c) - target_link_libraries(${target} PRIVATE cutils) + target_link_libraries(${target} PRIVATE qjs-libc) endif() endmacro() macro(add_static_if_needed target) @@ -291,6 +290,12 @@ target_include_directories(qjs PUBLIC target_link_libraries(qjs PUBLIC ${qjs_libs}) target_link_libraries(qjs PRIVATE $) +if(NOT QJS_BUILD_LIBC) + add_library(qjs-libc STATIC quickjs-libc.c) + target_compile_definitions(qjs-libc PRIVATE ${qjs_defines}) + target_link_libraries(qjs-libc PRIVATE ${qjs_libs} qjs cutils) +endif() + if(EMSCRIPTEN) add_executable(qjs_wasm ${qjs_sources}) target_link_options(qjs_wasm PRIVATE @@ -306,7 +311,7 @@ if(EMSCRIPTEN) -sEXPORTED_RUNTIME_METHODS=ccall,cwrap ) target_compile_definitions(qjs_wasm PRIVATE ${qjs_defines}) - target_link_libraries(qjs_wasm m) + target_link_libraries(qjs_wasm PRIVATE m) endif() @@ -319,7 +324,7 @@ add_executable(qjsc add_qjs_libc_if_needed(qjsc) add_static_if_needed(qjsc) target_compile_definitions(qjsc PRIVATE ${qjs_defines}) -target_link_libraries(qjsc qjs cutils) +target_link_libraries(qjsc PRIVATE qjs cutils) # QuickJS CLI @@ -336,7 +341,7 @@ set_target_properties(qjs_exe PROPERTIES OUTPUT_NAME "qjs" ) target_compile_definitions(qjs_exe PRIVATE ${qjs_defines}) -target_link_libraries(qjs_exe qjs cutils) +target_link_libraries(qjs_exe PRIVATE qjs cutils) if(NOT WIN32) set_target_properties(qjs_exe PROPERTIES ENABLE_EXPORTS TRUE) endif() @@ -344,9 +349,9 @@ if(QJS_BUILD_CLI_WITH_MIMALLOC OR QJS_BUILD_CLI_WITH_STATIC_MIMALLOC) find_package(mimalloc REQUIRED) # Upstream mimalloc doesn't provide a way to know if both libraries are supported. if(QJS_BUILD_CLI_WITH_STATIC_MIMALLOC) - target_link_libraries(qjs_exe mimalloc-static) + target_link_libraries(qjs_exe PRIVATE mimalloc-static) else() - target_link_libraries(qjs_exe mimalloc) + target_link_libraries(qjs_exe PRIVATE mimalloc) endif() endif() @@ -359,7 +364,7 @@ if(NOT EMSCRIPTEN) ) add_qjs_libc_if_needed(run-test262) target_compile_definitions(run-test262 PRIVATE ${qjs_defines}) - target_link_libraries(run-test262 qjs cutils) + target_link_libraries(run-test262 PRIVATE qjs cutils) endif() # Interrupt test @@ -369,7 +374,7 @@ add_executable(api-test api-test.c ) target_compile_definitions(api-test PRIVATE ${qjs_defines}) -target_link_libraries(api-test qjs cutils) +target_link_libraries(api-test PRIVATE qjs cutils) # Unicode generator # @@ -387,7 +392,7 @@ add_executable(function_source add_qjs_libc_if_needed(function_source) target_include_directories(function_source PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(function_source PRIVATE ${qjs_defines}) -target_link_libraries(function_source qjs) +target_link_libraries(function_source PRIVATE qjs) # Examples # @@ -399,7 +404,7 @@ if(QJS_BUILD_EXAMPLES) add_qjs_libc_if_needed(hello) target_include_directories(hello PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(hello PRIVATE ${qjs_defines}) - target_link_libraries(hello qjs) + target_link_libraries(hello PRIVATE qjs) add_executable(hello_module gen/hello_module.c @@ -407,7 +412,7 @@ if(QJS_BUILD_EXAMPLES) add_qjs_libc_if_needed(hello_module) target_include_directories(hello_module PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(hello_module PRIVATE ${qjs_defines}) - target_link_libraries(hello_module qjs) + target_link_libraries(hello_module PRIVATE qjs) add_library(fib MODULE examples/fib.c) set_target_properties(fib PROPERTIES @@ -416,7 +421,7 @@ if(QJS_BUILD_EXAMPLES) ) target_compile_definitions(fib PRIVATE JS_SHARED_LIBRARY) if(WIN32) - target_link_libraries(fib qjs) + target_link_libraries(fib PRIVATE qjs) elseif(APPLE) target_link_options(fib PRIVATE -undefined dynamic_lookup) endif() @@ -428,7 +433,7 @@ if(QJS_BUILD_EXAMPLES) ) target_compile_definitions(point PRIVATE JS_SHARED_LIBRARY) if(WIN32) - target_link_libraries(point qjs) + target_link_libraries(point PRIVATE qjs) elseif(APPLE) target_link_options(point PRIVATE -undefined dynamic_lookup) endif() @@ -440,7 +445,7 @@ if(QJS_BUILD_EXAMPLES) add_qjs_libc_if_needed(test_fib) target_include_directories(test_fib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(test_fib PRIVATE ${qjs_defines}) - target_link_libraries(test_fib qjs) + target_link_libraries(test_fib PRIVATE qjs) endif() # Install target From 8b874cda05dfd5ebcd9808219b49cef90bdafed1 Mon Sep 17 00:00:00 2001 From: Zurab Kvachadze Date: Mon, 5 Jan 2026 15:40:02 +0100 Subject: [PATCH 4/6] Consolidate JS_EXTERN, js_force_inline in quickjs.h, add Windows support Since the macros like JS_EXTERN are used in more than one place, it is quite reasonable and helpful to declare them in one place. Additionally, the Windows support with the __declspec tango was missing. This commit, in addition to reorganising definitions, also plumbs Windows support for the renamed QJS_EXTERN, QJS_LIBC_EXTERN and QJS_FORCE_INLINE. Signed-off-by: Zurab Kvachadze --- CMakeLists.txt | 15 +++++++++- meson.build | 19 ++++++++++-- quickjs-libc.h | 40 ++++++++++--------------- quickjs.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 122 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 410d4674c..b357449e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,9 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) set(CMAKE_C_STANDARD 11) +# Used to properly define QJS_LIBC_EXTERN. +add_compile_definitions(QUICKJS_NG_BUILD) + # MINGW doesn't exist in older cmake versions, newer versions don't know # about CMAKE_COMPILER_IS_MINGW, and there is no unique CMAKE_C_COMPILER_ID # for mingw-based compilers... @@ -253,6 +256,8 @@ set(qjs_sources if(QJS_BUILD_LIBC) list(APPEND qjs_sources quickjs-libc.c) + # The definition must be added to the entire project. + add_compile_definitions(QJS_BUILD_LIBC) endif() list(APPEND qjs_defines _GNU_SOURCE) if(WIN32) @@ -282,7 +287,7 @@ target_compile_definitions(cutils PRIVATE ${qjs_defines}) target_link_libraries(cutils PRIVATE ${qjs_libs}) add_library(qjs ${qjs_sources}) -target_compile_definitions(qjs PRIVATE ${qjs_defines}) +target_compile_definitions(qjs PRIVATE ${qjs_defines} QUICKJS_NG_QJS_INTERNAL) target_include_directories(qjs PUBLIC $ $ @@ -290,6 +295,14 @@ target_include_directories(qjs PUBLIC target_link_libraries(qjs PUBLIC ${qjs_libs}) target_link_libraries(qjs PRIVATE $) +# Pass a compiler definition so that Windows gets its declspec's right. +get_target_property(QJS_LIB_TYPE qjs TYPE) +if(QJS_LIB_TYPE STREQUAL "SHARED_LIBRARY") + # We use PUBLIC here because we want the consumers to also have this + # definition. + target_compile_definitions(qjs PUBLIC QUICKJS_NG_DLL) +endif() + if(NOT QJS_BUILD_LIBC) add_library(qjs-libc STATIC quickjs-libc.c) target_compile_definitions(qjs-libc PRIVATE ${qjs_defines}) diff --git a/meson.build b/meson.build index f69dcf6db..0710a73bb 100644 --- a/meson.build +++ b/meson.build @@ -15,6 +15,8 @@ project( host_system = host_machine.system() cc = meson.get_compiler('c') +add_project_arguments('-DQUICKJS_NG_BUILD', language: 'c') + qjs_gcc_warning_args = [ '-Wno-unsafe-buffer-usage', '-Wno-sign-conversion', @@ -148,6 +150,10 @@ qjs_libc_hdrs = files('quickjs-libc.h') if qjs_libc qjs_hdrs += qjs_libc_hdrs + add_project_arguments( + '-DQJS_BUILD_LIBC', + language: 'c' + ) endif qjs_parser = get_option('parser') @@ -184,7 +190,8 @@ qjs_lib = library( dependencies: qjs_sys_deps, link_with: cutils_lib, - c_args: qjs_c_args, + c_args: [qjs_c_args, '-DQUICKJS_NG_QJS_INTERNAL'], + c_shared_args: ['-DQUICKJS_NG_DLL'], gnu_symbol_visibility: 'hidden', install: true, @@ -195,7 +202,15 @@ qjs_export_variables = [ f'have_parser=@qjs_parser@' ] +# Be conservative here: only pass -DQUICKJS_NG_DLL iff default_library == +# 'shared', thus qjs is guarranteed to be a DLL. Not passing -DQUICKJS_NG_DLL if +# qjs is a DLL is not harmful, but passing the definition for DLL qjs is desired. +# Alternatively, if -DQUICKJS_NG_DLL is passed for static qjs, the consumers +# will not compile. +qjs_dep_args = get_option('default_library') == 'shared' ? ['-DQUICKJS_NG_DLL'] : [] + qjs_dep = declare_dependency( + compile_args: qjs_dep_args, link_with: qjs_lib, dependencies: qjs_sys_deps, include_directories: qjs_lib.private_dir_include(), @@ -346,7 +361,7 @@ if meson.is_cross_build() qjs_libc_srcs, dependencies: qjs_sys_native_deps, - c_args: qjs_c_args, + c_args: [qjs_c_args, '-DQUICKJS_NG_QJS_INTERNAL'], gnu_symbol_visibility: 'hidden', build_by_default: false, diff --git a/quickjs-libc.h b/quickjs-libc.h index 8617700ef..a14e62fb2 100644 --- a/quickjs-libc.h +++ b/quickjs-libc.h @@ -34,45 +34,37 @@ extern "C" { #endif -#if defined(__GNUC__) || defined(__clang__) -#define JS_EXTERN __attribute__((visibility("default"))) -#else -#define JS_EXTERN /* nothing */ -#endif - -JS_EXTERN JSModuleDef *js_init_module_std(JSContext *ctx, +JS_LIBC_EXTERN JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); -JS_EXTERN JSModuleDef *js_init_module_os(JSContext *ctx, +JS_LIBC_EXTERN JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); -JS_EXTERN JSModuleDef *js_init_module_bjson(JSContext *ctx, +JS_LIBC_EXTERN JSModuleDef *js_init_module_bjson(JSContext *ctx, const char *module_name); -JS_EXTERN void js_std_add_helpers(JSContext *ctx, int argc, char **argv); -JS_EXTERN int js_std_loop(JSContext *ctx); -JS_EXTERN JSValue js_std_await(JSContext *ctx, JSValue obj); -JS_EXTERN void js_std_init_handlers(JSRuntime *rt); -JS_EXTERN void js_std_free_handlers(JSRuntime *rt); -JS_EXTERN void js_std_dump_error(JSContext *ctx); -JS_EXTERN uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, +JS_LIBC_EXTERN void js_std_add_helpers(JSContext *ctx, int argc, char **argv); +JS_LIBC_EXTERN int js_std_loop(JSContext *ctx); +JS_LIBC_EXTERN JSValue js_std_await(JSContext *ctx, JSValue obj); +JS_LIBC_EXTERN void js_std_init_handlers(JSRuntime *rt); +JS_LIBC_EXTERN void js_std_free_handlers(JSRuntime *rt); +JS_LIBC_EXTERN void js_std_dump_error(JSContext *ctx); +JS_LIBC_EXTERN uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); -JS_EXTERN int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, +JS_LIBC_EXTERN int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, bool use_realpath, bool is_main); -JS_EXTERN JSModuleDef *js_module_loader(JSContext *ctx, +JS_LIBC_EXTERN JSModuleDef *js_module_loader(JSContext *ctx, const char *module_name, void *opaque); -JS_EXTERN void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, +JS_LIBC_EXTERN void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, int flags); -JS_EXTERN void js_std_promise_rejection_tracker(JSContext *ctx, +JS_LIBC_EXTERN void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, bool is_handled, void *opaque); // Defaults to JS_NewRuntime, no-op if compiled without worker support. // Call before creating the first worker thread. -JS_EXTERN void js_std_set_worker_new_runtime_func(JSRuntime *(*func)(void)); +JS_LIBC_EXTERN void js_std_set_worker_new_runtime_func(JSRuntime *(*func)(void)); // Defaults to JS_NewContext, no-op if compiled without worker support. // Call before creating the first worker thread. -JS_EXTERN void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); - -#undef JS_EXTERN +JS_LIBC_EXTERN void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); #ifdef __cplusplus } /* extern "C" { */ diff --git a/quickjs.h b/quickjs.h index 2c2e1cbb0..75fe64da5 100644 --- a/quickjs.h +++ b/quickjs.h @@ -39,12 +39,80 @@ extern "C" { #define QUICKJS_NG 1 +/* Helpers. */ +#if defined(_WIN32) || defined(__CYGWIN__) +# define QUICKJS_NG_PLAT_WIN32 1 +#endif /* defined(_WIN32) || defined(__CYGWIN__) */ + #if defined(__GNUC__) || defined(__clang__) -#define js_force_inline inline __attribute__((always_inline)) -#define JS_EXTERN __attribute__((visibility("default"))) +# define QUICKJS_NG_CC_GNULIKE 1 +#endif /* defined(__GNUC__) || defined(__clang__) */ + +/* + * `js_force_inline` -- helper macro forcing the inlining of a function. + */ +#ifdef QUICKJS_NG_PLAT_WIN32 +# ifdef QUICKJS_NG_CC_GNULIKE +# define js_force_inline inline __attribute__((always_inline)) +# else +# define js_force_inline __forceinline +# endif +#else +# ifdef QUICKJS_NG_CC_GNULIKE +# define js_force_inline inline __attribute__((always_inline)) +# else +# define js_force_inline inline +# endif +#endif /* QUICKJS_NG_PLAT_WIN32 */ + +/* + * `JS_EXTERN` -- helper macro that must be used to mark the external + * interfaces of libqjs. + * + * Explicitly undefine the macro first in case a previous header included this. + * This is done in order to avoid the redefinition warnings/errors upon a + * different definition of `JS_EXTERN`. + * + * Windows note: The `__declspec` syntax is supported by both Clang and GCC. + * The QUICKJS_NG_QJS_INTERNAL macro must be defined for libqjs (and only for + * it) to properly export symbols. + */ +#ifdef QUICKJS_NG_PLAT_WIN32 +/* Define QUICKJS_NG_DLL if building or using shared qjs. */ +# ifdef QUICKJS_NG_DLL +# ifdef QUICKJS_NG_QJS_INTERNAL +# define JS_EXTERN __declspec(dllexport) +# else +# define JS_EXTERN __declspec(dllimport) +# endif +# else +# define JS_EXTERN /* nothing */ +# endif #else -#define js_force_inline inline -#define JS_EXTERN /* nothing */ +# ifdef QUICKJS_NG_CC_GNULIKE +# define JS_EXTERN __attribute__((visibility("default"))) +# else +# define JS_EXTERN /* nothing */ +# endif +#endif /* QUICKJS_NG_PLAT_WIN32 */ + +/* + * `JS_LIBC_EXTERN` -- helper macro that must be used to mark the extern + * interfaces of quickjs-libc specifically. + */ +#if defined(QUICKJS_NG_BUILD) && !defined(QJS_BUILD_LIBC) && defined(QUICKJS_NG_PLAT_WIN32) +/* + * We are building QuickJS-NG, quickjs-libc is a static library and we are on + * Windows. Then, make sure to not export any interfaces. + */ +# define JS_LIBC_EXTERN /* nothing */ +#else +/* + * Otherwise, if we are either (1) not building QuickJS-NG, (2) libc is built as + * a part of libqjs, or (3) we are not on Windows, define JS_LIBC_EXTERN to + * JS_EXTERN. + */ +# define JS_LIBC_EXTERN JS_EXTERN #endif /* Borrowed from Folly */ @@ -65,6 +133,9 @@ extern "C" { #endif #endif +#undef QUICKJS_NG_CC_GNULIKE +#undef QUICKJS_NG_PLAT_WIN32 + typedef struct JSRuntime JSRuntime; typedef struct JSContext JSContext; typedef struct JSObject JSObject; @@ -1304,7 +1375,6 @@ JS_EXTERN const char* JS_GetVersion(void); /* Integration point for quickjs-libc.c, not for public use. */ JS_EXTERN uintptr_t js_std_cmd(int cmd, ...); -#undef JS_EXTERN #undef js_force_inline #ifdef __cplusplus From aff00e39417863e3dc29292c4509a6a6868f91f9 Mon Sep 17 00:00:00 2001 From: Zurab Kvachadze Date: Wed, 19 Nov 2025 03:31:30 +0100 Subject: [PATCH 5/6] ci/meson: Set -Db_lundef=false when building with sanitisers under Clang Clang cannot handle building shared libraries with sanitizers and -Wl,--no-undefined (set by default unless explicitly disabled with -Db_lundef=false). This commit prefixes CI in case shared libraries are built with sanitisers. Signed-off-by: Zurab Kvachadze --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7d897a1c..cad6e54b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -643,11 +643,14 @@ jobs: - name: clang+sanitize args: >- "-Db_sanitize=address,undefined" + "-Db_lundef=false" extra_envs: CC: clang CXX: clang++ - name: clang+msan - args: -Db_sanitize=memory + args: >- + "-Db_sanitize=memory" + "-Db_lundef=false" extra_envs: CC: clang CXX: clang++ @@ -657,6 +660,7 @@ jobs: - name: clang-cl+sanitize args: >- "-Db_sanitize=address,undefined" + "-Db_lundef=false" extra_envs: CC: clang-cl CXX: clang-cl From 209e830fceccf22b7d8c92cd665016b688094fa9 Mon Sep 17 00:00:00 2001 From: Zurab Kvachadze Date: Sat, 1 Nov 2025 17:41:37 +0100 Subject: [PATCH 6/6] meson: Do not hardcode default_library=static During the build, the default library can be overridden via the -Ddefault_library=type flag. Presetting this key in meson.build makes life harder for distributions which almost always want to build shared libraries. Those requiring static libraries can always force that via the aforementioned flag. Signed-off-by: Zurab Kvachadze --- meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/meson.build b/meson.build index 0710a73bb..33f0fe475 100644 --- a/meson.build +++ b/meson.build @@ -5,7 +5,6 @@ project( default_options: [ 'c_std=gnu11,c11', 'warning_level=3', - 'default_library=static', ], license: 'MIT', license_files: 'LICENSE',