Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/bun_core/env_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ pub mod feature_flag {
new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG, "BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG", {});
new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER, "BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER", {});
new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_ISOLATION_SOURCE_CACHE, "BUN_FEATURE_FLAG_DISABLE_ISOLATION_SOURCE_CACHE", {});
new_feature_flag!(pub BUN_FEATURE_FLAG_EXPERIMENTAL_ISOLATION_GLOBAL_REUSE, "BUN_FEATURE_FLAG_EXPERIMENTAL_ISOLATION_GLOBAL_REUSE", {});
new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_DNS_CACHE, "BUN_FEATURE_FLAG_DISABLE_DNS_CACHE", {});
new_feature_flag!(pub BUN_FEATURE_FLAG_DISABLE_DNS_CACHE_LIBINFO, "BUN_FEATURE_FLAG_DISABLE_DNS_CACHE_LIBINFO", {});
// Force the event loop to use epoll_pwait(2) instead of epoll_pwait2(2).
Expand Down
6 changes: 6 additions & 0 deletions src/js/internal-for-testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,12 @@ export const dnsCacheSeed = $newRustFunction("runtime/dns_jsc/dns.rs", "internal
addresses: string[],
) => number[];

export const testIsolationResetStats = $newCppFunction(
"InternalForTesting.cpp",
"jsFunction_testIsolationResetStats",
0,
) as () => { reuse: number; swap: number };

export const fetchH2Internals = {
liveCounts: $newRustFunction("http/H2Client.rs", "TestingAPIs.liveCounts", 0) as () => {
sessions: number;
Expand Down
10 changes: 10 additions & 0 deletions src/jsc/JSGlobalObject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,14 @@ impl JSGlobalObject {
Zig__GlobalObject__createForTestIsolation(old_global, console)
}

pub(crate) fn capture_test_isolation_baseline(global: &JSGlobalObject) {
Zig__GlobalObject__captureTestIsolationBaseline(global)
}

pub(crate) fn try_reset_for_test_isolation(global: &JSGlobalObject) -> bool {
Zig__GlobalObject__tryResetForTestIsolation(global)
}

pub fn report_uncaught_exception_from_error(&self, proof: JsError) {
crate::mark_binding();
let exc = self
Expand Down Expand Up @@ -1649,6 +1657,8 @@ unsafe extern "C" {
old_global: &JSGlobalObject,
console: *mut c_void,
) -> *mut JSGlobalObject;
safe fn Zig__GlobalObject__captureTestIsolationBaseline(global: &JSGlobalObject);
safe fn Zig__GlobalObject__tryResetForTestIsolation(global: &JSGlobalObject) -> bool;
}

impl ScriptExecutionContextIdentifier {
Expand Down
27 changes: 23 additions & 4 deletions src/jsc/VirtualMachine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ pub struct VirtualMachine {
#[derive(Default)]
pub struct TestIsolationState {
pub saved_cwd: Option<Box<[u8]>>,
/// Cleared on every full swap so the next file re-captures its baseline.
pub baseline_captured: bool,
pub global_reuse: bool,
}

// ──────────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -4454,6 +4457,11 @@ impl VirtualMachine {
}
}

if self.test_isolation_state.global_reuse && !self.test_isolation_state.baseline_captured {
JSGlobalObject::capture_test_isolation_baseline(self.global());
self.test_isolation_state.baseline_captured = true;
}

// Note: reshaped for borrowck.
let global = self.global;
let main_str = bun_core::String::from_bytes(self.main());
Expand Down Expand Up @@ -4690,13 +4698,24 @@ impl VirtualMachine {
self.unhandled_error_counter = 0;

let old_global = self.global;
let old_global_ref = JSGlobalObject::opaque_ref(old_global);

// Scrub and reuse the global if the file left it in its post-preload
// shape; node_modules CodeBlocks and JIT'd code then survive. Preload
// re-evaluates on the reused global, so re-capture the baseline after.
Comment thread
robobun marked this conversation as resolved.
if self.test_isolation_state.global_reuse
&& JSGlobalObject::try_reset_for_test_isolation(old_global_ref)
{
self.test_isolation_state.baseline_captured = false;
return;
}
Comment thread
robobun marked this conversation as resolved.

// `old_global` valid for VM lifetime (safe ZST-handle deref);
// `console` is the live per-VM ConsoleObject.
let new_global: *mut JSGlobalObject = JSGlobalObject::create_for_test_isolation(
JSGlobalObject::opaque_ref(old_global),
self.console.cast(),
);
let new_global: *mut JSGlobalObject =
JSGlobalObject::create_for_test_isolation(old_global_ref, self.console.cast());
self.global = new_global;
self.test_isolation_state.baseline_captured = false;
VMHolder::set_cached_global_object(Some(new_global));
self.regular_event_loop.global = NonNull::new(new_global);
self.macro_event_loop.global = NonNull::new(new_global);
Expand Down
7 changes: 7 additions & 0 deletions src/jsc/bindings/BunClientData.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class GlobalObject;

namespace Bun {
class StrongRootBlock;
struct TestIsolationBaseline;
}

namespace WebCore {
Expand Down Expand Up @@ -141,6 +142,12 @@ class JSVMClientData : public JSC::VM::ClientData {
// after every swap.
WTF::UncheckedKeyHashMap<WTF::String, RefPtr<JSC::SourceProvider>> isolationSourceProviderCache;

// See Zig__GlobalObject__captureTestIsolationBaseline (ZigGlobalObject.cpp).
struct TestIsolationBaselineDeleter {
void operator()(Bun::TestIsolationBaseline*) const;
};
std::unique_ptr<Bun::TestIsolationBaseline, TestIsolationBaselineDeleter> testIsolationBaseline;

private:
bool isWebCoreJSClientData() const final { return true; }

Expand Down
1 change: 1 addition & 0 deletions src/jsc/bindings/BunProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class Process : public WebCore::JSEventEmitter {

JSString* cachedCwd() { return m_cachedCwd.get(); }
void setCachedCwd(JSC::VM& vm, JSString* cwd) { m_cachedCwd.set(vm, this, cwd); }
void clearCachedCwd() { m_cachedCwd.clear(); }

JSValue getArgv(JSGlobalObject* globalObject);
void setArgv(JSGlobalObject* globalObject, JSValue argv);
Expand Down
12 changes: 12 additions & 0 deletions src/jsc/bindings/InternalForTesting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#endif

extern "C" void BunString__toThreadSafe(BunString* str);
extern "C" void Zig__GlobalObject__testIsolationResetStats(Zig::GlobalObject*, uint32_t*, uint32_t*);

namespace Bun {

Expand All @@ -31,6 +32,17 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_lowercaseHeaderNameSIMD, (JSC::JSGlobalObjec
return JSC::JSValue::encode(JSC::jsString(vm, WebCore::lowercaseHeaderName(string)));
}

JSC_DEFINE_HOST_FUNCTION(jsFunction_testIsolationResetStats, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*))
{
auto& vm = globalObject->vm();
uint32_t reuse = 0, swap = 0;
Zig__GlobalObject__testIsolationResetStats(defaultGlobalObject(globalObject), &reuse, &swap);
auto* obj = JSC::constructEmptyObject(globalObject);
obj->putDirect(vm, Identifier::fromString(vm, "reuse"_s), jsNumber(reuse));
obj->putDirect(vm, Identifier::fromString(vm, "swap"_s), jsNumber(swap));
return JSValue::encode(obj);
}

JSC_DEFINE_HOST_FUNCTION(jsFunction_arrayBufferViewHasBuffer, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto value = callFrame->argument(0);
Expand Down
1 change: 1 addition & 0 deletions src/jsc/bindings/InternalForTesting.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ JSC_DECLARE_HOST_FUNCTION(jsFunction_BunString_toThreadSafeRefCountDelta);
JSC_DECLARE_HOST_FUNCTION(jsFunction_lowercaseHeaderNameSIMD);
JSC_DECLARE_HOST_FUNCTION(jsFunction_emitMemoryPressure);
JSC_DECLARE_HOST_FUNCTION(jsFunction_isMemoryPressureWatcherInstalled);
JSC_DECLARE_HOST_FUNCTION(jsFunction_testIsolationResetStats);

}
Loading