mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: TLA 模块求值 emit 侧编译为合成异步函数
This commit is contained in:
Generated
+21
-21
@@ -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",
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.18.4"
|
||||
version = "0.18.5"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<u32> = 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<String>,
|
||||
) -> Result<(IRFunction, bool, HashSet<String>, HashSet<String>), String> {
|
||||
) -> Result<(IRFunction, bool, HashSet<String>, HashSet<String>, 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,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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::<crate::vm::Vm> as *const (), 2),
|
||||
("__moduleWaitAll", module_wait_all as *const (), 0),
|
||||
("eval", oxide_builtins::eval::eval::<crate::vm::Vm> as *const (), 1),
|
||||
(
|
||||
"structuredClone",
|
||||
|
||||
@@ -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) 实参调用。
|
||||
|
||||
Reference in New Issue
Block a user