mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 补内建函数 ToNumber 抛错路径与自身属性等正确性
This commit is contained in:
@@ -10,21 +10,43 @@ pub use annex_b::{js_escape, js_unescape};
|
||||
pub use uri::{decode_uri, decode_uri_component, encode_uri, encode_uri_component};
|
||||
|
||||
/// `isNaN(x)`:ToNumber 后检查是否为 NaN(隐式类型转换)。
|
||||
///
|
||||
/// ToNumber 可抛:Symbol 抛 TypeError,对象 ToPrimitive 触发 valueOf/toString
|
||||
/// 抛出的原生异常原样传播。
|
||||
pub fn global_is_nan<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let n = if args.len() < 2 {
|
||||
f64::NAN
|
||||
} else {
|
||||
oxide_runtime_api::to_number(vm.reg(args[1]))
|
||||
match oxide_runtime_api::to_number_full(vm.reg(args[1]), vm) {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
if let Some(exc) = vm.take_uncaught_value() {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a number"));
|
||||
}
|
||||
}
|
||||
};
|
||||
NativeResult::Ok(JsValue::bool(n.is_nan()))
|
||||
}
|
||||
|
||||
/// `isFinite(x)`:ToNumber 后检查是否为有限数(隐式类型转换)。
|
||||
///
|
||||
/// ToNumber 可抛:Symbol 抛 TypeError,对象 ToPrimitive 触发 valueOf/toString
|
||||
/// 抛出的原生异常原样传播。
|
||||
pub fn global_is_finite<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let n = if args.len() < 2 {
|
||||
f64::NAN
|
||||
} else {
|
||||
oxide_runtime_api::to_number(vm.reg(args[1]))
|
||||
match oxide_runtime_api::to_number_full(vm.reg(args[1]), vm) {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
if let Some(exc) = vm.take_uncaught_value() {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a number"));
|
||||
}
|
||||
}
|
||||
};
|
||||
NativeResult::Ok(JsValue::bool(n.is_finite()))
|
||||
}
|
||||
|
||||
@@ -14,14 +14,39 @@ use super::{
|
||||
|
||||
// ── 静态方法 / 构造 ─────────────────────────────────────────────────────
|
||||
|
||||
/// `String.fromCharCode(...codes)`:把各参数按 ToUint32 低 16 位转为单元拼接
|
||||
/// `String.fromCharCode(...codes)`:各参数经 ToUint16(ToNumber) 转单元拼接
|
||||
/// 为字符串(surrogate 区间合法产出孤立 surrogate 单元)。
|
||||
///
|
||||
/// ToNumber 可抛:Symbol 抛 TypeError,BigInt 抛 TypeError,对象 ToPrimitive
|
||||
/// 触发 valueOf/toString 抛出的原生异常原样传播。
|
||||
pub fn string_from_char_code<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
builtins_debug!("String.prototype.fromCharCode called with {} args", args.len());
|
||||
let mut units: Vec<u16> = Vec::new();
|
||||
for &arg_reg in args.iter().skip(1) {
|
||||
let code = oxide_runtime_api::to_uint32(vm.reg(arg_reg)) & 0xFFFF;
|
||||
units.push(code as u16);
|
||||
// 经 native_arg_count/native_arg_at 读取:大实参集在寄存器窗口外走 spill
|
||||
// 溢出区,小实参集与既有寄存器路径一致。
|
||||
for i in 0..vm.native_arg_count(args) {
|
||||
let arg_val = vm.native_arg_at(args, i);
|
||||
// ToNumber(BigInt) 按规范抛 TypeError;to_number_full 的 BigInt 快路径
|
||||
// 静默转换,须在此显式拦截。
|
||||
if arg_val.is_bigint() {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert a BigInt value to a number"));
|
||||
}
|
||||
let n = match oxide_runtime_api::to_number_full(arg_val, vm) {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
if let Some(exc) = vm.take_uncaught_value() {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a number"));
|
||||
}
|
||||
};
|
||||
// ToUint16:floorMod(n, 2^16);NaN/±0/±Infinity 归零。
|
||||
let code = if n == 0.0 || !n.is_finite() {
|
||||
0
|
||||
} else {
|
||||
n.trunc().rem_euclid(65536.0) as u16
|
||||
};
|
||||
units.push(code);
|
||||
}
|
||||
NativeResult::Ok(vm.new_string_units_owned(units))
|
||||
}
|
||||
|
||||
@@ -219,9 +219,13 @@ pub(crate) fn take_code_points(units: &[u16], count: usize) -> &[u16] {
|
||||
&units[..i]
|
||||
}
|
||||
|
||||
/// ES 白空格判定的单元口径:Rust 空白表与 White_Space+LineTerminator 等价;
|
||||
/// ES 白空格判定的单元口径:Rust 空白表与 White_Space+LineTerminator 等价,
|
||||
/// 另补 U+FEFF(ES White_Space 含 BOM,Unicode White_Space 不含);
|
||||
/// 孤立 surrogate 非白空格。
|
||||
pub(crate) fn is_trim_unit(u: u16) -> bool {
|
||||
if u == 0xFEFF {
|
||||
return true;
|
||||
}
|
||||
if u < 0x80 {
|
||||
return matches!(u, 0x09..=0x0D | 0x20);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,17 @@ pub(crate) fn tag_number_proto(proto: &P<JsObject>) {
|
||||
obj.set_boxed_value(JsValue::int(0));
|
||||
}
|
||||
|
||||
/// 把 Boolean.prototype 标成 Boolean 对象本体:规范 20.7.3 规定其是
|
||||
/// [[BooleanData]] = false 的 Boolean object。type_tag 是品牌表判据
|
||||
/// (置位后报 "[object Boolean]"),boxed 值供 valueOf/toString 的通用分支读取。
|
||||
pub(crate) fn tag_boolean_proto(proto: &P<JsObject>) {
|
||||
let ptr = proto.as_ptr() as *mut JsObject;
|
||||
// SAFETY: proto 是 make_named_pair 刚建的本进程对象,P 引用与裸指针同址。
|
||||
let obj = unsafe { &mut *ptr };
|
||||
obj.type_tag = JsObject::OBJ_TYPE_BOOLEAN_OBJ;
|
||||
obj.set_boxed_value(JsValue::bool(false));
|
||||
}
|
||||
|
||||
/// 把 String.prototype 标成 String 对象本体:规范 22.7.3 规定其是
|
||||
/// [[StringData]] = 空串的 String object。type_tag 是品牌表判据,boxed 值
|
||||
/// 供 thisStringValue/valueOf 的通用分支读取;length 自身属性按构造期
|
||||
@@ -476,6 +487,7 @@ impl BuiltinWorld {
|
||||
let (number_proto, number_constructor) = make_named_pair(string_forge, shape_forge, labels, "Number");
|
||||
tag_number_proto(&number_proto);
|
||||
let (boolean_proto, boolean_constructor) = make_named_pair(string_forge, shape_forge, labels, "Boolean");
|
||||
tag_boolean_proto(&boolean_proto);
|
||||
let (error_proto, error_constructor) = make_named_pair(string_forge, shape_forge, labels, "Error");
|
||||
let (symbol_proto, symbol_constructor) = make_named_pair(string_forge, shape_forge, labels, "Symbol");
|
||||
|
||||
|
||||
@@ -191,21 +191,29 @@ impl Vm {
|
||||
|
||||
/// 入队一个请求;生成器处于可恢复状态(New/YieldSuspended/Completed)时立即
|
||||
/// 启动处理,正在执行 / await 挂起时留待当前请求结算后再处理。
|
||||
///
|
||||
/// 坏 receiver(非对象或非异步生成器对象)按规范不同步抛错,而是返回
|
||||
/// 携带 TypeError 的 rejected Promise。
|
||||
pub(crate) fn async_generator_enqueue(
|
||||
&mut self, gen_val: JsValue, mode: GeneratorResumeMode,
|
||||
) -> Result<JsValue, JsValue> {
|
||||
if !gen_val.is_object() {
|
||||
return Err(oxide_builtins::error::create_type_error(
|
||||
let is_gen = if gen_val.is_object() {
|
||||
unsafe { &*gen_val.as_js_object_ptr() }.is_async_generator_obj()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !is_gen {
|
||||
let (promise, _, _) = self.new_promise_capability();
|
||||
let exc = oxide_builtins::error::create_type_error(
|
||||
self,
|
||||
"AsyncGenerator methods called on non-object",
|
||||
));
|
||||
}
|
||||
let obj = unsafe { &*gen_val.as_js_object_ptr() };
|
||||
if !obj.is_async_generator_obj() {
|
||||
return Err(oxide_builtins::error::create_type_error(
|
||||
self,
|
||||
"AsyncGenerator methods called on incompatible receiver",
|
||||
));
|
||||
if gen_val.is_object() {
|
||||
"AsyncGenerator methods called on incompatible receiver"
|
||||
} else {
|
||||
"AsyncGenerator methods called on non-object"
|
||||
},
|
||||
);
|
||||
let _ = self.reject_promise(promise, exc);
|
||||
return Ok(promise);
|
||||
}
|
||||
let (promise, resolve, reject) = self.new_promise_capability();
|
||||
let state_ptr = self.async_gen_state_ptr(gen_val);
|
||||
|
||||
@@ -38,5 +38,24 @@ pub fn bind_function(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
ctor.ensure_hash_props().push(JsValue::int(1));
|
||||
let length_pos = ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||
ctor.set_data_meta(length_pos, PropAttributes::new(false, false, true));
|
||||
|
||||
// Function.prototype 自身属性:length = 0、name = ""(不可写不可枚举可配置),
|
||||
// 自身属性名序 length 在 name 之前。
|
||||
let proto_ptr = session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
let proto_length_shape = core.shape_forge().make_shape(proto.shape_id(), length_si);
|
||||
proto.set_shape_id(proto_length_shape);
|
||||
proto.ensure_hash_props().push(JsValue::int(0));
|
||||
let proto_length_pos = proto.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||
proto.set_data_meta(proto_length_pos, PropAttributes::new(false, false, true));
|
||||
let name_si = core.perm_interner().intern("name").0;
|
||||
let name_shape = core.shape_forge().make_shape(proto.shape_id(), name_si);
|
||||
proto.set_shape_id(name_shape);
|
||||
proto
|
||||
.ensure_hash_props()
|
||||
.push(JsValue::perm_string(core.perm_interner().string_ptr(core.perm_interner().intern("").0)));
|
||||
let name_pos = proto.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||
proto.set_data_meta(name_pos, PropAttributes::new(false, false, true));
|
||||
|
||||
bind_global_value(core, global, "Function", JsValue::from_js_object(function_ctor));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
oxide_builtins::number::number_is_safe_integer::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
("parseInt", oxide_builtins::number::number_parse_int::<crate::vm::Vm> as *const (), 1),
|
||||
// parseInt 形式参数为 (string, radix),length 按规范取 2。
|
||||
("parseInt", oxide_builtins::number::number_parse_int::<crate::vm::Vm> as *const (), 2),
|
||||
("parseFloat", oxide_builtins::number::number_parse_float::<crate::vm::Vm> as *const (), 1),
|
||||
],
|
||||
);
|
||||
@@ -86,7 +87,8 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
global,
|
||||
core,
|
||||
&[
|
||||
("parseInt", oxide_builtins::number::number_parse_int::<crate::vm::Vm> as *const (), 1),
|
||||
// parseInt 形式参数为 (string, radix),length 按规范取 2。
|
||||
("parseInt", oxide_builtins::number::number_parse_int::<crate::vm::Vm> as *const (), 2),
|
||||
("parseFloat", oxide_builtins::number::number_parse_float::<crate::vm::Vm> as *const (), 1),
|
||||
("isNaN", oxide_builtins::global::global_is_nan::<crate::vm::Vm> as *const (), 1),
|
||||
("isFinite", oxide_builtins::global::global_is_finite::<crate::vm::Vm> as *const (), 1),
|
||||
|
||||
@@ -775,7 +775,8 @@ fn bind_global_functions(core: &Arc<KernelCore>, session: &KernelSession, global
|
||||
global,
|
||||
core,
|
||||
&[
|
||||
("parseInt", oxide_builtins::number::number_parse_int::<crate::vm::Vm> as *const (), 1),
|
||||
// parseInt 形式参数为 (string, radix),length 按规范取 2。
|
||||
("parseInt", oxide_builtins::number::number_parse_int::<crate::vm::Vm> as *const (), 2),
|
||||
("parseFloat", oxide_builtins::number::number_parse_float::<crate::vm::Vm> as *const (), 1),
|
||||
("isNaN", oxide_builtins::global::global_is_nan::<crate::vm::Vm> as *const (), 1),
|
||||
("isFinite", oxide_builtins::global::global_is_finite::<crate::vm::Vm> as *const (), 1),
|
||||
|
||||
Reference in New Issue
Block a user