diff --git a/Cargo.lock b/Cargo.lock index 55fe4627..cf3caa4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.82" +version = "0.8.83" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.82" +version = "0.8.83" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.82" +version = "0.8.83" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.82" +version = "0.8.83" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.82" +version = "0.8.83" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.82" +version = "0.8.83" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.82" +version = "0.8.83" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.82" +version = "0.8.83" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.82" +version = "0.8.83" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.82" +version = "0.8.83" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.82" +version = "0.8.83" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 1a311b87..eea107b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.8.82" +version = "0.8.83" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/array_buffer.rs b/crates/oxide_builtins/src/array_buffer.rs index 4cfbeac9..0510719a 100644 --- a/crates/oxide_builtins/src/array_buffer.rs +++ b/crates/oxide_builtins/src/array_buffer.rs @@ -1457,4 +1457,137 @@ mod tests { .unwrap(); assert_eq!(vm.lookup_str(r).unwrap(), "TypeError"); } + + /// growable 构造器预分配账目钉:byteLength 读活长、maxByteLength 读真实 + /// 上限、growable 为真;核的 `shared_buffer_bytes` 增量等于真实上限 + /// (预分配一次性到位),而非初始活长。定长对照臂:增量即活长、 + /// growable 为假、maxByteLength 解码取活长。 + #[test] + fn growable_constructor_preallocates_to_max() { + let mut vm = Vm::new(); + let before = vm.kernel_core().shared_buffer_bytes(); + let r = eval_ab( + &mut vm, + "var sab = new SharedArrayBuffer(2, {maxByteLength: 8}); \ + globalThis.__sab = sab; \ + [sab.byteLength, sab.maxByteLength, sab.growable].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "2,8,true"); + assert_eq!( + vm.kernel_core().shared_buffer_bytes() - before, + 8, + "growable 预分配账目是真实上限而非初始活长" + ); + // 定长对照臂:账目即活长,growable 为假,maxByteLength 解码取活长。 + let before_fixed = vm.kernel_core().shared_buffer_bytes(); + let r = eval_ab( + &mut vm, + "var fixed = new SharedArrayBuffer(4); \ + globalThis.__fixed = fixed; \ + [fixed.byteLength, fixed.maxByteLength, fixed.growable].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "4,4,false"); + assert_eq!(vm.kernel_core().shared_buffer_bytes() - before_fixed, 4); + } + + /// grow 前缀保留与零填充钉:写入前缀后 grow,前缀字节不变、增补区 + /// 全零、活长推进到目标值。 + #[test] + fn grow_preserves_prefix_and_zero_fills() { + let mut vm = Vm::new(); + let r = eval_ab( + &mut vm, + "(function () { var sab = new SharedArrayBuffer(2, {maxByteLength: 8}); \ + var v = new Uint8Array(sab); v[0] = 0xAA; v[1] = 0xBB; \ + sab.grow(6); \ + var v2 = new Uint8Array(sab); \ + return v2[0] === 0xAA && v2[1] === 0xBB && v2[2] === 0 && v2[3] === 0 \ + && v2[4] === 0 && v2[5] === 0 && sab.byteLength === 6; })()", + ) + .unwrap(); + assert!(r.as_bool()); + } + + /// grow-only 与超上限 RangeError 钉:缩长(当前活长以下任意值)与超 + /// 真实上限各抛 RangeError,恰好到上限成功;恰好等于当前活长是 no-op + /// 成功(只增不缩,相等放行)。 + #[test] + fn grow_only_and_over_max_range_errors() { + let mut vm = Vm::new(); + let r = eval_ab( + &mut vm, + "(function () { var sab = new SharedArrayBuffer(4, {maxByteLength: 8}); \ + var out = ''; \ + try { sab.grow(3); out += 'no-throw'; } \ + catch (e) { out += (e.name === 'RangeError' ? 'R' : 'X'); } \ + try { sab.grow(4); out += 'ok'; } \ + catch (e) { out += 'X'; } \ + try { sab.grow(9); out += 'no-throw'; } \ + catch (e) { out += (e.name === 'RangeError' ? 'R' : 'X'); } \ + try { sab.grow(8); out += 'ok'; } \ + catch (e) { out += 'X'; } \ + return out + '|' + sab.byteLength; })()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "RokRok|8"); + } + + /// grow 非 growable 守卫钉:定长 SAB 调 grow 抛 TypeError,守卫先于 + /// 参数强转(valueOf 副作用不运行)。 + #[test] + fn grow_non_growable_guard_before_coercion() { + let mut vm = Vm::new(); + let r = eval_ab( + &mut vm, + "(function () { var calls = 0; \ + var sab = new SharedArrayBuffer(4); \ + try { sab.grow({ valueOf() { calls++; return 8; } }); \ + return 'no-throw' + calls; } \ + catch (err) { return (err.name === 'TypeError' ? 'T' : 'X') + calls; } })()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "T0"); + } + + /// grow 强转窗口重取钉:界判消费强转后重取的活长——valueOf 内先 + /// grow 推进活长,外层 grow 到重取活长之下抛 RangeError(若消费 + /// 求值前快照则会成功)。 + #[test] + fn grow_coercion_window_refetch() { + let mut vm = Vm::new(); + let r = eval_ab( + &mut vm, + "(function () { var sab = new SharedArrayBuffer(4, {maxByteLength: 8}); \ + try { sab.grow({ valueOf() { sab.grow(6); return 5; } }); \ + return 'no-throw|' + sab.byteLength; } \ + catch (e) { return (e.name === 'RangeError' ? 'R' : 'X') + '|' + sab.byteLength; } })()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "R|6"); + } + + /// SAB 无 detach 面钉:$262.detachArrayBuffer 对 SAB 抛 TypeError + /// (读入口标签校验臂),SAB 原型无 AB 专属方法(transfer / resize / + /// markImmutable / sliceToImmutable 均 undefined),detach 失败后缓冲 + /// 状态不变。 + #[test] + fn sab_has_no_detach_surface() { + let mut vm = Vm::new(); + let r = eval_ab( + &mut vm, + "(function () { var sab = new SharedArrayBuffer(4, {maxByteLength: 8}); \ + var out = ''; \ + try { $262.detachArrayBuffer(sab); out += 'no-throw'; } \ + catch (e) { out += (e.name === 'TypeError' ? 'T' : 'X'); } \ + out += ',' + (sab.transfer === undefined ? 'u' : 'x') \ + + '|' + (sab.resize === undefined ? 'u' : 'x') \ + + '|' + (sab.markImmutable === undefined ? 'u' : 'x') \ + + '|' + (sab.sliceToImmutable === undefined ? 'u' : 'x'); \ + return out + '|' + sab.byteLength + '|' + sab.growable; })()", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "T,u|u|u|u|4|true"); + } }