Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start fixing bugs discovered by Node.js's Node-API tests #14501

Draft
wants to merge 208 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 171 commits
Commits
Show all changes
208 commits
Select commit Hold shift + click to select a range
1f53597
Defer finalization for napi callbacks to the immediate task queue
Jarred-Sumner Jul 19, 2024
4181393
Apply formatting changes
Jarred-Sumner Jul 19, 2024
eb8d465
Merge branch 'main' into jarred/napi-2
dylan-conway Jul 23, 2024
e66ec2a
Merge branch 'main' into jarred/napi-2
190n Oct 4, 2024
dfa2a6b
Fix tsfn finalizers
190n Oct 4, 2024
2583f33
Make NAPI tests allocate in the finalizer
190n Oct 4, 2024
afcf7b1
Route all finalizers through NapiFinalizer
190n Oct 4, 2024
23dc0fe
Test that threadsafe function finalizers run on the next tick
190n Oct 4, 2024
85f617f
Fix bugs in napi_create_class and napi_get_value_bigint_*
190n Oct 12, 2024
6169f10
Fix bugs with napi_define_class
190n Oct 12, 2024
6a440aa
Merge branch 'main' into ben/fix-node-napi-tests
190n Oct 14, 2024
ef4728c
WIP convert napi.cpp functions to set the last error
190n Oct 15, 2024
49b2de9
Set last error in all C++ napi functions
190n Oct 15, 2024
1649c03
Merge branch 'main' into ben/fix-node-napi-tests
190n Oct 15, 2024
a0c2a73
Fix napi_create_bigint_words error handling
190n Oct 15, 2024
ed4175b
Arg checking in napi_define_class
190n Oct 15, 2024
216e5b3
Fix napi_coerce_to_*
190n Oct 16, 2024
2fee09f
Change sizeof division to std::size
190n Oct 16, 2024
b773e66
Move napi_get_value_string_{latin1,utf16} to C++
190n Oct 17, 2024
2d0e0c9
Set error code in Zig napi functions
190n Oct 17, 2024
7be1bf3
Fix napi_create_dataview
190n Oct 17, 2024
8c571d8
Merge branch 'main' into ben/fix-node-napi-tests
190n Oct 17, 2024
6d1db2c
WIP supporting napi_wrap for more values
190n Oct 18, 2024
ea1ddb2
napi_wrap WIP + rip out NAPICallFrame tagging
190n Oct 19, 2024
5bae294
More (self-contained for now) napi tests
190n Oct 19, 2024
e5ffd66
Merge branch 'main' into ben/fix-node-napi-tests
190n Oct 21, 2024
a240093
Pass data pointer to NAPI constructors
190n Oct 21, 2024
020c32b
clangd config for NAPI tests
190n Oct 23, 2024
d612cff
Misc CallFrame fixes
190n Oct 23, 2024
e5e643d
Merge branch 'main' into ben/fix-node-napi-tests
190n Oct 23, 2024
c44eb73
Do not propagate nullptr out of NAPIFunction::call
190n Oct 23, 2024
710f779
Delete NapiPrototype::napiRef
190n Oct 23, 2024
b2080c8
JS exceptions instead of assertions in napi tests
190n Oct 23, 2024
a7bc53b
Split NAPI tests out of the huge C++ file
190n Oct 24, 2024
b753e4b
Fix providing class's data pointer to method without data pointer
190n Oct 24, 2024
b8aba83
napi_wrap fixes
190n Oct 25, 2024
bdcca41
Refine NAPI tests
190n Oct 25, 2024
e04f461
Work on leak testing for NAPI wrap/ref/external
190n Oct 25, 2024
d29e72f
Add missing #include
190n Oct 25, 2024
f71b440
Add filename to napi_log
190n Oct 25, 2024
600bc1c
Fix napi_ref finalizers
190n Oct 25, 2024
3ba398f
Stress test napi_wrap and napi_external
190n Oct 25, 2024
43d7cfc
Fix NAPI tests compiling on Windows
190n Oct 26, 2024
3587391
Add napi_type_tag_object and napi_check_type_tag
190n Oct 26, 2024
c28d419
Reset last NAPI error before calling into a native module
190n Oct 28, 2024
71101e1
Fix NAPI bugs
190n Oct 29, 2024
249227d
Fix NAPI string creation bugs
heimskr Oct 29, 2024
39b442b
Move napi_create_typedarray from napi.zig to napi.cpp to produce Rang…
heimskr Oct 29, 2024
528d9a6
Merge branch 'main' into ben/fix-node-napi-tests
heimskr Oct 29, 2024
6999978
Make napi_create_external_arraybuffer produce a non-shared ArrayBuffer
heimskr Oct 29, 2024
19b0fed
Include all properties except enums in napi_get_property_names, and e…
heimskr Oct 29, 2024
73579e1
Support filtering by property descriptor in napi_get_all_property_names
190n Oct 30, 2024
84c4f96
Merge branch 'main' into ben/fix-node-napi-tests
190n Oct 30, 2024
8b19e08
Rename wrap-lifetime-test.mjs
190n Oct 30, 2024
774bb89
Fix node_api_symbol_for behavior for null description parameters
heimskr Oct 30, 2024
8a0a88c
Merge branch 'ben/fix-node-napi-tests' of github.com:oven-sh/bun into…
heimskr Oct 30, 2024
838ca00
Replace NAPIFunction with NapiClass
heimskr Oct 31, 2024
059185f
Fix napi_get_new_target
heimskr Oct 31, 2024
197a26f
Fix some confusion over the "length" parameter in NapiClass
heimskr Oct 31, 2024
91a5231
Merge branch 'main' into ben/fix-node-napi-tests
190n Oct 31, 2024
a669ff1
Build napi-app in debug mode
190n Oct 31, 2024
5176ab5
Test napi_set_property and napi_set_named_property
190n Oct 31, 2024
fb6a48a
Fix property-related NAPI methods
heimskr Oct 31, 2024
d2c4a9a
Fix napi_get_all_property_names not including inherited properties wh…
heimskr Oct 31, 2024
dc4177f
Keep napi_envs separate, keep track of them, associate module info
heimskr Nov 1, 2024
a1c4240
Integrate #12660 into bun/fix-node-napi-tests
heimskr Nov 1, 2024
fe18b87
Move napi_wrap lifetime test to existing test suite
190n Nov 1, 2024
936ae5a
Fix and test edge cases calling NAPI constructors
190n Nov 1, 2024
b6dfd89
Remove reference to nonexistent test
190n Nov 1, 2024
2d96ec0
Fix napi_set_property
190n Nov 1, 2024
f15059f
Handle exceptions in NAPI finalizers as uncaught exceptions
heimskr Nov 1, 2024
dffc718
Merge branch 'ben/fix-node-napi-tests' of github.com:oven-sh/bun into…
heimskr Nov 1, 2024
8bb8193
Fix use after free
heimskr Nov 1, 2024
1293039
Clean up some code in node validators (#14897)
Jarred-Sumner Oct 31, 2024
b5ed0f0
ci: If only tests change, use artifacts from last successful build (#…
Electroid Oct 31, 2024
664c080
Fixes #14918 (#14921)
Jarred-Sumner Oct 31, 2024
a8a2403
Add `bytesWritten` property to Bun.Socket, fix encoding issue in node…
Jarred-Sumner Nov 1, 2024
30fe8d5
fix(install): only globally link requested packages (#12506)
dylan-conway Nov 1, 2024
7fab670
Redact secrets in `bunfig.toml` and `npmrc` logs (#14919)
dylan-conway Nov 1, 2024
08116e4
Fix napi property methods on non-objects (#14935)
190n Nov 1, 2024
03d945e
Run tests from npm packages, `elysia` to start (#14932)
Electroid Nov 1, 2024
7110c07
Inline `process.versions.bun` in `bun build --compile` (#14940)
Jarred-Sumner Nov 1, 2024
1d5da9e
Fixes #11754 (#14948)
Jarred-Sumner Nov 1, 2024
afd023a
Fix remaining error in NAPI property functions
190n Nov 2, 2024
7a20f51
Fix NAPI property tests
190n Nov 2, 2024
066b1da
Fix errors from rebase
190n Nov 2, 2024
e3b5927
Work on NAPI leak tests
190n Nov 2, 2024
d8557ea
Merge branch 'main' into ben/fix-node-napi-tests
190n Nov 4, 2024
3303a5d
Improve finalizer support
heimskr Nov 4, 2024
a60ae54
Merge branch 'ben/fix-node-napi-tests' of github.com:oven-sh/bun into…
heimskr Nov 4, 2024
c659b3b
Require key to be a string or symbol in napi_has_own_property
heimskr Nov 4, 2024
ce46947
Use WebKit branch kai/inherited-property-names
heimskr Nov 5, 2024
a6d707a
Don't require node_api_module_get_api_version_v1, default to version 8
heimskr Nov 5, 2024
1c06dbd
Fix multiple dispatch of beforeExit
heimskr Nov 5, 2024
f5dc049
Fix some formatting
heimskr Nov 5, 2024
ab92fc5
Add a typedef for node_api_basic_env in napi-app
heimskr Nov 5, 2024
3296a6e
Fix napi_instanceof
heimskr Nov 5, 2024
d4b7102
Clear NAPI error after native function invocations
heimskr Nov 5, 2024
07a217f
Fix napi_get_version misunderstanding
heimskr Nov 6, 2024
71f3089
Defer external (array)buffer finalizers if necessary
heimskr Nov 6, 2024
0dce736
oops
heimskr Nov 6, 2024
7978505
Fix napi_get_buffer_info return code
heimskr Nov 6, 2024
80b7426
Add node_api_create_buffer_from_arraybuffer
heimskr Nov 6, 2024
a152557
Fix napi_env usage in FFI
heimskr Nov 7, 2024
0f29267
Add node_api_get_module_file_name
heimskr Nov 7, 2024
85dcebe
Fix typedef in napi-app
heimskr Nov 7, 2024
fceeb22
Don't dlclose if node_api_module_get_api_version_v1 is missing
heimskr Nov 8, 2024
855b710
Use node_api_post_finalizer in napi-app and add some missing includes
heimskr Nov 8, 2024
c30ef2c
Fix more missing includes in napi-app
heimskr Nov 8, 2024
07a3913
Escape NAPI file:// URIs
heimskr Nov 8, 2024
9f3b0f7
Adjust some NAPI error messages to match Node equivalents
heimskr Nov 8, 2024
7c13e63
Update WebKit version
heimskr Nov 9, 2024
e34673c
Fix test_reference_by_node_api_version
heimskr Nov 9, 2024
86e421a
Merge branch 'main' into ben/fix-node-napi-tests
190n Nov 11, 2024
657f5b9
Set WebKit version to merge commit instead of commit on branch of ove…
190n Nov 11, 2024
8b5fb34
Assert there is an env when calling external finalizer
190n Nov 12, 2024
1de2319
Update TODO in napi-app
190n Nov 12, 2024
3a71be3
Use node_api_post_finalize in napi-app
190n Nov 12, 2024
0bee1c9
Don't create napi_envs for FFI unless actually needed
heimskr Nov 12, 2024
1d8423e
Allow null data in napi_async_work
heimskr Nov 12, 2024
9490c30
Remove stray include
190n Nov 12, 2024
469be87
Merge branch 'main' into ben/fix-node-napi-tests
190n Nov 12, 2024
9ea9925
Rename napiEnv FFI symbol to avoid potential collisions
heimskr Nov 12, 2024
c17e05c
Better use of types in FFI.h
heimskr Nov 12, 2024
09a6a11
Don't needlessly tick the event loop if the napi finalizer queue is e…
heimskr Nov 12, 2024
7993f4f
Address some feedback
heimskr Nov 12, 2024
bd45a65
Fix napi module file URIs
heimskr Nov 12, 2024
2335e35
Remove `defer` parameter from NapiRef constructor
heimskr Nov 12, 2024
6e7240b
Derefcountify NapiFinalizer
heimskr Nov 12, 2024
563b3c0
Address more feedback
heimskr Nov 12, 2024
d11a483
Merge branch 'ben/fix-node-napi-tests' into kai/fix-node-napi-tests
heimskr Nov 12, 2024
83f536f
Make parameter const
190n Nov 13, 2024
86d4dbe
Treat a call to process.exit() inside napi_call_function as if it thr…
heimskr Nov 13, 2024
c20c0de
Undo the previous commit and add a TODO about Web Worker termination
heimskr Nov 13, 2024
7b25ce1
Merge branch 'ben/fix-node-napi-tests' into kai/fix-node-napi-tests
heimskr Nov 13, 2024
a8fa566
Fix beforeOnExit being dispatched multiple times
heimskr Nov 13, 2024
ac8c6f0
Run some of Node's Node-API tests in CI
190n Nov 13, 2024
5970006
Merge branch 'main' into ben/fix-node-napi-tests
190n Nov 13, 2024
ccd7275
Remove console log
190n Nov 13, 2024
440111f
`bun run prettier:extra`
190n Nov 13, 2024
b11d631
Ensure NapiRef finalizers are called by the time the env is cleaned up
heimskr Nov 13, 2024
d3b509e
Merge branch 'ben/fix-node-napi-tests' into kai/fix-node-napi-tests
heimskr Nov 13, 2024
adc00e0
Don't defer finalizers if the VM is shutting down
heimskr Nov 13, 2024
f54f4e6
Remove stray iostream include
heimskr Nov 13, 2024
9dbe40d
Compile tests in parallel
190n Nov 13, 2024
8358f4d
Merge branch 'main' into ben/fix-node-napi-tests
190n Nov 13, 2024
6dd369e
Merge branch 'ben/fix-node-napi-tests' into kai/fix-node-napi-tests
heimskr Nov 13, 2024
06d37bf
Actually call instance data finalizers
heimskr Nov 13, 2024
bb8b465
Remove workaround for #15111
190n Nov 13, 2024
0e7ed99
Remove stray import
190n Nov 14, 2024
544dd24
Increase timeout
190n Nov 14, 2024
f439dac
Merge branch 'main' into ben/fix-node-napi-tests
190n Nov 14, 2024
e11a683
Fix compile error
190n Nov 14, 2024
83a2c24
Merge branch 'ben/fix-node-napi-tests' into kai/fix-node-napi-tests
heimskr Nov 14, 2024
9fa480c
Fix env cleanup hooks
heimskr Nov 14, 2024
2646ea0
Fix async cleanup hooks?
heimskr Nov 14, 2024
ed1f25e
Skip node-api/test_async_cleanup_hook/test.js because it uses libuv f…
heimskr Nov 14, 2024
f37df90
Add typedef for node_api_basic_env in napi tests
heimskr Nov 14, 2024
134f66c
Undo accidental formatting
heimskr Nov 14, 2024
2afb5e6
Skip compiling Node NAPI tests that we skip running
190n Nov 14, 2024
90852a3
Use correct path separators on Windows
190n Nov 14, 2024
f501143
Fix incorrect calling convention usage
heimskr Nov 14, 2024
f9718af
Make checkGC fail if running a finalizer during napi env cleanup
heimskr Nov 14, 2024
f73ef54
Compile Node tests using Node 23 headers
190n Nov 14, 2024
4103b73
Report NAPI assertion failures more forcefully
heimskr Nov 15, 2024
2aee623
Remove async cleanup hooks from the list after they're called, not be…
heimskr Nov 15, 2024
28830f0
Don't defer finalizers if not in GC
heimskr Nov 15, 2024
d9c8f27
Merge branch 'ben/fix-node-napi-tests' into kai/fix-node-napi-tests
heimskr Nov 15, 2024
e5c5033
Use clang 16 to compile Node NAPI tests on CI linux
190n Nov 15, 2024
6603871
Add logs for NAPI refs and handle scopes
190n Nov 15, 2024
cf960b5
Change how GC is detected
heimskr Nov 15, 2024
eef79ce
Skip test_worker_buffer_callback/test-free-called.js
heimskr Nov 16, 2024
d090501
Change Windows crash behavior
heimskr Nov 18, 2024
73e9866
Merge branch 'main' into ben/fix-node-napi-tests
heimskr Nov 18, 2024
3e085b5
Cast argument to quick_exit in crash()
heimskr Nov 18, 2024
ba5490d
Change abort exit code on Windows
heimskr Nov 18, 2024
b02fb24
Add Bun__crashHandler binding
heimskr Nov 18, 2024
d93122a
Typedef napi_env in napi_with_version.h and then use it in the node_a…
heimskr Nov 19, 2024
c38eca2
Node NAPI tests: get clang(++) from $PATH in CI
heimskr Nov 19, 2024
437d333
Actually never mind on that last one
heimskr Nov 19, 2024
7f9935a
Will this fix the Windows timeout problem?
heimskr Nov 19, 2024
5949777
Or maybe this might
heimskr Nov 19, 2024
1e649b4
Merge branch 'main' into ben/fix-node-napi-tests
heimskr Nov 19, 2024
b0a30ca
Fix leak in napi_threadsafe_function
190n Nov 19, 2024
01bbe30
Disable part of js-native-api/test_instance_data/test.js
heimskr Nov 19, 2024
dec572e
Merge branch 'ben/fix-node-napi-tests' of github.com:oven-sh/bun into…
heimskr Nov 19, 2024
6ba0563
Add test for napi_threadsafe_function memory leak
190n Nov 20, 2024
7a73f14
Fix napi_threadsafe_function memory leak with fixed queue size
190n Nov 20, 2024
24a3f96
Flush stdout explicitly in test_cleanup_hook
heimskr Nov 21, 2024
2406936
Merge branch 'ben/fix-node-napi-tests' of github.com:oven-sh/bun into…
heimskr Nov 21, 2024
b191968
Skip test_callback_scope/test.js on Windows
heimskr Nov 21, 2024
e0414d0
Allow ten minutes for compiling node-napi tests
heimskr Nov 21, 2024
30eda1e
Maybe increase the timeout with setDefaultTimeout too
heimskr Nov 21, 2024
8169061
Revert "Maybe increase the timeout with setDefaultTimeout too"
heimskr Nov 21, 2024
9426039
Add Prisma leak test from #15289
190n Nov 21, 2024
f78ac63
Merge branch 'ben/fix-node-napi-tests' of github.com:oven-sh/bun into…
heimskr Nov 21, 2024
7a623fe
Print compilation progress in node-napi.test.ts
heimskr Nov 21, 2024
3fc6ad4
Increase napi timeout
heimskr Nov 21, 2024
e817928
Reenable Worker test in js-native-api/test_instance_data/test.js
heimskr Nov 22, 2024
7886182
Fix setRawMode return value on Windows
heimskr Nov 23, 2024
33d3732
Merge branch 'main' into ben/fix-node-napi-tests
heimskr Nov 25, 2024
4a10bf2
Oops
heimskr Nov 25, 2024
bb33176
Oops, again
heimskr Nov 25, 2024
1789364
Add missing globals in napi tests
heimskr Nov 25, 2024
1baa1b6
Clean up missing globals code a bit
heimskr Nov 25, 2024
c1a25d0
Merge branch 'main' into ben/fix-node-napi-tests
heimskr Nov 26, 2024
769c6de
Merge branch 'main' into ben/fix-node-napi-tests
heimskr Nov 26, 2024
7ee91a9
Merge branch 'main' into ben/fix-node-napi-tests
heimskr Dec 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 3 additions & 2 deletions src/bun.js/api/FFI.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ typedef enum {
napi_detachable_arraybuffer_expected,
napi_would_deadlock // unused
} napi_status;
void* NapiHandleScope__open(void* jsGlobalObject, bool detached);
void NapiHandleScope__close(void* jsGlobalObject, void* handleScope);
void* NapiHandleScope__open(void* napi_env, bool detached);
void NapiHandleScope__close(void* napi_env, void* handleScope);
extern struct napi_env__ Bun__thisFFIModuleNapiEnv;
#endif


Expand Down
66 changes: 54 additions & 12 deletions src/bun.js/api/ffi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ const URL = @import("../../url.zig").URL;
const VirtualMachine = JSC.VirtualMachine;
const IOTask = JSC.IOTask;

const napi = @import("../../napi/napi.zig");

const TCC = @import("../../tcc.zig");
extern fn pthread_jit_write_protect_np(enable: bool) callconv(.C) void;

Expand Down Expand Up @@ -432,6 +434,13 @@ pub const FFI = struct {
return error.DeferredErrors;
}

for (this.symbols.map.values()) |symbol| {
if (symbol.needsNapiEnv()) {
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", globalThis.makeNapiEnvForFFI());
break;
}
}

for (this.define.items) |define| {
TCC.tcc_define_symbol(state, define[0], define[1]);

Expand Down Expand Up @@ -786,12 +795,14 @@ pub const FFI = struct {
// we are unable to free memory safely in certain cases here.
}

const napi_env = makeNapiEnvIfNeeded(compile_c.symbols.map.values(), globalThis);

var obj = JSC.JSValue.createEmptyObject(globalThis, compile_c.symbols.map.count());
for (compile_c.symbols.map.values()) |*function| {
const function_name = function.base_name.?;
const allocator = bun.default_allocator;

function.compile(allocator) catch |err| {
function.compile(allocator, napi_env) catch |err| {
if (!globalThis.hasException()) {
const ret = JSC.toInvalidArguments("{s} when translating symbol \"{s}\"", .{
@errorName(err),
Expand Down Expand Up @@ -1112,6 +1123,9 @@ pub const FFI = struct {
var obj = JSC.JSValue.createEmptyObject(global, size);
obj.protect();
defer obj.unprotect();

const napi_env = makeNapiEnvIfNeeded(symbols.values(), global);

for (symbols.values()) |*function| {
const function_name = function.base_name.?;

Expand All @@ -1131,7 +1145,7 @@ pub const FFI = struct {
function.symbol_from_dynamic_library = resolved_symbol;
}

function.compile(allocator) catch |err| {
function.compile(allocator, napi_env) catch |err| {
const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\" in \"{s}\"", .{
bun.asByteSlice(@errorName(err)),
bun.asByteSlice(function_name),
Expand Down Expand Up @@ -1223,6 +1237,9 @@ pub const FFI = struct {
var obj = JSValue.createEmptyObject(global, symbols.count());
obj.ensureStillAlive();
defer obj.ensureStillAlive();

const napi_env = makeNapiEnvIfNeeded(symbols.values(), global);

for (symbols.values()) |*function| {
const function_name = function.base_name.?;

Expand All @@ -1236,7 +1253,7 @@ pub const FFI = struct {
return ret;
}

function.compile(allocator) catch |err| {
function.compile(allocator, napi_env) catch |err| {
const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\"", .{
bun.asByteSlice(@errorName(err)),
bun.asByteSlice(function_name),
Expand Down Expand Up @@ -1541,10 +1558,7 @@ pub const FFI = struct {

const tcc_options = "-std=c11 -nostdlib -Wl,--export-all-symbols" ++ if (Environment.isDebug) " -g" else "";

pub fn compile(
this: *Function,
allocator: std.mem.Allocator,
) !void {
pub fn compile(this: *Function, allocator: std.mem.Allocator, napiEnv: ?*napi.NapiEnv) !void {
var source_code = std.ArrayList(u8).init(allocator);
var source_code_writer = source_code.writer();
try this.printSourceCode(&source_code_writer);
Expand All @@ -1566,6 +1580,10 @@ pub const FFI = struct {

_ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY);

if (napiEnv) |env| {
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", env);
}

CompilerRT.define(state);

// TCC.tcc_define_symbol(
Expand Down Expand Up @@ -1673,6 +1691,10 @@ pub const FFI = struct {

_ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY);

if (this.needsNapiEnv()) {
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", js_context.makeNapiEnvForFFI());
}

CompilerRT.define(state);

const compilation_result = TCC.tcc_compile_string(
Expand Down Expand Up @@ -1800,7 +1822,7 @@ pub const FFI = struct {

if (this.needsHandleScope()) {
try writer.writeAll(
\\ void* handleScope = NapiHandleScope__open(JS_GLOBAL_OBJECT, false);
\\ void* handleScope = NapiHandleScope__open(&Bun__thisFFIModuleNapiEnv, false);
\\
);
}
Expand All @@ -1813,7 +1835,7 @@ pub const FFI = struct {
for (this.arg_types.items, 0..) |arg, i| {
if (arg == .napi_env) {
try writer.print(
\\ napi_env arg{d} = (napi_env)JS_GLOBAL_OBJECT;
\\ napi_env arg{d} = (napi_env)&Bun__thisFFIModuleNapiEnv;
\\ argsPtr++;
\\
,
Expand Down Expand Up @@ -1913,7 +1935,7 @@ pub const FFI = struct {

if (this.needsHandleScope()) {
try writer.writeAll(
\\ NapiHandleScope__close(JS_GLOBAL_OBJECT, handleScope);
\\ NapiHandleScope__close(&Bun__thisFFIModuleNapiEnv, handleScope);
\\
);
}
Expand Down Expand Up @@ -2045,6 +2067,16 @@ pub const FFI = struct {

try writer.writeAll(";\n}\n\n");
}

fn needsNapiEnv(this: *const FFI.Function) bool {
for (this.arg_types.items) |arg| {
if (arg == .napi_env or arg == .napi_value) {
return true;
}
}

return false;
}
};

// Must be kept in sync with JSFFIFunction.h version
Expand Down Expand Up @@ -2229,7 +2261,7 @@ pub const FFI = struct {
try writer.writeAll("JSVALUE_TO_FLOAT(");
},
.napi_env => {
try writer.writeAll("(napi_env)JS_GLOBAL_OBJECT");
try writer.writeAll("((napi_env)&Bun__thisFFIModuleNapiEnv)");
return;
},
.napi_value => {
Expand Down Expand Up @@ -2290,7 +2322,7 @@ pub const FFI = struct {
try writer.print("FLOAT_TO_JSVALUE({s})", .{self.symbol});
},
.napi_env => {
try writer.writeAll("JS_GLOBAL_OBJECT");
try writer.writeAll("((napi_env)&Bun__thisFFIModuleNapiEnv)");
},
.napi_value => {
try writer.print("((EncodedJSValue) {{.asNapiValue = {s} }} )", .{self.symbol});
Expand Down Expand Up @@ -2520,3 +2552,13 @@ const CompilerRT = struct {
};

pub const Bun__FFI__cc = FFI.Bun__FFI__cc;

fn makeNapiEnvIfNeeded(functions: []const FFI.Function, globalThis: *JSGlobalObject) ?*napi.NapiEnv {
for (functions) |function| {
if (function.needsNapiEnv()) {
return globalThis.makeNapiEnvForFFI();
}
}

return null;
}
115 changes: 109 additions & 6 deletions src/bun.js/bindings/BunProcess.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "napi.h"

#include "BunProcess.h"
#include <JavaScriptCore/InternalFieldTuple.h>
#include <JavaScriptCore/JSMicrotask.h>
Expand All @@ -13,7 +15,6 @@
#include "JavaScriptCore/PutPropertySlot.h"
#include "ScriptExecutionContext.h"
#include "headers-handwritten.h"
#include "node_api.h"
#include "ZigGlobalObject.h"
#include "headers.h"
#include "JSEnvironmentVariableMap.h"
Expand All @@ -26,6 +27,7 @@
#include <JavaScriptCore/LazyPropertyInlines.h>
#include <JavaScriptCore/VMTrapsInlines.h>
#include "wtf-bindings.h"
#include "EventLoopTask.h"

#include "ProcessBindingTTYWrap.h"
#include "wtf/text/ASCIILiteral.h"
Expand Down Expand Up @@ -82,6 +84,8 @@ typedef int mode_t;
#include <unistd.h> // setuid, getuid
#endif

#include <cstring>

namespace Bun {

using namespace JSC;
Expand Down Expand Up @@ -274,6 +278,69 @@ extern "C" bool Bun__resolveEmbeddedNodeFile(void*, BunString*);
extern "C" HMODULE Bun__LoadLibraryBunString(BunString*);
#endif

/// Returns a pointer that needs to be freed with `delete[]`.
static char* toFileURI(std::string_view path)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this return the right string on Windows?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not. It expects ASCII.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UTF8, actually, which it's receiving from the WTF::String. And the NAPI docs don't specify anything about a required encoding. The const char ** type suggests to me that it's not expecting the output to be UTF16/UCS2.

{
auto needs_escape = [](char ch) {
return !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('0' <= ch && ch <= '9')
|| ch == '_' || ch == '-' || ch == '.' || ch == '!' || ch == '~' || ch == '*' || ch == '\'' || ch == '(' || ch == ')' || ch == '/' || ch == ':');
};

auto to_hex = [](uint8_t nybble) -> char {
if (nybble < 0xa) {
return '0' + nybble;
}

return 'a' + (nybble - 0xa);
};

size_t escape_count = 0;
for (char ch : path) {
#if OS(WINDOWS)
if (needs_escape(ch) && ch != '\\') {
#else
if (needs_escape(ch)) {
#endif
++escape_count;
}
}

#if OS(WINDOWS)
#define FILE_URI_START "file:///"
#else
#define FILE_URI_START "file://"
#endif

const size_t string_size = sizeof(FILE_URI_START) + path.size() + 2 * escape_count; // null byte is included in the sizeof expression
char* characters = new char[string_size];
strncpy(characters, FILE_URI_START, sizeof(FILE_URI_START));
size_t i = sizeof(FILE_URI_START) - 1;
for (char ch : path) {
#if OS(WINDOWS)
if (ch == '\\') {
characters[i++] = '/';
continue;
}
#endif
if (needs_escape(ch)) {
characters[i++] = '%';
characters[i++] = to_hex(static_cast<uint8_t>(ch) >> 4);
characters[i++] = to_hex(ch & 0xf);
} else {
characters[i++] = ch;
}
}

characters[i] = '\0';
ASSERT(i + 1 == string_size);
return characters;
}

static char* toFileURI(std::span<const uint8_t> span)
{
return toFileURI(std::string_view(reinterpret_cast<const char*>(span.data()), span.size()));
}

extern "C" size_t Bun__process_dlopen_count;

JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
Expand Down Expand Up @@ -390,18 +457,17 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
return JSValue::encode(jsUndefined());
}

JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject* globalObject,
JSC::EncodedJSValue exports);
#if OS(WINDOWS)
#define dlsym GetProcAddress
#endif

// TODO(@190n) look for node_register_module_vXYZ according to BuildOptions.reported_nodejs_version
// (bun/src/env.zig:36) and the table at https://github.com/nodejs/node/blob/main/doc/abi_version_registry.json
napi_register_module_v1 = reinterpret_cast<JSC::EncodedJSValue (*)(JSC::JSGlobalObject*,
JSC::EncodedJSValue)>(
auto napi_register_module_v1 = reinterpret_cast<napi_value (*)(napi_env, napi_value)>(
dlsym(handle, "napi_register_module_v1"));

auto node_api_module_get_api_version_v1 = reinterpret_cast<int32_t (*)()>(dlsym(handle, "node_api_module_get_api_version_v1"));

#if OS(WINDOWS)
#undef dlsym
#endif
Expand All @@ -412,14 +478,39 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
#else
dlclose(handle);
#endif

JSC::throwTypeError(globalObject, scope, "symbol 'napi_register_module_v1' not found in native module. Is this a Node API (napi) module?"_s);
return {};
}

// TODO(@heimskr): get the API version without node_api_module_get_api_version_v1 a different way
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the other way? curious

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in some cases there's a global variable of type napi_module that's pre-initialized.

int module_version = 8;
if (node_api_module_get_api_version_v1) {
module_version = node_api_module_get_api_version_v1();
}

NapiHandleScope handleScope(globalObject);

EncodedJSValue exportsValue = JSC::JSValue::encode(exports);
JSC::JSValue resultValue = JSValue::decode(napi_register_module_v1(globalObject, exportsValue));

char* filename_cstr = toFileURI(filename.utf8().span());

napi_module nmodule {
.nm_version = module_version,
.nm_flags = 0,
.nm_filename = filename_cstr,
.nm_register_func = nullptr,
.nm_modname = "[no modname]",
.nm_priv = nullptr,
.reserved = {},
};

static_assert(sizeof(napi_value) == sizeof(EncodedJSValue), "EncodedJSValue must be reinterpretable as a pointer");

auto env = globalObject->makeNapiEnv(nmodule);
env->filename = filename_cstr;

JSC::JSValue resultValue = JSValue::decode(reinterpret_cast<EncodedJSValue>(napi_register_module_v1(env, reinterpret_cast<napi_value>(exportsValue))));

RETURN_IF_EXCEPTION(scope, {});

Expand Down Expand Up @@ -2701,6 +2792,18 @@ extern "C" void Bun__Process__queueNextTick1(GlobalObject* globalObject, Encoded
}
JSC_DECLARE_HOST_FUNCTION(Bun__Process__queueNextTick1);

extern "C" bool Bun__queueFinishNapiFinalizers(JSGlobalObject* jsGlobalObject)
{
Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(jsGlobalObject);
if (globalObject->hasNapiFinalizers()) {
globalObject->queueTask(new EventLoopTask([](ScriptExecutionContext&) {
// TODO(@heimskr): do something more sensible
}));
Comment on lines +2806 to +2808
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment why this is needed too

return true;
}
return false;
}

JSValue Process::constructNextTickFn(JSC::VM& vm, Zig::GlobalObject* globalObject)
{
JSValue nextTickQueueObject;
Expand Down
Loading