diff --git a/Cargo.lock b/Cargo.lock index 09b75a0c..67c1f1a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.18.4" +version = "0.18.5" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.18.4" +version = "0.18.5" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.18.4" +version = "0.18.5" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.18.4" +version = "0.18.5" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1272,7 +1272,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.18.4" +version = "0.18.5" dependencies = [ "num-bigint", "oxc_allocator", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "oxide_fold" -version = "0.18.4" +version = "0.18.5" dependencies = [ "num-bigint", "oxide_bytecode", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.18.4" +version = "0.18.5" dependencies = [ "bumpalo", "dashmap", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.18.4" +version = "0.18.5" dependencies = [ "tracing", "tracing-appender", @@ -1356,11 +1356,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.18.4" +version = "0.18.5" [[package]] name = "oxide_parser" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.18.4" +version = "0.18.5" dependencies = [ "num-bigint", "num-traits", @@ -1404,7 +1404,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.18.4" +version = "0.18.5" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.18.4" +version = "0.18.5" dependencies = [ "bumpalo", "dashmap", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.18.4" +version = "0.18.5" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index bd702447..d06f93d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.18.4" +version = "0.18.5" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_compiler/src/flatten.rs b/crates/oxide_compiler/src/flatten.rs index e9916465..f6e66af1 100644 --- a/crates/oxide_compiler/src/flatten.rs +++ b/crates/oxide_compiler/src/flatten.rs @@ -12,13 +12,40 @@ use oxide_bytecode::module::CompiledModule; use oxide_bytecode::opcode::{self, OpCode}; /// 给整棵子模块树分配扁平 id 并重写 `CREATE_CLOSURE` 操作数。 +/// +/// TLA 模块(`is_async`)额外做 entry rehome:平表下标 0 是 native 哨兵 +/// (`call_bytecode_function_inline` 拒绝 `sub_module_index == 0`),故在 `sub_modules` +/// 头部插一个占位槽(flat_id 0)、entry 落 flat_id 1、其余子模块顺延。 pub fn flatten_submodules(module: &mut CompiledModule) { - let mut next_id = 1u32; - assign_ids(module, &mut next_id); + let rehome = module.is_async; + if rehome { + module.sub_modules.insert(0, Arc::new(CompiledModule::new())); + module.flat_id = 1; + // entry 的 CREATE_CLOSURE 相对下标整体 +1(占位槽占据下标 1)。 + for instr in Arc::make_mut(&mut module.bytecode).iter_mut() { + if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE { + let rel = opcode::imm16(*instr) as usize; + let rd = opcode::rd(*instr); + *instr = opcode::encode( + OpCode::CREATE_CLOSURE, + rd, + ((rel + 1) & 0xFF) as u8, + (((rel + 1) >> 8) & 0xFF) as u8, + ); + } + } + } + let mut next_id = if rehome { 2u32 } else { 1u32 }; + assign_ids(module, &mut next_id, rehome); } -fn assign_ids(module: &mut CompiledModule, next_id: &mut u32) { - for sub in &mut module.sub_modules { +fn assign_ids(module: &mut CompiledModule, next_id: &mut u32, skip_placeholder: bool) { + for (i, sub) in module.sub_modules.iter_mut().enumerate() { + // TLA 模块首个子模块是占位槽(flat_id 0),不参与递增分配。 + if skip_placeholder && i == 0 { + Arc::make_mut(sub).flat_id = 0; + continue; + } // 子模块以 Arc 共享;flatten 在编译末端跑(refcount 1),make_mut 无拷贝。 Arc::make_mut(sub).flat_id = *next_id; *next_id += 1; @@ -37,7 +64,7 @@ fn assign_ids(module: &mut CompiledModule, next_id: &mut u32) { } } for sub in &mut module.sub_modules { - assign_ids(Arc::make_mut(sub), next_id); + assign_ids(Arc::make_mut(sub), next_id, false); } } @@ -69,4 +96,29 @@ mod tests { // mid 的 CREATE_CLOSURE 相对 1 → flat 2 assert_eq!(opcode::imm16(top.sub_modules[0].bytecode[0]), 2); } + + #[test] + fn flatten_rehomes_async_entry_to_index_one() { + // 非 TLA 模块:entry 落 flat_id 0,占位槽不存在。 + let mut sync = CompiledModule::new(); + sync.bytecode = Arc::from(vec![opcode::encode(OpCode::CREATE_CLOSURE, 1, 1, 0)]); + sync.sub_modules = vec![Arc::new(CompiledModule::new())]; + flatten_submodules(&mut sync); + assert_eq!(sync.flat_id, 0); + assert_eq!(sync.sub_modules.len(), 1); + assert_eq!(sync.sub_modules[0].flat_id, 1); + + // TLA 模块:占位槽 0、entry 落 1、子模块顺延 2。 + let mut tla = CompiledModule::new(); + tla.is_async = true; + tla.bytecode = Arc::from(vec![opcode::encode(OpCode::CREATE_CLOSURE, 1, 1, 0)]); + tla.sub_modules = vec![Arc::new(CompiledModule::new())]; + flatten_submodules(&mut tla); + assert_eq!(tla.flat_id, 1); + assert_eq!(tla.sub_modules.len(), 2); + assert_eq!(tla.sub_modules[0].flat_id, 0); // 占位槽 + assert_eq!(tla.sub_modules[1].flat_id, 2); + // entry 的 CREATE_CLOSURE 相对 1 → 占位槽插入后指向 flat 2 + assert_eq!(opcode::imm16(tla.bytecode[0]), 2); + } } diff --git a/crates/oxide_emit/src/module.rs b/crates/oxide_emit/src/module.rs index 63858b6d..0c70db81 100644 --- a/crates/oxide_emit/src/module.rs +++ b/crates/oxide_emit/src/module.rs @@ -23,8 +23,11 @@ use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_ir::{IRFunction, ParamLayout}; use oxide_parser::{ - BindingPattern, Declaration, ExportDefaultDeclarationKind, Expression, ImportAttributeKey, - ImportDeclarationSpecifier, ImportPhase, ModuleExportName, Statement, VariableDeclarationKind, WithClause, + ArrayExpressionElement, AssignmentTarget, AssignmentTargetMaybeDefault, AssignmentTargetProperty, + BindingPattern, ChainElement, Declaration, ExportDefaultDeclarationKind, Expression, + ForStatementInit, ForStatementLeft, ImportAttributeKey, ImportDeclarationSpecifier, ImportPhase, + ModuleExportName, ObjectPropertyKind, SimpleAssignmentTarget, Statement, VariableDeclarationKind, + WithClause, }; use oxide_types::MODULE_NAMESPACE_BINDING; @@ -334,6 +337,301 @@ fn is_hoisted_function_decl(stmt: &Statement) -> bool { } } +/// 判断模块体是否含顶层 `await`(top-level await,模块自身 `[[HasTLA]]` 标志)。 +/// +/// # 边界与前提 +/// - 递归进入语句与表达式容器;不进入函数体(函数声明、函数表达式、箭头函数、 +/// 类方法)——函数体内的 `await` 属函数自身异步语义,不使模块变异步。 +/// - `for await…of` 在模块代码使模块异步(规范强制),其 `await` 关键字与 +/// 左值、右值、体一并判定。 +/// +/// # 注意事项 +/// - 取向宁假阳勿假阴:漏判会使模块按同步编译、运行期 `AWAIT` 守卫硬拒,红色不静默; +/// 误判仅使模块多走异步编译路径,语义不变。 +fn module_has_top_level_await(body: &[Statement]) -> bool { + body.iter().any(|stmt| stmt_has_top_level_await(stmt)) +} + +/// 语句级顶层 `await` 游走:进入子语句与子表达式,函数/类声明体不进入。 +fn stmt_has_top_level_await(stmt: &Statement) -> bool { + match stmt { + Statement::BlockStatement(block) => { + block.body.iter().any(|s| stmt_has_top_level_await(s)) + } + Statement::IfStatement(if_stmt) => { + stmt_has_top_level_await(&if_stmt.consequent) + || if_stmt + .alternate + .as_ref() + .is_some_and(|alt| stmt_has_top_level_await(alt)) + } + Statement::ForStatement(for_stmt) => { + for_stmt.init.as_ref().is_some_and(|init| match init { + ForStatementInit::VariableDeclaration(vd) => vd + .declarations + .iter() + .any(|d| d.init.as_ref().is_some_and(|i| expr_has_top_level_await(i))), + _ => init.as_expression().is_some_and(expr_has_top_level_await), + }) + || for_stmt.test.as_ref().is_some_and(|t| expr_has_top_level_await(t)) + || for_stmt.update.as_ref().is_some_and(|u| expr_has_top_level_await(u)) + || stmt_has_top_level_await(&for_stmt.body) + } + Statement::ForInStatement(for_in) => { + for_left_has_top_level_await(&for_in.left) + || expr_has_top_level_await(&for_in.right) + || stmt_has_top_level_await(&for_in.body) + } + Statement::ForOfStatement(for_of) => { + // `for await…of` 在模块代码使模块按异步求值(规范强制模块异步; + // `await` 关键字本身非 AwaitExpression,但语句整体触发 [[HasTLA]])。 + // 左值、右值与体照常游走。 + for_of.r#await + || for_left_has_top_level_await(&for_of.left) + || expr_has_top_level_await(&for_of.right) + || stmt_has_top_level_await(&for_of.body) + } + Statement::WhileStatement(while_stmt) => { + expr_has_top_level_await(&while_stmt.test) || stmt_has_top_level_await(&while_stmt.body) + } + Statement::DoWhileStatement(do_while) => { + expr_has_top_level_await(&do_while.test) || stmt_has_top_level_await(&do_while.body) + } + Statement::SwitchStatement(switch_stmt) => { + expr_has_top_level_await(&switch_stmt.discriminant) + || switch_stmt.cases.iter().any(|c| { + c.test.as_ref().is_some_and(|t| expr_has_top_level_await(t)) + || c.consequent.iter().any(|s| stmt_has_top_level_await(s)) + }) + } + Statement::TryStatement(try_stmt) => { + try_stmt.block.body.iter().any(|s| stmt_has_top_level_await(s)) + || try_stmt + .handler + .as_ref() + .is_some_and(|h| h.body.body.iter().any(|s| stmt_has_top_level_await(s))) + || try_stmt + .finalizer + .as_ref() + .is_some_and(|f| f.body.iter().any(|s| stmt_has_top_level_await(s))) + } + Statement::LabeledStatement(labeled) => stmt_has_top_level_await(&labeled.body), + Statement::WithStatement(with_stmt) => { + expr_has_top_level_await(&with_stmt.object) || stmt_has_top_level_await(&with_stmt.body) + } + Statement::ExpressionStatement(expr_stmt) => expr_has_top_level_await(&expr_stmt.expression), + Statement::ReturnStatement(ret) => ret + .argument + .as_ref() + .is_some_and(|a| expr_has_top_level_await(a)), + Statement::ThrowStatement(throw) => expr_has_top_level_await(&throw.argument), + Statement::VariableDeclaration(vd) => vd + .declarations + .iter() + .any(|d| d.init.as_ref().is_some_and(|i| expr_has_top_level_await(i))), + // 函数/类声明:体不进入(函数体是独立异步语义边界)。 + Statement::FunctionDeclaration(_) | Statement::ClassDeclaration(_) => false, + // import/export 与无子表达式的语句(break/continue/debugger/empty)不含顶层 await。 + _ => false, + } +} + +/// `for-in`/`for-of` 左值顶层 `await` 游走:变量声明形态游走初始化器, +/// 其余形态(绑定目标)经 `target_has_top_level_await` 游走。 +fn for_left_has_top_level_await(left: &ForStatementLeft) -> bool { + match left { + ForStatementLeft::VariableDeclaration(vd) => vd + .declarations + .iter() + .any(|d| d.init.as_ref().is_some_and(|i| expr_has_top_level_await(i))), + _ => left.as_assignment_target().is_some_and(target_has_top_level_await), + } +} + +/// 赋值目标顶层 `await` 游走:模式目标递归进元素/属性,简单目标(标识符、 +/// 成员、TS 包装)经 `simple_target_has_top_level_await` 游走。 +fn target_has_top_level_await(target: &AssignmentTarget) -> bool { + match target { + AssignmentTarget::ArrayAssignmentTarget(arr) => arr + .elements + .iter() + .flatten() + .any(|e| target_maybe_default_has_top_level_await(e)) + || arr + .rest + .as_ref() + .is_some_and(|r| target_has_top_level_await(&r.target)), + AssignmentTarget::ObjectAssignmentTarget(obj) => { + obj.properties.iter().any(|p| match p { + AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(id) => id + .init + .as_ref() + .is_some_and(|i| expr_has_top_level_await(i)), + AssignmentTargetProperty::AssignmentTargetPropertyProperty(prop) => { + prop.name + .as_expression() + .is_some_and(expr_has_top_level_await) + || target_maybe_default_has_top_level_await(&prop.binding) + } + }) + || obj + .rest + .as_ref() + .is_some_and(|r| target_has_top_level_await(&r.target)) + } + _ => target + .as_simple_assignment_target() + .is_some_and(simple_target_has_top_level_await), + } +} + +/// 简单赋值目标顶层 `await` 游走:计算成员键是可求值子表达式,TS 包装进入内层, +/// 纯标识符无子表达式。 +fn simple_target_has_top_level_await(t: &SimpleAssignmentTarget) -> bool { + match t { + SimpleAssignmentTarget::ComputedMemberExpression(m) => { + expr_has_top_level_await(&m.object) || expr_has_top_level_await(&m.expression) + } + SimpleAssignmentTarget::StaticMemberExpression(m) => expr_has_top_level_await(&m.object), + SimpleAssignmentTarget::PrivateFieldExpression(m) => expr_has_top_level_await(&m.object), + SimpleAssignmentTarget::TSAsExpression(e) => expr_has_top_level_await(&e.expression), + SimpleAssignmentTarget::TSSatisfiesExpression(e) => expr_has_top_level_await(&e.expression), + SimpleAssignmentTarget::TSNonNullExpression(e) => expr_has_top_level_await(&e.expression), + SimpleAssignmentTarget::TSTypeAssertion(e) => expr_has_top_level_await(&e.expression), + SimpleAssignmentTarget::AssignmentTargetIdentifier(_) => false, + } +} + +/// 带默认值的赋值目标顶层 `await` 游走:默认值是可求值子表达式, +/// 绑定目标本身递归进 `target_has_top_level_await`。 +fn target_maybe_default_has_top_level_await(t: &AssignmentTargetMaybeDefault) -> bool { + match t { + AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(d) => { + target_has_top_level_await(&d.binding) || expr_has_top_level_await(&d.init) + } + _ => t.as_assignment_target().is_some_and(target_has_top_level_await), + } +} + +/// 表达式级顶层 `await` 游走:命中 `AwaitExpression` 即真;进入子表达式容器, +/// 函数表达式/箭头/类表达式体不进入。 +fn expr_has_top_level_await(expr: &Expression) -> bool { + match expr { + Expression::AwaitExpression(_) => true, + Expression::BinaryExpression(bin) => { + expr_has_top_level_await(&bin.left) || expr_has_top_level_await(&bin.right) + } + Expression::LogicalExpression(logic) => { + expr_has_top_level_await(&logic.left) || expr_has_top_level_await(&logic.right) + } + Expression::ConditionalExpression(cond) => { + expr_has_top_level_await(&cond.test) + || expr_has_top_level_await(&cond.consequent) + || expr_has_top_level_await(&cond.alternate) + } + Expression::CallExpression(call) => { + expr_has_top_level_await(&call.callee) + || call + .arguments + .iter() + .any(|a| a.as_expression().is_some_and(expr_has_top_level_await)) + } + Expression::NewExpression(new_expr) => { + expr_has_top_level_await(&new_expr.callee) + || new_expr + .arguments + .iter() + .any(|a| a.as_expression().is_some_and(expr_has_top_level_await)) + } + Expression::ComputedMemberExpression(m) => { + expr_has_top_level_await(&m.object) || expr_has_top_level_await(&m.expression) + } + Expression::StaticMemberExpression(m) => expr_has_top_level_await(&m.object), + Expression::PrivateFieldExpression(m) => expr_has_top_level_await(&m.object), + Expression::ChainExpression(chain) => chain_element_has_top_level_await(&chain.expression), + Expression::ArrayExpression(arr) => arr + .elements + .iter() + .any(|e| match e { + ArrayExpressionElement::SpreadElement(spread) => { + expr_has_top_level_await(&spread.argument) + } + _ => e.as_expression().is_some_and(expr_has_top_level_await), + }), + Expression::ObjectExpression(obj) => obj.properties.iter().any(|p| match p { + ObjectPropertyKind::ObjectProperty(prop) => { + (prop.computed + && prop + .key + .as_expression() + .is_some_and(expr_has_top_level_await)) + || expr_has_top_level_await(&prop.value) + } + ObjectPropertyKind::SpreadProperty(spread) => expr_has_top_level_await(&spread.argument), + }), + Expression::SequenceExpression(seq) => { + seq.expressions.iter().any(|e| expr_has_top_level_await(e)) + } + Expression::TemplateLiteral(tmpl) => { + tmpl.expressions.iter().any(|e| expr_has_top_level_await(e)) + } + Expression::TaggedTemplateExpression(tagged) => { + expr_has_top_level_await(&tagged.tag) + || tagged.quasi.expressions.iter().any(|e| expr_has_top_level_await(e)) + } + // 赋值左值是绑定目标(计算成员键可含可求值子表达式),右值与左值目标都游走。 + Expression::AssignmentExpression(assign) => { + target_has_top_level_await(&assign.left) || expr_has_top_level_await(&assign.right) + } + Expression::UpdateExpression(update) => { + simple_target_has_top_level_await(&update.argument) + } + Expression::UnaryExpression(unary) => expr_has_top_level_await(&unary.argument), + Expression::ParenthesizedExpression(paren) => expr_has_top_level_await(&paren.expression), + Expression::YieldExpression(yield_expr) => yield_expr + .argument + .as_ref() + .is_some_and(|a| expr_has_top_level_await(a)), + Expression::PrivateInExpression(private_in) => expr_has_top_level_await(&private_in.right), + Expression::ImportExpression(import_expr) => { + expr_has_top_level_await(&import_expr.source) + || import_expr + .options + .as_ref() + .is_some_and(|o| expr_has_top_level_await(o)) + } + // 函数体:独立异步语义边界,不进入。 + Expression::FunctionExpression(_) + | Expression::ArrowFunctionExpression(_) + | Expression::ClassExpression(_) => false, + // 字面量/标识符/this/super/meta 等无子表达式。 + _ => false, + } +} + +/// 可选链元素顶层 `await` 游走:链元素是成员/调用/TS 非空形态,逐一进入子表达式。 +fn chain_element_has_top_level_await(element: &ChainElement) -> bool { + match element { + ChainElement::CallExpression(call) => { + expr_has_top_level_await(&call.callee) + || call + .arguments + .iter() + .any(|a| a.as_expression().is_some_and(expr_has_top_level_await)) + } + ChainElement::TSNonNullExpression(non_null) => expr_has_top_level_await(&non_null.expression), + ChainElement::ComputedMemberExpression(computed) => { + expr_has_top_level_await(&computed.object) || expr_has_top_level_await(&computed.expression) + } + ChainElement::StaticMemberExpression(static_member) => { + expr_has_top_level_await(&static_member.object) + } + ChainElement::PrivateFieldExpression(private_field) => { + expr_has_top_level_await(&private_field.object) + } + } +} + /// 合成默认导出的源绑定名(匿名 default 函数/类与 default 表达式共用)。 /// 含 `*`,与任何用户标识符都不可能重名。 pub(crate) const SYNTHETIC_DEFAULT_BINDING: &str = "*default*"; @@ -526,6 +824,12 @@ impl Emitter { let own_export_names = module_own_export_names(body); let var_scoped_export_names = module_var_scoped_export_names(body); + // 本模块自身顶层 `await` 标志(`[[HasTLA]]`):决定本模块是否按异步求值编译。 + let own_tla = module_has_top_level_await(body); + // 异步 eager 依赖的 (spec, `__moduleEval` 返回的 promise 寄存器):prelude + // 聚合 gate 消费。defer-only 依赖不求值、不产生 promise,不入此集。 + let mut async_dep_promises: Vec<(String, u32)> = Vec::new(); + // —— 编译并求值依赖:eager 组先行(按 eager 语句序),defer-only 组随后 —— for spec in eager_order { let phase = &phase_map[&spec]; @@ -540,19 +844,21 @@ impl Emitter { return Err(format!("circular module import not supported: {}", resolved.path)); } path_stack.push(resolved.path.clone()); - let (dep_ir, dep_reassignable, dep_own_names, dep_var_scoped) = match resolved.kind { + let (dep_ir, dep_reassignable, dep_own_names, dep_var_scoped, dep_eval_async) = match resolved.kind { ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack)?, ModuleKind::Json => ( self.compile_data_dep("json", &resolved.source)?, false, HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + false, ), ModuleKind::Text => ( self.compile_data_dep("text", &resolved.source)?, false, HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + false, ), ModuleKind::Bytes => return Err("bytes module not supported".into()), }; @@ -562,6 +868,11 @@ impl Emitter { let fn_reg = ctx.alloc_reg(); ctx.inst(Inst::create_closure(Operand::Reg(fn_reg), ctx.nested.len() as u16)); let real_ns = self.emit_module_call(ctx, "__moduleEval", &[fn_reg])?; + // 异步依赖:`__moduleEval` 返回 capability promise(非命名空间), + // 登记进聚合 gate;命名空间在 gate 后从 promise 结算值提取。 + if dep_eval_async { + async_dep_promises.push((spec.clone(), real_ns)); + } (real_ns, Some(fn_reg), dep_own_names, dep_var_scoped) }; ctx.module_dep_ns_regs.insert(spec.clone(), dep_ns_reg); @@ -586,6 +897,11 @@ impl Emitter { } } + // 本模块异步求值标志:自身含顶层 `await`,或任一 eager 依赖按异步求值 + // (传递异步经 `dep_eval_async` 递归回传),模块体按合成异步函数编译 + // (`assemble_ir` 据此回写 IR 的 `is_async`)。非 TLA 模块恒 false,零 diff。 + ctx.is_async = own_tla || !async_dep_promises.is_empty(); + // defer-only 组(eager == false 的 spec,按 defer_order):不求值、不发 // __moduleEval。祖先判定(自导入或父链回边)命中时发哨兵形态 // __moduleDeferObject(undefined, undefined)——不递归、不压 path_stack、不编译; @@ -608,19 +924,21 @@ impl Emitter { ctx.module_dep_paths.insert(spec.clone(), resolved.path.clone()); } else { path_stack.push(resolved.path.clone()); - let (dep_ir, _dep_reassignable, dep_own_names, dep_var_scoped) = match resolved.kind { + let (dep_ir, _dep_reassignable, dep_own_names, dep_var_scoped, _dep_eval_async) = match resolved.kind { ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack)?, ModuleKind::Json => ( self.compile_data_dep("json", &resolved.source)?, false, HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + false, ), ModuleKind::Text => ( self.compile_data_dep("text", &resolved.source)?, false, HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + false, ), ModuleKind::Bytes => return Err("bytes module not supported".into()), }; @@ -637,6 +955,30 @@ impl Emitter { } } + // —— 聚合 gate:异步依赖的 `__moduleEval` 返回 capability promise(非命名空间), + // 其命名空间在 gate 后从结算值提取。单 AWAIT 挂起模块体直至全部异步依赖结算 + // (由 drain 微任务释放),非 TLA 模块此集为空、零 IR 变化。—— + if !async_dep_promises.is_empty() { + let promise_regs: Vec = async_dep_promises.iter().map(|(_, r)| *r).collect(); + let gate_reg = self.emit_module_call(ctx, "__moduleWaitAll", &promise_regs)?; + ctx.inst(Inst::await_expr(Operand::Reg(gate_reg))); + // promise settle 后恢复值(结算值数组)经 reg 0 交付(与 YIELD/CALL 同协议)。 + let settled_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(settled_reg), Operand::None, Operand::None)); + // 逐依赖从结算数组提取命名空间,覆盖链接寄存器(原为 promise)。 + for (i, (spec, _)) in async_dep_promises.iter().enumerate() { + let idx_reg = self.load_string_const(&i.to_string(), ctx); + let ns_reg = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::GET_PROP_DYNAMIC, + Operand::Reg(settled_reg), + Operand::Reg(idx_reg), + Operand::Reg(ns_reg), + )); + ctx.module_dep_ns_regs.insert(spec.clone(), ns_reg); + } + } + // —— 导入绑定初始化(普通依赖为链接期快照;命名空间绑定直接引用 ns 对象; // defer 命名空间绑定引用 deferred 对象;自导入绑定解析为本模块源绑定的活别名)—— // 无命名 `export * from 'x'` 会把依赖的全部导出转发给自身;自导入名可能来自 @@ -951,18 +1293,21 @@ impl Emitter { } /// 编译 JS 依赖模块(递归),回传 IR、「是否含可重赋导出」标志、 - /// 自身静态导出名集与 var 作用域导出名集。 + /// 自身静态导出名集、var 作用域导出名集与「是否异步求值」标志。 /// /// # 边界与前提 /// - 可重赋标志取自依赖 ctx 的 `module_live_dep`;入口模块不参与,依赖模块恒按 /// `!top_level` 判定,故 const-only 依赖回传 false。 /// - 导出名集在函数内对解析产物就地计算,供导入方 defer 预注册消费。 + /// - 异步求值标志取自依赖 ctx 的 `is_async`(依赖自身含顶层 `await` 或含异步 + /// 传递依赖时为真),供导入方判定是否需 prelude 聚合 gate。 /// /// # 注意事项 - /// - 导入方据标志决定是否登记活读映射;标志不改变本模块 IR。 + /// - 导入方据标志决定是否登记活读映射与聚合 gate;标志不改变本模块 IR。 + #[allow(clippy::type_complexity)] fn compile_js_dep( &self, resolved: &ResolvedModule, loader: &mut dyn ModuleSourceLoader, path_stack: &mut Vec, - ) -> Result<(IRFunction, bool, HashSet, HashSet), String> { + ) -> Result<(IRFunction, bool, HashSet, HashSet, bool), String> { // 线程本地分配器:依赖模块递归解析共享同一分配器, // 重入深度守卫防止嵌套 reset 销毁外层 AST。 oxide_parser::with_parse_allocator(|alloc| { @@ -978,6 +1323,7 @@ impl Emitter { ctx.is_strict = true; self.emit_module_into_ctx(&program, &resolved.path, loader, path_stack, &mut ctx, false)?; let reassignable = ctx.module_live_dep; + let eval_async = ctx.is_async; let own_export_names = module_own_export_names(&program.body); let var_scoped = module_var_scoped_export_names(&program.body); Ok(( @@ -985,6 +1331,7 @@ impl Emitter { reassignable, own_export_names, var_scoped, + eval_async, )) }) } diff --git a/crates/oxide_emit/tests/module_link_tests.rs b/crates/oxide_emit/tests/module_link_tests.rs index 08a2dabe..ded357de 100644 --- a/crates/oxide_emit/tests/module_link_tests.rs +++ b/crates/oxide_emit/tests/module_link_tests.rs @@ -473,3 +473,107 @@ fn no_defer_object_for_non_defer_modules() { assert_eq!(native_calls_to(&ir, "__moduleDeferObject"), 0, "非 defer 模块不得构造 deferred 对象"); assert_eq!(native_calls_to(&ir, "__modulePreRegister"), 0, "非 defer 模块不得预注册依赖"); } + +/// 面 19:模块自身顶层 `await` 使 `is_async` 为真,体 await 编译为 AWAIT; +/// 无异步依赖时不发聚合 gate。 +#[test] +fn tla_module_compiles_async_without_gate() { + let (ir, _) = emit_module( + "const x = await Promise.resolve(1); export { x };", + "./entry.js", + &[], + ) + .expect("TLA 模块应编译成功"); + assert!(ir.is_async, "含顶层 await 的模块应标记异步求值"); + assert_eq!( + ir.insts.iter().filter(|i| i.op == OpCode::AWAIT).count(), + 1, + "顶层 await 应编译为一条 AWAIT" + ); + assert_eq!(native_calls_to(&ir, "__moduleWaitAll"), 0, "无异步依赖不得发聚合 gate"); +} + +/// 面 20:无顶层 `await` 的模块保持同步编译(`is_async` 假、主身零 AWAIT); +/// 异步函数体内的 await 不使模块变异步(await 落在嵌套子模块)。 +#[test] +fn non_tla_module_stays_sync() { + let (ir, _) = emit_module( + "async function f() { return await Promise.resolve(1); } export { f };", + "./entry.js", + &[], + ) + .expect("非 TLA 模块应编译成功"); + assert!(!ir.is_async, "无顶层 await 的模块不得标记异步"); + assert_eq!( + ir.insts.iter().filter(|i| i.op == OpCode::AWAIT).count(), + 0, + "主身不得含 AWAIT" + ); +} + +/// 面 21:`for await…of` 在模块代码使模块异步(规范强制模块异步求值, +/// `await` 关键字触发 [[HasTLA]];右值与体照常游走)。 +#[test] +fn for_await_of_makes_module_async() { + let (ir, _) = emit_module( + "for await (const x of iter) { globalThis.v = x; }", + "./entry.js", + &[], + ) + .expect("for await…of 模块应编译成功"); + assert!(ir.is_async, "for await…of 应使模块标记异步"); +} + +/// 面 22:普通 `for…of`(无 `await`)不使模块异步,主身零 AWAIT。 +#[test] +fn for_of_is_not_tla() { + let (ir, _) = emit_module( + "for (const x of iter) { globalThis.v = x; }", + "./entry.js", + &[], + ) + .expect("for…of 模块应编译成功"); + assert!(!ir.is_async, "普通 for…of 不得使模块标记异步"); + assert_eq!( + ir.insts.iter().filter(|i| i.op == OpCode::AWAIT).count(), + 0, + "主身不得含 AWAIT" + ); +} + +/// 面 23:异步 eager 依赖聚合 gate——依赖含顶层 `await` 时 `__moduleEval` 返回 +/// capability promise,导入方发 `__moduleWaitAll` 加单 AWAIT 挂起,模块标记异步。 +#[test] +fn async_dep_emits_wait_all_gate() { + let (ir, _) = emit_module( + "import { x } from \"./dep.js\"; export { x };", + "./entry.js", + &[("./dep.js", ModuleKind::Js, "const v = await Promise.resolve(1); export const x = v;")], + ) + .expect("含异步依赖的模块应编译成功"); + assert!(ir.is_async, "含异步依赖的导入方应标记异步求值"); + assert_eq!(native_calls_to(&ir, "__moduleWaitAll"), 1, "聚合 gate 应发一次"); + assert_eq!( + ir.insts.iter().filter(|i| i.op == OpCode::AWAIT).count(), + 1, + "gate 应以单 AWAIT 挂起" + ); +} + +/// 面 24:同步依赖零 diff 守卫——无 gate、无 AWAIT、模块保持同步。 +#[test] +fn sync_dep_no_gate() { + let (ir, _) = emit_module( + "import { x } from \"./dep.js\"; export { x };", + "./entry.js", + &[("./dep.js", ModuleKind::Js, "export const x = 1;")], + ) + .expect("含同步依赖的模块应编译成功"); + assert!(!ir.is_async, "无顶层 await 且依赖同步的模块不得标记异步"); + assert_eq!(native_calls_to(&ir, "__moduleWaitAll"), 0, "同步依赖不得发聚合 gate"); + assert_eq!( + ir.insts.iter().filter(|i| i.op == OpCode::AWAIT).count(), + 0, + "主身不得含 AWAIT" + ); +} diff --git a/crates/oxide_vm/src/bindings/bind_global.rs b/crates/oxide_vm/src/bindings/bind_global.rs index 9092a279..20c3fd53 100644 --- a/crates/oxide_vm/src/bindings/bind_global.rs +++ b/crates/oxide_vm/src/bindings/bind_global.rs @@ -1,6 +1,13 @@ +use oxide_runtime_api::NativeResult; use oxide_types::object::JsObject; use crate::bindings::{apply_binding_table, BindEnv}; +use crate::vm::Vm; + +/// `__moduleWaitAll` 原生入口:委托 `Vm::module_wait_all`(聚合 gate,见 promise 模块)。 +fn module_wait_all(vm: &mut Vm, args: &[u8]) -> NativeResult { + vm.module_wait_all(args) +} /// 把普通全局函数(`isNaN`、`parseInt`、`decodeURIComponent` 等)绑定到 global。 pub fn bind_global(env: BindEnv<'_>, global: &mut JsObject) { @@ -56,6 +63,7 @@ pub fn bind_global(env: BindEnv<'_>, global: &mut JsObject) { 2, ), ("__moduleData", oxide_builtins::module::module_data:: as *const (), 2), + ("__moduleWaitAll", module_wait_all as *const (), 0), ("eval", oxide_builtins::eval::eval:: as *const (), 1), ( "structuredClone", diff --git a/crates/oxide_vm/src/promise/aggregate.rs b/crates/oxide_vm/src/promise/aggregate.rs index 638b5dd2..e0ed62cb 100644 --- a/crates/oxide_vm/src/promise/aggregate.rs +++ b/crates/oxide_vm/src/promise/aggregate.rs @@ -163,6 +163,36 @@ impl Vm { JsValue::from_js_object(ptr) } + /// `__moduleWaitAll(p1, p2, ...)`:模块求值聚合 gate。等待全部实参 promise + /// 结算后以结算值数组完成新 promise,任一实参拒绝则拒绝。 + /// + /// # 步骤 + /// 1. 按实参序收集各 promise 值(`args[0]` 为实参个数,`args[1..]` 为寄存器下标)。 + /// 2. 以实参 promise 建数组,经 `Promise.all` 共享聚合路径(`perform_promise_combine`) + /// 结算:全部完成时以结算值数组完成,任一拒绝时拒绝。 + /// + /// # 边界与前提 + /// - 实参须为 promise(模块依赖的 capability promise);非 promise 由 + /// `Promise.all` 的 `C.resolve` 包装为已完成 promise,语义不变。 + /// + /// # 副作用 + /// - 新建聚合 promise 与记录对象;结算反应入微任务队列。 + pub fn module_wait_all(&mut self, args: &[u8]) -> NativeResult { + let n = args.len().saturating_sub(1); + let mut promises = Vec::with_capacity(n); + for i in 0..n { + promises.push(self.reg(args[i + 1])); + } + let array = self.make_plain_array(promises); + let ctor = JsValue::from_js_object( + self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject, + ); + match perform_promise_combine(self, ctor, array, AggregateKind::All) { + Ok(promise) => NativeResult::Ok(promise), + Err(err) => NativeResult::Err(err), + } + } + /// 建 Promise.try 的包装闭包 W:native 函数对象,executor 与转发实参数组挂 /// 自身属性(GC 边走属性区,免 native_data 接线);W 被构造器以 /// (resolve, reject) 实参调用。