Skip to content

Commit 98888ae

Browse files
committed
chore: clean up module loading
1 parent b8893c9 commit 98888ae

17 files changed

Lines changed: 517 additions & 199 deletions

File tree

native/v8-runtime/src/execution.rs

Lines changed: 88 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -930,9 +930,7 @@ pub fn execute_module(
930930
prefetch_module_imports(tc, bridge_ctx, module, resource_name_str);
931931

932932
// Instantiate (calls resolve callback for each import — mostly cache hits now)
933-
eprintln!("[v8-runtime] instantiating module...");
934933
let inst_result = module.instantiate_module(tc, module_resolve_callback);
935-
eprintln!("[v8-runtime] instantiate result: {:?}", inst_result.is_some());
936934
if inst_result.is_none()
937935
{
938936
clear_module_state();
@@ -946,10 +944,7 @@ pub fn execute_module(
946944
}
947945

948946
// Evaluate
949-
let eval_start = std::time::Instant::now();
950-
eprintln!("[v8-runtime] evaluating module (resource={})...", resource_name_str);
951947
let eval_result = module.evaluate(tc);
952-
eprintln!("[v8-runtime] evaluate done: result={} elapsed={}ms", eval_result.is_some(), eval_start.elapsed().as_millis());
953948
if eval_result.is_none() {
954949
clear_module_state();
955950
return match tc.exception() {
@@ -1028,7 +1023,8 @@ pub fn execute_module(
10281023
}
10291024
};
10301025

1031-
clear_module_state();
1026+
// Keep module resolve state available after the initial module finishes.
1027+
// Dynamic imports can still fire later on the same session event loop.
10321028
(0, Some(exports_bytes), None)
10331029
}
10341030
}
@@ -1122,11 +1118,7 @@ fn prefetch_module_imports(
11221118

11231119
// Detect CJS and wrap in ESM shim if needed
11241120
let is_cjs = is_likely_cjs(source_code);
1125-
if resolved_path.contains("balanced") {
1126-
eprintln!("[v8-runtime] balanced-match check: is_cjs={} source_len={} first_100={}", is_cjs, source_code.len(), &source_code[..std::cmp::min(source_code.len(), 100)]);
1127-
}
11281121
let effective_source = if is_cjs {
1129-
eprintln!("[v8-runtime] CJS→ESM shim (prefetch): {}", resolved_path);
11301122
let exports = extract_cjs_export_names(source_code);
11311123
let mut shim = format!(
11321124
"const _cjsModule = globalThis._requireFrom(\"{}\", \"/\");\nexport default _cjsModule;\n",
@@ -1219,9 +1211,9 @@ fn resolve_or_compile_module<'s>(
12191211
// Phase 2: Get bridge context.
12201212
let bridge_ctx_ptr = MODULE_RESOLVE_STATE.with(|cell| {
12211213
let borrow = cell.borrow();
1222-
let state = borrow.as_ref().expect("module resolve state not set");
1223-
state.bridge_ctx
1214+
borrow.as_ref().map(|state| state.bridge_ctx)
12241215
});
1216+
let bridge_ctx_ptr = bridge_ctx_ptr?;
12251217
let ctx = unsafe { &*bridge_ctx_ptr };
12261218

12271219
// Phase 3: Resolve module path.
@@ -1238,16 +1230,13 @@ fn resolve_or_compile_module<'s>(
12381230
}
12391231

12401232
// Phase 5: Load and compile the module source.
1241-
eprintln!("[v8-runtime] loading module: {} (specifier={} referrer={})", &resolved_path, specifier_str, referrer_name);
12421233
let raw_source = load_module_via_ipc(scope, ctx, &resolved_path)?;
1243-
eprintln!("[v8-runtime] loaded {} bytes, is_cjs={}", raw_source.len(), is_likely_cjs(&raw_source));
12441234

12451235
// Phase 5b: Detect CJS modules and wrap in ESM shim.
12461236
// CJS modules use module.exports/exports which isn't valid ESM syntax.
12471237
// Real Node.js wraps CJS in a default+named export shim. We do the same
12481238
// by generating: `const _m = globalThis._requireFrom(path, "/"); export default _m; export const {named1, named2, ...} = _m;`
12491239
let source_code = if is_likely_cjs(&raw_source) {
1250-
eprintln!("[v8-runtime] CJS→ESM shim for: {}", &resolved_path);
12511240
// Extract potential named exports by scanning for common patterns
12521241
let exports = extract_cjs_export_names(&raw_source);
12531242
let mut shim = format!(
@@ -1291,8 +1280,6 @@ fn resolve_or_compile_module<'s>(
12911280
};
12921281
let mut compiled = v8::script_compiler::Source::new(v8_source, Some(&origin));
12931282
let module = v8::script_compiler::compile_module(scope, &mut compiled)?;
1294-
eprintln!("[v8-runtime] compiled OK: {}", &resolved_path);
1295-
12961283
MODULE_RESOLVE_STATE.with(|cell| {
12971284
if let Some(state) = cell.borrow_mut().as_mut() {
12981285
state
@@ -1548,13 +1535,10 @@ fn module_resolve_callback<'a>(
15481535

15491536
let referrer_name = MODULE_RESOLVE_STATE.with(|cell| {
15501537
let borrow = cell.borrow();
1551-
let state = borrow.as_ref().expect("module resolve state not set");
1552-
state
1553-
.module_names
1554-
.get(&referrer_hash)
1555-
.cloned()
1556-
.unwrap_or_default()
1538+
let state = borrow.as_ref()?;
1539+
state.module_names.get(&referrer_hash).cloned()
15571540
});
1541+
let referrer_name = referrer_name?;
15581542
resolve_or_compile_module(scope, &specifier_str, &referrer_name)
15591543
}
15601544

@@ -1627,7 +1611,6 @@ fn load_module_via_ipc(
16271611
};
16281612

16291613
let ipc_result = ctx.sync_call("_loadFile", args);
1630-
eprintln!("[v8-runtime] _loadFile result: is_ok={} has_bytes={}", ipc_result.is_ok(), ipc_result.as_ref().ok().map(|r| r.is_some()).unwrap_or(false));
16311614
match ipc_result {
16321615
Ok(Some(bytes)) => match deserialize_v8_value(scope, &bytes) {
16331616
Ok(val) => {
@@ -5021,6 +5004,87 @@ mod tests {
50215004
);
50225005
}
50235006

5007+
// Part 69: Dynamic import works after execute_module returns
5008+
{
5009+
let mut iso = isolate::create_isolate(None);
5010+
iso.set_host_import_module_dynamically_callback(dynamic_import_callback);
5011+
iso.set_host_initialize_import_meta_object_callback(import_meta_object_callback);
5012+
let ctx = isolate::create_context(&mut iso);
5013+
5014+
let mut response_buf = Vec::new();
5015+
5016+
let resolve_result = v8_serialize_str(&mut iso, &ctx, "/dep.mjs");
5017+
crate::ipc_binary::write_frame(
5018+
&mut response_buf,
5019+
&crate::ipc_binary::BinaryFrame::BridgeResponse {
5020+
session_id: String::new(),
5021+
call_id: 1,
5022+
status: 0,
5023+
payload: resolve_result,
5024+
},
5025+
)
5026+
.unwrap();
5027+
5028+
let load_result = v8_serialize_str(&mut iso, &ctx, "export const value = 42;");
5029+
crate::ipc_binary::write_frame(
5030+
&mut response_buf,
5031+
&crate::ipc_binary::BinaryFrame::BridgeResponse {
5032+
session_id: String::new(),
5033+
call_id: 2,
5034+
status: 0,
5035+
payload: load_result,
5036+
},
5037+
)
5038+
.unwrap();
5039+
5040+
let bridge_ctx = BridgeCallContext::new(
5041+
Box::new(Vec::new()),
5042+
Box::new(Cursor::new(response_buf)),
5043+
"test-session".into(),
5044+
);
5045+
5046+
let user_code = r#"
5047+
globalThis.loadDep = async () => (await import("./dep.mjs")).value;
5048+
export const ready = true;
5049+
"#;
5050+
let (code, exports, error) = {
5051+
let scope = &mut v8::HandleScope::new(&mut iso);
5052+
let local = v8::Local::new(scope, &ctx);
5053+
let scope = &mut v8::ContextScope::new(scope, local);
5054+
execute_module(
5055+
scope,
5056+
&bridge_ctx,
5057+
"",
5058+
user_code,
5059+
Some("/app/main.mjs"),
5060+
&mut None,
5061+
)
5062+
};
5063+
5064+
assert_eq!(code, 0, "error: {:?}", error);
5065+
assert!(error.is_none());
5066+
assert!(exports.is_some());
5067+
5068+
{
5069+
let scope = &mut v8::HandleScope::new(&mut iso);
5070+
let local = v8::Local::new(scope, &ctx);
5071+
let scope = &mut v8::ContextScope::new(scope, local);
5072+
let tc = &mut v8::TryCatch::new(scope);
5073+
let source = v8::String::new(
5074+
tc,
5075+
"globalThis.__depPromise = globalThis.loadDep().then((value) => { globalThis.__depValue = value; return value; });",
5076+
)
5077+
.unwrap();
5078+
let script = v8::Script::compile(tc, source, None).unwrap();
5079+
assert!(script.run(tc).is_some());
5080+
tc.perform_microtask_checkpoint();
5081+
assert!(tc.exception().is_none());
5082+
}
5083+
5084+
assert_eq!(eval(&mut iso, &ctx, "String(globalThis.__depValue)"), "42");
5085+
clear_module_state();
5086+
}
5087+
50245088
// --- Part 57: serialize_v8_value_into reuses buffer capacity ---
50255089
{
50265090
let mut iso = isolate::create_isolate(None);

native/v8-runtime/src/host_call.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ impl BridgeCallContext {
204204
/// blocks on read() for the BridgeResponse, and returns the result.
205205
/// Error responses from the host are returned as Err.
206206
pub fn sync_call(&self, method: &str, args: Vec<u8>) -> Result<Option<Vec<u8>>, String> {
207-
eprintln!("[v8-ipc] sync_call: {} session={}", method, self.session_id);
208207
let call_id = self.next_call_id.fetch_add(1, Ordering::Relaxed);
209208

210209
// Register call_id in pending set (reject duplicates)

native/v8-runtime/src/session.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -556,12 +556,6 @@ fn session_thread(
556556
|| execution::has_pending_script_evaluation()
557557
|| !deferred_queue.lock().unwrap().is_empty();
558558
let event_loop_status = if should_enter_event_loop {
559-
eprintln!("[v8-runtime] entering event loop: pending={} module_eval={} script_eval={} deferred={} esm={}",
560-
pending.len(),
561-
execution::has_pending_module_evaluation(),
562-
execution::has_pending_script_evaluation(),
563-
!deferred_queue.lock().unwrap().is_empty(),
564-
mode != 0);
565559
let scope = &mut v8::HandleScope::new(iso);
566560
let ctx = v8::Local::new(scope, &exec_context);
567561
let scope = &mut v8::ContextScope::new(scope, ctx);
@@ -601,10 +595,6 @@ fn session_thread(
601595
// the session alive while handles (timers, child processes,
602596
// stdin listeners) are active. This creates a pending promise
603597
// that the event loop pumps until all handles resolve.
604-
eprintln!("[v8-runtime] post-eval: terminated={} mode={} error={} code={}", terminated, mode, error.is_some(), code);
605-
if let Some(ref err) = error {
606-
eprintln!("[v8-runtime] error: type={} message={}", err.error_type, &err.message[..std::cmp::min(err.message.len(), 200)]);
607-
}
608598
if !terminated && mode != 0 && error.is_none() {
609599
// Phase 1: call _waitForActiveHandles() to register a pending promise
610600
{
@@ -620,7 +610,6 @@ fn session_thread(
620610
if let Some(result) = func.call(scope, recv, &[]) {
621611
if result.is_promise() {
622612
let promise = v8::Local::<v8::Promise>::try_from(result).unwrap();
623-
eprintln!("[v8-runtime] _waitForActiveHandles promise state: {:?}", promise.state());
624613
if promise.state() == v8::PromiseState::Pending {
625614
execution::set_pending_script_evaluation(scope, promise);
626615
}
@@ -635,7 +624,6 @@ fn session_thread(
635624
|| execution::has_pending_script_evaluation()
636625
|| !deferred_queue.lock().unwrap().is_empty()
637626
{
638-
eprintln!("[v8-runtime] pumping event loop for ESM active handles");
639627
let scope = &mut v8::HandleScope::new(iso);
640628
let ctx = v8::Local::new(scope, &exec_context);
641629
let scope = &mut v8::ContextScope::new(scope, ctx);
@@ -752,14 +740,17 @@ fn session_thread(
752740
///
753741
/// Sync functions block V8 while the host processes the call (applySync/applySyncPromise).
754742
/// Async functions return a Promise to V8, resolved when the host responds (apply).
755-
pub(crate) const SYNC_BRIDGE_FNS: [&str; 32] = [
743+
pub(crate) const SYNC_BRIDGE_FNS: [&str; 34] = [
756744
// Console
757745
"_log",
758746
"_error",
759747
// Module loading (syncPromise — host resolves async, Rust blocks)
760748
"_resolveModule",
761749
"_loadFile",
762750
"_loadPolyfill",
751+
// Sync module loading (bypass _loadPolyfill dispatch, used by CJS require)
752+
"_resolveModuleSync",
753+
"_loadFileSync",
763754
// Crypto
764755
"_cryptoRandomFill",
765756
"_cryptoRandomUUID",

packages/core/isolate-runtime/src/inject/require-setup.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4184,7 +4184,6 @@
41844184
_debugRequire('start', moduleName, fromDir);
41854185
// Strip node: prefix
41864186
const name = moduleName.replace(/^node:/, '');
4187-
41884187
// For absolute paths (resolved paths), use as cache key
41894188
// For relative/bare imports, resolve first
41904189
let cacheKey = name;

0 commit comments

Comments
 (0)