69
69
#include < sys/types.h>
70
70
#include < pwd.h>
71
71
#include < grp.h>
72
+ #include < mimalloc.h>
72
73
#else
73
74
#include < uv.h>
74
75
#include < io.h>
@@ -361,6 +362,9 @@ static char* toFileURI(std::span<const char> span)
361
362
362
363
extern " C" size_t Bun__process_dlopen_count;
363
364
365
+ // "Fire and forget" wrapper around unlink for c usage that handles EINTR
366
+ extern " C" void Bun__unlink (const char *, size_t );
367
+
364
368
extern " C" void CrashHandler__setDlOpenAction (const char * action);
365
369
extern " C" bool Bun__VM__allowAddons (void * vm);
366
370
@@ -408,11 +412,13 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
408
412
Strong<JSC::JSObject> strongModule = { vm, moduleObject };
409
413
410
414
WTF::String filename = callFrame->uncheckedArgument (1 ).toWTFString (globalObject);
411
- if (filename.isEmpty ()) {
415
+
416
+ if (filename.isEmpty () && !scope.exception ()) {
412
417
JSC::throwTypeError (globalObject, scope, " dlopen requires a non-empty string as the second argument" _s);
413
- return {};
414
418
}
415
419
420
+ RETURN_IF_EXCEPTION (scope, {});
421
+
416
422
if (filename.startsWith (" file://" _s)) {
417
423
WTF::URL fileURL = WTF::URL (filename);
418
424
if (!fileURL.isValid () || !fileURL.protocolIsFile ()) {
@@ -423,29 +429,82 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
423
429
filename = fileURL.fileSystemPath ();
424
430
}
425
431
432
+ CString utf8;
433
+
426
434
// Support embedded .node files
427
435
// See StandaloneModuleGraph.zig for what this "$bunfs" thing is
428
436
#if OS(WINDOWS)
429
437
#define StandaloneModuleGraph__base_path " B:/~BUN/" _s
430
438
#else
431
439
#define StandaloneModuleGraph__base_path " /$bunfs/" _s
432
440
#endif
441
+ bool deleteAfter = false ;
433
442
if (filename.startsWith (StandaloneModuleGraph__base_path)) {
434
443
BunString bunStr = Bun::toString (filename);
435
444
if (Bun__resolveEmbeddedNodeFile (globalObject->bunVM (), &bunStr)) {
436
445
filename = bunStr.toWTFString (BunString::ZeroCopy);
446
+ deleteAfter = !filename.startsWith (" /proc/" _s);
437
447
}
438
448
}
439
449
440
450
RETURN_IF_EXCEPTION (scope, {});
451
+
452
+ // For bun build --compile, we copy the .node file to a temp directory.
453
+ // It's best to delete it as soon as we can.
454
+ // https://github.com/oven-sh/bun/issues/19550
455
+ const auto tryToDeleteIfNecessary = [&]() {
456
+ #if OS(WINDOWS)
457
+ if (deleteAfter) {
458
+ // Only call it once
459
+ deleteAfter = false ;
460
+ if (filename.is8Bit ()) {
461
+ filename.convertTo16Bit ();
462
+ }
463
+
464
+ // Convert to 16-bit with a sentinel zero value.
465
+ auto span = filename.span16 ();
466
+ auto dupeZ = new wchar_t [span.size () + 1 ];
467
+ if (dupeZ) {
468
+ memcpy (dupeZ, span.data (), span.size_bytes ());
469
+ dupeZ[span.size ()] = L' \0 ' ;
470
+
471
+ // We can't immediately delete the file on Windows.
472
+ // Instead, we mark it for deletion on reboot.
473
+ MoveFileExW (
474
+ dupeZ,
475
+ NULL , // NULL destination means delete
476
+ MOVEFILE_DELAY_UNTIL_REBOOT);
477
+ delete[] dupeZ;
478
+ }
479
+ }
480
+ #else
481
+ if (deleteAfter) {
482
+ deleteAfter = false ;
483
+ Bun__unlink (utf8.data (), utf8.length ());
484
+ }
485
+ #endif
486
+ };
487
+
488
+ {
489
+ auto utf8_filename = filename.tryGetUTF8 (ConversionMode::LenientConversion);
490
+ if (UNLIKELY (!utf8_filename)) {
491
+ JSC::throwTypeError (globalObject, scope, " process.dlopen requires a valid UTF-8 string for the filename" _s);
492
+ return {};
493
+ }
494
+ utf8 = *utf8_filename;
495
+ }
496
+
441
497
#if OS(WINDOWS)
442
498
BunString filename_str = Bun::toString (filename);
443
499
HMODULE handle = Bun__LoadLibraryBunString (&filename_str);
500
+
501
+ // On Windows, we use GetLastError() for error messages, so we can only delete after checking for errors
444
502
#else
445
- CString utf8 = filename.utf8 ();
446
503
CrashHandler__setDlOpenAction (utf8.data ());
447
504
void * handle = dlopen (utf8.data (), RTLD_LAZY);
448
505
CrashHandler__setDlOpenAction (nullptr );
506
+
507
+ tryToDeleteIfNecessary ();
449
508
#endif
450
509
451
510
globalObject->m_pendingNapiModuleDlopenHandle = handle;
@@ -482,12 +541,19 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
482
541
WTF::String msg = errorBuilder.toString ();
483
542
if (messageBuffer)
484
543
LocalFree (messageBuffer); // Free the buffer allocated by FormatMessageW
544
+
545
+ // Since we're relying on LastError(), we have to delete after checking for errors
546
+ tryToDeleteIfNecessary ();
485
547
#else
486
548
WTF::String msg = WTF::String::fromUTF8 (dlerror ());
487
549
#endif
488
550
return throwError (globalObject, scope, ErrorCode::ERR_DLOPEN_FAILED, msg);
489
551
}
490
552
553
+ #if OS(WINDOWS)
554
+ tryToDeleteIfNecessary ();
555
+ #endif
556
+
491
557
if (callCountAtStart != globalObject->napiModuleRegisterCallCount ) {
492
558
JSValue resultValue = globalObject->m_pendingNapiModuleAndExports [0 ].get ();
493
559
globalObject->napiModuleRegisterCallCount = 0 ;
@@ -528,7 +594,9 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
528
594
dlclose (handle);
529
595
#endif
530
596
531
- JSC::throwTypeError (globalObject, scope, " symbol 'napi_register_module_v1' not found in native module. Is this a Node API (napi) module?" _s);
597
+ if (LIKELY (!scope.exception ())) {
598
+ JSC::throwTypeError (globalObject, scope, " symbol 'napi_register_module_v1' not found in native module. Is this a Node API (napi) module?" _s);
599
+ }
532
600
return {};
533
601
}
534
602
@@ -542,7 +610,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
542
610
543
611
EncodedJSValue exportsValue = JSC::JSValue::encode (exports);
544
612
545
- char * filename_cstr = toFileURI (filename. utf8 () .span ());
613
+ char * filename_cstr = toFileURI (utf8.span ());
546
614
547
615
napi_module nmodule {
548
616
.nm_version = module_version,
0 commit comments