chore: 全仓 rustfmt 格式化归拢
This commit is contained in:
@@ -11,10 +11,10 @@
|
||||
pub mod array;
|
||||
/// ArrayBuffer 内置对象实现(字节缓冲区与 byteLength/slice/isView)。
|
||||
pub mod array_buffer;
|
||||
/// Boolean 内置对象实现(constructor 与 prototype 的 valueOf/toString)。
|
||||
pub mod boolean;
|
||||
/// BigInt 内置对象实现(constructor 与 prototype 的 toString)。
|
||||
pub mod bigint;
|
||||
/// Boolean 内置对象实现(constructor 与 prototype 的 valueOf/toString)。
|
||||
pub mod boolean;
|
||||
/// builtins 层日志宏(`builtins_error`/`builtins_info` 等),target 为 `oxide::builtins`。
|
||||
pub mod builtins_log;
|
||||
/// DataView 内置对象实现(对 ArrayBuffer 的定点读写视图)。
|
||||
|
||||
@@ -25,12 +25,7 @@ pub fn module_object<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
let tag_si = vm.kernel_core().perm_interner().intern("@@toStringTag").0;
|
||||
let tag_val = vm.new_string("Module");
|
||||
let obj_ref = unsafe { &mut *obj };
|
||||
if let Err(e) = vm.define_data_property(
|
||||
obj_ref,
|
||||
tag_si,
|
||||
tag_val,
|
||||
PropAttributes::new(false, false, false),
|
||||
) {
|
||||
if let Err(e) = vm.define_data_property(obj_ref, tag_si, tag_val, PropAttributes::new(false, false, false)) {
|
||||
return NativeResult::Err(crate::error::create_error(vm, &e));
|
||||
}
|
||||
NativeResult::Ok(JsValue::from_js_object(obj))
|
||||
@@ -98,10 +93,7 @@ pub fn module_link_get<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let name_si = vm.property_key_si(name_val);
|
||||
let obj = unsafe { &*ns_ptr };
|
||||
if vm.get_own_property_slot(obj, name_si).is_none() {
|
||||
return NativeResult::Err(crate::error::create_syntax_error(
|
||||
vm,
|
||||
"requested module export is not exported",
|
||||
));
|
||||
return NativeResult::Err(crate::error::create_syntax_error(vm, "requested module export is not exported"));
|
||||
}
|
||||
match vm.ordinary_get(obj, name_si, ns_val) {
|
||||
Ok(v) => NativeResult::Ok(v),
|
||||
|
||||
@@ -455,7 +455,8 @@ fn instant_like_epoch_ns<H: VmHost>(vm: &mut H, value: JsValue) -> Result<i128,
|
||||
}
|
||||
// 对象按规范走 ToString -> ParseTemporalInstantString(解析失败 RangeError)。
|
||||
let input = oxide_runtime_api::to_string_full(value, vm).map_err(|error| native_engine_error(vm, &error))?;
|
||||
return parse_instant_string(&input).ok_or_else(|| crate::error::create_range_error(vm, "invalid ISO 8601 string"));
|
||||
return parse_instant_string(&input)
|
||||
.ok_or_else(|| crate::error::create_range_error(vm, "invalid ISO 8601 string"));
|
||||
}
|
||||
// 非字符串原始值(undefined/null/boolean/number/bigint/symbol)按规范直接抛 TypeError。
|
||||
if !value.is_string() {
|
||||
@@ -1728,16 +1729,9 @@ pub fn duration_total<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
match parse_plain_date_time_string(&text)
|
||||
.or_else(|_| parse_plain_date_string(&text).map(|(y, m, d)| (y, m, d, 0.0)))
|
||||
{
|
||||
Ok((year, month, day, _time_ns)) => Some((
|
||||
i128::from(year),
|
||||
i128::from(month),
|
||||
i128::from(day),
|
||||
)),
|
||||
Ok((year, month, day, _time_ns)) => Some((i128::from(year), i128::from(month), i128::from(day))),
|
||||
Err(_) => {
|
||||
return NativeResult::Err(crate::error::create_range_error(
|
||||
vm,
|
||||
"invalid relativeTo string",
|
||||
));
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid relativeTo string"));
|
||||
}
|
||||
}
|
||||
} else if relative_raw.is_object() {
|
||||
@@ -1934,10 +1928,7 @@ pub fn duration_add<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let other = native_try!(duration_like_values(vm, other_val));
|
||||
let receiver = duration_values(obj);
|
||||
if receiver[..3].iter().any(|value| *value != 0.0) || other[..3].iter().any(|value| *value != 0.0) {
|
||||
return NativeResult::Err(crate::error::create_range_error(
|
||||
vm,
|
||||
"cannot add durations with calendar units",
|
||||
));
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "cannot add durations with calendar units"));
|
||||
}
|
||||
// 最大单位取 receiver 与参数中最大的非零时间单位(days=3 最大,ns=9 最小)。
|
||||
let mut largest = 9_usize;
|
||||
@@ -1952,15 +1943,8 @@ pub fn duration_add<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
// 分量按精确整数求和(f64 分量是精确整数;超过 2^53 的和需在 i128 上保持精确,规范按数学值计算)。
|
||||
let mut total_ns = 0_i128;
|
||||
const SUM_SCALES: [i128; 7] = [
|
||||
86_400_000_000_000,
|
||||
3_600_000_000_000,
|
||||
60_000_000_000,
|
||||
1_000_000_000,
|
||||
1_000_000,
|
||||
1_000,
|
||||
1,
|
||||
];
|
||||
const SUM_SCALES: [i128; 7] =
|
||||
[86_400_000_000_000, 3_600_000_000_000, 60_000_000_000, 1_000_000_000, 1_000_000, 1_000, 1];
|
||||
for (index, scale) in (3..10).zip(SUM_SCALES) {
|
||||
let Some(a) = duration_component_integer(receiver[index]) else {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid duration"));
|
||||
@@ -1986,7 +1970,7 @@ pub fn duration_add<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
break;
|
||||
}
|
||||
let base = match unit {
|
||||
7..=9 => 1_000,
|
||||
7..=9 => 1_000,
|
||||
6 | 5 => 60,
|
||||
_ => 24,
|
||||
};
|
||||
@@ -2007,10 +1991,7 @@ pub fn duration_add<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
];
|
||||
for (index, scale) in (3..10).zip(UNIT_SCALES) {
|
||||
if values[index] != 0.0 && values[index].abs() * scale >= MAX_TIME_NANOSECONDS {
|
||||
return NativeResult::Err(crate::error::create_range_error(
|
||||
vm,
|
||||
"duration time fields are out of range",
|
||||
));
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "duration time fields are out of range"));
|
||||
}
|
||||
}
|
||||
make_duration(vm, values)
|
||||
@@ -2173,15 +2154,8 @@ pub fn duration_round<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let Some(time_ns) = duration_time_nanoseconds(&values) else {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "duration is out of range"));
|
||||
};
|
||||
const UNIT_NS: [i128; 7] = [
|
||||
86_400_000_000_000,
|
||||
3_600_000_000_000,
|
||||
60_000_000_000,
|
||||
1_000_000_000,
|
||||
1_000_000,
|
||||
1_000,
|
||||
1,
|
||||
];
|
||||
const UNIT_NS: [i128; 7] =
|
||||
[86_400_000_000_000, 3_600_000_000_000, 60_000_000_000, 1_000_000_000, 1_000_000, 1_000, 1];
|
||||
let quantum = if smallest_index == 3 {
|
||||
increment * UNIT_NS[0]
|
||||
} else {
|
||||
@@ -2810,10 +2784,7 @@ fn parse_plain_date_string(input: &str) -> Result<(i32, u32, u32), String> {
|
||||
parse_temporal_string_impl(input, false).map(|(year, month, day, _)| (year, month, day))
|
||||
}
|
||||
|
||||
fn parse_temporal_string_impl(
|
||||
input: &str,
|
||||
enforce_date_time_range: bool,
|
||||
) -> Result<(i32, u32, u32, f64), String> {
|
||||
fn parse_temporal_string_impl(input: &str, enforce_date_time_range: bool) -> Result<(i32, u32, u32, f64), String> {
|
||||
let trimmed = input.trim();
|
||||
if trimmed.contains('\u{2212}') {
|
||||
return Err("variant minus sign is not valid for PlainDateTime".into());
|
||||
@@ -3167,8 +3138,7 @@ fn plain_date_time_object_parts<H: VmHost>(
|
||||
let (hour, minute, second) = (hour as u32, minute as u32, second as u32);
|
||||
let (millisecond, microsecond, nanosecond) = (millisecond as u32, microsecond as u32, nanosecond as u32);
|
||||
if !valid_iso_date(year, month, day)
|
||||
|| (!ignore_time
|
||||
&& !valid_plain_time(hour, minute, second, millisecond, microsecond, nanosecond))
|
||||
|| (!ignore_time && !valid_plain_time(hour, minute, second, millisecond, microsecond, nanosecond))
|
||||
{
|
||||
return Err(crate::error::create_range_error(vm, "invalid date-time component"));
|
||||
}
|
||||
@@ -4097,9 +4067,7 @@ fn plain_date_unit_index(value: &str) -> Option<usize> {
|
||||
/// roundingIncrement → roundingMode → smallestUnit。date_only 时单位限定
|
||||
/// year/month/week/day,smallestUnit 缺省 "day"(含时间时缺省 "nanosecond")。
|
||||
fn parse_difference_settings<H: VmHost>(
|
||||
vm: &mut H,
|
||||
options_value: JsValue,
|
||||
date_only: bool,
|
||||
vm: &mut H, options_value: JsValue, date_only: bool,
|
||||
) -> Result<DifferenceSettings, JsValue> {
|
||||
let unit_index = |value: &str| -> Option<usize> {
|
||||
if date_only {
|
||||
@@ -4178,22 +4146,27 @@ fn parse_difference_settings<H: VmHost>(
|
||||
return Err(crate::error::create_range_error(vm, "invalid roundingIncrement"));
|
||||
}
|
||||
}
|
||||
Ok(DifferenceSettings { largest_index, smallest_index, increment, mode })
|
||||
Ok(DifferenceSettings {
|
||||
largest_index,
|
||||
smallest_index,
|
||||
increment,
|
||||
mode,
|
||||
})
|
||||
}
|
||||
|
||||
/// 差值核心:internal = end - start,按设置取整;since 用 NegateRoundingMode
|
||||
/// 的舍入模式并在最后整体取反(不调换两端,调换会改变 0.5 边界所在的年长)。
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn difference_core<H: VmHost>(
|
||||
vm: &mut H,
|
||||
start: (i128, i128, i128),
|
||||
start_time_ns: i128,
|
||||
end: (i128, i128, i128),
|
||||
end_time_ns: i128,
|
||||
settings: DifferenceSettings,
|
||||
since: bool,
|
||||
vm: &mut H, start: (i128, i128, i128), start_time_ns: i128, end: (i128, i128, i128), end_time_ns: i128,
|
||||
settings: DifferenceSettings, since: bool,
|
||||
) -> NativeResult {
|
||||
let DifferenceSettings { largest_index, smallest_index, increment, mut mode } = settings;
|
||||
let DifferenceSettings {
|
||||
largest_index,
|
||||
smallest_index,
|
||||
increment,
|
||||
mut mode,
|
||||
} = settings;
|
||||
if since {
|
||||
mode = match mode {
|
||||
InstantRoundingMode::Ceil => InstantRoundingMode::Floor,
|
||||
@@ -4381,7 +4354,6 @@ pub fn plain_date_time_since<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
plain_date_time_difference(vm, args, true)
|
||||
}
|
||||
|
||||
|
||||
/// `Temporal.PlainDateTime.prototype.add(durationLike, options)`。
|
||||
pub fn plain_date_time_add<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
plain_date_time_apply_duration(vm, args, 1)
|
||||
@@ -4470,7 +4442,6 @@ fn date_like_ymd<H: VmHost>(vm: &mut H, val: JsValue) -> Result<(i32, u32, u32),
|
||||
Ok(ymd)
|
||||
}
|
||||
|
||||
|
||||
/// `Temporal.PlainDate.prototype.dayOfWeek` getter:ISO 周几(周一 1 … 周日 7)。
|
||||
pub fn plain_date_day_of_week<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let date = match plain_date_naive(vm, args) {
|
||||
@@ -4719,7 +4690,6 @@ fn add_signed_months(date: NaiveDate, months: i64) -> Option<NaiveDate> {
|
||||
NaiveDate::from_ymd_opt(year, month, day)
|
||||
}
|
||||
|
||||
|
||||
/// `Temporal.PlainDate.prototype.until(other, options)`:date-only 单位
|
||||
/// (year/month/week/day)差值 + 舍入语义。
|
||||
pub fn plain_date_until<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
@@ -44,7 +44,8 @@ fn replacer_function_skip_property() {
|
||||
|
||||
#[test]
|
||||
fn replacer_function_array_null() {
|
||||
let (_vm, result) = eval(r#"JSON.stringify([1,2,3], function(k,v){if(k==='1')return undefined;return v})"#).unwrap();
|
||||
let (_vm, result) =
|
||||
eval(r#"JSON.stringify([1,2,3], function(k,v){if(k==='1')return undefined;return v})"#).unwrap();
|
||||
assert_eq!(stringify_val(&result), "[1,null,3]");
|
||||
}
|
||||
|
||||
|
||||
@@ -191,4 +191,3 @@ fn average_metrics(metrics: &[MetricCollection]) -> MetricCollection {
|
||||
ic_misses: (metrics.iter().map(|m| m.ic_misses as f64).sum::<f64>() / n) as u64,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
//! `compiled_module_hash` 纳入绑定名(精确匹配避免错误复用)。
|
||||
|
||||
use oxide_parser::{
|
||||
ArrayExpressionElement, BindingPattern, ChainElement, Class, ClassElement, Declaration, ExportDefaultDeclarationKind,
|
||||
Expression, ForStatementInit, ForStatementLeft, Function, ImportDeclarationSpecifier, ModuleExportName,
|
||||
ObjectPropertyKind, PropertyKey, SimpleAssignmentTarget, Statement,
|
||||
ArrayExpressionElement, BindingPattern, ChainElement, Class, ClassElement, Declaration,
|
||||
ExportDefaultDeclarationKind, Expression, ForStatementInit, ForStatementLeft, Function, ImportDeclarationSpecifier,
|
||||
ModuleExportName, ObjectPropertyKind, PropertyKey, SimpleAssignmentTarget, Statement,
|
||||
};
|
||||
use std::hash::Hash;
|
||||
|
||||
|
||||
@@ -193,9 +193,7 @@ fn hash_for_statement(fr: &oxide_parser::ForStatement<'_>, h: &mut rustc_hash::F
|
||||
hash_statement(&fr.body, h, include_binding_names);
|
||||
}
|
||||
|
||||
fn hash_function_declaration(
|
||||
fd: &Function<'_>, h: &mut rustc_hash::FxHasher, include_binding_names: bool,
|
||||
) {
|
||||
fn hash_function_declaration(fd: &Function<'_>, h: &mut rustc_hash::FxHasher, include_binding_names: bool) {
|
||||
if include_binding_names {
|
||||
if let Some(id) = &fd.id {
|
||||
id.name.as_str().hash(h);
|
||||
|
||||
@@ -60,7 +60,11 @@ fn await_yield_plain_distinguished() {
|
||||
fn object_literals_distinguished() {
|
||||
assert_ne!(compiled("var o = { a: 1 }"), compiled("var o = { a: 2 }"));
|
||||
assert_ne!(compiled("var o = { a: 1 }"), compiled("var o = { b: 1 }"));
|
||||
assert_ne!(compiled("var o = { a: 1 }"), compiled("var o = { a: 1, b: 2 }"), "property count must be hashed");
|
||||
assert_ne!(
|
||||
compiled("var o = { a: 1 }"),
|
||||
compiled("var o = { a: 1, b: 2 }"),
|
||||
"property count must be hashed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -25,7 +25,14 @@ impl Emitter {
|
||||
}
|
||||
if matches!(method.key, PropertyKey::PrivateIdentifier(_)) {
|
||||
let home_reg = if method.r#static { ctor_reg } else { proto_reg };
|
||||
self.emit_private_method_init(Operand::Reg(home_reg), method, Operand::Reg(home_reg), self_binding, class_self_cell, ctx)?;
|
||||
self.emit_private_method_init(
|
||||
Operand::Reg(home_reg),
|
||||
method,
|
||||
Operand::Reg(home_reg),
|
||||
self_binding,
|
||||
class_self_cell,
|
||||
ctx,
|
||||
)?;
|
||||
continue;
|
||||
}
|
||||
let home_reg = if method.r#static { ctor_reg } else { proto_reg };
|
||||
@@ -39,8 +46,14 @@ impl Emitter {
|
||||
} else {
|
||||
self.class_property_name(&method.key)?
|
||||
};
|
||||
let accessor_reg =
|
||||
self.emit_class_method_function(method, &method_name, Operand::Reg(home_reg), ctx, self_binding, class_self_cell)?;
|
||||
let accessor_reg = self.emit_class_method_function(
|
||||
method,
|
||||
&method_name,
|
||||
Operand::Reg(home_reg),
|
||||
ctx,
|
||||
self_binding,
|
||||
class_self_cell,
|
||||
)?;
|
||||
match method.kind {
|
||||
MethodDefinitionKind::Method => {
|
||||
// class 方法按规范为非枚举数据属性(DefineMethod:writable/configurable,enumerable=false)。
|
||||
|
||||
@@ -410,8 +410,7 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
oxide_parser::Declaration::FunctionDeclaration(fd) => {
|
||||
let body: &[Statement] =
|
||||
fd.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||
let body: &[Statement] = fd.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||
let mut inner = shadow.clone();
|
||||
inner.extend(self.collect_fn_param_names(&fd.params));
|
||||
inner.extend(self.collect_own_binding_names(&[], body));
|
||||
|
||||
@@ -7,9 +7,7 @@ use oxide_ir::operand::Operand;
|
||||
use oxide_parser::AwaitExpression;
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_await_expression(
|
||||
&self, ae: &AwaitExpression, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
pub(crate) fn emit_await_expression(&self, ae: &AwaitExpression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
// 求值被等待的值(`await undefined` 时求值 undefined)。
|
||||
let value_reg = self.emit_expression(&ae.argument, ctx)?;
|
||||
ctx.inst(Inst::await_expr(Operand::Reg(value_reg)));
|
||||
|
||||
@@ -55,7 +55,9 @@ impl Emitter {
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
pub(crate) fn emit_function_expression(&self, fe: &oxide_parser::Function, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
pub(crate) fn emit_function_expression(
|
||||
&self, fe: &oxide_parser::Function, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
// 函数表达式:编译函数体为子模块,create_closure 实例化闭包
|
||||
let mut param_names = Vec::new();
|
||||
for (idx, param) in fe.params.items.iter().enumerate() {
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
|
||||
pub mod class;
|
||||
pub mod closure;
|
||||
pub mod module;
|
||||
pub mod emit;
|
||||
mod emit_log;
|
||||
pub mod emit_ctx;
|
||||
mod emit_log;
|
||||
pub mod expr;
|
||||
pub mod module;
|
||||
pub mod prepass;
|
||||
pub mod shared;
|
||||
pub mod stmt;
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
//! 支持提升语义。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_parser::{BindingPattern, Declaration, ExportDefaultDeclarationKind, Expression, Statement, VariableDeclarationKind};
|
||||
use oxide_parser::{
|
||||
BindingPattern, Declaration, ExportDefaultDeclarationKind, Expression, Statement, VariableDeclarationKind,
|
||||
};
|
||||
|
||||
impl Emitter {
|
||||
/// 在临时寄存器池之前分配 builtin 槽位。
|
||||
@@ -390,12 +392,8 @@ impl Emitter {
|
||||
for d in &decl.declarations {
|
||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ = ctx.declare_initialized(
|
||||
bi.name.as_str(),
|
||||
reg,
|
||||
VariableDeclarationKind::Var,
|
||||
false,
|
||||
);
|
||||
let _ =
|
||||
ctx.declare_initialized(bi.name.as_str(), reg, VariableDeclarationKind::Var, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -547,33 +545,23 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::ExportDefaultDeclaration(exp) => {
|
||||
match &exp.declaration {
|
||||
ExportDefaultDeclarationKind::ClassDeclaration(cd) => {
|
||||
if let Some(id) = &cd.id {
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ = ctx.declare_predeclared(
|
||||
id.name.as_str(),
|
||||
reg,
|
||||
VariableDeclarationKind::Const,
|
||||
true,
|
||||
);
|
||||
}
|
||||
Statement::ExportDefaultDeclaration(exp) => match &exp.declaration {
|
||||
ExportDefaultDeclarationKind::ClassDeclaration(cd) => {
|
||||
if let Some(id) = &cd.id {
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ =
|
||||
ctx.declare_predeclared(id.name.as_str(), reg, VariableDeclarationKind::Const, true);
|
||||
}
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(fd) => {
|
||||
if let Some(id) = &fd.id {
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ = ctx.declare_predeclared(
|
||||
id.name.as_str(),
|
||||
reg,
|
||||
VariableDeclarationKind::Const,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(fd) => {
|
||||
if let Some(id) = &fd.id {
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ =
|
||||
ctx.declare_predeclared(id.name.as_str(), reg, VariableDeclarationKind::Const, true);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,12 +76,7 @@ impl Emitter {
|
||||
};
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||
let op = if fresh_cell { OpCode::MAKE_CELL_FRESH } else { OpCode::MAKE_CELL };
|
||||
ctx.inst(Inst::new(
|
||||
op,
|
||||
Operand::Reg(src_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::new(op, Operand::Reg(src_reg), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
} else if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) {
|
||||
// with 内 var 初始化:对象有该属性则写对象,否则写提升槽(动态解析)。
|
||||
// ponytail: with 内顶层 var 不写全局对象属性,with 语句本身已是稀见用例。
|
||||
@@ -131,8 +126,8 @@ impl Emitter {
|
||||
}
|
||||
|
||||
pub(crate) fn emit_binding_pattern(
|
||||
&self, pattern: &BindingPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool,
|
||||
fresh_cell: bool, ctx: &mut CompileCtx,
|
||||
&self, pattern: &BindingPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, fresh_cell: bool,
|
||||
ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
match pattern {
|
||||
BindingPattern::BindingIdentifier(bi) => {
|
||||
|
||||
@@ -56,14 +56,13 @@ impl Emitter {
|
||||
} else {
|
||||
// let/const 无初始化器:复用本块预声明槽位,否则新声明
|
||||
// (for 头等未预声明路径)。
|
||||
let var_reg =
|
||||
if let Some(reg) = ctx.scopes.symbols.consume_predeclared_slot(bi.name.as_str()) {
|
||||
reg
|
||||
} else {
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.declare(bi.name.as_str(), r, decl.kind, is_const)?;
|
||||
r
|
||||
};
|
||||
let var_reg = if let Some(reg) = ctx.scopes.symbols.consume_predeclared_slot(bi.name.as_str()) {
|
||||
reg
|
||||
} else {
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.declare(bi.name.as_str(), r, decl.kind, is_const)?;
|
||||
r
|
||||
};
|
||||
var_reg
|
||||
};
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(bi.name.as_str()) {
|
||||
|
||||
@@ -423,7 +423,9 @@ mod tests {
|
||||
assert!(!Inst::new(OpCode::SET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f));
|
||||
assert!(!Inst::new(OpCode::SUPER_GET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f));
|
||||
assert!(!Inst::new(OpCode::DEFINE_ACCESSOR, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f));
|
||||
assert!(!Inst::new(OpCode::DEFINE_ACCESSOR_DYNAMIC, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f));
|
||||
assert!(
|
||||
!Inst::new(OpCode::DEFINE_ACCESSOR_DYNAMIC, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)
|
||||
);
|
||||
assert!(!Inst::new(OpCode::DELETE_PROP_STATIC, Operand::Reg(0), Operand::Reg(0), Operand::None).is_pure(&f));
|
||||
assert!(!Inst::new(OpCode::DELETE_PROP_DYNAMIC, Operand::Reg(0), Operand::None, Operand::Reg(2)).is_pure(&f));
|
||||
// 调用 / 异常 / 控制流
|
||||
@@ -495,48 +497,24 @@ mod tests {
|
||||
/// 每个 opcode 构造一条规范指令:rd/a/b 置 Reg(1/2/3),ext 按族填真实布局。
|
||||
fn canonical_inst(op: OpCode) -> Inst {
|
||||
match op {
|
||||
OpCode::CALL | OpCode::CALL_NATIVE => {
|
||||
Inst::call(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 2)
|
||||
}
|
||||
OpCode::NEW_EXPRESSION => {
|
||||
Inst::new_expression(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 2)
|
||||
}
|
||||
OpCode::CALL | OpCode::CALL_NATIVE => Inst::call(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 2),
|
||||
OpCode::NEW_EXPRESSION => Inst::new_expression(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 2),
|
||||
OpCode::SUPER_CALL => Inst::super_call(Operand::Reg(1), Operand::Reg(2), 2),
|
||||
OpCode::CALL_SPREAD => {
|
||||
Inst::call_spread(Operand::Reg(1), Operand::Reg(2), &[5, 0x8000_0000 | 9])
|
||||
}
|
||||
OpCode::CALL_SPREAD => Inst::call_spread(Operand::Reg(1), Operand::Reg(2), &[5, 0x8000_0000 | 9]),
|
||||
OpCode::NEW_EXPRESSION_SPREAD => {
|
||||
Inst::new_expression_spread(Operand::Reg(1), Operand::Reg(2), &[5, 0x8000_0000 | 9])
|
||||
}
|
||||
OpCode::SUPER_CALL_SPREAD => {
|
||||
Inst::super_call_spread(Operand::Reg(1), &[5, 0x8000_0000 | 9])
|
||||
}
|
||||
OpCode::SUPER_CALL_SPREAD => Inst::super_call_spread(Operand::Reg(1), &[5, 0x8000_0000 | 9]),
|
||||
OpCode::IC_GET_PROP => Inst::ic_get(Operand::Reg(1), Operand::Reg(2)),
|
||||
OpCode::IC_SET_PROP => Inst::ic_set(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::MEMBER_INC => {
|
||||
Inst::member_inc(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::MEMBER_DEC => {
|
||||
Inst::member_dec(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_ADD => {
|
||||
Inst::compound_member_add(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_SUB => {
|
||||
Inst::compound_member_sub(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_MUL => {
|
||||
Inst::compound_member_mul(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_DIV => {
|
||||
Inst::compound_member_div(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_MOD => {
|
||||
Inst::compound_member_mod(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_EXP => {
|
||||
Inst::compound_member_exp(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::MEMBER_INC => Inst::member_inc(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::MEMBER_DEC => Inst::member_dec(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_ADD => Inst::compound_member_add(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_SUB => Inst::compound_member_sub(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_MUL => Inst::compound_member_mul(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_DIV => Inst::compound_member_div(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_MOD => Inst::compound_member_mod(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_EXP => Inst::compound_member_exp(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_BIT_AND => {
|
||||
Inst::compound_member_bit_and(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
@@ -546,35 +524,23 @@ mod tests {
|
||||
OpCode::COMPOUND_MEMBER_BIT_XOR => {
|
||||
Inst::compound_member_bit_xor(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_SHL => {
|
||||
Inst::compound_member_shl(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_SHR => {
|
||||
Inst::compound_member_shr(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_SHL => Inst::compound_member_shl(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_SHR => Inst::compound_member_shr(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
|
||||
OpCode::COMPOUND_MEMBER_USHR => {
|
||||
Inst::compound_member_ushr(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
|
||||
}
|
||||
OpCode::TEMPLATE_STR => Inst::template_str(Operand::Reg(1), 2, 10, &[0x1234, 0x8000_0000 | 5]),
|
||||
OpCode::CONCAT_N => Inst::concat_n(Operand::Reg(1), &[2, 5, 9]),
|
||||
OpCode::GET_PRIVATE => {
|
||||
Inst::get_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7, 99)
|
||||
}
|
||||
OpCode::SET_PRIVATE => {
|
||||
Inst::set_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7, 99)
|
||||
}
|
||||
OpCode::INIT_PRIVATE => {
|
||||
Inst::init_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), true)
|
||||
}
|
||||
OpCode::GET_PRIVATE => Inst::get_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7, 99),
|
||||
OpCode::SET_PRIVATE => Inst::set_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7, 99),
|
||||
OpCode::INIT_PRIVATE => Inst::init_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), true),
|
||||
OpCode::PRIVATE_BRAND_IN => {
|
||||
Inst::private_brand_in(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7, 99)
|
||||
}
|
||||
OpCode::SPILL => Inst::inst_spill(Operand::Reg(1), 42),
|
||||
OpCode::UNSPILL => Inst::inst_unspill(Operand::Reg(1), 42),
|
||||
OpCode::REST_OBJECT => Inst::rest_object(Operand::Reg(1), Operand::Reg(2), 7, None),
|
||||
OpCode::DEFINE_ACCESSOR => {
|
||||
Inst::define_accessor(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 42)
|
||||
}
|
||||
OpCode::DEFINE_ACCESSOR => Inst::define_accessor(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 42),
|
||||
OpCode::DEFINE_ACCESSOR_DYNAMIC => {
|
||||
Inst::define_accessor_dynamic(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7)
|
||||
}
|
||||
@@ -703,7 +669,12 @@ mod tests {
|
||||
);
|
||||
assert_contract(OpCode::FOR_AWAIT_OF_DONE, Some(1), &[2], false);
|
||||
// 自增/自减:def=rd, uses=[rd]
|
||||
assert_group(&[OpCode::INC_PRE, OpCode::INC_POST, OpCode::DEC_PRE, OpCode::DEC_POST], Some(1), &[1], false);
|
||||
assert_group(
|
||||
&[OpCode::INC_PRE, OpCode::INC_POST, OpCode::DEC_PRE, OpCode::DEC_POST],
|
||||
Some(1),
|
||||
&[1],
|
||||
false,
|
||||
);
|
||||
// 占位 opcode:def=rd 保守,不可删
|
||||
assert_group(
|
||||
&[
|
||||
|
||||
@@ -40,7 +40,10 @@ fn smoke_try_catch() {
|
||||
let (info, cfg, _) =
|
||||
run_liveness("function x() {} function y(e) {} function f() { try { x(); } catch (e) { y(e); } } f()");
|
||||
// entry 块 liveIn 不含 reg 0(异常边 reg0 截断在真实产物上成立)
|
||||
assert!(!oxide_liveness::bitset_get(&info.block_live_in[cfg.entry], 0), "entry 块 liveIn 不得含 reg0");
|
||||
assert!(
|
||||
!oxide_liveness::bitset_get(&info.block_live_in[cfg.entry], 0),
|
||||
"entry 块 liveIn 不得含 reg0"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -464,8 +464,8 @@ impl oxide_emit::module::ModuleSourceLoader for Test262ModuleLoader {
|
||||
let canonical = full
|
||||
.canonicalize()
|
||||
.map_err(|e| format!("cannot resolve module {specifier}: {e}"))?;
|
||||
let content = std::fs::read_to_string(&canonical)
|
||||
.map_err(|e| format!("cannot read module {specifier}: {e}"))?;
|
||||
let content =
|
||||
std::fs::read_to_string(&canonical).map_err(|e| format!("cannot read module {specifier}: {e}"))?;
|
||||
Ok(ResolvedModule {
|
||||
source: content,
|
||||
path: canonical.to_string_lossy().to_string(),
|
||||
@@ -607,10 +607,7 @@ fn judge_async_result(
|
||||
return TestResult::fail(
|
||||
path.to_path_buf(),
|
||||
dur,
|
||||
format!(
|
||||
"expected runtime error ({}), got: async complete",
|
||||
neg.error_type
|
||||
),
|
||||
format!("expected runtime error ({}), got: async complete", neg.error_type),
|
||||
);
|
||||
}
|
||||
return TestResult::pass(path.to_path_buf(), dur, "async ok");
|
||||
|
||||
@@ -30,7 +30,11 @@ pub fn bind_global(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
("__moduleObject", oxide_builtins::module::module_object::<crate::vm::Vm> as *const (), 0),
|
||||
("__moduleSet", oxide_builtins::module::module_set::<crate::vm::Vm> as *const (), 3),
|
||||
("__moduleGet", oxide_builtins::module::module_get::<crate::vm::Vm> as *const (), 2),
|
||||
("__moduleLinkGet", oxide_builtins::module::module_link_get::<crate::vm::Vm> as *const (), 2),
|
||||
(
|
||||
"__moduleLinkGet",
|
||||
oxide_builtins::module::module_link_get::<crate::vm::Vm> as *const (),
|
||||
2,
|
||||
),
|
||||
("__moduleStar", oxide_builtins::module::module_star::<crate::vm::Vm> as *const (), 2),
|
||||
("__moduleSeal", oxide_builtins::module::module_seal::<crate::vm::Vm> as *const (), 1),
|
||||
("__moduleEval", oxide_builtins::module::module_eval::<crate::vm::Vm> as *const (), 1),
|
||||
|
||||
@@ -550,26 +550,14 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
oxide_builtins::temporal::plain_date_time_to_json::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"add",
|
||||
oxide_builtins::temporal::plain_date_time_add::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
("add", oxide_builtins::temporal::plain_date_time_add::<crate::vm::Vm> as *const (), 1),
|
||||
(
|
||||
"subtract",
|
||||
oxide_builtins::temporal::plain_date_time_subtract::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
(
|
||||
"until",
|
||||
oxide_builtins::temporal::plain_date_time_until::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
(
|
||||
"since",
|
||||
oxide_builtins::temporal::plain_date_time_since::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
("until", oxide_builtins::temporal::plain_date_time_until::<crate::vm::Vm> as *const (), 1),
|
||||
("since", oxide_builtins::temporal::plain_date_time_since::<crate::vm::Vm> as *const (), 1),
|
||||
(
|
||||
"equals",
|
||||
oxide_builtins::temporal::plain_date_time_equals::<crate::vm::Vm> as *const (),
|
||||
|
||||
@@ -104,16 +104,8 @@ impl Vm {
|
||||
return self.new_string_owned(buf);
|
||||
}
|
||||
// 大链走 Cons rope:非字符串操作数先转叶子,再 O(1) 链接(文本惰性扁平化)。
|
||||
let lv = if lhs.is_string() {
|
||||
lhs
|
||||
} else {
|
||||
self.string_leaf(lhs)
|
||||
};
|
||||
let rv = if rhs.is_string() {
|
||||
rhs
|
||||
} else {
|
||||
self.string_leaf(rhs)
|
||||
};
|
||||
let lv = if lhs.is_string() { lhs } else { self.string_leaf(lhs) };
|
||||
let rv = if rhs.is_string() { rhs } else { self.string_leaf(rhs) };
|
||||
self.new_cons_string(lv, rv)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
//! 核心入口为 [`vm::Vm`];`VmPool` 负责 VM 实例的复用;`session_gc` 提供
|
||||
//! session 内存回收;`bindings` 把内置对象绑定到每个 session 的 global 上。
|
||||
|
||||
mod async_func;
|
||||
mod async_from_sync;
|
||||
mod async_func;
|
||||
mod async_generator;
|
||||
/// 内置对象绑定模块:向 session 的 global 对象安装 Object/Array/... 及各原型。
|
||||
pub mod bindings;
|
||||
|
||||
@@ -193,8 +193,16 @@ fn function_symbol_has_instance_bound() {
|
||||
fn function_symbol_has_instance_poisoned_prototype_throws() {
|
||||
let mut vm = Vm::new();
|
||||
// 可调用但 prototype 非对象 → TypeError(OrdinaryHasInstance 唯一抛错点)。
|
||||
let err = eval(&mut vm, "var f = function(){}; f.prototype = 1; try { f[Symbol.hasInstance]({}) } catch (e) { e }").unwrap();
|
||||
let name = eval(&mut vm, "var f = function(){}; f.prototype = null; try { f[Symbol.hasInstance]({}) } catch (e) { e.name }").unwrap();
|
||||
let err = eval(
|
||||
&mut vm,
|
||||
"var f = function(){}; f.prototype = 1; try { f[Symbol.hasInstance]({}) } catch (e) { e }",
|
||||
)
|
||||
.unwrap();
|
||||
let name = eval(
|
||||
&mut vm,
|
||||
"var f = function(){}; f.prototype = null; try { f[Symbol.hasInstance]({}) } catch (e) { e.name }",
|
||||
)
|
||||
.unwrap();
|
||||
let _ = err;
|
||||
assert_eq!(vm.lookup_str(name).unwrap_or_default(), "TypeError");
|
||||
}
|
||||
@@ -208,7 +216,10 @@ fn bound_function_construct_semantics() {
|
||||
("function C(v){ this.v = v; } var B = C.bind({}, 1); new B(2).v", 1.0),
|
||||
("function C(a, b){ this.s = a + b; } var B = C.bind(null, 2); new B(3).s", 5.0),
|
||||
// 多层 bound 链:绑定实参按 内层先、外层后 拼接。
|
||||
("function C(v){ this.v = v; } var B = C.bind({}, 1); var D = B.bind({}, 2); new D().v", 1.0),
|
||||
(
|
||||
"function C(v){ this.v = v; } var B = C.bind({}, 1); var D = B.bind({}, 2); new D().v",
|
||||
1.0,
|
||||
),
|
||||
// native 构造器 target。
|
||||
("var arr = Array.bind(null); new arr(3).length", 3.0),
|
||||
];
|
||||
|
||||
@@ -211,7 +211,8 @@ fn has_instance_preserves_ordinary_has_instance() {
|
||||
#[test]
|
||||
fn symbol_keys_stay_distinct() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var s1=Symbol('a'), s2=Symbol('b'); var o={}; o[s1]=1; o[s2]=2; [o[s1],o[s2]]").unwrap();
|
||||
let result =
|
||||
eval(&mut vm, "var s1=Symbol('a'), s2=Symbol('b'); var o={}; o[s1]=1; o[s2]=2; [o[s1],o[s2]]").unwrap();
|
||||
let arr = unsafe { &*result.as_js_object_ptr() };
|
||||
assert_eq!(arr.get_prop_at(0).as_int(), 1);
|
||||
assert_eq!(arr.get_prop_at(1).as_int(), 2);
|
||||
@@ -248,7 +249,11 @@ fn symbol_keys_excluded_from_for_in() {
|
||||
#[test]
|
||||
fn get_own_property_symbols_roundtrip() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var s=Symbol('x'); var o={}; o[s]=7; var k=Object.getOwnPropertySymbols(o)[0]; o[k]").unwrap();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var s=Symbol('x'); var o={}; o[s]=7; var k=Object.getOwnPropertySymbols(o)[0]; o[k]",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result.as_int(), 7);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,9 +67,8 @@ fn reset_preserves_session_strings_after_runtime_gc() {
|
||||
#[test]
|
||||
fn strings_only_then_reset_keeps_global_subtree_strings() {
|
||||
let mut vm = vm_with_threshold(4096);
|
||||
let first = compile(
|
||||
"globalThis.kept = { s: 'he' + 'llo' }; var t; for (var i = 0; i < 500000; i++) { t = 'x' + i; } 0",
|
||||
);
|
||||
let first =
|
||||
compile("globalThis.kept = { s: 'he' + 'llo' }; var t; for (var i = 0; i < 500000; i++) { t = 'x' + i; } 0");
|
||||
vm.run(&first).expect("run1");
|
||||
assert!(vm.session_gc_stats().total_collections > 0, "run1 应触发执行期字符串 GC");
|
||||
vm.reset();
|
||||
@@ -92,12 +91,7 @@ fn regexp_exec_strings_survive_runtime_gc() {
|
||||
);
|
||||
let result = vm.run(&module).expect("run");
|
||||
let text = vm.lookup_str(result).expect("exec 结果应为字符串").to_string();
|
||||
let expected = format!(
|
||||
"{}|{}|{}",
|
||||
"a".repeat(500) + &"b".repeat(500),
|
||||
"a".repeat(500),
|
||||
"b".repeat(500)
|
||||
);
|
||||
let expected = format!("{}|{}|{}", "a".repeat(500) + &"b".repeat(500), "a".repeat(500), "b".repeat(500));
|
||||
assert_eq!(text, expected);
|
||||
assert!(vm.session_gc_stats().total_collections > 0, "执行期应触发字符串 GC");
|
||||
}
|
||||
|
||||
@@ -108,4 +108,3 @@ fn yield_star_inner_without_return() {
|
||||
"1,9,true"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -125,10 +125,7 @@ fn eval_computed_compound_assign_getter_order() {
|
||||
fn eval_computed_compound_assign_read_error_skips_rhs() {
|
||||
// 读取抛错时 RHS 不求值,其副作用不泄漏。
|
||||
assert_eq!(eval("var h={get a(){throw 1}}; try{h[\"a\"]+=1}catch(e){e}"), "1");
|
||||
assert_eq!(
|
||||
eval("var side=0; var h={get a(){throw 1}}; try{h[\"a\"]+=(side=99)}catch(e){} side"),
|
||||
"0"
|
||||
);
|
||||
assert_eq!(eval("var side=0; var h={get a(){throw 1}}; try{h[\"a\"]+=(side=99)}catch(e){} side"), "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user