@@ -3,11 +3,14 @@
//!
//! 本核以 `VmHost` 为接口、目标 realm 为分配上下文,做同 realm 克隆(源与目标
//! 同一 realm,符号映射为恒等)。可克隆类型白名单:原始值、数组、plain 对象、
//! Map、Set、Date、RegExp、Error 家族;其余对象(function / DOM / 模块命名空间 /
//! Promise / 迭代器 / 缓冲区 / 特型对象)一律 DataCloneError。缓冲区克隆(含
//! transfer 转移)在缓冲区克隆子任务落地,当前统一报 DataCloneError。
//! Map、Set、Date、RegExp、Error 家族、缓冲区族(ArrayBuffer / SharedArrayBuffer /
//! TypedArray / DataView);其余对象(function / DOM / 模块命名空间 / Promise /
//! 迭代器 / 特型对象)一律 DataCloneError。缓冲区克隆支持 transfer 转移:
//! 命中转移集合的 ArrayBuffer 移动载荷(源 detach),未命中克隆字节;
//! SharedArrayBuffer 克隆共享同一载荷盒。
use std ::collections ::{ HashMap , HashSet } ;
use std ::sync ::Arc ;
use oxide_kernel ::shape_forge ::EMPTY_SHAPE_ID ;
use oxide_types ::object ::{ JsObject , NativeFnPtr , PropAttributes } ;
@@ -21,8 +24,8 @@ use crate::object::{walk_own_keys, walk_own_symbol_keys};
use crate ::set ::{ SetInner , SetKey } ;
/// 克隆状态:`seen` 映射保共享引用(同源对象二次出现复用同一克隆,循环引用不
/// 死循环);`transfer` 集合枚举待转移的 ArrayBuffer( 缓冲区转移语义在缓冲区
/// 克隆落地时启用,当前恒空 )。
/// 死循环);`transfer` 集合枚举待转移的 ArrayBuffer( 命中移动载荷,未命中
/// 克隆字节 )。
struct CloneState {
seen : HashMap < * const JsObject , * mut JsObject > ,
transfer : HashSet < * const JsObject > ,
@@ -37,25 +40,79 @@ impl CloneState {
}
}
/// `structuredClone(value, options)` 入口:解析 value,建克隆状态,调核心。
/// `structuredClone(value, options)` 入口:解析 value 与 options.transfer,
/// 建克隆状态,调核心。
///
/// # 步骤
/// 1. 取第一实参 value(缺省 undefined)
/// 2. 建 `CloneState`
/// 3. 调 `c lone_value` 递归克隆
/// 2. 取第二实参 options(缺省 undefined);在场时解析 transfer 列表
/// 3. 建 `C loneState`
/// 4. 调 `clone_value` 递归克隆
///
/// # 边界与前提
/// - options(含 transfer)在缓冲区克隆落地时处理,当前忽略;
/// - 返回 `Ok(克隆值)` 或 `Err( DataCloneError)`。
/// - options 非对象 → TypeError; `transfer` 非数组 → TypeError;元素非
/// ArrayBuffer 或重复 → DataCloneError;
/// - 返回 `Ok(克隆值)` 或 `Err(TypeError / DataCloneError)`。
pub fn structured_clone_entry < H : VmHost > ( vm : & mut H , args : & [ u8 ] ) -> NativeResult {
let value = if args . len ( ) > 1 { vm . reg ( args [ 1 ] ) } else { JsValue ::undefined ( ) } ;
let options = if args . len ( ) > 2 { vm . reg ( args [ 2 ] ) } else { JsValue ::undefined ( ) } ;
let mut state = CloneState ::new ( ) ;
if ! options . is_undefined ( ) {
if let Err ( e ) = parse_transfer_list ( vm , options , & mut state ) {
return NativeResult ::Err ( e ) ;
}
}
match clone_value ( vm , & mut state , value ) {
Ok ( v ) = > NativeResult ::Ok ( v ) ,
Err ( e ) = > NativeResult ::Err ( e ) ,
}
}
/// 解析 options.transfer 入 transfer 集合:options 须为对象,`transfer` 须为
/// 数组,元素须为不重复的 ArrayBuffer(缺省 `transfer` 为空集)。
///
/// # 边界与前提
/// - `transfer` 属性读取是用户代码窗口(getter 副作用与异常原值传播);
/// - 转移集合只收 ArrayBuffer 对象指针(SAB 不可转移,非 AB 元素报
/// DataCloneError)。
fn parse_transfer_list < H : VmHost > ( vm : & mut H , options : JsValue , state : & mut CloneState ) -> Result < ( ) , JsValue > {
if ! options . is_object ( ) {
return Err ( crate ::error ::create_type_error ( vm , " structuredClone options must be an object " ) ) ;
}
let opt_ptr = options . as_js_object_ptr ( ) ;
// SAFETY: is_object 保证非空指针;对象在 native 执行期间被根保持、不被回收。
let opt = unsafe { & * opt_ptr } ;
let si_transfer = vm . kernel_core ( ) . perm_interner ( ) . intern ( " transfer " ) . 0 ;
let transfer_val = match vm . ordinary_get ( opt , si_transfer , options ) {
Ok ( v ) = > v ,
Err ( e ) = > {
if let Some ( exc ) = vm . take_uncaught_value ( ) {
return Err ( exc ) ;
}
return Err ( crate ::error ::create_type_error ( vm , & e ) ) ;
}
} ;
if transfer_val . is_undefined ( ) {
return Ok ( ( ) ) ;
}
if ! transfer_val . is_object ( ) | | ! unsafe { & * transfer_val . as_js_object_ptr ( ) } . is_array ( ) {
return Err ( crate ::error ::create_type_error ( vm , " transfer list must be an array " ) ) ;
}
let list = unsafe { & * transfer_val . as_js_object_ptr ( ) } ;
let count = list . logical_len ( ) as usize ;
for i in 0 .. count {
let e = list . get_prop_at ( i ) ;
if ! e . is_object ( ) | | ! unsafe { & * e . as_js_object_ptr ( ) } . is_array_buffer_obj ( ) {
return Err ( data_clone_error ( vm , " transfer list element is not an ArrayBuffer " ) ) ;
}
let ptr = e . as_js_object_ptr ( ) as * const JsObject ;
if ! state . transfer . insert ( ptr ) {
return Err ( data_clone_error ( vm , " duplicate buffer in transfer list " ) ) ;
}
}
Ok ( ( ) )
}
/// 递归克隆一个值:原始值原样拷贝,对象按类型分派。
///
/// # 步骤
@@ -67,8 +124,9 @@ pub fn structured_clone_entry<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
///
/// # 边界与前提
/// - 源对象由调用方寄存器保活;克隆经 `alloc_object` 入目标 realm 对象表即成根;
/// - 可克隆白名单:数组 / plain / Map / Set / Date / RegExp / Error, 其余对象
/// (含缓冲区)报 DataCloneError。
/// - 可克隆白名单:数组 / plain / Map / Set / Date / RegExp / Error / 缓冲区族 ,
/// 其余对象(function / DOM / 模块命名空间 / Promise / 迭代器 / 特型对象)
/// 报 DataCloneError。
fn clone_value < H : VmHost > ( vm : & mut H , state : & mut CloneState , value : JsValue ) -> Result < JsValue , JsValue > {
if ! value . is_object ( ) {
return Ok ( value ) ;
@@ -91,12 +149,14 @@ fn clone_value<H: VmHost>(vm: &mut H, state: &mut CloneState, value: JsValue) ->
alloc_regexp_clone ( vm , src ) ?
} else if src . is_error_obj ( ) {
alloc_error_clone ( vm , src )
} else if is_buffer ( src ) {
// transfer 集合命中时走转移语义(缓冲区克隆落地后启用);当前缓冲区一律不可克隆。
if state . transfer . contains ( & ( src_ptr as * const JsObject ) ) {
return Err ( data_clone_error ( vm , " transferred buffer is not cloneable " ) ) ;
}
return Err ( data_clone_error ( vm , " buffer value is not cloneable " ) ) ;
} else if src . is_array_buffer_obj ( ) {
alloc_array_buffer_clone ( vm , state , src , src_ptr as * const JsObject ) ?
} else if src . is_shared_array_buffer_obj ( ) {
alloc_shared_array_buffer_clone ( vm , src ) ?
} else if src . is_typed_array_obj ( ) {
alloc_typed_array_clone ( vm , state , src ) ?
} else if src . is_data_view_obj ( ) {
alloc_data_view_clone ( vm , state , src ) ?
} else if is_plain_object ( src ) {
alloc_plain_clone ( vm )
} else {
@@ -108,7 +168,8 @@ fn clone_value<H: VmHost>(vm: &mut H, state: &mut CloneState, value: JsValue) ->
}
/// 填充克隆内容:Map / Set 走各自条目复制,数组 / plain / Error 统一走自有属性复制。
/// Date / RegExp 在分配时已填充(时间戳 / 源与标志),此处无操作。
/// Date / RegExp / 缓冲区族 在分配时已填充(时间戳 / 源与标志 / 载荷与视图状态盒 ),
/// 此处无操作。
fn fill_clone < H : VmHost > (
vm : & mut H , state : & mut CloneState , src : & JsObject , clone_ptr : * mut JsObject ,
) -> Result < ( ) , JsValue > {
@@ -122,6 +183,11 @@ fn fill_clone<H: VmHost>(
if src . is_date_obj ( ) | | src . is_regexp_obj ( ) {
return Ok ( ( ) ) ;
}
// 缓冲区族(AB / SAB / TypedArray / DataView)在分配时已填充载荷与视图
// 状态盒,无自有属性可复制。
if is_buffer ( src ) {
return Ok ( ( ) ) ;
}
fill_object_props ( vm , state , src , clone_ptr )
}
@@ -324,6 +390,110 @@ fn alloc_error_clone<H: VmHost>(vm: &mut H, src: &JsObject) -> *mut JsObject {
vm . alloc_object ( obj )
}
/// 分配 ArrayBuffer 克隆:transfer 命中移动载荷(源 `data → None` 即 detach),
/// 未命中克隆字节;存储态上限与 immutable 标志原样拷贝,detached 源产 detached
/// 克隆。
fn alloc_array_buffer_clone < H : VmHost > (
vm : & mut H , state : & mut CloneState , src : & JsObject , src_ptr : * const JsObject ,
) -> Result < * mut JsObject , JsValue > {
let Some ( payload_ptr ) = crate ::array_buffer ::array_buffer_payload_ptr ( src ) else {
return Err ( data_clone_error ( vm , " ArrayBuffer internal state invalid " ) ) ;
} ;
let transferred = state . transfer . contains ( & src_ptr ) ;
// SAFETY: payload_ptr 经 array_buffer_payload_ptr 校验为存活载荷盒;标量
// 拷出后借用即结束,不跨 JS 调用。
let ( data , max_byte_length , immutable ) = unsafe {
let p = & mut * payload_ptr ;
// 转移命中移动载荷(源 data → None);未命中克隆字节。
let data = if transferred { p . data . take ( ) } else { p . data . clone ( ) } ;
( data , p . max_byte_length , p . immutable )
} ;
let proto = crate ::array_buffer ::default_array_buffer_proto ( vm ) ;
let clone_ptr = alloc_buffer_object ( vm , data , max_byte_length , immutable , proto ) ;
Ok ( clone_ptr )
}
/// 分配携给定载荷形态(`data` 为 `None` 即 detached)的 ArrayBuffer 对象。
fn alloc_buffer_object < H : VmHost > (
vm : & mut H , data : Option < Vec < u8 > > , max_byte_length : usize , immutable : bool , proto : JsValue ,
) -> * mut JsObject {
let mut obj = JsObject ::new_empty ( EMPTY_SHAPE_ID , proto ) ;
obj . type_tag = JsObject ::OBJ_TYPE_ARRAY_BUFFER ;
let payload = crate ::array_buffer ::ArrayBufferPayload { data , max_byte_length , immutable } ;
let payload_ptr = Arc ::into_raw ( Arc ::new ( payload ) ) ;
// SAFETY: ArrayBuffer 对象不可调用,native_fn 槽复用为不透明载荷盒指针,
// 与 new_array_buffer 的存储形态一致。
obj . set_native_fn ( Some ( unsafe { NativeFnPtr ::from_raw ( payload_ptr as * const ( ) ) } ) ) ;
vm . alloc_object ( obj )
}
/// 分配 SharedArrayBuffer 克隆:共享同一载荷盒(引用计数加一),两对象共享
/// 字节缓冲;SAB 无 detach 路径。
fn alloc_shared_array_buffer_clone < H : VmHost > ( vm : & mut H , src : & JsObject ) -> Result < * mut JsObject , JsValue > {
let Some ( payload_ptr ) = crate ::array_buffer ::shared_array_buffer_payload_ptr ( src ) else {
return Err ( data_clone_error ( vm , " SharedArrayBuffer internal state invalid " ) ) ;
} ;
// SAFETY: payload_ptr 经 shared_array_buffer_payload_ptr 校验为存活载荷盒;
// 克隆共享同一载荷(引用计数加一),释放经计数归零统一收口。
unsafe { Arc ::increment_strong_count ( payload_ptr ) } ;
let proto = crate ::array_buffer ::default_shared_array_buffer_proto ( vm ) ;
let mut obj = JsObject ::new_empty ( EMPTY_SHAPE_ID , proto ) ;
obj . type_tag = JsObject ::OBJ_TYPE_SHARED_ARRAY_BUFFER ;
obj . set_native_fn ( Some ( unsafe { NativeFnPtr ::from_raw ( payload_ptr as * const ( ) ) } ) ) ;
Ok ( vm . alloc_object ( obj ) )
}
/// 分配 TypedArray 克隆:递归克隆(或转移)底层缓冲,再按同类型 / 同偏移 /
/// 同长度构造新视图。
fn alloc_typed_array_clone < H : VmHost > (
vm : & mut H , state : & mut CloneState , src : & JsObject ,
) -> Result < * mut JsObject , JsValue > {
let Some ( data_ptr ) = src
. native_fn ( )
. map ( | p | p . as_ptr ( ) as * const crate ::typed_array ::TypedArrayData )
. filter ( | p | ! p . is_null ( ) )
else {
return Err ( data_clone_error ( vm , " TypedArray internal state invalid " ) ) ;
} ;
// SAFETY: data_ptr 经对象类型标签校验为存活状态盒;Copy 语义,不跨 JS 调用持借用。
let data = unsafe { * data_ptr } ;
let cloned_buffer = clone_value ( vm , state , data . buffer ) ? ;
let proto = JsValue ::from_js_object ( crate ::typed_array ::typed_array_proto_ptr ( vm , data . kind ) ) ;
let clone_ptr =
crate ::typed_array ::create_typed_array ( vm , data . kind , cloned_buffer , data . byte_offset , data . length , data . auto_length , proto ) ;
Ok ( clone_ptr )
}
/// 分配 DataView 克隆:递归克隆(或转移)底层缓冲,再按同偏移 / 同长度构造
/// 新视图。
fn alloc_data_view_clone < H : VmHost > (
vm : & mut H , state : & mut CloneState , src : & JsObject ,
) -> Result < * mut JsObject , JsValue > {
let Some ( data_ptr ) = src
. native_fn ( )
. map ( | p | p . as_ptr ( ) as * const crate ::data_view ::DataViewData )
. filter ( | p | ! p . is_null ( ) )
else {
return Err ( data_clone_error ( vm , " DataView internal state invalid " ) ) ;
} ;
// SAFETY: data_ptr 经对象类型标签校验为存活状态盒;Copy 语义,不跨 JS 调用持借用。
let data = unsafe { * data_ptr } ;
let cloned_buffer = clone_value ( vm , state , data . buffer ) ? ;
let proto = JsValue ::from_js_object ( vm . session ( ) . builtin_world ( ) . data_view_proto . as_ptr ( ) as * mut JsObject ) ;
let mut obj = JsObject ::new_empty ( EMPTY_SHAPE_ID , proto ) ;
obj . type_tag = JsObject ::OBJ_TYPE_DATA_VIEW ;
let new_data = Box ::into_raw ( Box ::new ( crate ::data_view ::DataViewData {
buffer : cloned_buffer ,
byte_offset : data . byte_offset ,
byte_length : data . byte_length ,
length_is_auto : data . length_is_auto ,
} ) ) ;
// SAFETY: DataView 对象不可调用,native_fn 槽存不透明 `Box<DataViewData>`
// 指针,与构造器存储形态一致。
obj . set_native_fn ( Some ( unsafe { NativeFnPtr ::from_raw ( new_data as * const ( ) ) } ) ) ;
Ok ( vm . alloc_object ( obj ) )
}
/// 是否缓冲区对象(ArrayBuffer / SharedArrayBuffer / DataView / TypedArray)。
fn is_buffer ( src : & JsObject ) -> bool {
src . is_array_buffer_obj ( ) | | src . is_shared_array_buffer_obj ( ) | | src . is_data_view_obj ( ) | | src . is_typed_array_obj ( )
@@ -515,11 +685,172 @@ mod tests {
assert! ( vm . lookup_str ( obj_prop ( & vm , err , " message " ) ) . is_some ( ) ) ;
}
/// 把 value 与 options 置入寄存器并调克隆入口,返回克隆值或错误。
fn clone_with_options ( vm : & mut Vm , value : JsValue , options : JsValue ) -> Result < JsValue , JsValue > {
vm . set_reg ( 1 , value ) ;
vm . set_reg ( 2 , options ) ;
let args = [ 0 u8 , 1 , 2 ] ;
match structured_clone_entry ( vm , & args ) {
NativeResult ::Ok ( v ) = > Ok ( v ) ,
NativeResult ::Err ( e ) = > Err ( e ) ,
NativeResult ::TailCall { .. } = > unreachable! ( " structuredClone 不返回 TailCall " ) ,
}
}
/// 读 ArrayBuffer 载荷字节序列(detached 返回 None)。
fn ab_bytes ( ab : & JsObject ) -> Option < Vec < u8 > > {
let ptr = crate ::array_buffer ::array_buffer_payload_ptr ( ab ) ? ;
// SAFETY: ptr 经 array_buffer_payload_ptr 校验为存活载荷盒。
unsafe { & * ptr } . data . as_ref ( ) . cloned ( )
}
/// 读 TypedArray 视图状态盒(Copy)。
fn ta_data ( ta : & JsObject ) -> crate ::typed_array ::TypedArrayData {
let ptr = ta . native_fn ( ) . map ( | p | p . as_ptr ( ) as * const crate ::typed_array ::TypedArrayData ) . unwrap ( ) ;
// SAFETY: ptr 经对象类型标签校验为存活状态盒。
unsafe { * ptr }
}
#[ test ]
fn clone_arraybuffer_data_clone_error ( ) {
let ( mut vm , v ) = eval ( " var ab = new ArrayB uffer(8) ; ab " ) . unwrap ( ) ;
let e = clone ( & mut vm , v ) . unwrap_err ( ) ;
assert! ( e . is_object ( ) ) ;
fn clone_arraybuffer ( ) {
let ( mut vm , v ) = eval ( " var ab = new Uint8Array([1, 2, 3, 4]).b uffer; ab " ) . unwrap ( ) ;
let c = clone ( & mut vm , v ) . unwrap ( ) ;
let cl = unsafe { & * c . as_js_object_ptr ( ) } ;
assert! ( cl . is_array_buffer_obj ( ) ) ;
assert! ( ! std ::ptr ::eq ( v . as_js_object_ptr ( ) , c . as_js_object_ptr ( ) ) , " 克隆应为新对象 " ) ;
assert_eq! ( ab_bytes ( cl ) , Some ( vec! [ 1 , 2 , 3 , 4 ] ) ) ;
// 源缓冲未 detach。
assert_eq! ( ab_bytes ( unsafe { & * v . as_js_object_ptr ( ) } ) , Some ( vec! [ 1 , 2 , 3 , 4 ] ) ) ;
}
#[ test ]
fn clone_arraybuffer_transfer ( ) {
let ( mut vm , v ) =
eval ( " var ab = new Uint8Array([9, 8, 7]).buffer; var opts = {transfer: [ab]}; ({ab: ab, opts: opts}) " ) . unwrap ( ) ;
let holder = unsafe { & * v . as_js_object_ptr ( ) } ;
let si_ab = vm . kernel_core ( ) . perm_interner ( ) . intern ( " ab " ) . 0 ;
let si_opts = vm . kernel_core ( ) . perm_interner ( ) . intern ( " opts " ) . 0 ;
let ab = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_ab ) . unwrap ( ) ) ;
let opts = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_opts ) . unwrap ( ) ) ;
let c = clone_with_options ( & mut vm , ab , opts ) . unwrap ( ) ;
let cl = unsafe { & * c . as_js_object_ptr ( ) } ;
assert! ( cl . is_array_buffer_obj ( ) ) ;
assert_eq! ( ab_bytes ( cl ) , Some ( vec! [ 9 , 8 , 7 ] ) ) ;
// 源缓冲已 detach。
assert! ( ab_bytes ( unsafe { & * ab . as_js_object_ptr ( ) } ) . is_none ( ) ) ;
}
#[ test ]
fn clone_typed_array ( ) {
let ( mut vm , v ) = eval ( " var ta = new Uint8Array([5, 6, 7]); ta " ) . unwrap ( ) ;
let c = clone ( & mut vm , v ) . unwrap ( ) ;
let cl = unsafe { & * c . as_js_object_ptr ( ) } ;
assert! ( cl . is_typed_array_obj ( ) ) ;
let data = ta_data ( cl ) ;
let src_data = ta_data ( unsafe { & * v . as_js_object_ptr ( ) } ) ;
assert! ( ! std ::ptr ::eq ( data . buffer . as_js_object_ptr ( ) , src_data . buffer . as_js_object_ptr ( ) ) , " 克隆缓冲应为新对象 " ) ;
assert_eq! ( data . length , 3 ) ;
assert_eq! ( data . byte_offset , 0 ) ;
let buf = unsafe { & * data . buffer . as_js_object_ptr ( ) } ;
assert_eq! ( ab_bytes ( buf ) , Some ( vec! [ 5 , 6 , 7 ] ) ) ;
}
#[ test ]
fn clone_typed_array_with_transfer ( ) {
let ( mut vm , v ) =
eval ( " var ta = new Uint8Array([1, 2, 3]); var opts = {transfer: [ta.buffer]}; ({ta: ta, opts: opts}) " ) . unwrap ( ) ;
let holder = unsafe { & * v . as_js_object_ptr ( ) } ;
let si_ta = vm . kernel_core ( ) . perm_interner ( ) . intern ( " ta " ) . 0 ;
let si_opts = vm . kernel_core ( ) . perm_interner ( ) . intern ( " opts " ) . 0 ;
let ta = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_ta ) . unwrap ( ) ) ;
let opts = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_opts ) . unwrap ( ) ) ;
let c = clone_with_options ( & mut vm , ta , opts ) . unwrap ( ) ;
let cl = unsafe { & * c . as_js_object_ptr ( ) } ;
assert! ( cl . is_typed_array_obj ( ) ) ;
let data = ta_data ( cl ) ;
let buf = unsafe { & * data . buffer . as_js_object_ptr ( ) } ;
assert_eq! ( ab_bytes ( buf ) , Some ( vec! [ 1 , 2 , 3 ] ) ) ;
// 源缓冲已 detach。
let src_data = ta_data ( unsafe { & * ta . as_js_object_ptr ( ) } ) ;
let src_buf = unsafe { & * src_data . buffer . as_js_object_ptr ( ) } ;
assert! ( ab_bytes ( src_buf ) . is_none ( ) ) ;
}
#[ test ]
fn clone_data_view ( ) {
let ( mut vm , v ) = eval ( " var dv = new DataView(new Uint8Array([10, 20, 30, 40]).buffer); dv " ) . unwrap ( ) ;
let c = clone ( & mut vm , v ) . unwrap ( ) ;
let cl = unsafe { & * c . as_js_object_ptr ( ) } ;
assert! ( cl . is_data_view_obj ( ) ) ;
let data_ptr = cl . native_fn ( ) . map ( | p | p . as_ptr ( ) as * const crate ::data_view ::DataViewData ) . unwrap ( ) ;
// SAFETY: data_ptr 经对象类型标签校验为存活状态盒。
let data = unsafe { * data_ptr } ;
assert_eq! ( data . byte_length , 4 ) ;
assert_eq! ( data . byte_offset , 0 ) ;
let buf = unsafe { & * data . buffer . as_js_object_ptr ( ) } ;
assert_eq! ( ab_bytes ( buf ) , Some ( vec! [ 10 , 20 , 30 , 40 ] ) ) ;
}
#[ test ]
fn clone_shared_array_buffer_shares_payload ( ) {
let ( mut vm , v ) = eval ( " var sab = new SharedArrayBuffer(4); sab " ) . unwrap ( ) ;
let c = clone ( & mut vm , v ) . unwrap ( ) ;
let cl = unsafe { & * c . as_js_object_ptr ( ) } ;
assert! ( cl . is_shared_array_buffer_obj ( ) ) ;
// 两对象共享同一载荷盒(克隆侧写入对源侧可见)。
let src_payload = crate ::array_buffer ::shared_array_buffer_payload_ptr ( unsafe { & * v . as_js_object_ptr ( ) } ) . unwrap ( ) ;
let cl_payload = crate ::array_buffer ::shared_array_buffer_payload_ptr ( cl ) . unwrap ( ) ;
assert! ( std ::ptr ::eq ( src_payload , cl_payload ) , " 克隆应共享同一载荷 " ) ;
// SAFETY: 载荷盒存活。
unsafe { ( * cl_payload ) . data . as_mut ( ) . unwrap ( ) [ 0 ] = 0xAB } ;
assert_eq! ( unsafe { & * src_payload } . data . as_ref ( ) . unwrap ( ) [ 0 ] , 0xAB ) ;
}
#[ test ]
fn transfer_non_arraybuffer_data_clone_error ( ) {
let ( mut vm , v ) =
eval ( " var sab = new SharedArrayBuffer(4); var opts = {transfer: [sab]}; ({v: sab, opts: opts}) " ) . unwrap ( ) ;
let holder = unsafe { & * v . as_js_object_ptr ( ) } ;
let si_v = vm . kernel_core ( ) . perm_interner ( ) . intern ( " v " ) . 0 ;
let si_opts = vm . kernel_core ( ) . perm_interner ( ) . intern ( " opts " ) . 0 ;
let val = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_v ) . unwrap ( ) ) ;
let opts = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_opts ) . unwrap ( ) ) ;
let e = clone_with_options ( & mut vm , val , opts ) . unwrap_err ( ) ;
let err = unsafe { & * e . as_js_object_ptr ( ) } ;
assert! ( err . is_error_obj ( ) ) ;
}
#[ test ]
fn transfer_duplicate_buffer_data_clone_error ( ) {
let ( mut vm , v ) =
eval ( " var ab = new ArrayBuffer(4); var opts = {transfer: [ab, ab]}; ({v: ab, opts: opts}) " ) . unwrap ( ) ;
let holder = unsafe { & * v . as_js_object_ptr ( ) } ;
let si_v = vm . kernel_core ( ) . perm_interner ( ) . intern ( " v " ) . 0 ;
let si_opts = vm . kernel_core ( ) . perm_interner ( ) . intern ( " opts " ) . 0 ;
let val = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_v ) . unwrap ( ) ) ;
let opts = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_opts ) . unwrap ( ) ) ;
let e = clone_with_options ( & mut vm , val , opts ) . unwrap_err ( ) ;
let err = unsafe { & * e . as_js_object_ptr ( ) } ;
assert! ( err . is_error_obj ( ) ) ;
}
#[ test ]
fn transfer_options_not_object_type_error ( ) {
let ( mut vm , v ) = eval ( " 1 " ) . unwrap ( ) ;
let e = clone_with_options ( & mut vm , v , JsValue ::int ( 42 ) ) . unwrap_err ( ) ;
let err = unsafe { & * e . as_js_object_ptr ( ) } ;
assert! ( err . is_error_obj ( ) ) ;
}
#[ test ]
fn transfer_list_not_array_type_error ( ) {
let ( mut vm , v ) = eval ( " var opts = {transfer: 'x'}; ({v: 1, opts: opts}) " ) . unwrap ( ) ;
let holder = unsafe { & * v . as_js_object_ptr ( ) } ;
let si_v = vm . kernel_core ( ) . perm_interner ( ) . intern ( " v " ) . 0 ;
let si_opts = vm . kernel_core ( ) . perm_interner ( ) . intern ( " opts " ) . 0 ;
let val = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_v ) . unwrap ( ) ) ;
let opts = holder . get_prop_at ( vm . get_own_property_slot ( holder , si_opts ) . unwrap ( ) ) ;
let e = clone_with_options ( & mut vm , val , opts ) . unwrap_err ( ) ;
let err = unsafe { & * e . as_js_object_ptr ( ) } ;
assert! ( err . is_error_obj ( ) ) ;
}