mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
0302640056
根因:generator/async/construct 三个调度标志是 VM 全局字段,不经 InlineSyncState 在 state-swap 边界保存/恢复。C 侧路径(builtin 经 ordinary_get 无帧调用 accessor,走 call_bytecode_function_inline 状态 交换、不压边界帧)在生成器/异步属主 dispatch 上下文内运行时继承泄漏 标志;被调函数内 new 字节码构造器() 的构造帧是嵌套 dispatch 帧栈唯一 帧,构造器 RETURN 弹出后帧栈清空,do_return 的 frames.is_empty() && 调度标志 误判为属主 dispatch,提前交付构造残留,被调函数构造点之后 的字节码整体不执行(getter 的 throw 被吞,属性读拿到 undefined)。 上层 FDI rest 绑定循环因此永不见 done,步数上限后初始化/恢复方把 异常折成通用错误。 修复:三标志纳入 InlineSyncState,新增 flag_zero 算子臂(save 快照 属主值并清零 VM 侧、restore 写回)——嵌套内联调用不继承外层调度 上下文。既有置位点全部在 save 之后、属主 dispatch 之前,语义不变; do_return 交付条件注释同步更新。 回归钉:inline_dispatch_scoping_tests——C 侧 accessor 构造后 throw 穿透 instanceof Error、C 侧取值 42、字节码帧边界对照、顶层对照 (修复前两枚 C 侧钉失败)。
76 lines
3.6 KiB
Rust
76 lines
3.6 KiB
Rust
//! 内嵌 dispatch 调度标志作用域测试:`do_return` 的"属主内嵌 dispatch 交付"条件
|
||
//! (frames 清空且 generator/async/construct 调度标志为真)只应终止标志的属主
|
||
//! dispatch。state-swap 边界(C 侧 builtin 经 `ordinary_get` 无帧调用 accessor
|
||
//! 等嵌套内联 dispatch)不得继承外层调度上下文——否则被调 getter 内
|
||
//! `new 字节码构造器()` 的构造帧是帧栈唯一帧,其弹出后 frames 清空,嵌套
|
||
//! dispatch 被误判为属主而提前交付,getter 构造之后的字节码(throw、后续语句)
|
||
//! 被整体跳过,C 侧读到构造残留值而非本应传播的异常。
|
||
//! 覆盖生成器体内 C 侧路径触发的构造后 throw 与取值两种形态;帧边界
|
||
//! (字节码 GET_PROP 压 getter 帧)形态与循环失控形态(test262 族)另行回测。
|
||
|
||
use oxide_compiler::compiler::Compiler;
|
||
use oxide_vm::vm::Vm;
|
||
|
||
fn eval_str(source: &str) -> String {
|
||
let allocator = oxide_parser::Allocator::default();
|
||
let program = oxide_parser::parse(&allocator, source).expect("parse");
|
||
let module = Compiler::new().compile(&program).expect("compile");
|
||
let mut vm = Vm::new();
|
||
let result = vm.run(&module).expect("run");
|
||
vm.lookup_str(result).unwrap_or_else(|| format!("{result}"))
|
||
}
|
||
|
||
// ── 生成器体内 Object.assign(C 侧 ordinary_get 无帧调 getter):
|
||
// getter 构造后再 throw,异常须穿透 next()(钉的是传播本身;builtin 侧
|
||
// 错误消息保真是独立缺口,不在本钉断言内) ──
|
||
#[test]
|
||
fn c_side_accessor_construct_then_throw_in_generator_propagates() {
|
||
let s = eval_str(
|
||
"function E() { this.tag = 1; }\n\
|
||
var o = { get v() { var e = new E(); throw new Error('boom'); } };\n\
|
||
function* f() { Object.assign({}, o); }\n\
|
||
var ok = false;\n\
|
||
try { f().next(); } catch (e) { ok = e instanceof Error; }\n\
|
||
ok",
|
||
);
|
||
assert_eq!(s, "true", "C 侧 accessor 构造后 throw 应穿透生成器 next(),实际 {s:?}");
|
||
}
|
||
|
||
// ── 同场景取值形态:getter 构造后 return 值须原样交付(而非构造残留) ──
|
||
#[test]
|
||
fn c_side_accessor_construct_then_return_in_generator_delivers_value() {
|
||
let s = eval_str(
|
||
"function E() { this.tag = 1; }\n\
|
||
var o = { get v() { var e = new E(); return 42; } };\n\
|
||
function* f() { return Object.assign({}, o).v; }\n\
|
||
f().next().value",
|
||
);
|
||
assert_eq!(s, "42", "C 侧 accessor 构造后的 return 值应原样交付,实际 {s:?}");
|
||
}
|
||
|
||
// ── 帧边界对照:字节码 GET_PROP 压 getter 帧执行(非 state-swap),
|
||
// 构造后 throw 本就穿透(防过度修正改变帧路径行为) ──
|
||
#[test]
|
||
fn frame_bound_accessor_construct_then_throw_in_generator_propagates() {
|
||
let s = eval_str(
|
||
"function E() { this.tag = 1; }\n\
|
||
var o = { get v() { var e = new E(); throw new Error('boom'); } };\n\
|
||
function* f() { return o.v; }\n\
|
||
var caught = '';\n\
|
||
try { f().next(); } catch (e) { caught = e.message; }\n\
|
||
caught",
|
||
);
|
||
assert_eq!(s, "boom", "帧边界 getter 构造后 throw 应穿透生成器 next(),实际 {s:?}");
|
||
}
|
||
|
||
// ── 顶层对照:无调度标志时 C 侧 accessor 构造后 return 本就正常 ──
|
||
#[test]
|
||
fn c_side_accessor_construct_then_return_top_level_delivers_value() {
|
||
let s = eval_str(
|
||
"function E() { this.tag = 1; }\n\
|
||
var o = { get v() { var e = new E(); return 42; } };\n\
|
||
Object.assign({}, o).v",
|
||
);
|
||
assert_eq!(s, "42", "顶层 C 侧 accessor 构造后 return 值应原样交付,实际 {s:?}");
|
||
}
|