docs: 补充注释与格式化
This commit is contained in:
@@ -9,17 +9,20 @@ pub use crate::hash::{compiled_module_hash, structural_hash};
|
||||
|
||||
/// 编译入口:状态 = DCE 开关 + RegAlloc 开关,编译其余状态在 emit/lower 内部。
|
||||
pub struct Compiler {
|
||||
/// 是否在 emit 与 lower 之间运行死代码消除(D-10:默认开启)。
|
||||
/// 是否在 emit 与 lower 之间运行死代码消除(默认开启)。
|
||||
enable_dce: bool,
|
||||
/// 是否在保守 DCE 后运行 liveness→精确 DCE→RegAlloc 链(D-18:默认开启)。
|
||||
/// off 时 vreg 原样当物理号走 lower 降级路径(>253 → RangeError)。
|
||||
/// 是否在保守 DCE 后运行 liveness→精确 DCE→RegAlloc 链(默认开启)。
|
||||
/// 关闭时 vreg 原样当物理号走 lower 降级路径(>253 → RangeError)。
|
||||
enable_regalloc: bool,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
/// 构造 `Compiler`:默认开启 DCE 与 RegAlloc(D-10/D-18)。
|
||||
/// 构造 `Compiler`:默认开启 DCE 与 RegAlloc。
|
||||
pub fn new() -> Self {
|
||||
Self { enable_dce: true, enable_regalloc: true }
|
||||
Self {
|
||||
enable_dce: true,
|
||||
enable_regalloc: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// 显式设置 DCE 开关(`with_dce(false)` 关闭死代码消除)。
|
||||
@@ -29,7 +32,10 @@ impl Compiler {
|
||||
|
||||
/// 显式设置 RegAlloc 开关(`with_regalloc(false)` 关闭 liveness/精确 DCE/RegAlloc 链)。
|
||||
pub fn with_regalloc(self, enable: bool) -> Self {
|
||||
Self { enable_regalloc: enable, ..self }
|
||||
Self {
|
||||
enable_regalloc: enable,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// 编译整个 program:AST → IR(`Emitter::emit_program`)→(可选 DCE)→(可选
|
||||
@@ -38,10 +44,10 @@ impl Compiler {
|
||||
crate::compiler_debug!("compile: starting...");
|
||||
let mut ir = Emitter::new().emit_program(program)?;
|
||||
if self.enable_dce {
|
||||
oxide_dce::dce(&mut ir); // 顶层函数;nested 不递归(D-03)
|
||||
oxide_dce::dce(&mut ir); // 顶层函数;nested 不递归
|
||||
}
|
||||
if self.enable_regalloc && !ir.const_overflow {
|
||||
// D-17 管线:liveness → 精确 DCE → 重算 liveness → RegAlloc
|
||||
// 优化管线:liveness → 精确 DCE → 重算 liveness → RegAlloc
|
||||
// const_overflow 时跳过:程序已知无效(lower 报 "too many constants"),
|
||||
// 且超大常量池伴生的海量 vreg 会使 liveness 稠密 bitset 爆内存。
|
||||
let cfg = oxide_cfg::build_cfg(&ir);
|
||||
@@ -49,7 +55,7 @@ impl Compiler {
|
||||
oxide_dce::dce_precise(&mut ir, &live); // 精确二轮消费第一轮 LiveInfo
|
||||
let cfg2 = oxide_cfg::build_cfg(&ir); // 精确 DCE 删指令后 inst 下标位移,CFG 重建
|
||||
let live2 = oxide_liveness::liveness(&ir, &cfg2); // 重算,绝不用过期 live
|
||||
oxide_regalloc::alloc(&mut ir, &live2)?; // D-02:无可行染色 → RangeError 上抛
|
||||
oxide_regalloc::alloc(&mut ir, &live2)?; // 无可行染色 → RangeError 上抛
|
||||
}
|
||||
crate::compiler_debug!("compile: done, {} instructions", ir.insts.len());
|
||||
lower(&ir)
|
||||
|
||||
@@ -14,11 +14,11 @@ fn compile_source(source: &str) -> oxide_bytecode::module::CompiledModule {
|
||||
compiler.compile(&program).expect("compile failed")
|
||||
}
|
||||
|
||||
// -- Arrow function structural hash tests --
|
||||
// -- 箭头函数结构哈希测试 --
|
||||
|
||||
#[test]
|
||||
fn hash_arrow_expression_body() {
|
||||
// Expression-body arrows at top level compile successfully
|
||||
// 表达式体的顶层箭头可正常编译。
|
||||
let hash = parse_to_hash("var f = () => 42;");
|
||||
assert!(hash != 0, "arrow function should produce a non-zero hash");
|
||||
}
|
||||
@@ -45,18 +45,18 @@ fn hash_arrow_block_body() {
|
||||
|
||||
#[test]
|
||||
fn hash_arrow_same_shape() {
|
||||
// Same structural shape: different variable name, same arrow expression
|
||||
// 结构形状相同:变量名不同、箭头表达式相同。
|
||||
let hash_a = parse_to_hash("var f = () => 1 + 2;");
|
||||
let hash_b = parse_to_hash("var g = () => 1 + 2;");
|
||||
assert_eq!(hash_a, hash_b, "same-shaped arrow functions should have same hash");
|
||||
}
|
||||
|
||||
// -- Arrow function compilation tests --
|
||||
// -- 箭头函数编译测试 --
|
||||
|
||||
#[test]
|
||||
fn compile_arrow_expression_body() {
|
||||
let module = compile_source("var f = () => 42; f();");
|
||||
// Verify the sub_module for the arrow is marked as is_arrow
|
||||
// 校验箭头的子模块被标记为 is_arrow。
|
||||
let arrow_found = module.sub_modules.iter().any(|m| m.is_arrow);
|
||||
assert!(arrow_found, "arrow function sub_module should have is_arrow=true");
|
||||
}
|
||||
@@ -71,7 +71,7 @@ fn compile_arrow_block_body() {
|
||||
#[test]
|
||||
fn compile_arrow_name_inference() {
|
||||
let module = compile_source("var myArrow = () => 42;");
|
||||
// The sub_module for the arrow should have function_name set
|
||||
// 箭头的子模块应带有 function_name。
|
||||
let named = module
|
||||
.sub_modules
|
||||
.iter()
|
||||
@@ -82,7 +82,7 @@ fn compile_arrow_name_inference() {
|
||||
#[test]
|
||||
fn compile_regular_function_not_arrow() {
|
||||
let module = compile_source("var f = function() { return 42; }; f();");
|
||||
// Regular FunctionExpression should NOT have is_arrow=true
|
||||
// 普通 FunctionExpression 不应标记 is_arrow。
|
||||
let has_arrow = module.sub_modules.iter().any(|m| m.is_arrow);
|
||||
assert!(!has_arrow, "regular function expression should not be marked as arrow");
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ fn compile_negation() {
|
||||
fn compile_multiple_stmts() {
|
||||
let module = compile_source("1; 2; 3;");
|
||||
|
||||
// D-11 放宽:Compiler 默认 DCE 开启,死 `1; 2` 链的 LOAD_CONST 被删除,
|
||||
// Compiler 默认 DCE 开启:死 `1; 2` 链的 LOAD_CONST 被删除,
|
||||
// 仅剩顶层值 `3` 的 LOAD_CONST(HALT 经 reg0 返回)。精确计数断言放宽为存在性断言。
|
||||
let has_load = module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::LOAD_CONST);
|
||||
assert!(has_load, "expected at least one LOAD_CONST for the final value");
|
||||
@@ -505,30 +505,61 @@ fn compile_comparison_complement_ops() {
|
||||
// 结果被使用(赋值)→ 比较指令不被 DCE 删除(精确 DCE 会删结果丢弃的死比较)
|
||||
let module = compile_source("let a = 1, b = 2; var r = a > b; var s = a <= b; var t = a >= b; var u = a != b;");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::GT), "a > b should emit GT");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::LTE), "a <= b should emit LTE");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::GTE), "a >= b should emit GTE");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEQ), "a != b should emit NEQ");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::LTE),
|
||||
"a <= b should emit LTE"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::GTE),
|
||||
"a >= b should emit GTE"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEQ),
|
||||
"a != b should emit NEQ"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_template_literal_emits_template_str() {
|
||||
let module = compile_source("let name = 'x'; `hello ${name}`;");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::TEMPLATE_STR), "template literal should emit TEMPLATE_STR");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::TEMPLATE_STR),
|
||||
"template literal should emit TEMPLATE_STR"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_dynamic_member_ops() {
|
||||
let module = compile_source("let obj = {}, k = 'x', arr = [1]; obj[k]; obj[k] = 1; arr[0];");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEW_ARRAY), "array literal should emit NEW_ARRAY");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::GET_PROP_DYNAMIC), "obj[k] should emit GET_PROP_DYNAMIC");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::SET_PROP_DYNAMIC), "obj[k] = should emit SET_PROP_DYNAMIC");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEW_ARRAY),
|
||||
"array literal should emit NEW_ARRAY"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::GET_PROP_DYNAMIC),
|
||||
"obj[k] should emit GET_PROP_DYNAMIC"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::SET_PROP_DYNAMIC),
|
||||
"obj[k] = should emit SET_PROP_DYNAMIC"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_delete_new_instanceof_in() {
|
||||
let module = compile_source("let obj = { x: 1 }, B = function(){}; delete obj.x; new B(); obj instanceof B; 'x' in obj;");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::DELETE_PROP_STATIC), "delete obj.x should emit DELETE_PROP_STATIC");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEW_EXPRESSION), "new B() should emit NEW_EXPRESSION");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::INSTANCEOF), "instanceof should emit INSTANCEOF");
|
||||
let module =
|
||||
compile_source("let obj = { x: 1 }, B = function(){}; delete obj.x; new B(); obj instanceof B; 'x' in obj;");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::DELETE_PROP_STATIC),
|
||||
"delete obj.x should emit DELETE_PROP_STATIC"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEW_EXPRESSION),
|
||||
"new B() should emit NEW_EXPRESSION"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::INSTANCEOF),
|
||||
"instanceof should emit INSTANCEOF"
|
||||
);
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::IN), "in should emit IN");
|
||||
}
|
||||
|
||||
@@ -120,9 +120,9 @@ fn regression_for_var_init() {
|
||||
|
||||
#[test]
|
||||
fn regression_var_decl_counter_no_init() {
|
||||
// `for (var i; ...)` initializes `i` to undefined (var semantics, not TDZ), and the
|
||||
// counter/emitter agree on the no-init declarator's instruction count, so it compiles
|
||||
// to a well-formed loop header instead of panicking.
|
||||
// `for (var i; ...)` 把 `i` 初始化为 undefined(var 语义而非 TDZ),且
|
||||
// 编译器对无初始化声明符的指令数保持一致,因此可编译为良构循环头
|
||||
// 而非 panic。
|
||||
let module = compile_source("for (var i; i < 3; i = i + 1) {}");
|
||||
assert!(!module.bytecode.is_empty(), "for(var) no-init should compile");
|
||||
assert!(
|
||||
@@ -336,18 +336,39 @@ fn compile_empty_statements_are_skipped() {
|
||||
#[test]
|
||||
fn compile_for_of_emits_iterator_protocol() {
|
||||
let module = compile_source("let arr = [1]; for (const v of arr) { v; }");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_OF_INIT), "for-of should emit FOR_OF_INIT");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_OF_NEXT), "for-of should emit FOR_OF_NEXT");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_OF_DONE), "for-of should emit FOR_OF_DONE");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_OF_CLOSE), "for-of should emit FOR_OF_CLOSE");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_OF_INIT),
|
||||
"for-of should emit FOR_OF_INIT"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_OF_NEXT),
|
||||
"for-of should emit FOR_OF_NEXT"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_OF_DONE),
|
||||
"for-of should emit FOR_OF_DONE"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_OF_CLOSE),
|
||||
"for-of should emit FOR_OF_CLOSE"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_try_catch_emits_try_begin_and_throw() {
|
||||
let module = compile_source("try { throw new Error('x'); } catch (e) { e; }");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::THROW), "throw should emit THROW");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::TRY_BEGIN), "try-catch should emit TRY_BEGIN");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::TRY_END), "try-catch should emit TRY_END");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::THROW),
|
||||
"throw should emit THROW"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::TRY_BEGIN),
|
||||
"try-catch should emit TRY_BEGIN"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::TRY_END),
|
||||
"try-catch should emit TRY_END"
|
||||
);
|
||||
}
|
||||
|
||||
fn has_opcode(module: &CompiledModule, target: OpCode) -> bool {
|
||||
@@ -387,8 +408,20 @@ fn compile_closure_capture_arrow_emits_make_cell() {
|
||||
#[test]
|
||||
fn compile_compound_assignment_complement_ops() {
|
||||
let module = compile_source("let x = 10; x -= 1; x *= 2; x /= 3; x %= 4;");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_SUB), "x -= should emit COMPOUND_SUB");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_MUL), "x *= should emit COMPOUND_MUL");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_DIV), "x /= should emit COMPOUND_DIV");
|
||||
assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_MOD), "x %= should emit COMPOUND_MOD");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_SUB),
|
||||
"x -= should emit COMPOUND_SUB"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_MUL),
|
||||
"x *= should emit COMPOUND_MUL"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_DIV),
|
||||
"x /= should emit COMPOUND_DIV"
|
||||
);
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_MOD),
|
||||
"x %= should emit COMPOUND_MOD"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ fn hash_var_let_const_differ() {
|
||||
let let_ast = oxide_parser::parse(&allocator, "let x = 1").unwrap();
|
||||
let const_ast = oxide_parser::parse(&allocator, "const x = 1").unwrap();
|
||||
|
||||
// Different AST structures should produce different bytecode
|
||||
// 不同的 AST 结构应产出不同的字节码。
|
||||
let var_module = compiler.compile(&var_ast).unwrap();
|
||||
let let_module = compiler.compile(&let_ast).unwrap();
|
||||
let const_module = compiler.compile(&const_ast).unwrap();
|
||||
|
||||
@@ -31,16 +31,16 @@ fn symbol_table_nested_scopes() {
|
||||
|
||||
#[test]
|
||||
fn symbol_table_var_self_init_in_block_is_legal() {
|
||||
// `var` has no temporal dead zone and hoists to the function scope, so a block-level
|
||||
// `var x = x` reads the same already-initialized binding — legal in JS, not an error.
|
||||
// `var` 无暂时性死区且提升到函数作用域,块级 `var x = x` 读取的是
|
||||
// 同一已初始化绑定——JS 合法,非错误。
|
||||
let module = compile_source("var x = 1; { var x = x; }");
|
||||
assert!(!module.bytecode.is_empty(), "var self-init in block should compile");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_table_duplicate_var_is_legal() {
|
||||
// Duplicate `var` in the same scope is legal in JavaScript (even in strict mode);
|
||||
// only duplicate `let`/`const` declarations are a SyntaxError.
|
||||
// JavaScript 中同作用域重复 `var` 合法(严格模式亦然);
|
||||
// 只有重复 `let`/`const` 才是 SyntaxError。
|
||||
let module = compile_source("var x = 1; var x = 2;");
|
||||
assert!(!module.bytecode.is_empty(), "duplicate var should compile");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user