diff --git a/Cargo.lock b/Cargo.lock index 319fa28f..cb2d633c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.13.11" +version = "0.13.12" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.13.11" +version = "0.13.12" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.13.11" +version = "0.13.12" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.13.11" +version = "0.13.12" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.13.11" +version = "0.13.12" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.13.11" +version = "0.13.12" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.13.11" +version = "0.13.12" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.13.11" +version = "0.13.12" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.13.11" +version = "0.13.12" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.13.11" +version = "0.13.12" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.13.11" +version = "0.13.12" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index b8455861..d299059a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.13.11" +version = "0.13.12" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 532c3a91..12ae7788 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -39,7 +39,8 @@ impl Vm { /// 2. `build_native_args` 打包实参下标表。 /// 3. 经 `native_fn_ptr_to_fn` 取得函数项并执行;执行前后快照/恢复 `regs[254]`。 /// 4. 按 `NativeResult` 三态回写:`Ok` 写 `regs[0]`;`Err` 以原始值走 `unwind`; - /// `TailCall` 目标为 native 时同步调用,否则压字节码帧。 + /// `TailCall` 目标为 native 时同步调用,异步与生成器字节码目标走内联入口, + /// 其余压字节码帧。 /// /// # 边界与前提 /// - 调用方须保证 `obj` 已是 native 函数:`native_fn()` 为空时此处 unwrap 失败。 @@ -129,6 +130,18 @@ impl Vm { 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 { return self.raise_type_error("CALL target is not callable"); diff --git a/crates/oxide_vm/tests/builtins_function_tests.rs b/crates/oxide_vm/tests/builtins_function_tests.rs index f9fc181c..b59da845 100644 --- a/crates/oxide_vm/tests/builtins_function_tests.rs +++ b/crates/oxide_vm/tests/builtins_function_tests.rs @@ -386,3 +386,67 @@ fn bound_function_length_name_pins() { let result = eval(&mut vm, "function target() {} target.bind().bind().name").unwrap(); 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 { + 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"); +}