mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-27 04:01:11 +08:00
fix: 变量绑定名发射按名排序稳定字节码
This commit is contained in:
@@ -673,11 +673,13 @@ impl Emitter {
|
|||||||
// 只是赋值;否则声明语句前创建的闭包读取占位 cell → TDZ 误报。参数与
|
// 只是赋值;否则声明语句前创建的闭包读取占位 cell → TDZ 误报。参数与
|
||||||
// arguments 已在上方初始化(跳过以免覆盖参数值);let/const/class 保持
|
// arguments 已在上方初始化(跳过以免覆盖参数值);let/const/class 保持
|
||||||
// TDZ 语义不动。声明语句的 MAKE_CELL 按占位更新语义覆盖此初值。
|
// TDZ 语义不动。声明语句的 MAKE_CELL 按占位更新语义覆盖此初值。
|
||||||
let var_names: Vec<String> = collect_var_binding_names(body_stmts)
|
let mut var_names: Vec<String> = collect_var_binding_names(body_stmts)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|n| !param_names.contains(&n.as_str()) && n != "arguments")
|
.filter(|n| !param_names.contains(&n.as_str()) && n != "arguments")
|
||||||
.filter(|n| ctx.captured_bindings.contains_key(n))
|
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||||||
.collect();
|
.collect();
|
||||||
|
// HashSet 迭代序带随机种子,排序后入口 MAKE_CELL 发射序跨进程稳定。
|
||||||
|
var_names.sort();
|
||||||
if !var_names.is_empty() {
|
if !var_names.is_empty() {
|
||||||
let undef_reg = self.emit_undefined(ctx);
|
let undef_reg = self.emit_undefined(ctx);
|
||||||
for name in var_names {
|
for name in var_names {
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ impl Emitter {
|
|||||||
// 严格 eval 代码函数声明绑定 eval 自身 lexical 环境、不触全局、不抛,门禁
|
// 严格 eval 代码函数声明绑定 eval 自身 lexical 环境、不触全局、不抛,门禁
|
||||||
// 随 !is_strict 关闭,其写点抑制在声明发射处。
|
// 随 !is_strict 关闭,其写点抑制在声明发射处。
|
||||||
if ctx.is_eval_script && !ctx.is_strict {
|
if ctx.is_eval_script && !ctx.is_strict {
|
||||||
for name in collect_top_level_function_names(&program.body) {
|
for name in collect_top_level_function_names_ordered(&program.body) {
|
||||||
if NON_WRITABLE_GLOBAL_BUILTINS.contains(&name.as_str()) {
|
if NON_WRITABLE_GLOBAL_BUILTINS.contains(&name.as_str()) {
|
||||||
let _ = self.emit_throw_error(
|
let _ = self.emit_throw_error(
|
||||||
"TypeError",
|
"TypeError",
|
||||||
@@ -356,10 +356,12 @@ impl Emitter {
|
|||||||
// 声明语句执行前创建的闭包读取到 undefined(脚本 GlobalDeclarationInstantiation
|
// 声明语句执行前创建的闭包读取到 undefined(脚本 GlobalDeclarationInstantiation
|
||||||
// 语义),而非占位 cell 的 TDZ 误报。声明语句的 MAKE_CELL 覆盖此初值。
|
// 语义),而非占位 cell 的 TDZ 误报。声明语句的 MAKE_CELL 覆盖此初值。
|
||||||
// 名集保持 var-only:函数声明名从不入捕获集(tier 剔除),无需入口 cell。
|
// 名集保持 var-only:函数声明名从不入捕获集(tier 剔除),无需入口 cell。
|
||||||
let var_names: Vec<String> = var_names
|
let mut var_names: Vec<String> = var_names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|n| ctx.captured_bindings.contains_key(n))
|
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||||||
.collect();
|
.collect();
|
||||||
|
// HashSet 迭代序带随机种子,排序后入口 MAKE_CELL 发射序跨进程稳定。
|
||||||
|
var_names.sort();
|
||||||
if !var_names.is_empty() {
|
if !var_names.is_empty() {
|
||||||
let undef_reg = self.emit_undefined(&mut ctx);
|
let undef_reg = self.emit_undefined(&mut ctx);
|
||||||
for name in var_names {
|
for name in var_names {
|
||||||
@@ -385,13 +387,15 @@ impl Emitter {
|
|||||||
// 把全局属性值预载进固定寄存器槽),既有属性不改动值,值幂等保留。
|
// 把全局属性值预载进固定寄存器槽),既有属性不改动值,值幂等保留。
|
||||||
// 序言名集 = 顶层 var 名 ∪ 顶层块级函数泄漏名(sloppy 模式浏览器兼容行为的
|
// 序言名集 = 顶层 var 名 ∪ 顶层块级函数泄漏名(sloppy 模式浏览器兼容行为的
|
||||||
// 外层绑定同样在求值前实例化,同走 define-if-absent)。
|
// 外层绑定同样在求值前实例化,同走 define-if-absent)。
|
||||||
let mut gdi_var_names = collect_var_binding_names(&program.body);
|
let mut gdi_var_names: Vec<String> = collect_var_binding_names(&program.body).into_iter().collect();
|
||||||
gdi_var_names.extend(
|
gdi_var_names.extend(
|
||||||
block_fn_names
|
block_fn_names
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|n| !ctx.block_fn_suppressed.contains(*n) && !CompileCtx::is_non_writable_global_builtin(n))
|
.filter(|n| !ctx.block_fn_suppressed.contains(*n) && !CompileCtx::is_non_writable_global_builtin(n))
|
||||||
.cloned(),
|
.cloned(),
|
||||||
);
|
);
|
||||||
|
// HashSet 迭代序带随机种子,排序后 GDI 序言与内置镜像槽分配序跨进程稳定。
|
||||||
|
gdi_var_names.sort();
|
||||||
if !gdi_var_names.is_empty() {
|
if !gdi_var_names.is_empty() {
|
||||||
let undef_reg = self.emit_undefined(&mut ctx);
|
let undef_reg = self.emit_undefined(&mut ctx);
|
||||||
for name in &gdi_var_names {
|
for name in &gdi_var_names {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//! 脚本顶层形态的 IR 级结构断言:parse → `emit_program` 为止,钉死现状 emit 行为。
|
//! 脚本顶层形态的 IR 级结构断言:parse → `emit_program` 为止,钉死现状 emit 行为。
|
||||||
//!
|
//!
|
||||||
//! 断言顺序无关:只表达 存在 / 计数 / 名字集 / 键名集。GDI 序言对组与 var 入口
|
//! 结构断言顺序无关:只表达 存在 / 计数 / 名字集 / 键名集。GDI 序言对组与 var 入口
|
||||||
//! MAKE_CELL 组的指令序逐进程轮换,这两组只断言键名集;断言不依赖寄存器号
|
//! MAKE_CELL 组的发射序已按名排序固定,但断言仍只取键名集,不依赖寄存器号
|
||||||
//! 与 `n_registers` 绝对值。
|
//! 与 `n_registers` 绝对值;发射序确定性由 `emission_order_stable_across_compiles`
|
||||||
|
//! 单独锁定。
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@@ -22,6 +23,59 @@ fn emit_ir(src: &str) -> IRFunction {
|
|||||||
.unwrap_or_else(|e| panic!("emit 失败 {src:?}: {e}"))
|
.unwrap_or_else(|e| panic!("emit 失败 {src:?}: {e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 字节码指纹:`CompiledModule` 未派生 `Debug`,逐字段展开为确定串(子模块递归)。
|
||||||
|
///
|
||||||
|
/// 覆盖字节码位模式、常量池、寄存器布局、内置槽映射与闭包捕获描述。
|
||||||
|
fn bytecode_fingerprint(m: &oxide_bytecode::module::CompiledModule) -> String {
|
||||||
|
let mut out = format!(
|
||||||
|
"bc={:?};k={:?};nr={};na={};pb={};bim={:?};uv={:?};cn={};",
|
||||||
|
m.bytecode,
|
||||||
|
m.constants,
|
||||||
|
m.n_registers,
|
||||||
|
m.n_args,
|
||||||
|
m.param_base,
|
||||||
|
m.builtin_reg_map,
|
||||||
|
m.upvalue_captures,
|
||||||
|
m.cells_needed
|
||||||
|
);
|
||||||
|
for sub in &m.sub_modules {
|
||||||
|
out.push_str(&bytecode_fingerprint(sub));
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 同进程连续编译 src 八次,逐次比对 IR 与字节码指纹,断言全等。
|
||||||
|
fn assert_emission_stable(src: &str, is_eval_script: bool) {
|
||||||
|
let mut fingerprints: Vec<(String, String)> = Vec::new();
|
||||||
|
for _ in 0..8 {
|
||||||
|
let alloc = oxide_parser::Allocator::default();
|
||||||
|
let program = oxide_parser::parse(&alloc, src).unwrap_or_else(|e| panic!("parse 失败 {src:?}: {e:?}"));
|
||||||
|
let ir = Emitter::new()
|
||||||
|
.emit_program(&program, false, is_eval_script)
|
||||||
|
.unwrap_or_else(|e| panic!("emit 失败 {src:?}: {e}"));
|
||||||
|
let bytecode = oxide_ir::lower::lower(&ir).unwrap_or_else(|e| panic!("lower 失败 {src:?}: {e}"));
|
||||||
|
fingerprints.push((format!("{ir:?}"), bytecode_fingerprint(&bytecode)));
|
||||||
|
}
|
||||||
|
let first = &fingerprints[0];
|
||||||
|
for (i, fp) in fingerprints.iter().enumerate().skip(1) {
|
||||||
|
assert_eq!(fp.0, first.0, "第 {i} 次编译 IR 指纹漂移:{src:?}");
|
||||||
|
assert_eq!(fp.1, first.1, "第 {i} 次编译字节码指纹漂移:{src:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发射序确定性:同进程多次编译,GDI 序言、函数入口 MAKE_CELL 的指令序及
|
||||||
|
/// 常量池/内置槽映射/字节码逐位不随 HashSet 随机迭代序漂移。
|
||||||
|
#[test]
|
||||||
|
fn emission_order_stable_across_compiles() {
|
||||||
|
// 多个顶层 var 驱动 GDI 序言;函数内多个被捕获 var 驱动函数入口 MAKE_CELL。
|
||||||
|
assert_emission_stable(
|
||||||
|
"var b=1,a=2,d=3,c=4; function f(){ var z=1,y=2; return function(){ return z+y+a; }; } f();",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
// eval 顶层函数声明撞多个不可写全局内置名时,抛错消息名选择按源序稳定。
|
||||||
|
assert_emission_stable("function Infinity(){} function NaN(){}", true);
|
||||||
|
}
|
||||||
|
|
||||||
fn count_op(insts: &[Inst], op: OpCode) -> usize {
|
fn count_op(insts: &[Inst], op: OpCode) -> usize {
|
||||||
insts.iter().filter(|i| i.op == op).count()
|
insts.iter().filter(|i| i.op == op).count()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user