feat: 九原型新增 toSource 遗留扩展方法

This commit is contained in:
2026-10-09 11:41:50 +00:00
parent 0fc105243d
commit 11903fc520
14 changed files with 375 additions and 22 deletions
+173
View File
@@ -0,0 +1,173 @@
//! toSource(SpiderMonkey 遗留扩展):Object / Function / Array / String / Boolean /
//! Number / Date / RegExp / Error 九原型各一个 own 方法。
//!
//! 关键约定:
//! - 门禁统一走 CheckObjectCoercible 口径——this 为 null / undefined 抛 TypeError,
//! 其余原始值与原对象放行;
//! - 返回值取最小合理源码串(不追求 SpiderMonkey 完整序列化):Function 空串、
//! Object / Array 恒形、String 带引号、Boolean / Number 字面量、Date / Error 构造形、
//! RegExp 斜杠形;
//! - 本模块不提供 test262 度量面,验收靠单测钉 own 存在性与门禁抛错。
use oxide_runtime_api::{NativeResult, VmHost};
use oxide_types::value::JsValue;
/// 九原型 toSource 共享门禁:this 为 null / undefined 抛 TypeError(CheckObjectCoercible
/// 口径),其余原样返回 this 值供各臂消费。
fn to_source_check_this<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<JsValue, JsValue> {
let this_val = vm.reg(args[0]);
if this_val.is_null() || this_val.is_undefined() {
return Err(crate::error::create_type_error(vm, "toSource called on null or undefined"));
}
Ok(this_val)
}
/// `Object.prototype.toSource`:门禁后恒返 `"{}"`。
pub fn to_source_object<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if let Err(err) = to_source_check_this(vm, args) {
return NativeResult::Err(err);
}
NativeResult::Ok(vm.new_string_owned("{}".to_string()))
}
/// `Function.prototype.toSource`:引擎无函数源文本,门禁后恒返空串(不抛错)。
pub fn to_source_function<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if let Err(err) = to_source_check_this(vm, args) {
return NativeResult::Err(err);
}
NativeResult::Ok(vm.new_string_owned(String::new()))
}
/// `Array.prototype.toSource`:门禁后恒返 `"[]"`。
pub fn to_source_array<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if let Err(err) = to_source_check_this(vm, args) {
return NativeResult::Err(err);
}
NativeResult::Ok(vm.new_string_owned("[]".to_string()))
}
/// `String.prototype.toSource`:门禁后读 this 串值,双引号包裹返回。
///
/// # 边界与前提
/// - this 为字符串时直读单元文本;非字符串走完整 ToString(对象经 ToPrimitive,
/// 用户转换异常原样传播)。
pub fn to_source_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = match to_source_check_this(vm, args) {
Ok(v) => v,
Err(err) => return NativeResult::Err(err),
};
let text = if this_val.is_string() {
vm.lookup_str(this_val).unwrap_or_default()
} else {
match oxide_runtime_api::to_string_full(this_val, vm) {
Ok(s) => s,
Err(e) => return NativeResult::Err(crate::iterator::engine_error(vm, &e)),
}
};
NativeResult::Ok(vm.new_string_owned(format!("\"{}\"", text)))
}
/// `Boolean.prototype.toSource`:门禁后按 ToBoolean 判位,返 `"true"` / `"false"`。
pub fn to_source_boolean<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = match to_source_check_this(vm, args) {
Ok(v) => v,
Err(err) => return NativeResult::Err(err),
};
let b = oxide_runtime_api::to_boolean(this_val);
NativeResult::Ok(vm.new_string_owned(if b { "true".to_string() } else { "false".to_string() }))
}
/// `Number.prototype.toSource`:门禁后按 ToNumber 取 double,十进制字面量返回。
///
/// # 边界与前提
/// - this 为 double 时直读;非 double 走完整 ToNumber(用户转换异常原样传播)。
pub fn to_source_number<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = match to_source_check_this(vm, args) {
Ok(v) => v,
Err(err) => return NativeResult::Err(err),
};
let d = if this_val.is_double() {
this_val.as_double()
} else {
match oxide_runtime_api::to_number_full(this_val, vm) {
Ok(n) => n,
Err(e) => return NativeResult::Err(crate::iterator::engine_error(vm, &e)),
}
};
NativeResult::Ok(vm.number_to_string_cached(d))
}
/// `Date.prototype.toSource`:门禁后读时间戳(prop 0),返 `new Date(<ms>)` 形;
/// 非 Date 对象或时间戳缺失时回退 `new Date(NaN)`。
pub fn to_source_date<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = match to_source_check_this(vm, args) {
Ok(v) => v,
Err(err) => return NativeResult::Err(err),
};
let ms = if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if !ptr.is_null() && unsafe { &*ptr }.is_date_obj() {
let v = unsafe { &*ptr }.get_prop_at(0);
if v.is_double() {
v.as_double()
} else {
f64::NAN
}
} else {
f64::NAN
}
} else {
f64::NAN
};
let num_text = oxide_runtime_api::js_number_to_string(ms);
NativeResult::Ok(vm.new_string_owned(format!("new Date({})", num_text)))
}
/// `RegExp.prototype.toSource`:门禁后读 `[[OriginalSource]]` / `[[OriginalFlags]]` 槽,
/// 返 `/source/flags` 形;非 RegExp 对象回退 `/(?:)/`。
pub fn to_source_regexp<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = match to_source_check_this(vm, args) {
Ok(v) => v,
Err(err) => return NativeResult::Err(err),
};
if !this_val.is_object() {
return NativeResult::Ok(vm.new_string_owned("/(?:)/".to_string()));
}
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() || !unsafe { &*ptr }.is_regexp_obj() {
return NativeResult::Ok(vm.new_string_owned("/(?:)/".to_string()));
}
let re = unsafe { &*ptr };
let source = vm.lookup_str(re.get_regexp_source()).unwrap_or_default();
let flags = vm.lookup_str(re.get_regexp_flags()).unwrap_or_default();
NativeResult::Ok(vm.new_string_owned(format!("/{}/{}", source, flags)))
}
/// `Error.prototype.toSource`:门禁后读 `message` 属性,返 `new Error("message")` 形;
/// 无 message 时 message 段为空串。
///
/// # 边界与前提
/// - message 经原型链 Get 读取(访问器触发,用户异常吞掉回落空串);
/// - message 非 undefined 时走完整 ToString(Symbol 等不可转换场景回落空串)。
pub fn to_source_error<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = match to_source_check_this(vm, args) {
Ok(v) => v,
Err(err) => return NativeResult::Err(err),
};
let mut msg = String::new();
if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if !ptr.is_null() {
let obj = unsafe { &*ptr };
let si_msg = vm.perm_intern("message");
if let Ok(v) = vm.ordinary_get(obj, si_msg, this_val) {
if !v.is_undefined() {
if let Ok(s) = oxide_runtime_api::to_string_full(v, vm) {
msg = s;
}
}
}
}
}
NativeResult::Ok(vm.new_string_owned(format!("new Error({:?})", msg)))
}