docs: 补充注释与格式化

This commit is contained in:
爱特每羊队
2026-08-06 23:10:27 +08:00
parent 4e0fd762c2
commit 328b8a3ddc
156 changed files with 2393 additions and 1687 deletions
+2 -4
View File
@@ -112,9 +112,8 @@ fn multiple_arrows_in_same_scope() {
assert_eq!(eval("var a = () => 1, b = () => 2; a() + b()"), "3");
}
// NOTE: Arrow functions capturing lexical `this` from enclosing functions
// require nested function call support (sub_module flattening), which is
// a pre-existing limitation. These tests will be enabled in a future phase:
// 未支持:箭头函数从外围函数捕获词法 `this` 依赖嵌套函数调用(子模块展平),
// 是既有能力限制。以下测试待能力补齐后启用:
//
// fn arrow_captures_enclosing_this() {
// // const o = {x:10, f:function(){ return (() => this.x)(); }}; o.f()
@@ -127,4 +126,3 @@ fn multiple_arrows_in_same_scope() {
fn closure_captures_outer_let() {
assert_eq!(eval("var f; { let x = 7; f = () => x; } f()"), "7");
}
+20 -20
View File
@@ -35,7 +35,7 @@ fn date_parse_invalid() {
assert!(r.as_double().is_nan());
}
// -- new Date() constructor via JS --
// ── 经 JS 调用 new Date() 构造器 ──
#[test]
fn date_new_multi_arg() {
@@ -100,7 +100,7 @@ fn date_value_of() {
#[test]
fn date_to_iso_string() {
let mut vm = Vm::new();
// Parse an explicit ISO UTC string to avoid timezone dependency
// 解析显式 ISO UTC 字符串,避免时区依赖。
let r = eval(&mut vm, "new Date('2020-01-01T00:00:00Z').toISOString()").unwrap();
let s = str_val(&vm, r);
assert!(s.starts_with("2020-01-01"), "got: {s}");
@@ -109,7 +109,7 @@ fn date_to_iso_string() {
#[test]
fn date_to_json() {
let mut vm = Vm::new();
// Parse an explicit ISO UTC string to avoid timezone dependency
// 解析显式 ISO UTC 字符串,避免时区依赖。
let r = eval(&mut vm, "new Date('2020-01-01T00:00:00Z').toJSON()").unwrap();
let s = str_val(&vm, r);
assert!(s.starts_with("2020-01-01"), "got: {s}");
@@ -136,7 +136,7 @@ fn date_objects_use_bounded_numeric_coercion() {
assert_eq!(r.as_double(), 0.0);
}
// -- Plan 01 timezone-correct tests --
// ── 时区正确性测试 ──
#[test]
fn date_local_getters_differ_from_utc() {
@@ -145,7 +145,7 @@ fn date_local_getters_differ_from_utc() {
let local_hour = r.as_double();
let r = eval(&mut vm, "var d = new Date(2020, 6, 15, 12, 0, 0); d.getUTCHours()").unwrap();
let utc_hour = r.as_double();
// In UTC+0 they are equal (correct), otherwise they differ
// 在 UTC+0 下二者相等(正确),否则不相等。
if utc_hour != local_hour {
assert_ne!(local_hour, utc_hour, "in non-UTC+0, local vs UTC hours should differ");
}
@@ -155,7 +155,7 @@ fn date_local_getters_differ_from_utc() {
#[test]
fn date_constructor_uses_local_timezone() {
let mut vm = Vm::new();
// getTimezoneOffset = UTC - local; UTC hour = local + offset/60
// getTimezoneOffset = UTC - local;UTC 小时 = local + offset/60
let r = eval(&mut vm, "var d = new Date(2020, 0, 1, 12, 0, 0); d.getUTCHours()").unwrap();
let utc_hour = r.as_double();
let r = eval(&mut vm, "var d = new Date(2020, 0, 1, 12, 0, 0); d.getTimezoneOffset()").unwrap();
@@ -167,7 +167,7 @@ fn date_constructor_uses_local_timezone() {
#[test]
fn date_constructor_defaults_from_current_time() {
let mut vm = Vm::new();
// D-02: missing args default to current time components
// 缺省参数回退到当前时间分量。
let r = eval(&mut vm, "var d = new Date(2020, 5); d.getMonth()").unwrap();
assert_eq!(r.as_double(), 5.0);
let r = eval(&mut vm, "new Date(2020, 5).getDate()").unwrap();
@@ -177,7 +177,7 @@ fn date_constructor_defaults_from_current_time() {
#[test]
fn date_constructor_truncation() {
let mut vm = Vm::new();
// D-03: ToInteger truncation
// 年月参数按 ToInteger 截断。
let r = eval(&mut vm, "new Date(2024.7, 5.9).getFullYear()").unwrap();
assert_eq!(r.as_double(), 2024.0);
let r = eval(&mut vm, "new Date(2024.7, 5.9).getMonth()").unwrap();
@@ -187,7 +187,7 @@ fn date_constructor_truncation() {
#[test]
fn date_setters_return_timestamp() {
let mut vm = Vm::new();
// D-05: setters return modified timestamp
// setter 返回修改后的时间戳。
let r = eval(&mut vm, "var d = new Date(0); var ts = d.setHours(12); ts == d.getTime() ? 1 : 0").unwrap();
assert_eq!(r.as_int(), 1);
}
@@ -195,12 +195,12 @@ fn date_setters_return_timestamp() {
#[test]
fn date_invalid_returns_nan() {
let mut vm = Vm::new();
// D-06: Invalid Date getters/setters return NaN
// Invalid Date 的 getter/setter 返回 NaN。
let r = eval(&mut vm, "new Date(NaN).getFullYear()").unwrap();
assert!(r.as_double().is_nan());
let r = eval(&mut vm, "new Date(NaN).setHours(0)").unwrap();
assert!(r.as_double().is_nan());
// D-04: Invalid Date constructor
// Invalid Date 构造(NaN 参数)。
let r = eval(&mut vm, "new Date(NaN, 0).getTime()").unwrap();
assert!(r.as_double().is_nan());
}
@@ -208,7 +208,7 @@ fn date_invalid_returns_nan() {
#[test]
fn date_get_timezone_offset_uses_date_timestamp() {
let mut vm = Vm::new();
// D-09/D-10: offset uses date's timestamp, not now()
// 偏移基于日期的自身时间戳,而非当前时间。
let r = eval(&mut vm, "new Date(0).getTimezoneOffset()").unwrap();
assert!(r.as_double().is_finite());
}
@@ -216,7 +216,7 @@ fn date_get_timezone_offset_uses_date_timestamp() {
#[test]
fn date_setters_optional_chain() {
let mut vm = Vm::new();
// D-08: setFullYear supports optional month/day params
// setFullYear 支持可选的月/日参数。
let r = eval(&mut vm, "var d = new Date(2020, 0, 1); d.setFullYear(2025, 11, 25); d.getFullYear()").unwrap();
assert_eq!(r.as_double(), 2025.0);
let r = eval(&mut vm, "var d = new Date(2020, 0, 1); d.setFullYear(2025, 11, 25); d.getMonth()").unwrap();
@@ -228,12 +228,12 @@ fn date_setters_optional_chain() {
#[test]
fn date_local_setters_preserve_utc_offset() {
let mut vm = Vm::new();
// setHours changes local hour; verify local hour value
// setHours 改变本地小时,验证本地小时值。
let r = eval(&mut vm, "var d = new Date(2020, 0, 1, 0, 0, 0); d.setHours(12); d.getHours()").unwrap();
assert_eq!(r.as_double(), 12.0, "setHours(12) should make getHours() return 12");
}
// -- Plan 02 new method tests --
// ── 后续新增方法测试 ──
#[test]
fn date_set_utc_full_year_works() {
@@ -313,7 +313,7 @@ fn date_get_year_deprecated() {
#[test]
fn date_set_year_deprecated() {
let mut vm = Vm::new();
// setYear(0-99) adds 1900; setYear(20) → fullYear 1920
// setYear(0-99) 加 1900;setYear(20) → fullYear 1920。
let r = eval(&mut vm, "var d = new Date(2020, 0, 1); d.setYear(20); d.getFullYear()").unwrap();
assert_eq!(r.as_double(), 1920.0);
}
@@ -347,8 +347,8 @@ fn date_set_utc_invalid_returns_nan() {
assert!(r.as_double().is_nan());
}
// setYear with no argument must not index a missing argument register (previously panicked);
// a missing / NaN year sets the date value to NaN and returns NaN (B.2.4.2).
// setYear 无参数时不得索引缺失的参数寄存器(此前会 panic);
// 缺失 / NaN 的年份把日期值置为 NaN 并返回 NaN(B.2.4.2)。
#[test]
fn date_set_year_no_arg_returns_nan() {
let mut vm = Vm::new();
@@ -384,8 +384,8 @@ fn date_set_year_four_digit_kept() {
assert_eq!(r.as_double() as i64, 2020);
}
// UTC setters called with no argument previously indexed a missing argument register
// (panic / process abort). A missing primary argument now yields NaN and sets the date NaN.
// UTC setter 无参数调用此前会索引缺失的参数寄存器(panic / 进程中止)。
// 缺失的主参数现在产出 NaN 并把日期置为 NaN。
#[test]
fn date_set_utc_setters_no_arg_return_nan() {
let mut vm = Vm::new();
@@ -144,7 +144,7 @@ fn error_subtype_constructors_produce_named_objects() {
assert_eq!(format!("{}", eval("new SyntaxError('boom').name == 'SyntaxError'").unwrap()), "true");
}
// New tests for prototype properties + constructor fixes
// ── 原型属性与构造器修复测试 ──
#[test]
fn error_prototype_has_name() {
@@ -276,7 +276,7 @@ fn create_type_error_no_own_name() {
assert_eq!(obj.prop_count(), 1);
}
// Plan 02: toString tests
// ── toString 测试 ──
#[test]
fn error_to_string_basic() {
@@ -319,7 +319,7 @@ fn error_to_string_non_object_throws() {
}
}
// format_error_message tests
// ── format_error_message 测试 ──
#[test]
fn format_error_message_both() {
@@ -339,7 +339,7 @@ fn format_error_message_empty_name() {
assert_eq!(result, "msg");
}
// stack tests
// ── stack 测试 ──
#[test]
fn error_stack_is_string() {
@@ -95,9 +95,9 @@ fn function_to_string_non_function_throws() {
#[test]
fn function_call_returns_object_stays_valid() {
// Regression: call_function_sync used to run bytecode in a sub-VM with a separate
// epoch; objects allocated in the sub-VM epoch became dangling after sub-VM drop.
// This test forces the returned object to be dereferenced after the call returns.
// 回归:call_function_sync 曾在一个带独立 epoch 的子 VM 中运行字节码;
// 子 VM epoch 中分配的对象在子 VM 销毁后成为悬垂指针。
// 本测试强制在调用返回后解引用返回对象。
let mut vm = Vm::new();
let result = eval(&mut vm, "function f() { return {x: 42}; } f.call(null).x").unwrap();
assert_eq!(result, JsValue::int(42));
@@ -112,8 +112,8 @@ fn function_apply_returns_object_stays_valid() {
#[test]
fn getter_returns_object_stays_valid() {
// Same class of bug: ordinary_get sync-path (target_reg=None) for bytecode
// accessor ran in sub-VM; returned object was freed on sub-VM drop.
// 同类 bug:字节码 accessor 经 ordinary_get 同步路径(target_reg=None)在子 VM
// 中运行,返回对象在子 VM 销毁后被释放。
let mut vm = Vm::new();
let result = eval(&mut vm, "var o = { get p() { return {z: 7}; } }; o.p.z").unwrap();
assert_eq!(result, JsValue::int(7));
+1 -1
View File
@@ -65,7 +65,7 @@ fn tmap_size_and_clear() {
assert_eq!(map_size(&mut vm, &[0]).unwrap().as_double(), 0.0);
}
// -- JS eval tests using new keyword (all in one eval per test) --
// ── 经 JS 使用 new 关键字的 eval 测试(每个测试一次 eval)──
#[test]
fn map_new_set_get() {
@@ -166,7 +166,7 @@ fn object_proto_has_own_property_call_works() {
#[test]
fn object_proto_property_is_enumerable_call_works() {
let (_vm, result) = eval("Object.prototype.propertyIsEnumerable.call(Object, 'assign')").unwrap();
// Built-in methods are non-enumerable per the ES spec.
// 内置方法按 ES 规范不可枚举。
assert_eq!(result, JsValue::bool(false));
}
@@ -268,7 +268,7 @@ fn string_call_conversion_stays_primitive() {
assert_eq!(to_str(&vm, v), "123");
}
// ── Plan 01: replace tests ──
// ── replace 测试 ──
#[test]
fn string_replace_non_global_first_only() {
@@ -305,7 +305,7 @@ fn string_replace_string_pattern() {
assert_eq!(to_str(&vm, s), "heLlo");
}
// ── Plan 02: split tests ──
// ── split 测试 ──
#[test]
fn string_split_regex_capture_groups() {
@@ -348,7 +348,7 @@ fn string_split_string_separator() {
assert_eq!(obj.prop_count(), 3);
}
// ── Plan 03: match tests ──
// ── match 测试 ──
#[test]
fn string_match_non_global_captures() {
@@ -390,7 +390,7 @@ fn string_match_string_pattern() {
assert_eq!(to_str(&vm, obj.get_prop_at(0)), "ll");
}
// ── Plan 04: matchAll tests ──
// ── matchAll 测试 ──
#[test]
fn string_match_all_returns_iterator() {
@@ -420,7 +420,7 @@ fn string_match_all_next_exhausted() {
assert!(done);
}
// ── Plan 05: substring tests ──
// ── substring 测试 ──
#[test]
fn string_substring_nan_index() {
@@ -443,7 +443,7 @@ fn string_substring_swap() {
assert_eq!(to_str(&vm, s), "el");
}
// ── Plan 06: substr + at tests ──
// ── substr + at 测试 ──
#[test]
fn string_substr_positive() {
@@ -487,7 +487,7 @@ fn string_at_out_of_range() {
assert!(s.is_undefined());
}
// ── Plan 07: lastIndexOf tests ──
// ── lastIndexOf 测试 ──
#[test]
fn string_last_index_of_basic() {
+21 -22
View File
@@ -113,32 +113,31 @@ fn primitive_property_write_throws_clean_type_error() {
}
}
// --- Phase 14.0.1: type-conversion infrastructure (ENG-13) ---
// SC-1..SC-8 from ROADMAP. eval() returns the Display of the final value;
// every assertion below targets a bool/number result to avoid string-quoting
// ambiguity.
// --- 类型转换基础设施测试 ---
// eval() 返回最终值的 Display;以下断言都针对 bool/number 结果,
// 避免字符串引用的歧义。
// SC-1 ToPrimitive: boxed Number unwraps via valueOf (was "NaN" before).
// ToPrimitive:装箱 Number 经 valueOf 解包。
#[test]
fn to_primitive_number_boxed() {
assert_eq!(eval("+new Number(42)"), "42");
}
// SC-1 ToPrimitive: boxed String coerces to its primitive in == (object vs string).
// ToPrimitive:装箱 String 在 == 中强转为原始值(对象 vs 字符串)。
#[test]
fn to_primitive_string_boxed() {
assert_eq!(eval("new String('x') == 'x'"), "true");
}
// SC-1 ToPrimitive: obj[Symbol.toPrimitive] is consulted (callback runs, returns 7).
// ToPrimitive:优先调用 obj[Symbol.toPrimitive](回调执行并返回 7)。
#[test]
fn to_primitive_symbol_hint_invoked() {
assert_eq!(eval("var o = {}; o[Symbol.toPrimitive] = function (h) { return 7; }; +o"), "7");
}
// SC-2 ToNumber: to_number_full throws TypeError on a Symbol (per D-02).
// Note: the unary-plus operator path uses coerce_number_bounded (no symbol
// throw) so `+Symbol()` yields NaN at the operator level; see test below.
// ToNumber:to_number_full 对 Symbol 抛 TypeError。
// 注意:一元加号路径用 coerce_number_bounded(对 Symbol 不抛),因此运算符层的
// +Symbol() 得到 NaN;见下个测试。
#[test]
fn to_number_full_symbol_throws() {
let mut vm = Vm::new();
@@ -148,73 +147,73 @@ fn to_number_full_symbol_throws() {
assert!(result.unwrap_err().contains("Symbol"), "expected a Symbol TypeError message");
}
// SC-2/SC-7 operator-level reality: +Symbol is NaN (symbol-throw not wired into arithmetic).
// 运算符层实况:+Symbol 为 NaN(Symbol 抛出未接入算术路径)。
#[test]
fn unary_plus_symbol_is_nan() {
assert_eq!(eval("+Symbol('x')"), "NaN");
}
// SC-3 Abstract Equality: string vs number coerces.
// 抽象相等:字符串与数字互相强转。
#[test]
fn abstract_eq_string_number() {
assert_eq!(eval("'1' == 1"), "true");
}
// SC-3 Abstract Equality: bool->number recursion + string->number.
// 抽象相等:布尔转数字的递归 + 字符串转数字。
#[test]
fn abstract_eq_bool_number() {
assert_eq!(eval("'1' == true"), "true");
}
// SC-3 Abstract Equality: object vs number via ToPrimitive ([42] -> "42" -> 42).
// 抽象相等:对象 vs 数字经 ToPrimitive([42] -> "42" -> 42)。
#[test]
fn abstract_eq_object_primitive() {
assert_eq!(eval("[42] == 42"), "true");
}
// SC-3 Abstract Equality: null == undefined (regression guard, spec step 2-3).
// 抽象相等:null == undefined(回归锚,规范第 2-3 步)。
#[test]
fn abstract_eq_null_undefined() {
assert_eq!(eval("null == undefined"), "true");
}
// SC-3 Abstract Equality: null == 0 is false (spec falls through to step 14).
// 抽象相等:null == 0 为 false(规范落到第 14 步)。
#[test]
fn abstract_eq_null_zero_mismatch() {
assert_eq!(eval("null == 0"), "false");
}
// SC-3 Abstract Equality: same-type delegates to strict equality.
// 抽象相等:同类型委托给严格相等。
#[test]
fn abstract_eq_same_type_string() {
assert_eq!(eval("'abc' == 'abc'"), "true");
}
// SC-4 Strict Equality: +0 === -0 is true (verify-only; already correct).
// 严格相等:+0 === -0 为 true(仅验证)。
#[test]
fn strict_eq_signed_zero_eval() {
assert_eq!(eval("+0 === -0"), "true");
}
// SC-4 Strict Equality: NaN === NaN is false (verify-only; already correct).
// 严格相等:NaN === NaN 为 false(仅验证)。
#[test]
fn strict_eq_nan_eval() {
assert_eq!(eval("NaN === NaN"), "false");
}
// SC-5 Arithmetic +: object operand coerces via valueOf.
// 算术 +:对象操作数经 valueOf 强转。
#[test]
fn add_object_valueof_coercion() {
assert_eq!(eval("var o = {valueOf: function () { return 42; }}; o + 1"), "43");
}
// SC-6 Relational: object operands coerce via ToPrimitive then compare as strings.
// 关系比较:对象操作数经 ToPrimitive 后按字符串比较。
#[test]
fn relational_object_coercion() {
assert_eq!(eval("[3] > [2]"), "true");
}
// SC-1/D-02 Date: Date object coerces to its timestamp in == (default hint -> valueOf).
// Date 对象在 == 中强转为自身时间戳(默认 hint -> valueOf)。
#[test]
fn date_object_equals_timestamp() {
assert_eq!(eval("new Date(0) == 0"), "true");
@@ -19,8 +19,7 @@ fn eval(source: &str) -> String {
}
}
// Destructured parameter followed by a loop body: the counter must count the parameter
// destructuring prologue or the loop's back-jump lands at the wrong PC.
// 解构参数后跟循环体:计数器必须计入参数解构 prologue,否则循环回跳落到错误 PC。
#[test]
fn param_array_pattern_with_loop_body() {
assert_eq!(eval("function f([a]){var s=0; while(a>0){s=s+1;a=a-1;} return s;} f([3])"), "3");
@@ -31,32 +30,31 @@ fn param_array_pattern_default_element() {
assert_eq!(eval("function f([a=5]){return a;} f([])"), "5");
}
// Array assignment target with a default element followed by a loop.
// 含默认元素的数组赋值目标后跟循环。
#[test]
fn array_assignment_default_element_then_loop() {
assert_eq!(eval("var a,c=0; [a=5]=[]; while(c<a){c=c+1;} c"), "5");
}
// Nested array assignment target element.
// 嵌套数组赋值目标元素。
#[test]
fn array_assignment_nested_element() {
assert_eq!(eval("var a,b; [[a],b]=[[1],2]; a*10+b"), "12");
}
// Destructuring catch param followed by a loop: the counter must not count a STORE_VAR the
// emitter does not emit for a pattern catch binding.
// 解构 catch 参数后跟循环:计数器不得计入模式 catch 绑定未发射的 STORE_VAR。
#[test]
fn destructuring_catch_param_then_loop() {
assert_eq!(eval("var c=0; try{throw 1;}catch({e}){} while(c<3){c=c+1;} c"), "3");
}
// Regression: for-init declaration without an initializer followed by loop iterations.
// 回归:for-init 声明无初始化器后跟循环迭代。
#[test]
fn for_init_no_initializer_declarator() {
assert_eq!(eval("var c=0; for(var i; c<3; c=c+1){} c"), "3");
}
// Identifier element array assignment stays correct (simple path unchanged).
// 标识符元素数组赋值保持正确(简单路径未变)。
#[test]
fn array_assignment_identifier_elements() {
assert_eq!(eval("var a,b; [a,b]=[7,8]; a*10+b"), "78");
@@ -19,8 +19,8 @@ fn eval(source: &str) -> String {
}
}
// A class body must not drift the instruction count of code that follows it. Each repro
// places a bounded loop after the class so a wrong count corrupts the loop's jump target.
// 类体不得漂移其后代码的指令计数。每个复现都在类后放置有界循环,
// 计数错误会破坏循环跳转目标。
#[test]
fn non_static_private_method_then_loop() {
@@ -32,8 +32,7 @@ fn static_private_method_then_loop() {
assert_eq!(eval("class C{static #m(){return 1;}} var c=0; while(c<3){c=c+1;} c"), "3");
}
// Multi-word computed key — a single-identifier key happens to be one word and would not
// expose the drift.
// 多字计算键——单标识符键恰好是单字,暴露不出漂移。
#[test]
fn computed_method_key_then_loop() {
assert_eq!(eval("class C{[\"x\"+\"y\"](){return 1;}} var c=0; while(c<3){c=c+1;} c"), "3");
@@ -44,8 +43,8 @@ fn computed_static_field_key_then_loop() {
assert_eq!(eval("class C{static [\"a\"+\"b\"]=1;} var c=0; while(c<3){c=c+1;} c"), "3");
}
// Derived constructor with an instance field and a loop after super(): the field code is
// injected after SUPER_CALL, so the loop's jump target must account for it.
// 含实例字段的派生构造函数在 super() 后跟循环:字段代码注入在 SUPER_CALL 之后,
// 循环跳转目标必须计入它。
#[test]
fn derived_ctor_field_then_post_super_loop() {
assert_eq!(
@@ -67,7 +66,7 @@ fn derived_ctor_statement_before_super() {
);
}
// Non-derived class with an instance field and a ctor loop stays correct (unchanged path).
// 非派生类含实例字段与构造循环保持正确(路径未变)。
#[test]
fn non_derived_ctor_field_then_loop() {
assert_eq!(
@@ -76,7 +75,7 @@ fn non_derived_ctor_field_then_loop() {
);
}
// Behavioral regressions: methods/getters/private calls still work.
// 行为回归:方法/getter/私有调用仍正常。
#[test]
fn plain_method_call_regression() {
assert_eq!(eval("class C{m(){return 9;}} new C().m()"), "9");
@@ -19,8 +19,8 @@ fn eval(source: &str) -> String {
}
}
// Non-simple `??` (side-effecting operands) followed by a label-bearing construct must not
// shift the downstream label id (which produced `Label ... not found` at emit time).
// 非简单 `??`(带副作用操作数)后跟带标签的构造不得把下游 label id 移位
// (否则 emit 期报 `Label ... not found`)。
#[test]
fn coalesce_nonsimple_then_if() {
@@ -38,10 +38,9 @@ fn coalesce_nonsimple_then_while() {
);
}
// A jump-bearing construct (if/loop) followed by a hoisted function declaration:
// the count pass must walk in the same function-hoisting order as the emit pass,
// otherwise the construct's jump targets drift past the hoisted body and the
// program loops forever at run time.
// 含跳转的构造(if/loop)后跟被提升的函数声明:
// 计数 pass 必须与 emit pass 采用相同的函数提升顺序,
// 否则构造的跳转目标越过提升的函数体,程序运行时无限循环。
#[test]
fn if_then_hoisted_function() {
@@ -58,9 +57,9 @@ fn while_then_hoisted_function() {
assert_eq!(eval("var n=0; while(n<3){n=n+1;} function f(){return 100} n + f()"), "103");
}
// A hoisted function declaration is emitted before the `var` statements it closes over,
// so the count pass must register top-level `var` names too (not just function names);
// otherwise the hoisted body cannot resolve the outer var ("Identifier not defined").
// 提升的函数声明先于它闭包的 `var` 语句发射,
// 因此计数 pass 必须同时登记顶层 `var` 名(不只是函数名);
// 否则提升的函数体无法解析外层 var("Identifier not defined")。
#[test]
fn hoisted_function_reads_outer_var() {
@@ -82,15 +81,14 @@ fn coalesce_nonsimple_then_for() {
#[test]
fn coalesce_nonsimple_short_circuits_on_value() {
// 0 is not nullish, so `a() ?? b()` is 0 and the loop never runs.
// 0 不是 nullish,因此 `a() ?? b()` 为 0,循环不会运行。
assert_eq!(
eval("function a(){return 0;} function b(){return 9;} var v=a()??b(); var c=0; while(c<v){c=c+1;} c"),
"0"
);
}
// Static member compound assignment must not over-count vs the emitter (the extra +1 drifted
// the following loop's jump target).
// 静态成员复合赋值不得相对 emitter 多计(多出的 +1 会漂移其后循环的跳转目标)。
#[test]
fn static_member_compound_assign_then_loop() {
@@ -102,7 +100,7 @@ fn static_member_compound_assign_in_loop() {
assert_eq!(eval("var o={x:0}; var c=0; while(o.x<3){o.x+=1; c=c+1;} c"), "3");
}
// Regressions: simple coalesce, nested coalesce, And/Or, compound member value, plain assign.
// 回归:简单 coalesce、嵌套 coalesce、And/Or、复合成员值、普通赋值。
#[test]
fn simple_coalesce_regression() {
assert_eq!(eval("var x = null ?? 5; x"), "5");
+4 -4
View File
@@ -1,8 +1,8 @@
//! B011 const guard 回归测试:const 声明路径不依赖寄存器槽新鲜度。
//! const guard 回归测试:const 声明路径不依赖寄存器槽新鲜度。
//!
//! 背景:`dispatch_store_var` 的 const guard(b!=0)读 `regs[rd]` 判断目标槽是否已初始化。
//! 声明路径带 b=1 时,checkpoint/寄存器复用残留陈旧值会误抛 "Assignment to constant variable"。
//! Phase 5 RegAlloc 复用寄存器后此 bug 必现——本测试锁死声明路径恒 b=0 的行为。
//! 声明路径带 b=1 时,checkpoint/寄存器复用残留的陈旧值会误抛 "Assignment to constant variable"。
//! 寄存器分配复用寄存器后此问题必现——本测试锁死声明路径恒 b=0 的行为。
use oxide_compiler::compiler::Compiler;
use oxide_parser::Allocator;
@@ -25,7 +25,7 @@ fn eval(source: &str) -> String {
}
}
// B011 复现样例:switch 语句(checkpoint 复用槽)后跟 const 声明。
// 复现样例:switch 语句(checkpoint 复用槽)后跟 const 声明。
// 修复前误抛 "Assignment to constant variable",修复后输出 5。
#[test]
fn switch_then_const_declaration_no_false_positive() {
@@ -22,35 +22,35 @@ fn assert_err_contains(result: Result<JsValue, String>, expected: &str) {
}
}
// -- Partial writable update preserves existing value --
// ── 部分 writable 更新保留既有值 ──
#[test]
fn define_property_partial_writable_preserves_value() {
let r = eval_str("var obj={}; obj.x=42; Object.defineProperty(obj,'x',{writable:false}); obj.x").unwrap();
assert!(r.is_int() && r.as_int() == 42, "value should be preserved at 42, got {:?}", r);
}
// -- Mixed descriptor (value+get) throws TypeError --
// ── 混合描述符(value+get)抛 TypeError ──
#[test]
fn define_property_mixed_value_get_throws() {
let result = eval_str("var obj={}; Object.defineProperty(obj,'x',{value:1,get:function(){}});");
assert_err_contains(result, "mix");
}
// -- Mixed descriptor (writable+set) throws TypeError --
// ── 混合描述符(writable+set)抛 TypeError ──
#[test]
fn define_property_mixed_writable_set_throws() {
let result = eval_str("var obj={}; Object.defineProperty(obj,'x',{writable:true,set:function(){}});");
assert_err_contains(result, "mix");
}
// -- Partial enumerable-only update preserves value --
// ── 仅 enumerable 的部分更新保留既有值 ──
#[test]
fn define_property_partial_enumerable_only_preserves_value() {
let r = eval_str("var obj={}; obj.x=42; Object.defineProperty(obj,'x',{enumerable:true}); obj.x").unwrap();
assert!(r.is_int() && r.as_int() == 42, "value should be preserved at 42, got {:?}", r);
}
// -- getOwnPropertyDescriptor returns data fields (no get/set) --
// ── getOwnPropertyDescriptor 返回数据字段(无 get/set)──
#[test]
fn get_own_property_descriptor_data_fields() {
let r = eval_str(
@@ -70,7 +70,7 @@ fn get_own_property_descriptor_data_fields() {
assert!(obj.get_prop_at(5).is_undefined(), "set should be undefined");
}
// -- getOwnPropertyDescriptor returns accessor fields (no value/writable) --
// ── getOwnPropertyDescriptor 返回访问器字段(无 value/writable)──
#[test]
fn get_own_property_descriptor_accessor_fields() {
let r = eval_str(
@@ -92,7 +92,7 @@ fn get_own_property_descriptor_accessor_fields() {
);
}
// -- New property without value or accessor defaults to data descriptor (ES spec behavior) --
// ── 无 value/accessor 的新属性默认为数据描述符(ES 规范行为)──
#[test]
fn define_property_new_attributes_only_defaults_value_undefined() {
let r = eval_str("var obj={}; Object.defineProperty(obj,'x',{enumerable:true}); obj.x").unwrap();
@@ -103,7 +103,7 @@ fn define_property_new_attributes_only_defaults_value_undefined() {
);
}
// -- Partial configurable on existing property preserves value --
// ── 既有属性的部分 configurable 更新保留既有值 ──
#[test]
fn define_property_partial_configurable_preserves_value() {
let r = eval_str("var obj={}; obj.x=1; Object.defineProperty(obj,'x',{configurable:false}); [obj.x]").unwrap();
+4 -5
View File
@@ -88,10 +88,9 @@ fn function_and_method_parameter_destructuring() {
assert_num(result, 10.0);
}
// Regression: a for-of whose assignment target has a NESTED rest pattern (the rest
// element is itself a pattern, e.g. `[...[x]]`) used to loop forever / error, because the
// counter undercounted the rest target's instructions vs the emitter, corrupting the loop's
// jump offsets. (Object rest with a nested target is a syntax error per spec, so untested.)
// 回归:赋值目标含嵌套 rest 模式(rest 元素本身是模式,如 `[...[x]]`)的 for-of
// 曾无限循环 / 报错,因为计数器相对 emitter 少计了 rest 目标的指令,破坏了循环跳转
// 偏移。(对象 rest 嵌套目标按规范是语法错误,故不测。)
#[test]
fn for_of_assignment_nested_rest_target_runs_once() {
let mut vm = Vm::new();
@@ -109,7 +108,7 @@ fn for_of_assignment_nested_rest_empty_body_binds_inner() {
#[test]
fn for_of_assignment_nested_rest_with_leading_element() {
let mut vm = Vm::new();
// outer iterates once; a=1, rest=[2,3,4], then [x,y] from rest -> x=2, y=3
// 外层迭代一次;a=1,rest=[2,3,4],再从 rest 解构 [x,y] -> x=2, y=3
let result = eval(&mut vm, "var a,x,y; for([a,...[x,y]] of [[1,2,3,4]]){} a*100+x*10+y").unwrap();
assert_num(result, 123.0);
}
@@ -44,15 +44,14 @@ fn for_in_null_does_not_execute_body() {
#[test]
fn for_in_null_does_not_throw() {
// Must NOT produce a "vm error: ..." — a quiet empty loop, then a sentinel value.
// 不得产生 "vm error: ..."——静默空循环,然后哨兵值。
assert_eq!(eval("for(var k in null){} 7"), "7", "for-in null must not throw");
}
#[test]
fn for_in_number_primitive_currently_throws() {
// Interim behavior: ToObject coercion for non-null/undefined primitives is not
// implemented yet, so a number right-hand side still throws TypeError. When
// ToObject lands this becomes an empty loop and this assertion should change.
// 过渡行为:非 null/undefined 基本类型的 ToObject 强转尚未实现,
// 因此数字右侧仍抛 TypeError。ToObject 落地后它应变成空循环,届时本断言需变更。
let out = eval("for(var k in 42){}");
assert!(out.contains("TypeError"), "expected TypeError for number primitive, got: {out}");
}
@@ -19,10 +19,9 @@ fn eval(source: &str) -> String {
}
}
// Order is asserted via a position map built inside JS, so the comparison
// returns a boolean (JsValue Display only reveals number/bool, not string
// contents). This is also tolerant of any inherited enumerable keys the
// engine appends after the object's own keys.
// 顺序经 JS 内部构建的位置映射断言,比较返回布尔(JsValue 的 Display 只暴露
// number/bool,不暴露字符串内容)。该写法也容忍引擎在对象自有键之后追加的
// 任意继承可枚举键。
#[test]
fn for_in_ordering_integer_indices_first() {
@@ -19,10 +19,10 @@ fn eval(source: &str) -> String {
}
}
// Spec (ECMA-262 §13.7.5): next()-throw propagates the ORIGINAL value WITHOUT calling
// return(); IteratorClose (return()) runs only on BODY abrupt completion (throw/break/return).
// return() is observed via globalThis (a function CAN read/write globalThis properties;
// it cannot write a plain outer `var` in this engine).
// 规范(ECMA-262 §13.7.5):next()-throw 传播原始值且不调用 return();
// IteratorClose(return())只在循环体突然完成(throw/break/return)时执行。
// return() 经 globalThis 观测(函数可读写 globalThis 属性;
// 本引擎中它不能写入外层普通 `var`)。
#[test]
fn for_of_next_throw_is_catchable_with_original_type() {
@@ -19,10 +19,10 @@ fn eval(source: &str) -> String {
}
}
// Assertions stay inside JS and resolve to a boolean/number, because JsValue Display
// reveals numbers/bools but not string/object contents.
// 断言放在 JS 内部并解析为布尔/数字,因为 JsValue 的 Display 只暴露
// 数字/布尔,不暴露字符串/对象内容。
// ---- for-of loop over Map/Set (default iterators) ----
// ── 对 Map/Set(默认迭代器)的 for-of ──
#[test]
fn for_of_map_yields_key_value_entries() {
@@ -52,7 +52,7 @@ fn for_of_empty_set_runs_zero_times() {
assert_eq!(eval("var s=new Set();var r=0;for(var v of s){r=r+1;}r===0"), "true");
}
// ---- Map.prototype.{entries,values,keys} ----
// ── Map.prototype.{entries,values,keys} ──
#[test]
fn map_values_iterator_yields_values() {
@@ -80,7 +80,7 @@ fn map_iterator_reports_done_at_end() {
assert_eq!(eval("var m=new Map();var it=m.values();it.next().done===true"), "true");
}
// ---- Set.prototype.{entries,values,keys} ----
// ── Set.prototype.{entries,values,keys} ──
#[test]
fn set_values_iterator_yields_values() {
+3 -3
View File
@@ -127,7 +127,7 @@ fn fd_calls_builtin_two_args() {
assert!((result.as_double() - 9.0).abs() < 0.0001);
}
// --- Multiple FDs, first FD calls second ---
// ── 多个函数声明,首个调用第二个 ──
#[test]
fn fd_chain_call() {
@@ -136,7 +136,7 @@ fn fd_chain_call() {
assert_eq!(result.as_int(), 10);
}
// --- new Xxx() in function ---
// ── 函数内的 new Xxx() ──
#[test]
fn fd_returns_new_object() {
@@ -152,7 +152,7 @@ fn fd_returns_new_array() {
assert!(result.is_object());
}
// --- this expression ---
// ── this 表达式 ──
#[test]
fn this_in_function_reads_value() {
@@ -22,7 +22,7 @@ fn assert_err_contains(result: Result<JsValue, String>, expected: &str) {
}
}
// -- Delete configurable property succeeds --
// ── 删除可配置属性成功 ──
#[test]
fn delete_configurable_property_succeeds_and_returns_true() {
let r = eval_many(&[
@@ -34,7 +34,7 @@ fn delete_configurable_property_succeeds_and_returns_true() {
assert!(r.is_bool() && r.as_bool(), "delete configurable should return true, got {:?}", r);
}
// -- Delete non-configurable property throws --
// ── 删除不可配置属性抛错 ──
#[test]
fn delete_non_configurable_throws_type_error() {
let result = eval_many(&[
@@ -45,21 +45,21 @@ fn delete_non_configurable_throws_type_error() {
assert_err_contains(result, "cannot delete non-configurable property");
}
// -- Delete non-existent property returns true --
// ── 删除不存在的属性返回 true ──
#[test]
fn delete_non_existent_returns_true() {
let r = eval_many(&["var obj = {}", "delete obj.x"]).unwrap();
assert!(r.is_bool() && r.as_bool(), "delete non-existent should return true, got {:?}", r);
}
// -- Delete default (no explicit defineProperty) configurable succeeds --
// ── 删除默认(无显式 defineProperty)可配置属性成功 ──
#[test]
fn delete_default_configurable_succeeds() {
let r = eval_many(&["var obj = {}", "obj.x = 1", "delete obj.x"]).unwrap();
assert!(r.is_bool() && r.as_bool(), "delete default configurable should return true, got {:?}", r);
}
// -- Delete non-configurable proto property on child: child doesn't own it, so returns true --
// ── 子对象删除原型上不可配置的属性:子对象不拥有它,因此返回 true ──
#[test]
fn delete_non_configurable_proto_property_on_child_returns_true() {
let r = eval_many(&[
@@ -22,7 +22,7 @@ fn keys_to_strings(result: JsValue) -> Vec<String> {
for i in 0..len {
let v = obj.get_prop_at(i);
if v.is_string() {
// SAFETY: string value pointing into VM arena (caller keeps vm alive)
// SAFETY: 字符串值指向 VM arena(调用方保持 vm 存活)。
let s = unsafe { &*v.as_string_ptr() };
keys.push(s.data.clone());
}
@@ -30,7 +30,7 @@ fn keys_to_strings(result: JsValue) -> Vec<String> {
keys
}
// -- Integer keys in ascending order --
// ── 整型键按升序 ──
#[test]
fn keys_integer_order_ascending() {
let r = eval_str("var obj={}; obj[3]='c'; obj[1]='a'; obj[2]='b'; Object.keys(obj).length").unwrap();
@@ -40,7 +40,7 @@ fn keys_integer_order_ascending() {
assert_eq!(keys, vec!["1", "2", "3"], "integer keys should be ascending, got {:?}", keys);
}
// -- Mixed keys: integers first, then strings in creation order --
// ── 混合键:整型优先,字符串按创建序 ──
#[test]
fn keys_mixed_order_integers_first() {
let (_vm, r) = eval("var obj={}; obj[2]='b'; obj['1']='a'; obj['a']=1; Object.keys(obj)").unwrap();
@@ -58,7 +58,7 @@ fn keys_mixed_order_integers_first() {
}
}
// -- Object.keys excludes non-enumerable properties --
// ── Object.keys 排除不可枚举属性 ──
#[test]
fn keys_excludes_non_enumerable() {
let r = eval_str(
@@ -68,7 +68,7 @@ fn keys_excludes_non_enumerable() {
assert!(r.is_int() && r.as_int() == 1, "keys should exclude non-enumerable, got {:?}", r);
}
// -- Object.getOwnPropertyNames includes non-enumerable properties --
// ── Object.getOwnPropertyNames 包含不可枚举属性 ──
#[test]
fn get_own_property_names_includes_non_enumerable() {
let r = eval_str("var obj={a:1}; Object.defineProperty(obj,'hidden',{value:2,enumerable:false}); Object.getOwnPropertyNames(obj).length").unwrap();
@@ -79,7 +79,7 @@ fn get_own_property_names_includes_non_enumerable() {
);
}
// -- Object.entries excludes non-enumerable properties --
// ── Object.entries 排除不可枚举属性 ──
#[test]
fn entries_excludes_non_enumerable() {
let r = eval_str(
@@ -89,7 +89,7 @@ fn entries_excludes_non_enumerable() {
assert!(r.is_int() && r.as_int() == 1, "entries should exclude non-enumerable, got {:?}", r);
}
// -- Object.values excludes non-enumerable properties --
// ── Object.values 排除不可枚举属性 ──
#[test]
fn values_excludes_non_enumerable() {
let r = eval_str(
@@ -99,7 +99,7 @@ fn values_excludes_non_enumerable() {
assert!(r.is_int() && r.as_int() == 1, "values should exclude non-enumerable, got {:?}", r);
}
// -- Leading zero strings are NOT integer indices --
// ── 前导零字符串不是整型下标 ──
#[test]
fn keys_with_leading_zero_not_integer_index() {
let (_vm, r) = eval("Object.keys({'01':'a','1':'b'})").unwrap();
@@ -15,7 +15,7 @@ fn eval_str(source: &str) -> Result<JsValue, String> {
eval(source).map(|(_vm, v)| v)
}
// Getter on proto sets marker on receiver, then check receiver has marker (not proto)
// 原型上的 getter 在 receiver 上设置标记,随后检查 receiver 持有标记(而非原型)。
#[test]
fn getter_on_proto_this_is_receiver() {
let r = eval_str(
@@ -24,7 +24,7 @@ fn getter_on_proto_this_is_receiver() {
assert!(r.is_int() && r.as_int() == 1, "getter should set marker on child (receiver), got {:?}", r);
}
// Getter on deep proto chain: grandchild → child → proto. Getter defined on proto.
// 深层原型链上的 getter:grandchild → child → proto,getter 定义在 proto 上。
#[test]
fn getter_on_proto_deep_chain_this_is_receiver() {
let r = eval_str(
@@ -33,7 +33,7 @@ fn getter_on_proto_deep_chain_this_is_receiver() {
assert!(r.is_bool() && r.as_bool(), "grandchild should have _deep marker, got {:?}", r);
}
// Setter on proto receives receiver as this, sets marker on receiver not proto
// 原型上的 setter 以 receiver 作为 this,在 receiver 而非原型上设置标记。
#[test]
fn setter_on_proto_this_is_receiver() {
let (_vm, r) = eval(
@@ -42,7 +42,7 @@ fn setter_on_proto_this_is_receiver() {
assert!(r.is_string(), "child should have _set_by_setter, got {:?}", r);
}
// Dynamic getter (obj[expr]) preserves receiver
// 动态 getter(obj[expr])保持 receiver。
#[test]
fn dynamic_getter_on_proto_this_is_receiver() {
let r = eval_str(
@@ -51,7 +51,7 @@ fn dynamic_getter_on_proto_this_is_receiver() {
assert!(r.is_int() && r.as_int() == 1, "dynamic getter should set marker on child, got {:?}", r);
}
// Dynamic setter (obj[expr]=val) preserves receiver
// 动态 setter(obj[expr]=val)保持 receiver。
#[test]
fn dynamic_setter_on_proto_this_is_receiver() {
let (_vm, r) = eval(
@@ -60,7 +60,7 @@ fn dynamic_setter_on_proto_this_is_receiver() {
assert!(r.is_string(), "child should have _dyn_set after dynamic setter, got {:?}", r);
}
// Own getter on object — this is the object itself
// 对象自身的 getter——this 即对象本身。
#[test]
fn own_getter_this_is_self() {
let r = eval_str(
@@ -69,7 +69,7 @@ fn own_getter_this_is_self() {
assert!(r.is_int() && r.as_int() == 42, "own getter should set marker on self, got {:?}", r);
}
// Own setter on object — this is the object itself
// 对象自身的 setter——this 即对象本身。
#[test]
fn own_setter_this_is_self() {
let (_vm, r) = eval(
@@ -78,7 +78,7 @@ fn own_setter_this_is_self() {
assert!(r.is_string(), "own setter should set marker on self, got {:?}", r);
}
// Getter returning undefined does not crash
// 返回 undefined 的 getter 不崩溃。
#[test]
fn getter_returns_undefined_does_not_crash() {
let r = eval_str(
@@ -87,7 +87,7 @@ fn getter_returns_undefined_does_not_crash() {
assert!(r.is_undefined(), "getter returning nothing should yield undefined, got {:?}", r);
}
// String .length still works after proto getter is defined (primitive auto-boxing not broken)
// 定义原型 getter 后字符串 .length 仍正常(基本类型自动装箱未被破坏)。
#[test]
fn string_length_still_works_after_proto_getter() {
let r = eval_str("'hello'.length").unwrap();
@@ -22,7 +22,7 @@ fn assert_err_contains(result: Result<JsValue, String>, expected: &str) {
}
}
// -- Assign to non-writable own property throws TypeError --
// ── 向不可写自身属性赋值抛 TypeError ──
#[test]
fn assign_to_non_writable_throws() {
let result = eval_many(&[
@@ -33,7 +33,7 @@ fn assign_to_non_writable_throws() {
assert_err_contains(result, "read-only");
}
// -- Assign to writable own property succeeds --
// ── 向可写自身属性赋值成功 ──
#[test]
fn assign_to_writable_succeeds() {
let r = eval_many(&[
@@ -46,7 +46,7 @@ fn assign_to_writable_succeeds() {
assert!(r.is_int() && r.as_int() == 2, "assign to writable should set value to 2, got {:?}", r);
}
// -- Assign to inherited non-writable property on proto throws --
// ── 向原型上不可写的继承属性赋值抛错 ──
#[test]
fn assign_to_inherited_non_writable_throws() {
let result = eval_many(&[
@@ -58,9 +58,9 @@ fn assign_to_inherited_non_writable_throws() {
assert_err_contains(result, "read-only");
}
// -- Assign creates own property on child when proto's is non-writable but child has no own prop
// ── 原型属性不可写但子对象无自有属性时,赋值会在子对象上创建自有属性
// ponytail: spec allows shadowing when proto prop is configurable; engine doesn't yet.
// The engine correctly throws for non-writable proto props. Full shadowing is a follow-up.
// 引擎对不可写原型属性正确抛错;完整遮蔽作为后续工作。
#[test]
fn assign_non_writable_proto_configurable_prop_throws() {
let result = eval_many(&[
+14 -14
View File
@@ -24,7 +24,7 @@ macro_rules! assert_undefined {
};
}
// -- Proto chains: every (non-Object) constructor.prototype should inherit from Object.prototype --
// ── 原型链:每个(非 Object)构造器的 prototype 都应继承自 Object.prototype ──
#[test]
fn array_proto_chain() {
assert_bool!(eval("Object.getPrototypeOf(Array.prototype) === Object.prototype"), true, "Array proto");
@@ -32,10 +32,10 @@ fn array_proto_chain() {
#[test]
fn function_proto_chain() {
// NOTE: Function global is not registered (pre-existing issue).
// Function.prototype.__proto__ IS wired to Object.prototype via kernel patch.
// Verified by checking that Function.prototype (accessed via other means) has correct proto.
// Skip this test until Function global is registered.
// 未支持:Function 全局尚未注册(既有能力限制)。
// Function.prototype.__proto__ 已经由 kernel 补丁接到 Object.prototype。
// 通过其它途径访问 Function.prototype 可验证其原型正确。
// 待 Function 全局注册后再启用本测试。
}
#[test]
@@ -87,14 +87,14 @@ fn regexp_proto_chain() {
assert_bool!(eval("Object.getPrototypeOf(RegExp.prototype) === Object.prototype"), true, "RegExp proto");
}
// -- Object.prototype is the root, its __proto__ is null --
// ── Object.prototype 是根,其 __proto__ 为 null ──
#[test]
fn object_proto_is_root() {
let r = eval("Object.getPrototypeOf(Object.prototype)").unwrap();
assert!(r.is_null(), "Object.prototype.__proto__ should be null");
}
// -- instanceof now works for all builtins against Object --
// ── 所有 builtin 对 Object 的 instanceof 现在都成立 ──
#[test]
fn array_instanceof_object() {
assert_bool!(eval("[] instanceof Object"), true, "[] instanceof Object");
@@ -108,16 +108,16 @@ fn function_instanceof_object() {
#[test]
fn string_instanceof_object() {
// NOTE: new String requires NEW_EXPRESSION support for user constructors.
// Pre-existing limitation. Verify String.prototype is accessible.
// 未支持:new String 依赖用户构造器的 NEW_EXPRESSION 支持(既有能力限制)。
// 验证 String.prototype 可访问。
let r = eval("typeof String.prototype").unwrap();
assert!(r.is_string(), "String.prototype should be object");
}
#[test]
fn boolean_not_instanceof_function() {
// NOTE: Function global is not registered (pre-existing issue).
// Skip until Function global registration is fixed.
// 未支持:Function 全局尚未注册(既有能力限制)。
// 待 Function 全局注册修复后再启用。
}
#[test]
@@ -179,7 +179,7 @@ fn derived_constructor_inherits_parent_constructor_chain() {
);
}
// -- instanceof for native types should NOT respond to their own false positives --
// ── native 类型的 instanceof 不应响应自身的误判 ──
#[test]
fn number_not_instanceof_array() {
assert_bool!(eval("new Number(1) instanceof Array"), false, "Number instanceof Array");
@@ -190,7 +190,7 @@ fn object_not_instanceof_regexp() {
assert_bool!(eval("({}) instanceof RegExp"), false, "({}) instanceof RegExp");
}
// -- Primitives should NOT be instanceof their wrapper constructors (no autoboxing) --
// ── 基本类型不应 instanceof 其包装构造器(不自动装箱)──
#[test]
fn primitives_not_instanceof() {
assert_bool!(eval("1 instanceof Number"), false, "1 instanceof Number");
@@ -198,7 +198,7 @@ fn primitives_not_instanceof() {
assert_bool!(eval("true instanceof Boolean"), false, "true instanceof Boolean");
}
// -- VOID regression --
// ── VOID 回归 ──
#[test]
fn void_returns_undefined() {
assert_undefined!(eval("void 0"), "void 0");
+2 -2
View File
@@ -95,11 +95,11 @@ fn closure_nested_expr_capture() {
#[test]
fn for_let_per_iteration_independent() {
let _ = eval("var fns=[]; for(let i=0;i<3;i++){ fns.push(function(){return i}); } fns[0]()+fns[1]()+fns[2]()");
// TODO: Plan 04 — implement for-let per-iteration binding
// 未支持:for-let 逐次迭代绑定尚未实现。
}
#[test]
fn tdz_access_before_init_throws() {
let _ = eval("x; let x=1");
// TODO: Plan 04 — implement precise TDZ
// 未支持:精确 TDZ 尚未实现。
}
+4 -4
View File
@@ -1,4 +1,4 @@
//! VM spill 栈执行语义测试(D-06/D-07/D-08):MOV 复制、SPILL/UNSPILL 往返、
//! VM spill 栈执行语义测试:MOV 复制、SPILL/UNSPILL 往返、
//! 帧边界(父 spill 跨子调用完好)、嵌套双 spill 交互、UNSPILL 越界防御。
//!
//! 手工 IR 是唯一可达路径——RegAlloc 未接入,真实 JS 编译产物不会含三指令。
@@ -28,7 +28,7 @@ fn int_module(insts: Vec<Inst>, value: i32, n_registers: u8, nested: Vec<IRFunct
f
}
/// 测试 1:MOV 寄存器复制语义(D-07)。r3=42 → MOV r5=r3 → HALT 输出 42。
/// 测试 1:MOV 寄存器复制语义。r3=42 → MOV r5=r3 → HALT 输出 42。
#[test]
fn mov_copies_register() {
let insts = vec![
@@ -41,7 +41,7 @@ fn mov_copies_register() {
assert_eq!(out, "42");
}
/// 测试 2:SPILL→UNSPILL 往返(D-06)。SPILL r3 slot0 → MOV 弄脏 r5 → UNSPILL r5 slot0 → 读回 42。
/// 测试 2:SPILL→UNSPILL 往返。SPILL r3 slot0 → MOV 弄脏 r5 → UNSPILL r5 slot0 → 读回 42。
#[test]
fn spill_unspill_roundtrip() {
let insts = vec![
@@ -56,7 +56,7 @@ fn spill_unspill_roundtrip() {
assert_eq!(out, "42");
}
/// 测试 3:帧边界——父函数 SPILL 后 CALL 子函数,子函数返回后父 UNSPILL 仍得 42(D-08)。
/// 测试 3:帧边界——父函数 SPILL 后 CALL 子函数,子函数返回后父 UNSPILL 仍得 42。
#[test]
fn frame_boundary_parent_spill_survives_call() {
let child = int_module(
+6 -7
View File
@@ -87,22 +87,21 @@ fn template_numeric_expression() {
assert_eq!(to_str(&vm, result.unwrap()), "2");
}
// -- Template tagging tests (basic) --
// ── 模板标签调用测试(基础)──
#[test]
fn tagged_template_basic() {
// Use Math.max as a simple native tag function to verify the CALL dispatch works.
// Math.max(cooked_array, raw_array, 42) should return 42 (the max of the args).
// This avoids bytecode function call complexity.
// 用 Math.max 作为简单 native 标签函数,验证 CALL 分发可用。
// Math.max(cooked_array, raw_array, 42) 应返回 42(参数最大值)。
// 由此避开字节码函数调用复杂度。
let result = eval("Math.max`hello ${42} world`");
// Math.max on the args should work - just verify it doesn't crash
// Math.max 作用于参数——仅验证不崩溃。
assert!(!result.starts_with("vm error:"), "Tagged template should not crash, got: {}", result);
}
#[test]
fn tagged_template_compiles_no_error() {
// Verify that tagged templates compile without error (even if the tag
// function behavior isn't fully tested)
// 验证带标签模板编译不报错(即使标签函数行为未被完整测试)。
let result = eval("function t(s,v){ return s[0]+v; } t`x${1}`");
assert!(!result.starts_with("compile error:"), "Tagged template should compile");
}
+1 -1
View File
@@ -69,7 +69,7 @@ fn test_throw_err_type_error_catchable() {
#[test]
fn test_throw_err_type_error_is_object() {
// IC_GET_PROP on null throws TypeError which is catchable; catch body runs
// 对 null 的 IC_GET_PROP 抛可捕获的 TypeError;catch 体执行。
let result = eval("try { null.prop; } catch(e) { 99; }");
assert_eq!(result, "99");
}