feat: bind/call/apply 异步目标 TailCall 改走异步调用入口

This commit is contained in:
2026-10-09 17:01:41 +00:00
parent 12656cb2dd
commit 0cbe4dc609
4 changed files with 98 additions and 21 deletions
Generated
+19 -19
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_builtins" name = "oxide_builtins"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"chrono", "chrono",
"fast-float", "fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_bytecode" name = "oxide_bytecode"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxide_log", "oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cfg" name = "oxide_cfg"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_emit", "oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cli" name = "oxide_cli"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"bumpalo", "bumpalo",
@@ -1226,7 +1226,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_code_cache" name = "oxide_code_cache"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"lru", "lru",
"oxide_bytecode", "oxide_bytecode",
@@ -1235,7 +1235,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_compiler" name = "oxide_compiler"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1255,7 +1255,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_dce" name = "oxide_dce"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1269,7 +1269,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_emit" name = "oxide_emit"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxc_allocator", "oxc_allocator",
@@ -1284,7 +1284,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_engine" name = "oxide_engine"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1296,7 +1296,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_ir" name = "oxide_ir"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_log", "oxide_log",
@@ -1305,7 +1305,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_kernel" name = "oxide_kernel"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1319,7 +1319,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_liveness" name = "oxide_liveness"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1331,7 +1331,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_log" name = "oxide_log"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"tracing", "tracing",
"tracing-appender", "tracing-appender",
@@ -1340,7 +1340,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_parser" name = "oxide_parser"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_regalloc" name = "oxide_regalloc"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1371,7 +1371,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_runtime_api" name = "oxide_runtime_api"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"num-traits", "num-traits",
@@ -1384,7 +1384,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_test262" name = "oxide_test262"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1402,7 +1402,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_types" name = "oxide_types"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1415,7 +1415,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_vm" name = "oxide_vm"
version = "0.13.11" version = "0.13.12"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"chrono", "chrono",
+1 -1
View File
@@ -23,7 +23,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.13.11" version = "0.13.12"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
rust-version = "1.80" rust-version = "1.80"
+14 -1
View File
@@ -39,7 +39,8 @@ impl Vm {
/// 2. `build_native_args` 打包实参下标表。 /// 2. `build_native_args` 打包实参下标表。
/// 3. 经 `native_fn_ptr_to_fn` 取得函数项并执行;执行前后快照/恢复 `regs[254]`。 /// 3. 经 `native_fn_ptr_to_fn` 取得函数项并执行;执行前后快照/恢复 `regs[254]`。
/// 4. 按 `NativeResult` 三态回写:`Ok` 写 `regs[0]`;`Err` 以原始值走 `unwind`; /// 4. 按 `NativeResult` 三态回写:`Ok` 写 `regs[0]`;`Err` 以原始值走 `unwind`;
/// `TailCall` 目标为 native 时同步调用,否则压字节码帧。 /// `TailCall` 目标为 native 时同步调用,异步与生成器字节码目标走内联入口,
/// 其余压字节码帧。
/// ///
/// # 边界与前提 /// # 边界与前提
/// - 调用方须保证 `obj` 已是 native 函数:`native_fn()` 为空时此处 unwrap 失败。 /// - 调用方须保证 `obj` 已是 native 函数:`native_fn()` 为空时此处 unwrap 失败。
@@ -129,6 +130,18 @@ impl Vm {
return Ok(()); return Ok(());
} }
} }
} else {
// 异步与生成器目标:走内联入口(内部创建异步 Promise 或生成器迭代器对象),
// 结果写结果槽,不压同步帧。
if obj.sub_module_index() > 0 {
let sub = self.callee_module(obj);
if matches!(sub, Some(m) if m.is_async || m.is_generator) {
self.constructing_native = false;
let result = self.call_bytecode_function_inline(callee, obj, this, &args)?;
self.regs[0] = result;
return Ok(());
}
}
} }
} else { } else {
return self.raise_type_error("CALL target is not callable"); return self.raise_type_error("CALL target is not callable");
@@ -386,3 +386,67 @@ fn bound_function_length_name_pins() {
let result = eval(&mut vm, "function target() {} target.bind().bind().name").unwrap(); let result = eval(&mut vm, "function target() {} target.bind().bind().name").unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "bound bound target"); assert_eq!(vm.lookup_str(result).unwrap_or_default(), "bound bound target");
} }
/// 执行源码并取顶层结果;Promise 结果 drain 到 settled 值(口径与 async_tests.rs 一致)。
fn eval_drain(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
vm.set_compiler_service(Arc::new(DefaultCompilerService));
let result = eval(vm, source)?;
if result.is_object() {
let obj = unsafe { &*result.as_js_object_ptr() };
if obj.is_promise_obj() {
match oxide_vm::promise::promise_settled_value(obj) {
Some((true, v)) => return Ok(v),
Some((false, v)) => return Err(format!("rejected: {v}")),
None => return Err("promise pending".to_string()),
}
}
}
Ok(result)
}
#[test]
fn function_call_apply_bind_async_target_returns_promise() {
// call/apply/bind 作用于异步目标:结果是 Promise,drain 后的值与直接调用一致。
let mut vm = Vm::new();
let result = eval_drain(&mut vm, "async function f(x){ return x; } f.call(null, 1)").unwrap();
assert_eq!(result, JsValue::int(1));
let result = eval_drain(&mut vm, "async function f(x){ return x; } f.apply(null, [2])").unwrap();
assert_eq!(result, JsValue::int(2));
let result = eval_drain(&mut vm, "async function f(x){ return x; } var b = f.bind(null); b(3)").unwrap();
assert_eq!(result, JsValue::int(3));
}
#[test]
fn function_call_async_target_rejection_keeps_value() {
// 异步目标抛错:Promise 以原值拒绝,.then 的拒绝臂收到原值。
let mut vm = Vm::new();
let result = eval_drain(
&mut vm,
"async function f(){ throw 42; } f.call(null).then(v => 'ok:'+v, e => 'err:'+e)",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "err:42");
}
#[test]
fn function_call_bind_generator_target_returns_iterator() {
// call/bind 作用于同步生成器目标:返回迭代器对象(next() 结果对象,非函数体返回值)。
let mut vm = Vm::new();
// call 结果是迭代器对象,不是函数体返回值 99。
let result = eval(&mut vm, "function* g(){ return 99; } typeof g.call(null)").unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "object");
// 迭代器首个 next() 返回 done: true(生成器未 yield 即 return)。
let result = eval(&mut vm, "function* g(){ return 99; } g.call(null).next().done").unwrap();
assert!(result.as_bool());
// bind 作用于生成器目标:包装器调用同样返回迭代器对象。
let result = eval(&mut vm, "function* g(){ return 99; } typeof g.bind(null)()").unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "object");
}
#[test]
fn function_bind_async_generator_target_returns_iterator() {
// bind 作用于异步生成器目标:返回异步生成器迭代器对象。
let mut vm = Vm::new();
let result = eval(&mut vm, "async function* g(){ return 99; } typeof g.bind(null)()").unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "object");
}