From 44f6f6e55bb6b0250f8a673112a56eb19bc0ccd1 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 22 Sep 2026 17:23:15 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20TA=20toStringTag=20=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=E5=99=A8=E6=94=B6=E7=AA=84=E5=B9=B6=E8=A1=A5=20ArrayBuffer=20t?= =?UTF-8?q?ag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/typed_array.rs | 13 +++-- .../oxide_vm/src/bindings/bind_typed_array.rs | 2 +- crates/oxide_vm/src/bindings/mod.rs | 3 +- .../tests/builtins_typed_array_tests.rs | 47 +++++++++++++++++++ 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index 4fb91d85..7714f76a 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -768,11 +768,18 @@ pub fn typed_array_length_getter(vm: &mut H, args: &[u8]) -> NativeRe NativeResult::Ok(JsValue::int(view.length as i32)) } -/// `%TypedArray%.prototype[@@toStringTag]` 访问器:返回具体类型名(如 `Int16Array`), -/// 供 `Object.prototype.toString` 区分类型。 +/// `%TypedArray%.prototype[@@toStringTag]` 访问器:返回具体类型名(如 +/// `Int16Array`),供 `Object.prototype.toString` 区分类型。 +/// +/// # 边界与前提 +/// - this 非对象、无 `[[TypedArrayName]]` 内部槽(如原型自身)或内部状态无效时 +/// 返回 undefined(规范语义,不抛错)。 pub fn typed_array_to_string_tag_getter(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); - let view = native_try!(get_typed_array_data(vm, this_val)); + let view = match get_typed_array_data(vm, this_val) { + Ok(view) => view, + Err(_) => return NativeResult::Ok(JsValue::undefined()), + }; NativeResult::Ok(vm.new_string(view.kind.name())) } diff --git a/crates/oxide_vm/src/bindings/bind_typed_array.rs b/crates/oxide_vm/src/bindings/bind_typed_array.rs index f9b1a5fa..1fd8a9c9 100644 --- a/crates/oxide_vm/src/bindings/bind_typed_array.rs +++ b/crates/oxide_vm/src/bindings/bind_typed_array.rs @@ -250,7 +250,7 @@ pub fn bind_typed_array(core: &Arc, session: &KernelSession, global: session, shared_proto, oxide_types::private_key::make_well_known_symbol_key(oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG), - "get @@toStringTag", + "get [Symbol.toStringTag]", oxide_builtins::typed_array::typed_array_to_string_tag_getter:: as *const (), ); diff --git a/crates/oxide_vm/src/bindings/mod.rs b/crates/oxide_vm/src/bindings/mod.rs index a4fb3c3b..8baa8220 100644 --- a/crates/oxide_vm/src/bindings/mod.rs +++ b/crates/oxide_vm/src/bindings/mod.rs @@ -628,8 +628,9 @@ fn install_to_string_tags(core: &Arc, session: &KernelSession) { let sh = core.shape_forge().as_ref(); let tag_id = oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG; let tag_key = oxide_types::private_key::make_well_known_symbol_key(tag_id); - let cases: [(*mut JsObject, &str); 11] = [ + let cases: [(*mut JsObject, &str); 12] = [ (world.map_proto.as_ptr() as *mut JsObject, "Map"), + (world.array_buffer_proto.as_ptr() as *mut JsObject, "ArrayBuffer"), (world.set_proto.as_ptr() as *mut JsObject, "Set"), (world.data_view_proto.as_ptr() as *mut JsObject, "DataView"), (world.array_iterator_proto.as_ptr() as *mut JsObject, "Array Iterator"), diff --git a/crates/oxide_vm/tests/builtins_typed_array_tests.rs b/crates/oxide_vm/tests/builtins_typed_array_tests.rs index 846ab23c..dedc2254 100644 --- a/crates/oxide_vm/tests/builtins_typed_array_tests.rs +++ b/crates/oxide_vm/tests/builtins_typed_array_tests.rs @@ -406,3 +406,50 @@ fn typed_array_bigint_methods_receive_bigint_values() { .unwrap(); assert!(result.as_bool()); } + +#[test] +fn typed_array_to_string_tag_undefined_for_non_ta_this() { + // 原型自身(无 [[TypedArrayName]] 内部槽)与基元/非 TA 对象 this 读值均为 + // undefined,规范语义不抛错。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var g = Object.getOwnPropertyDescriptor(TypedArray.prototype, Symbol.toStringTag).get; \ + g.call(TypedArray.prototype) === undefined && \ + g.call(null) === undefined && g.call(42) === undefined && \ + g.call('x') === undefined && g.call({}) === undefined && g.call([]) === undefined", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn typed_array_to_string_tag_accessor_name_label() { + // getter 的 name/length/set 描述符面按规范钉。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var d = Object.getOwnPropertyDescriptor(TypedArray.prototype, Symbol.toStringTag); \ + d.set === undefined && d.enumerable === false && d.configurable === true && \ + d.get.name === 'get [Symbol.toStringTag]' && d.get.length === 0", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn typed_array_instance_tag_and_arraybuffer_proto_tag() { + // 实例读类型名(经 Object.prototype.toString 与直读两面); + // ArrayBuffer.prototype 的 tag 为数据属性。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "new Int16Array(2)[Symbol.toStringTag] === 'Int16Array' && \ + Object.prototype.toString.call(new BigUint64Array(1)) === '[object BigUint64Array]' && \ + Object.prototype.toString.call(new ArrayBuffer(4)) === '[object ArrayBuffer]' && \ + ArrayBuffer.prototype[Symbol.toStringTag] === 'ArrayBuffer' && \ + Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, Symbol.toStringTag).writable === false", + ) + .unwrap(); + assert!(result.as_bool()); +}