Files
OxideJS-New/crates/oxide_builtins/src/json.rs
T

1459 lines
56 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::collections::HashSet;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::private_key::{int_key_value, is_int_key, make_int_key};
use oxide_types::value::JsValue;
use crate::object::walk_own_keys;
use oxide_runtime_api::{NativeResult, ProtoKind, VmHost};
/// `JSON.parse(text, reviver)`:解析 JSON 文本为 JS 值(递归下降解析器,
/// 逐原始值记录精确源文本切片)。提供 reviver 时以后序遍历逐属性调用
/// reviver 重建值,第三参 context 对象携带 `source` 属性。
///
/// `text` 经 `? ToString` 强转:对象走 ToPrimitive(string hint),Symbol
/// 抛 TypeError,强转期用户异常原值传播。
pub fn json_parse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Err(crate::error::create_syntax_error(vm, "JSON.parse requires 1 argument"));
}
let text_val = match oxide_runtime_api::to_string_value_full(vm.reg(args[1]), vm) {
Ok(v) => v,
Err(_) => {
// 强转期用户异常原值重抛;Symbol 无在途异常,按规范构造 TypeError。
if let Some(exc) = vm.take_uncaught_value() {
return NativeResult::Err(exc);
}
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string"));
}
};
let text = {
// SAFETY: text_val 已确认是字符串值。
unsafe { (*text_val.as_string_ptr()).to_owned_string() }
};
let parsed = match parse_json(&text) {
Ok(v) => v,
Err(msg) => return NativeResult::Err(crate::error::create_syntax_error(vm, &msg)),
};
let mut result = build_js_value(vm, &parsed);
// reviver 遍历以 holder 包装对象为根:解析得到的根值存于空串键槽,
// 后序遍历自该槽展开。返回值 = 根级 reviver 返回值原样(wrapper 的
// `''` 槽由父级循环施加,根级无父级,槽恒不被触碰)。
if args.len() > 2 {
let reviver_val = vm.reg(args[2]);
if reviver_val.is_object() {
let rptr = reviver_val.as_js_object_ptr();
if !rptr.is_null() && unsafe { (*rptr).is_function() } {
let empty_si = vm.perm_intern("");
let holder = create_wrapper(vm, result);
let holder_ptr = holder.as_js_object_ptr();
result = match walk_reviver(vm, holder_ptr, empty_si, reviver_val, Some(&parsed)) {
Ok(new_val) => new_val,
Err(e) => return NativeResult::Err(e),
};
}
}
}
NativeResult::Ok(result)
}
/// rawJSON 对象禁止的首/末 code unit 集:TAB/LF/CR/SPACE。
const RAW_JSON_FORBIDDEN_EDGE_UNITS: [u16; 4] = [0x09, 0x0A, 0x0D, 0x20];
/// `JSON.rawJSON(text)`:把合法 JSON 文本包装为 raw JSON 对象。
///
/// # 步骤
/// 1. `? ToString(text)`(Symbol 抛 TypeError,对象经 ToPrimitive,用户异常原值传播)。
/// 2. 空串或首/末 code unit 为 TAB/LF/CR/SPACE 抛 SyntaxError。
/// 3. 须为合法 JSON 文本且最外层非 object/array,否则 SyntaxError。
/// 4. 建 null 原型 frozen 对象,唯一自身属性 `rawJSON` = 文本
/// (writable:false、enumerable:true、configurable:false)。
///
/// # 副作用
/// - 经统一入口分配一个 session 对象与其字符串属性值。
pub fn json_raw_json<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let text_val = match oxide_runtime_api::to_string_value_full(
if args.len() >= 2 { vm.reg(args[1]) } else { JsValue::undefined() },
vm,
) {
Ok(v) => v,
Err(_) => {
// 强转期用户异常原值重抛;Symbol 无在途异常,按规范构造 TypeError。
if let Some(exc) = vm.take_uncaught_value() {
return NativeResult::Err(exc);
}
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert a Symbol value to a string"));
}
};
let text = {
// SAFETY: text_val 已确认是字符串值。
unsafe { (*text_val.as_string_ptr()).to_owned_string() }
};
// 边界字符校验:空串或首/末 code unit 属空白四元之一。
let units: Vec<u16> = text.encode_utf16().collect();
if units.is_empty()
|| RAW_JSON_FORBIDDEN_EDGE_UNITS.contains(&units[0])
|| RAW_JSON_FORBIDDEN_EDGE_UNITS.contains(&units[units.len() - 1])
{
return NativeResult::Err(crate::error::create_syntax_error(vm, "Invalid JSON text"));
}
// 合法性校验:解析失败即非合法 JSON 文本;最外层
// object/array 不满足 raw JSON 语义。
match parse_json(&text) {
Ok(JsonNode {
kind: JsonKind::Object(_) | JsonKind::Array(_),
..
}) => {
return NativeResult::Err(crate::error::create_syntax_error(vm, "Invalid JSON text"));
}
Ok(_) => {}
Err(msg) => return NativeResult::Err(crate::error::create_syntax_error(vm, &msg)),
}
// 建 null 原型对象:rawJSON 属性为唯一自身属性,frozen 形态收尾。
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());
obj.type_tag = JsObject::OBJ_TYPE_RAW_JSON;
let raw_si = vm.perm_intern("rawJSON");
let new_shape = vm.make_shape(obj.shape_id(), raw_si);
obj.set_shape_id(new_shape);
obj.ensure_hash_props().push(text_val);
let obj_ptr = vm.alloc_object(obj);
{
let obj = unsafe { &mut *obj_ptr };
obj.set_data_meta(0, PropAttributes::new(false, true, false));
obj.set_frozen(true);
obj.set_extensible(false);
}
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
}
/// `JSON.isRawJSON(value)`:value 带 raw JSON 类型标签时返回 true,其余一律 false。
pub fn json_is_raw_json<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let val = if args.len() >= 2 { vm.reg(args[1]) } else { JsValue::undefined() };
let is_raw = val.is_object() && {
let p = val.as_js_object_ptr();
!p.is_null() && unsafe { (*p).is_raw_json_obj() }
};
NativeResult::Ok(JsValue::bool(is_raw))
}
/// 解析后的 JSON 节点:值 + 精确源文本切片(仅原始值携带)。
/// 切片供 reviver context 的 `source` 属性与 SameValue-as 判定使用。
enum JsonKind {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<JsonNode>),
Object(Vec<(String, JsonNode)>),
}
struct JsonNode {
kind: JsonKind,
/// 精确源文本切片(数字不规范化、字符串含引号);array/object 为 `None`。
source: Option<String>,
}
/// 递归下降 JSON 解析器:接受 JSON 文本语法(空白限 TAB/LF/CR/SPACE),
/// 逐原始值记录精确源文本切片。
///
/// # 边界与前提
/// - 前导零数字、尾随逗号、溢出(1e400)、孤立 surrogate 均报 SyntaxError。
/// - 嵌套 object/array 深度上限 `MAX_JSON_DEPTH` 层,超限报 SyntaxError
/// (进入时先计入再判定,防深嵌套在递归展开前耗尽栈)。
/// - 重复对象键:末写胜(键序由 build_js_value 统一)。
fn parse_json(text: &str) -> Result<JsonNode, String> {
let mut p = JsonParser { text, pos: 0, depth: 0 };
p.skip_ws();
let node = p.parse_value()?;
p.skip_ws();
if p.pos != text.len() {
return Err("trailing characters".into());
}
Ok(node)
}
/// 嵌套容器深度上限:object/array 进入时计入,超限报 SyntaxError。
const MAX_JSON_DEPTH: usize = 128;
struct JsonParser<'a> {
text: &'a str,
pos: usize,
depth: usize,
}
impl<'a> JsonParser<'a> {
fn skip_ws(&mut self) {
while self.pos < self.text.len() {
match self.text.as_bytes()[self.pos] {
b' ' | b'\t' | b'\n' | b'\r' => self.pos += 1,
_ => break,
}
}
}
fn peek(&self) -> Option<u8> {
self.text.as_bytes().get(self.pos).copied()
}
fn peek_char(&self) -> Option<char> {
self.text.get(self.pos..).and_then(|s| s.chars().next())
}
/// 以当前游标为终点物化原始值节点(源切片 = `start..pos`)。
fn prim(&self, kind: JsonKind, start: usize) -> JsonNode {
JsonNode {
kind,
source: Some(self.text[start..self.pos].to_string()),
}
}
fn expect_word(&mut self, word: &str) -> Result<(), String> {
let end = self.pos + word.len();
if self.text.get(self.pos..end) == Some(word) {
self.pos = end;
Ok(())
} else {
Err("invalid literal".into())
}
}
fn parse_value(&mut self) -> Result<JsonNode, String> {
self.skip_ws();
let start = self.pos;
let c = self.peek().ok_or_else(|| "unexpected end of input".to_string())?;
match c {
b'n' => {
self.expect_word("null")?;
Ok(self.prim(JsonKind::Null, start))
}
b't' => {
self.expect_word("true")?;
Ok(self.prim(JsonKind::Bool(true), start))
}
b'f' => {
self.expect_word("false")?;
Ok(self.prim(JsonKind::Bool(false), start))
}
b'"' => {
let s = self.parse_string()?;
Ok(self.prim(JsonKind::String(s), start))
}
b'[' => {
// 深度计数:进入时先 +1 再判上限,超限在递归展开前拒绝。
self.depth += 1;
if self.depth > MAX_JSON_DEPTH {
self.depth -= 1;
return Err("recursion limit exceeded".into());
}
let items = self.parse_array()?;
self.depth -= 1;
Ok(JsonNode {
kind: JsonKind::Array(items),
source: None,
})
}
b'{' => {
self.depth += 1;
if self.depth > MAX_JSON_DEPTH {
self.depth -= 1;
return Err("recursion limit exceeded".into());
}
let entries = self.parse_object()?;
self.depth -= 1;
Ok(JsonNode {
kind: JsonKind::Object(entries),
source: None,
})
}
b'-' | b'0'..=b'9' => {
let n = self.parse_number()?;
Ok(self.prim(JsonKind::Number(n), start))
}
_ => Err("expected value".into()),
}
}
fn parse_array(&mut self) -> Result<Vec<JsonNode>, String> {
// 当前字节为 '['。
self.pos += 1;
let mut items = Vec::new();
self.skip_ws();
if self.peek() == Some(b']') {
self.pos += 1;
return Ok(items);
}
loop {
let item = self.parse_value()?;
items.push(item);
self.skip_ws();
match self.peek() {
Some(b',') => {
self.pos += 1;
self.skip_ws();
// 尾随逗号:',' 后必须跟值。
if self.peek() == Some(b']') {
return Err("trailing comma".into());
}
}
Some(b']') => {
self.pos += 1;
return Ok(items);
}
_ => return Err("expected ',' or ']'".into()),
}
}
}
fn parse_object(&mut self) -> Result<Vec<(String, JsonNode)>, String> {
// 当前字节为 '{'。
self.pos += 1;
let mut entries = Vec::new();
self.skip_ws();
if self.peek() == Some(b'}') {
self.pos += 1;
return Ok(entries);
}
loop {
self.skip_ws();
let key = match self.peek() {
Some(b'"') => self.parse_string()?,
_ => return Err("expected property name".into()),
};
self.skip_ws();
if self.peek() != Some(b':') {
return Err("expected ':'".into());
}
self.pos += 1;
let val = self.parse_value()?;
entries.push((key, val));
self.skip_ws();
match self.peek() {
Some(b',') => {
self.pos += 1;
self.skip_ws();
// 尾随逗号:',' 后必须跟键。
if self.peek() == Some(b'}') {
return Err("trailing comma".into());
}
}
Some(b'}') => {
self.pos += 1;
return Ok(entries);
}
_ => return Err("expected ',' or '}'".into()),
}
}
}
/// JSON 数字文法:`-?(0|[1-9]digits)(.digits)?([eE][+-]?digits)?`;
/// 溢出(1e400)报范围错误,下溢(1e-400)归零。
fn parse_number(&mut self) -> Result<f64, String> {
let start = self.pos;
if self.peek() == Some(b'-') {
self.pos += 1;
}
match self.peek() {
Some(b'0') => self.pos += 1,
Some(b'1'..=b'9') => {
while self.peek().is_some_and(|c| c.is_ascii_digit()) {
self.pos += 1;
}
}
_ => return Err("invalid number".into()),
}
if self.peek() == Some(b'.') {
self.pos += 1;
if !self.peek().is_some_and(|c| c.is_ascii_digit()) {
return Err("invalid number".into());
}
while self.peek().is_some_and(|c| c.is_ascii_digit()) {
self.pos += 1;
}
}
if let Some(c) = self.peek() {
if c == b'e' || c == b'E' {
self.pos += 1;
if let Some(s) = self.peek() {
if s == b'+' || s == b'-' {
self.pos += 1;
}
}
if !self.peek().is_some_and(|c| c.is_ascii_digit()) {
return Err("invalid number".into());
}
while self.peek().is_some_and(|c| c.is_ascii_digit()) {
self.pos += 1;
}
}
}
let slice = &self.text[start..self.pos];
let n = slice.parse::<f64>().map_err(|_| "invalid number".to_string())?;
if !n.is_finite() {
return Err("number out of range".into());
}
Ok(n)
}
fn parse_string(&mut self) -> Result<String, String> {
// 当前字节为开引号。
self.pos += 1;
let mut units: Vec<u16> = Vec::new();
loop {
let c = self.peek_char().ok_or_else(|| "unterminated string".to_string())?;
match c {
'"' => {
self.pos += 1;
// 解析期已拒绝孤立 surrogate,单元序列恒为良形 UTF-16。
return Ok(String::from_utf16(&units).expect("no lone surrogate survives parse"));
}
'\\' => {
self.pos += 1;
let e = self.peek_char().ok_or_else(|| "unterminated escape".to_string())?;
self.pos += 1;
let u = match e {
'"' => 0x22,
'\\' => 0x5C,
'/' => 0x2F,
'b' => 0x08,
'f' => 0x0C,
'n' => 0x0A,
'r' => 0x0D,
't' => 0x09,
'u' => self.parse_unicode_escape()?,
_ => return Err("invalid escape".into()),
};
units.push(u as u16);
}
c => {
if (c as u32) <= 0x1F {
return Err("unescaped control character in string".into());
}
// 码元编码(含 astral 字符双单元)。
let mut buf = [0u16; 2];
let written = c.encode_utf16(&mut buf);
units.extend_from_slice(written);
self.pos += c.len_utf8();
}
}
}
}
fn parse_unicode_escape(&mut self) -> Result<u32, String> {
let hi = self.parse_hex4()?;
if (0xD800..=0xDBFF).contains(&hi) {
// 代理对:高 surrogate 必须紧跟 \uXXXX 低 surrogate。
if self.text.get(self.pos..).is_some_and(|s| s.starts_with("\\u")) {
self.pos += 2;
let lo = self.parse_hex4()?;
if (0xDC00..=0xDFFF).contains(&lo) {
return Ok(0x10000 + ((u32::from(hi) - 0xD800) << 10) + (u32::from(lo) - 0xDC00));
}
return Err("invalid surrogate pair".into());
}
return Err("lone leading surrogate in hex escape".into());
}
if (0xDC00..=0xDFFF).contains(&hi) {
return Err("lone leading surrogate in hex escape".into());
}
Ok(u32::from(hi))
}
fn parse_hex4(&mut self) -> Result<u16, String> {
let mut v: u16 = 0;
for _ in 0..4 {
let c = self.peek_char().ok_or_else(|| "unexpected end of hex escape".to_string())?;
let d = c
.to_digit(16)
.map(|d| d as u16)
.ok_or_else(|| "invalid hex digit".to_string())?;
v = v * 16 + d;
self.pos += 1;
}
Ok(v)
}
}
/// InternalizeJSONProperty 后序遍历:Get 读当前值 → 递归子节点并把每子返回值
/// 施加于容器(undefined → [[Delete]],否则 CreateDataProperty,两失败路径
/// 静默)→ 构造 context 对象 → 调用 reviver。返回值 = 本级 reviver 返回值
/// 原样,由父级循环施加(根级由 json_parse 直接作 parse 结果)。
///
/// `node` 是本级对应的解析节点(可缺):子集恒按当前值(容器可被 reviver
/// 前向改写,键集与解析节点不再重合);节点仅作子节点映射与 context 的
/// `source` 属性来源,无节点处按空 context 处理。
fn walk_reviver<H: VmHost>(
vm: &mut H, holder_ptr: *mut JsObject, key_si: u32, reviver: JsValue, node: Option<&JsonNode>,
) -> Result<JsValue, JsValue> {
let holder_val = JsValue::from_js_object(holder_ptr);
// 读臂:完整 Get(原型链 + 自身访问器触发),getter 抛出传播原值。
let val = match vm.ordinary_get(unsafe { &*holder_ptr }, key_si, holder_val) {
Ok(v) => v,
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
return Err(exc);
}
};
// 后序遍历:先递归子节点,每子返回值施加于容器。
if val.is_object() {
let obj_ptr = val.as_js_object_ptr();
if !obj_ptr.is_null() {
if unsafe { (*obj_ptr).is_array() } {
let len = unsafe { (*obj_ptr).prop_count() } as usize;
for i in 0..len {
let child_si = make_int_key(i as u32);
let child_node = node.and_then(|n| match &n.kind {
JsonKind::Array(items) => items.get(i),
_ => None,
});
let new_val = walk_reviver(vm, obj_ptr, child_si, reviver, child_node)?;
apply_child_result(vm, obj_ptr, child_si, new_val);
}
} else {
// 对象臂:节点键经同一键规范化映射 si(重复键末写胜,
// 与 build_js_value 一致),容器键逐一按 si 定位节点。
let child_nodes = match node {
Some(JsonNode {
kind: JsonKind::Object(entries),
..
}) => {
let mut seen: Vec<(u32, &JsonNode)> = Vec::new();
for (key, val) in entries {
let si = vm.string_key_si(key);
match seen.iter_mut().find(|(s, _)| *s == si) {
Some(slot) => slot.1 = val,
None => seen.push((si, val)),
}
}
seen
}
_ => Vec::new(),
};
let keys = {
let obj = unsafe { &*obj_ptr };
walk_own_keys(vm, obj)
};
for (child_si, _child_pos) in keys {
let child_node = child_nodes.iter().find(|(s, _)| *s == child_si).map(|(_, n)| *n);
let new_val = walk_reviver(vm, obj_ptr, child_si, reviver, child_node)?;
apply_child_result(vm, obj_ptr, child_si, new_val);
}
}
}
}
// context 对象:仅当当前值与解析时值 SameValue-as 时携带 source。
let context = build_context(vm, val, node);
// 对当前值调用 reviver,返回值上抛由父级施加。
let key_val = crate::object::key_si_to_js_value(vm, key_si);
match vm.call_function_sync(reviver, holder_val, &[key_val, val, context]) {
Ok(new_val) => Ok(new_val),
Err(msg) => {
// reviver 内抛出的原始值原样传播(不折叠为 TypeError 文本)。
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
Err(exc)
}
}
}
/// 构造 reviver 的 context 对象:普通对象(proto = Object.prototype)。
/// 仅当节点带源切片且 `val` 与解析时值 SameValue-as 时注入 own `source`
/// 属性(w/e/c 全 true)——被 reviver 前向替换的值、array/object 节点
/// 与无节点处一律空 context。
fn build_context<H: VmHost>(vm: &mut H, val: JsValue, node: Option<&JsonNode>) -> JsValue {
let object_proto = vm.builtin_proto(ProtoKind::ObjectProto);
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
if let Some(n) = node {
if let Some(src) = n.source.as_ref() {
if same_value_as(vm, val, n) {
let source_si = vm.perm_intern("source");
let new_shape = vm.make_shape(obj.shape_id(), source_si);
obj.set_shape_id(new_shape);
obj.ensure_hash_props().push(vm.new_string(src));
}
}
}
let obj_ptr = vm.alloc_object(obj);
JsValue::from_js_object(obj_ptr)
}
/// SameValue-as 判定(当前值 vs 解析时值):数字区分 +0/-0、NaN 自反;
/// 字符串按内容比;布尔/null 按值。
fn same_value_as<H: VmHost>(vm: &H, val: JsValue, node: &JsonNode) -> bool {
match &node.kind {
JsonKind::Null => val.is_null(),
JsonKind::Bool(b) => val.is_bool() && val.as_bool() == *b,
JsonKind::Number(n) => val.is_double() && same_value_number(val.as_double(), *n),
JsonKind::String(s) => {
if !val.is_string() {
return false;
}
let parsed: Vec<u16> = s.encode_utf16().collect();
vm.string_units(val).as_ref() == parsed.as_slice()
}
_ => false,
}
}
fn same_value_number(a: f64, b: f64) -> bool {
if a.is_nan() {
return b.is_nan();
}
if a == b {
// SameValue:+0 与 -0 不相等。
a != 0.0 || a.is_sign_positive() == b.is_sign_positive()
} else {
false
}
}
/// 子节点递归返回值施加于容器:`undefined` → [[Delete]](非可配置保留),
/// 否则 CreateDataProperty(非可配置静默保旧值,新键建自身)。两失败路径
/// 均静默不抛(规范明注)。
fn apply_child_result<H: VmHost>(vm: &mut H, child_ptr: *mut JsObject, child_si: u32, new_val: JsValue) {
if new_val.is_undefined() {
// 子节点为 JSON.parse 新建的 plain 对象(非 deferred namespace),触发
// 恒 no-op;删除失败静默不抛(规范明注)。
let _ = crate::object::delete_own_property(vm, unsafe { &mut *child_ptr }, child_si);
} else {
let _ = vm.define_data_property(
unsafe { &mut *child_ptr },
child_si,
new_val,
PropAttributes::new(true, true, true),
);
}
}
/// 解析节点树 → JS 值树。对象键经同一字符串规范化(规范数字串映射整数键),
/// 重复键末写胜、键序字典序(与旧解析器语义一致)。
fn build_js_value<H: VmHost>(vm: &mut H, node: &JsonNode) -> JsValue {
match &node.kind {
JsonKind::Null => JsValue::null(),
JsonKind::Bool(b) => JsValue::bool(*b),
JsonKind::Number(n) => JsValue::float(*n),
JsonKind::String(s) => vm.new_string(s),
JsonKind::Array(items) => {
let array_proto = vm.builtin_proto(ProtoKind::ArrayProto);
let n = items.len();
let array_obj =
vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(array_proto), n));
for (i, item) in items.iter().enumerate() {
let jsv = build_js_value(vm, item);
unsafe {
(*array_obj).set_prop_at(i, jsv);
}
}
JsValue::from_js_object(array_obj)
}
JsonKind::Object(entries) => {
// 重复键末写胜;键序字典序(与旧解析器语义一致)。
let mut unique: Vec<(&String, &JsonNode)> = Vec::new();
for (key, val) in entries {
match unique.iter_mut().find(|(k, _)| *k == key.as_str()) {
Some(slot) => slot.1 = val,
None => unique.push((key, val)),
}
}
unique.sort_by(|a, b| a.0.cmp(b.0));
let object_proto = vm.builtin_proto(ProtoKind::ObjectProto);
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
for (key, val) in unique {
// 键经字符串规范化:规范数字串("0"/"5")映射整数键,与属性访问统一。
let si = vm.string_key_si(key);
let jsv = build_js_value(vm, val);
let new_shape = vm.make_shape(obj.shape_id(), si);
obj.set_shape_id(new_shape);
obj.ensure_hash_props().push(jsv);
}
let obj_ptr = vm.alloc_object(obj);
JsValue::from_js_object(obj_ptr)
}
}
}
fn create_wrapper<H: VmHost>(vm: &mut H, value: JsValue) -> JsValue {
let object_proto = vm.builtin_proto(ProtoKind::ObjectProto);
let empty_si = vm.perm_intern("");
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
let new_shape = vm.make_shape(obj.shape_id(), empty_si);
obj.set_shape_id(new_shape);
obj.ensure_hash_props().push(value);
let obj_ptr = vm.alloc_object(obj);
JsValue::from_js_object(obj_ptr)
}
/// space 参数文本化(Stringify 步 5):数字与装箱 Number 经 ToNumber →
/// ToIntegerOrInfinity 后 min(10, x) 个空格(+∞ → 10、−∞/NaN → 空);字符串与
/// 装箱 String 取前 10 码元(码元口径,astral 字符可截在代理对边界,截断产物
/// 保留孤立 surrogate;装箱 String 走完整 ToString,步 4c 同款语义,抛出值原样
/// 上抛);其余形态(装箱 Boolean、普通对象、Symbol 等)gap 为空。
fn process_space<H: VmHost>(vm: &mut H, val: JsValue) -> Result<Vec<u16>, JsValue> {
if val.is_int() || val.is_double() {
let n = oxide_runtime_api::to_integer_or_infinity(val);
return Ok(number_space_units(n));
}
if val.is_string() {
let units = vm.string_units(val);
return Ok(units.iter().take(10).copied().collect());
}
if val.is_object() {
let ptr = val.as_js_object_ptr();
if !ptr.is_null() {
let obj = unsafe { &*ptr };
// [[NumberData]]:ToNumber(ToPrimitive number hint,用户方法可触发)
// 后 ToIntegerOrInfinity。
if obj.is_number_obj() {
let n = match oxide_runtime_api::to_number_full(val, vm) {
Ok(n) => n,
Err(msg) => {
return Err(vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg)));
}
};
let n = oxide_runtime_api::to_integer_or_infinity(JsValue::float(n));
return Ok(number_space_units(n));
}
// [[StringData]]:完整 ToString。
if obj.is_string_obj() {
let s = match oxide_runtime_api::to_string_value_full(val, vm) {
Ok(s) => s,
Err(msg) => {
return Err(vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg)));
}
};
let units = vm.string_units(s);
return Ok(units.iter().take(10).copied().collect());
}
}
}
Ok(Vec::new())
}
/// 数字 space → min(10, ToIntegerOrInfinity(space)) 个空格码元:+∞ → 10、
/// −∞/NaN/小于 1 → 空。
fn number_space_units(n: f64) -> Vec<u16> {
let m = n.min(10.0);
if m.is_nan() || m < 1.0 {
Vec::new()
} else {
vec![0x20; m as usize]
}
}
/// 键 si 物化为单元序列:整数键 → ASCII 数字串,字符串键 → 码表键经 `decode_key`
/// 还原(含孤立 surrogate 单元)。序列化文本与 toJSON/replacer 键参数均用此真实串。
fn key_si_to_units<H: VmHost>(vm: &H, si: u32) -> Vec<u16> {
if is_int_key(si) {
int_key_value(si).to_string().encode_utf16().collect()
} else {
vm.perm_lookup(si)
.map(oxide_kernel::string_forge::decode_key)
.unwrap_or_default()
}
}
/// 白名单元素 → 键名提取(Stringify 步 4):String/Number 原语直取;装箱
/// String/Number 经 ToPrimitive string hint(toString 优先、valueOf 兜底、
/// 两法皆不可调用落盒值);其余形态(装箱 Boolean、普通对象、Symbol 等)
/// 返回 None 跳过。方法读取与调用异常传播原始抛出值。
fn whitelist_element_name<H: VmHost>(vm: &mut H, elem: JsValue) -> Result<Option<String>, JsValue> {
if elem.is_undefined() {
return Ok(None);
}
if elem.is_string() || elem.is_int() || elem.is_double() {
return Ok(Some(oxide_runtime_api::to_string(elem)));
}
if !elem.is_object() {
return Ok(None);
}
let ptr = elem.as_js_object_ptr();
if ptr.is_null() {
return Ok(None);
}
let obj = unsafe { &*ptr };
let boxed = obj.boxed_value();
if !boxed.is_string() && !boxed.is_int() && !boxed.is_double() {
return Ok(None);
}
// ToPrimitive string hint:toString 优先,valueOf 兜底。
let to_string_si = vm.perm_intern("toString");
let value_of_si = vm.perm_intern("valueOf");
for m_si in [to_string_si, value_of_si] {
let m = vm.ordinary_get(obj, m_si, elem).map_err(|msg| {
vm.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg))
})?;
if !m.is_object() {
continue;
}
let fptr = m.as_js_object_ptr();
if fptr.is_null() || !unsafe { (*fptr).is_function() } {
continue;
}
let r = vm.call_function_sync(m, elem, &[]).map_err(|msg| {
vm.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg))
})?;
return Ok(Some(oxide_runtime_api::to_string(r)));
}
// 装箱原型面理论不可达兜底:盒值直取。
Ok(Some(oxide_runtime_api::to_string(boxed)))
}
fn call_to_json<H: VmHost>(vm: &mut H, obj_val: JsValue, key: &[u16]) -> Result<JsValue, JsValue> {
// SerializeJSONProperty 步 2:对象与 BigInt 原语均查 toJSON(Get 对原语
// 自动装箱),BigInt 的查表对象取 %BigIntPrototype%。
if !obj_val.is_object() && !obj_val.is_bigint() {
return Ok(obj_val);
}
let obj_ptr = if obj_val.is_object() {
let p = obj_val.as_js_object_ptr();
if p.is_null() {
return Ok(obj_val);
}
p
} else {
vm.builtin_proto(ProtoKind::BigIntProto)
};
let tojson_si = vm.perm_intern("toJSON");
// Get 语义查表:访问器形 toJSON 触发 getter(receiver = 原值,BigInt 原语
// 以自身作 this),getter 异常传播原始抛出值。
let fn_val = match vm.ordinary_get(unsafe { &*obj_ptr }, tojson_si, obj_val) {
Ok(v) => v,
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
return Err(exc);
}
};
if fn_val.is_object() {
let fn_ptr = fn_val.as_js_object_ptr();
if !fn_ptr.is_null() && unsafe { (*fn_ptr).is_function() } {
let key_val = vm.new_string_units_owned(key.to_vec());
match vm.call_function_sync(fn_val, obj_val, &[key_val]) {
Ok(v) => Ok(v),
Err(msg) => {
// 用户抛出的原始值原样传播(toJSON 抛非 Error 值不降级为 TypeError)。
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
Err(exc)
}
}
} else {
Ok(obj_val)
}
} else {
Ok(obj_val)
}
}
/// 序列化族单自身属性值读(SerializeJSONProperty 步 2 的 Get):数据属性直读
/// 存储槽;访问器属性触发 getter(this = 容器对象),getter 异常传播原始
/// 抛出值。
fn read_json_property_value<H: VmHost>(
vm: &mut H, obj: &JsObject, obj_val: JsValue, si: u32, pos: u32,
) -> Result<JsValue, JsValue> {
if obj.is_accessor_meta(pos) {
match vm.ordinary_get(obj, si, obj_val) {
Ok(value) => Ok(value),
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
Err(exc)
}
}
} else {
Ok(obj.get_prop_at(pos))
}
}
/// `JSON.stringify(value, replacer, space)`:将 JS 值序列化为 JSON 文本。
/// 支持 replacer 函数/属性白名单、toJSON 钩子、缩进与循环引用检测。
/// 顶层值序为 Get → toJSON → replacer,undefined/函数/Symbol 早返在 replacer
/// 之后(replacer 可替换早返形态)。
pub fn json_stringify<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
}
let value = vm.reg(args[1]);
let mut replacer_fn: Option<JsValue> = None;
let mut replacer_whitelist: Option<Vec<String>> = None;
if args.len() > 2 {
let replacer_val = vm.reg(args[2]);
if replacer_val.is_object() {
let rptr = replacer_val.as_js_object_ptr();
if !rptr.is_null() {
let robj = unsafe { &*rptr };
if robj.is_function() {
replacer_fn = Some(replacer_val);
} else if robj.is_array() {
let len = robj.prop_count() as usize;
let mut whitelist: Vec<String> = Vec::new();
for i in 0..len {
let elem = robj.get_prop_at(i);
match whitelist_element_name(vm, elem) {
Ok(Some(name)) => {
// 有序去重:重复项不入列表。
if !whitelist.contains(&name) {
whitelist.push(name);
}
}
Ok(None) => {}
Err(exc) => return NativeResult::Err(exc),
}
}
replacer_whitelist = Some(whitelist);
}
}
}
}
let space = if args.len() > 3 {
match process_space(vm, vm.reg(args[3])) {
Ok(s) => s,
Err(exc) => return NativeResult::Err(exc),
}
} else {
Vec::new()
};
let holder = create_wrapper(vm, value);
let value = match call_to_json(vm, value, &[]) {
Ok(v) => v,
Err(e) => return NativeResult::Err(e),
};
let value = if let Some(replacer) = replacer_fn {
let key_val = vm.new_string("");
match vm.call_function_sync(replacer, holder, &[key_val, value]) {
Ok(v) => v,
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
return NativeResult::Err(exc);
}
}
} else {
value
};
// 顶层省略值:replacer 之后的 undefined、函数、Symbol 一律返回 undefined
// (非空串)——replacer 先于本判定应用(SerializeJSONProperty 步 3 先于步 8/10)。
let top_is_fn = value.is_object() && {
let p = value.as_js_object_ptr();
!p.is_null() && unsafe { (*p).is_function() }
};
if value.is_undefined() || value.is_symbol() || top_is_fn {
return NativeResult::Ok(JsValue::undefined());
}
let mut visited = HashSet::new();
let mut output: Vec<u16> = Vec::new();
let ctx = StringifyCtx {
visited: &mut visited,
out: &mut output,
replacer_fn,
replacer_whitelist: replacer_whitelist.as_ref(),
space: &space,
indent_level: 0,
};
match jsvalue_to_json(vm, value, ctx) {
Ok(()) => NativeResult::Ok(vm.new_string_units_owned(output)),
Err(exc) => NativeResult::Err(exc),
}
}
/// JSON 序列化上下文:环检表、输出流、replacer 与缩进状态。
/// 递归传递时经 [`StringifyCtx::child`] 派生下一缩进层。
struct StringifyCtx<'data, 'w> {
visited: &'w mut HashSet<*const JsObject>,
out: &'w mut Vec<u16>,
replacer_fn: Option<JsValue>,
replacer_whitelist: Option<&'data Vec<String>>,
space: &'data [u16],
indent_level: usize,
}
impl<'data, 'w> StringifyCtx<'data, 'w> {
/// 派生同缩进层上下文:共享同一环检表与输出流,indent_level 不变
/// (值位序列化入口传给对象/数组展开时用)。
fn same(&mut self) -> StringifyCtx<'data, '_> {
StringifyCtx {
visited: &mut *self.visited,
out: &mut *self.out,
replacer_fn: self.replacer_fn,
replacer_whitelist: self.replacer_whitelist,
space: self.space,
indent_level: self.indent_level,
}
}
/// 派生下一缩进层上下文:共享同一环检表与输出流,indent_level 加一
/// (对象/数组展开内递归回值位序列化时用)。
fn child(&mut self) -> StringifyCtx<'data, '_> {
StringifyCtx {
visited: &mut *self.visited,
out: &mut *self.out,
replacer_fn: self.replacer_fn,
replacer_whitelist: self.replacer_whitelist,
space: self.space,
indent_level: self.indent_level + 1,
}
}
}
/// 值位序列化(SerializeValue 及对象/数组展开)。toJSON 钩子不在本入口应用——
/// 唯一应用点在调用方(顶层 `json_stringify` 与 `stringify_object`/`stringify_array`
/// 的属性循环内,Get 之后、replacer 之前),保证每值位恰一次且与 replacer 顺序
/// 符合规范。`Err` 携带的原始异常值(含环检 TypeError)原样上抛。
fn jsvalue_to_json<H: VmHost>(vm: &mut H, val: JsValue, mut ctx: StringifyCtx<'_, '_>) -> Result<(), JsValue> {
if val.is_null() {
push_ascii(ctx.out, "null");
} else if val.is_undefined() {
} else if val.is_symbol() {
push_ascii(ctx.out, "null");
} else if val.is_bool() {
push_ascii(ctx.out, if val.as_bool() { "true" } else { "false" });
} else if val.is_int() {
push_ascii(ctx.out, &val.as_int().to_string());
} else if val.is_double() {
let n = val.as_double();
if !n.is_finite() {
push_ascii(ctx.out, "null");
} else {
push_number(ctx.out, n);
}
} else if val.is_bigint() {
// SerializeJSONProperty 步 10:BigInt 无 JSON 文本形态,抛 TypeError。
return Err(crate::error::create_type_error(vm, "Do not know how to serialize a BigInt"));
} else if val.is_string() {
// SAFETY: val 已确认是字符串值;单元视图按码单元流序列化(见 stringify_string_units)。
let units = unsafe { (*val.as_string_ptr()).units() };
stringify_string_units(&units, ctx.out);
} else if val.is_object() {
let obj_ptr = val.as_js_object_ptr();
if obj_ptr.is_null() {
push_ascii(ctx.out, "null");
return Ok(());
}
// SerializeJSONProperty 步 1a:raw JSON 对象直接输出原始文本(先于
// toJSON 查找;该对象仅一个字符串属性,不可能参与环,免环检)。
{
let obj = unsafe { &*obj_ptr };
if obj.is_raw_json_obj() {
let raw_val = obj.get_prop_at(0);
// SAFETY: rawJSON 属性构造期即写入字符串值。
let raw_text = unsafe { (*raw_val.as_string_ptr()).to_owned_string() };
push_ascii(ctx.out, &raw_text);
return Ok(());
}
}
if !ctx.visited.insert(obj_ptr as *const JsObject) {
return Err(crate::error::create_type_error(vm, "Converting circular structure to JSON"));
}
let obj = unsafe { &*obj_ptr };
// 步 4a:装箱 Number → ToNumber(用户方法可触发),非有限值 → null。
if obj.is_number_obj() {
let n = match oxide_runtime_api::to_number_full(val, vm) {
Ok(n) => n,
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
ctx.visited.remove(&(obj_ptr as *const JsObject));
return Err(exc);
}
};
if n.is_finite() {
push_number(ctx.out, n);
} else {
push_ascii(ctx.out, "null");
}
ctx.visited.remove(&(obj_ptr as *const JsObject));
return Ok(());
}
// 步 4b:装箱 Boolean → [[BooleanData]]。
if obj.is_boolean_obj() {
let b = obj.boxed_value();
push_ascii(ctx.out, if b.is_bool() && b.as_bool() { "true" } else { "false" });
ctx.visited.remove(&(obj_ptr as *const JsObject));
return Ok(());
}
// 步 4d:装箱 BigInt 无条件解包 [[BigIntData]],后续步 10 抛 TypeError。
if obj.boxed_value().is_bigint() {
return Err(crate::error::create_type_error(vm, "Do not know how to serialize a BigInt"));
}
// 步 4c:装箱 String 走完整 ToString([[StringData]] 臂):尊重
// toString/Symbol.toPrimitive 覆盖、不直读载荷,抛出值原样传播。
if obj.is_string_obj() {
let s = match oxide_runtime_api::to_string_value_full(val, vm) {
Ok(s) => s,
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
ctx.visited.remove(&(obj_ptr as *const JsObject));
return Err(exc);
}
};
let units = vm.string_units(s);
stringify_string_units(&units, ctx.out);
} else if obj.is_typed_array_obj() {
stringify_typed_array(vm, obj, ctx.same())?;
} else if obj.is_array() {
stringify_array(vm, obj, ctx.same())?;
} else {
stringify_object(vm, obj, ctx.same())?;
}
ctx.visited.remove(&(obj_ptr as *const JsObject));
}
Ok(())
}
/// 追加 ASCII 字面量到单元流(JSON 结构字符与转义序列恒为 ASCII)。
fn push_ascii(out: &mut Vec<u16>, s: &str) {
out.extend(s.encode_utf16());
}
/// 追加数字规范文本到单元流(write_number_into 输出恒为 ASCII)。
fn push_number(out: &mut Vec<u16>, n: f64) {
let mut s = String::new();
oxide_runtime_api::write_number_into(n, &mut s);
push_ascii(out, &s);
}
/// JSON 字符串序列化(按码单元流):代理对原样输出 astral 字符,非配对孤立
/// surrogate 输出 \uXXXX(well-formed JSON 要求,round-trip 经 JSON.parse 复原
/// 同值),C0/C1 控制码输出 \uXXXX,其余按 JSON 转义规则。
fn stringify_string_units(units: &[u16], out: &mut Vec<u16>) {
push_ascii(out, "\"");
let mut i = 0;
while i < units.len() {
let u = units[i];
if (0xD800..=0xDBFF).contains(&u) && i + 1 < units.len() && (0xDC00..=0xDFFF).contains(&units[i + 1]) {
out.push(u);
out.push(units[i + 1]);
i += 2;
continue;
}
match u {
0x22 => push_ascii(out, "\\\""),
0x5C => push_ascii(out, "\\\\"),
0x08 => push_ascii(out, "\\b"),
0x0C => push_ascii(out, "\\f"),
0x0A => push_ascii(out, "\\n"),
0x0D => push_ascii(out, "\\r"),
0x09 => push_ascii(out, "\\t"),
0x00..=0x1F | 0x7F..=0x9F | 0xD800..=0xDFFF => {
push_ascii(out, &format!("\\u{:04x}", u));
}
_ => out.push(u),
}
i += 1;
}
push_ascii(out, "\"");
}
fn stringify_object<H: VmHost>(vm: &mut H, obj: &JsObject, mut ctx: StringifyCtx<'_, '_>) -> Result<(), JsValue> {
let has_space = !ctx.space.is_empty();
push_ascii(ctx.out, "{");
// 键集:白名单给定时 K = P(列表序,不限自身可枚举,值读走 Get);
// 无白名单走自身可枚举序(EnumerableOwnPropertyNames)。
let entries: Vec<(Vec<u16>, u32, Option<u32>)> = if let Some(whitelist) = ctx.replacer_whitelist {
whitelist
.iter()
.map(|name| {
let si = vm.string_key_si(name);
(key_si_to_units(vm, si), si, None)
})
.collect()
} else {
let keys = walk_own_keys(vm, obj);
keys.into_iter()
.filter(|(_si, pos)| {
obj.prop_meta_at(*pos)
.map(|m| m.attributes.enumerable())
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable())
})
.map(|(si, pos)| (key_si_to_units(vm, si), si, Some(pos)))
.collect()
};
let obj_val = JsValue::from_js_object(obj as *const JsObject as *mut JsObject);
let mut first = true;
for (units, si, read_pos) in entries {
// 规范序 Get → toJSON → replacer(SerializeJSONProperty 步 1、2a、2b)。
// K = P 时值读经 Get(原型链 + 访问器),无白名单自身槽直读。
let val = match read_pos {
Some(pos) => read_json_property_value(vm, obj, obj_val, si, pos)?,
None => vm.ordinary_get(obj, si, obj_val).map_err(|msg| {
vm.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg))
})?,
};
let val = call_to_json(vm, val, &units)?;
// replacer 函数回调。
let val = if let Some(replacer) = ctx.replacer_fn {
let key_val = vm.new_string_units_owned(units.clone());
match vm.call_function_sync(replacer, obj_val, &[key_val, val]) {
Ok(v) => {
if v.is_undefined() {
continue;
}
v
}
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
return Err(exc);
}
}
} else {
val
};
let is_function = val.is_object() && {
let ptr = val.as_js_object_ptr();
!ptr.is_null() && unsafe { (*ptr).is_function() }
};
if val.is_undefined() || val.is_symbol() || is_function {
continue;
}
if !first {
push_ascii(ctx.out, ",");
}
first = false;
if has_space {
push_ascii(ctx.out, "\n");
for _ in 0..ctx.indent_level + 1 {
ctx.out.extend_from_slice(ctx.space);
}
stringify_string_units(&units, ctx.out);
push_ascii(ctx.out, ":");
push_ascii(ctx.out, " ");
} else {
stringify_string_units(&units, ctx.out);
push_ascii(ctx.out, ":");
}
jsvalue_to_json(vm, val, ctx.child())?;
}
if has_space && !first {
push_ascii(ctx.out, "\n");
for _ in 0..ctx.indent_level {
ctx.out.extend_from_slice(ctx.space);
}
}
push_ascii(ctx.out, "}");
Ok(())
}
fn stringify_array<H: VmHost>(vm: &mut H, obj: &JsObject, mut ctx: StringifyCtx<'_, '_>) -> Result<(), JsValue> {
let has_space = !ctx.space.is_empty();
push_ascii(ctx.out, "[");
let obj_val = JsValue::from_js_object(obj as *const JsObject as *mut JsObject);
let len = obj.prop_count() as usize;
for i in 0..len {
if i > 0 {
push_ascii(ctx.out, ",");
}
if has_space {
push_ascii(ctx.out, "\n");
for _ in 0..ctx.indent_level + 1 {
ctx.out.extend_from_slice(ctx.space);
}
}
let index_str = i.to_string();
let index_units: Vec<u16> = index_str.encode_utf16().collect();
// 规范序 Get → toJSON → replacer(数组元素位同款,getter 的 this = 数组自身)。
let val = read_json_property_value(vm, obj, obj_val, make_int_key(i as u32), i as u32)?;
let val = call_to_json(vm, val, &index_units)?;
// replacer 函数回调。
let val = if let Some(replacer) = ctx.replacer_fn {
let key_val = vm.new_string(&index_str);
match vm.call_function_sync(replacer, obj_val, &[key_val, val]) {
Ok(v) => {
if v.is_undefined() {
push_ascii(ctx.out, "null");
continue;
}
v
}
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
return Err(exc);
}
}
} else {
val
};
let is_function = val.is_object() && {
let ptr = val.as_js_object_ptr();
!ptr.is_null() && unsafe { (*ptr).is_function() }
};
if is_function || val.is_undefined() {
push_ascii(ctx.out, "null");
} else {
jsvalue_to_json(vm, val, ctx.child())?;
}
}
if has_space && len > 0 {
push_ascii(ctx.out, "\n");
for _ in 0..ctx.indent_level {
ctx.out.extend_from_slice(ctx.space);
}
}
push_ascii(ctx.out, "]");
Ok(())
}
/// TypedArray 序列化:整数索引是可枚举自身属性(元素存于原生载荷,不在形状链),
/// 逐元素按对象臂规范序 Get → toJSON → replacer 处理,输出形态与普通对象一致。
fn stringify_typed_array<H: VmHost>(vm: &mut H, obj: &JsObject, mut ctx: StringifyCtx<'_, '_>) -> Result<(), JsValue> {
let has_space = !ctx.space.is_empty();
let this_val = JsValue::from_js_object(obj as *const JsObject as *mut JsObject);
let view = crate::typed_array::get_typed_array_data(vm, this_val)?;
// live 口径:buffer 收缩/detach 后按当前 live 长度枚举(detached 空对象形态)。
let len = crate::typed_array::ta_live_length(view);
push_ascii(ctx.out, "{");
// 键集:白名单给定时 K = P(列表序,整数索引名读元素、越界名跳过、
// 非索引名经 Get 读命名键如 length);无白名单走升序元素序。
let entries: Vec<(Vec<u16>, JsValue)> = if let Some(whitelist) = ctx.replacer_whitelist {
let mut list: Vec<(Vec<u16>, JsValue)> = Vec::new();
for name in whitelist {
let si = vm.string_key_si(name);
let val = if is_int_key(si) {
let idx = int_key_value(si);
if idx as usize >= len {
continue;
}
crate::typed_array::typed_array_element_get(vm, obj, idx)
} else {
vm.ordinary_get(obj, si, this_val)
}
.map_err(|msg| crate::error::create_type_error(vm, &msg))?;
list.push((key_si_to_units(vm, si), val));
}
list
} else {
let mut list: Vec<(Vec<u16>, JsValue)> = Vec::with_capacity(len);
for i in 0..len {
let val = crate::typed_array::typed_array_element_get(vm, obj, i as u32)
.map_err(|msg| crate::error::create_type_error(vm, &msg))?;
list.push((i.to_string().encode_utf16().collect(), val));
}
list
};
let mut first = true;
for (index_units, val) in entries {
let val = call_to_json(vm, val, &index_units)?;
// replacer 函数回调。
let val = if let Some(replacer) = ctx.replacer_fn {
let key_val = vm.new_string_units_owned(index_units.clone());
match vm.call_function_sync(replacer, this_val, &[key_val, val]) {
Ok(v) => {
if v.is_undefined() {
continue;
}
v
}
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_type_error(vm, &msg));
return Err(exc);
}
}
} else {
val
};
let is_function = val.is_object() && {
let ptr = val.as_js_object_ptr();
!ptr.is_null() && unsafe { (*ptr).is_function() }
};
if val.is_undefined() || val.is_symbol() || is_function {
continue;
}
if !first {
push_ascii(ctx.out, ",");
}
first = false;
if has_space {
push_ascii(ctx.out, "\n");
for _ in 0..ctx.indent_level + 1 {
ctx.out.extend_from_slice(ctx.space);
}
stringify_string_units(&index_units, ctx.out);
push_ascii(ctx.out, ":");
push_ascii(ctx.out, " ");
} else {
stringify_string_units(&index_units, ctx.out);
push_ascii(ctx.out, ":");
}
jsvalue_to_json(vm, val, ctx.child())?;
}
if has_space && !first {
push_ascii(ctx.out, "\n");
for _ in 0..ctx.indent_level {
ctx.out.extend_from_slice(ctx.space);
}
}
push_ascii(ctx.out, "}");
Ok(())
}