fix: URI 四函数正确性修复并新增 AsyncIteratorPrototype 原型

This commit is contained in:
2026-09-24 18:39:28 +00:00
parent 53505dc38c
commit 31d4082210
8 changed files with 297 additions and 31 deletions
+9 -1
View File
@@ -59,6 +59,14 @@ fn string_arg<H: VmHost>(vm: &mut H, args: &[u8]) -> String {
}
}
/// 解析两位十六进制单元为字节:逐位严格匹配十六进制数字,不带符号位
/// (`from_str_radix` 会接受 `+1`/`-1`,`%+1` 这类序列按规范须判非法)。
fn parse_hex_u8(slice: &[u8]) -> Option<u8> {
std::str::from_utf8(slice).ok().and_then(|s| u8::from_str_radix(s, 16).ok())
let digit = |c: u8| match c {
b'0'..=b'9' => Some(c - b'0'),
b'a'..=b'f' => Some(c - b'a' + 10),
b'A'..=b'F' => Some(c - b'A' + 10),
_ => None,
};
Some(digit(slice[0])? * 16 + digit(slice[1])?)
}
+82 -17
View File
@@ -6,20 +6,56 @@ const URI_RESERVED: &str = ";/?:@&=+$,#";
const URI_SAFE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'();/?:@&=+$,#";
const URI_ERROR_MESSAGE: &str = "malformed URI sequence";
fn encode_uri_string(input: &str, safe: &str) -> String {
let mut out = String::with_capacity(input.len());
for ch in input.chars() {
if ch.is_ascii() && safe.contains(ch) {
out.push(ch);
/// 单元口径的 `encodeURI`/`encodeURIComponent` 共享循环:未转义字符原样
/// 透传,其余按 UTF-8 字节转成 `%XX`;良配 surrogate 对先合成码点再编码,
/// 孤立 surrogate 单元(0xD800-0xDFFF)无良形 UTF-8 编码,按规范抛 URIError
/// (以 `Err(())` 返回)。
fn encode_uri_units(input: &[u16], safe: &str) -> Result<String, ()> {
let mut out = String::with_capacity(input.len() * 3);
let mut i = 0;
while i < input.len() {
let unit = input[i];
// 高 surrogate 须紧跟低 surrogate:合成超平面码点后按 UTF-8 编码。
if (0xD800..=0xDBFF).contains(&unit) {
let next = *input.get(i + 1).ok_or(())?;
if !(0xDC00..=0xDFFF).contains(&next) {
return Err(());
}
let cp = 0x10000 + ((unit as u32 - 0xD800) << 10) + (next as u32 - 0xDC00);
let mut buf = [0u8; 4];
for byte in char::from_u32(cp).expect("合成码点必在超平面区间内").encode_utf8(&mut buf).as_bytes() {
out.push_str(&format!("%{byte:02X}"));
}
i += 2;
continue;
}
// 孤立低 surrogate(及任何未配对的 surrogate 单元):无良形编码。
if (0xD800..=0xDFFF).contains(&unit) {
return Err(());
}
if unit < 0x80 {
let ch = unit as u8 as char;
if safe.contains(ch) {
out.push(ch);
} else {
out.push_str(&format!("%{unit:02X}"));
}
i += 1;
continue;
}
// 非 surrogate 单元即良形 BMP 码点(surrogate 臂已先行处理)。
let mut buf = [0u8; 4];
let ch = char::from_u32(unit as u32).expect("非 surrogate 单元必为良形码点");
for byte in ch.encode_utf8(&mut buf).as_bytes() {
out.push_str(&format!("%{byte:02X}"));
}
i += 1;
}
out
Ok(out)
}
/// 从单元序列的 `%` 位置读一个 `%XX` 字节(hex 两位须为 ASCII 十六进制单元)。
@@ -103,27 +139,45 @@ fn uri_error<H: VmHost>(vm: &mut H) -> NativeResult {
}
/// 取 URI 函数实参的单元序列(单元口径,不 lossy):无实参等价于处理
/// `"undefined"`。ToPrimitive 抛错(Symbol 等)原样返回异常值。
/// `"undefined"`。对象经 ToPrimitive 转换,用户 valueOf/toString 抛出的原值
/// 优先于格式化文本传播(S15.1.3.x A6 族按原值比对)。
fn string_arg_units<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<Vec<u16>, NativeResult> {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
match oxide_runtime_api::to_units_full(val, vm) {
Ok(u) => Ok(u),
Err(e) => Err(NativeResult::Err(crate::error::create_from_text(vm, &e))),
Err(e) => {
if let Some(exc) = vm.take_uncaught_value() {
return Err(NativeResult::Err(exc));
}
Err(NativeResult::Err(crate::error::create_from_text(vm, &e)))
}
}
}
/// `encodeURI`:编码输入为 URI,保留未转义字符与保留字符 `;/?:@&=+$,#`,
/// 其余按 UTF-8 字节转成 `%XX`。
/// 其余按 UTF-8 字节转成 `%XX`;孤立 surrogate 单元抛 URIError。
pub fn encode_uri<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_SAFE)))
let input = match string_arg_units(vm, args) {
Ok(u) => u,
Err(e) => return e,
};
match encode_uri_units(&input, URI_SAFE) {
Ok(s) => NativeResult::Ok(vm.new_string_owned(s)),
Err(()) => uri_error(vm),
}
}
/// `encodeURIComponent`:编码输入为 URI component,仅保留未转义字符,
/// 保留字符(如 `=&`)也会被转成 `%XX`。
/// 保留字符(如 `=&`)也会被转成 `%XX`;孤立 surrogate 单元抛 URIError。
pub fn encode_uri_component<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_UNESCAPED)))
let input = match string_arg_units(vm, args) {
Ok(u) => u,
Err(e) => return e,
};
match encode_uri_units(&input, URI_UNESCAPED) {
Ok(s) => NativeResult::Ok(vm.new_string_owned(s)),
Err(()) => uri_error(vm),
}
}
/// `decodeURI`:解码 `%XX` 序列,但保留字符的转义形式原样保留(不还原),
@@ -155,7 +209,7 @@ pub fn decode_uri_component<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
#[cfg(test)]
mod tests {
use super::{decode_uri_units, encode_uri_string, URI_UNESCAPED};
use super::{decode_uri_units, encode_uri_units, URI_UNESCAPED};
fn units(s: &str) -> Vec<u16> {
s.encode_utf16().collect()
@@ -164,14 +218,25 @@ mod tests {
#[test]
fn encode_uri_keeps_reserved_and_encodes_space() {
assert_eq!(
encode_uri_string("https://example.com/path?q=hello world", super::URI_SAFE),
encode_uri_units(&units("https://example.com/path?q=hello world"), super::URI_SAFE).unwrap(),
"https://example.com/path?q=hello%20world"
);
}
#[test]
fn encode_uri_component_encodes_reserved() {
assert_eq!(encode_uri_string("a=1&b=2", URI_UNESCAPED), "a%3D1%26b%3D2");
assert_eq!(encode_uri_units(&units("a=1&b=2"), URI_UNESCAPED).unwrap(), "a%3D1%26b%3D2");
}
/// 孤立 surrogate 单元(0xD800-0xDFFF 全量遍历)必须抛 URIError,
/// 良配 surrogate 对按码点 UTF-8 编码。
#[test]
fn encode_uri_units_rejects_lone_surrogates() {
for u in 0xD800u16..=0xDFFF {
assert!(encode_uri_units(&[u], super::URI_SAFE).is_err());
assert!(encode_uri_units(&[0x61, u], URI_UNESCAPED).is_err());
}
assert_eq!(encode_uri_units(&[0xD83D, 0xDE00], URI_UNESCAPED).unwrap(), "%F0%9F%98%80");
}
#[test]
+4 -1
View File
@@ -371,7 +371,7 @@ pub(crate) fn wire_builtin_world_links(world: &BuiltinWorld) {
}
let obj_proto_val = JsValue::from_js_object(world.object_proto.as_ptr() as *mut JsObject);
let non_object_protos: [&P<JsObject>; 27] = [
let non_object_protos: [&P<JsObject>; 28] = [
&world.array_proto,
&world.function_proto,
&world.string_proto,
@@ -399,6 +399,7 @@ pub(crate) fn wire_builtin_world_links(world: &BuiltinWorld) {
&world.bigint_proto,
&world.disposable_stack_proto,
&world.async_disposable_stack_proto,
&world.async_iterator_proto,
];
for proto in &non_object_protos {
set_proto_if_changed(proto, obj_proto_val);
@@ -553,6 +554,7 @@ impl BuiltinWorld {
let set_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
let string_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
let regexp_string_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
let async_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
let iterator_helper_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
let disposable_stack_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
let async_disposable_stack_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
@@ -664,6 +666,7 @@ impl BuiltinWorld {
set_iterator_proto,
string_iterator_proto,
regexp_string_iterator_proto,
async_iterator_proto,
iterator_helper_proto,
disposable_stack_proto,
async_disposable_stack_proto,
@@ -456,6 +456,7 @@ impl BuiltinWorld {
set_iterator_proto,
string_iterator_proto,
regexp_string_iterator_proto,
async_iterator_proto,
iterator_helper_proto,
disposable_stack_proto,
async_disposable_stack_proto,
@@ -470,6 +471,7 @@ impl BuiltinWorld {
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
)
} else {
(
@@ -479,6 +481,7 @@ impl BuiltinWorld {
current.set_iterator_proto.clone(),
current.string_iterator_proto.clone(),
current.regexp_string_iterator_proto.clone(),
current.async_iterator_proto.clone(),
current.iterator_helper_proto.clone(),
current.disposable_stack_proto.clone(),
current.async_disposable_stack_proto.clone(),
@@ -594,6 +597,7 @@ impl BuiltinWorld {
set_iterator_proto,
string_iterator_proto,
regexp_string_iterator_proto,
async_iterator_proto,
iterator_helper_proto,
disposable_stack_proto,
async_disposable_stack_proto,
+6 -1
View File
@@ -134,6 +134,9 @@ pub struct BuiltinWorld {
pub string_default_iterator: std::cell::Cell<*const JsObject>,
/// `%RegExpStringIteratorPrototype%`:matchAll 返回的迭代器。
pub regexp_string_iterator_proto: P<JsObject>,
/// `%AsyncIteratorPrototype%`:异步迭代器原型(链到 Object.prototype),
/// 持有 `@@asyncIterator`/`@@asyncDispose`;`%AsyncGeneratorPrototype%` 链到它。
pub async_iterator_proto: P<JsObject>,
/// `%IteratorHelperPrototype%`:Iterator helpers 结果对象的共享原型,链到 %IteratorPrototype%。
pub iterator_helper_proto: P<JsObject>,
/// `DisposableStack.prototype`:同步资源栈原型(链到 Object.prototype),
@@ -350,7 +353,7 @@ impl BuiltinWorld {
/// session 收尾(`teardown_heap_data`)与选择性重建收尾(`retire_replaced`)
/// 的 P 字段枚举唯一入口:`BuiltinWorld` 新增 P 字段须在此同步补一行,否则
/// 收尾时该字段属性区无法释放、重建原型槽改写/释放漏掉该字段。
pub(crate) fn all_p_fields(&self) -> [&P<JsObject>; 107] {
pub(crate) fn all_p_fields(&self) -> [&P<JsObject>; 108] {
[
&self.object_proto,
&self.array_proto,
@@ -455,6 +458,7 @@ impl BuiltinWorld {
&self.set_iterator_proto,
&self.string_iterator_proto,
&self.regexp_string_iterator_proto,
&self.async_iterator_proto,
&self.iterator_helper_proto,
&self.disposable_stack_proto,
&self.async_disposable_stack_proto,
@@ -604,6 +608,7 @@ impl BuiltinWorld {
BuiltinId::BigIntConstructor => &self.bigint_constructor,
BuiltinId::BigIntProto => &self.bigint_proto,
BuiltinId::Console => &self.console_object,
BuiltinId::AsyncIteratorProto => &self.async_iterator_proto,
}
}
}
+10 -7
View File
@@ -1,4 +1,4 @@
//! 内置对象 id 枚举(`BuiltinId` 96 变体 + `ALL` 顺序钉表)、世代快照
//! 内置对象 id 枚举(`BuiltinId` 98 变体 + `ALL` 顺序钉表)、世代快照
//! (`BuiltinSnapshot`)与按家族划分的脏标记位集(`BuiltinDirtySet`);
//! `NUM_BUILTINS` 文档承载"新增 BuiltinWorld 字段须同步"四处约束注记。
@@ -11,7 +11,7 @@ use crate::builtin::BuiltinWorld;
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
/// builtin 家族。
pub const NUM_BUILTINS: usize = 97;
pub const NUM_BUILTINS: usize = 98;
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
///
@@ -118,6 +118,7 @@ pub enum BuiltinId {
SharedArrayBufferProto = 94,
SharedArrayBufferConstructor = 95,
AtomicsObject = 96,
AsyncIteratorProto = 97,
}
impl BuiltinId {
@@ -222,6 +223,7 @@ impl BuiltinId {
BuiltinId::SharedArrayBufferProto,
BuiltinId::SharedArrayBufferConstructor,
BuiltinId::AtomicsObject,
BuiltinId::AsyncIteratorProto,
];
}
@@ -369,19 +371,20 @@ impl BuiltinDirtySet {
mod tests {
use super::*;
/// 槽对齐面:表尾追加 Atomics 单例后总槽数 97,既有 96 个判别值零位移,
/// 快照数组随 NUM_BUILTINS 自动扩维、逐槽对齐。
/// 槽对齐面:表尾追加 AsyncIteratorPrototype 后总槽数 98,既有 97 个
/// 判别值零位移,快照数组随 NUM_BUILTINS 自动扩维、逐槽对齐。
#[test]
fn builtin_snapshot_all_slots_aligned() {
assert_eq!(NUM_BUILTINS, 97);
assert_eq!(NUM_BUILTINS, 98);
assert_eq!(BuiltinId::ALL.len(), NUM_BUILTINS);
// 前 96 项判别值 0-95 逐项不变(尾追加零位移)。
for i in 0..96usize {
// 前 97 项判别值 0-96 逐项不变(尾追加零位移)。
for i in 0..97usize {
assert_eq!(BuiltinId::ALL[i] as usize, i);
}
assert_eq!(BuiltinId::ALL[94], BuiltinId::SharedArrayBufferProto);
assert_eq!(BuiltinId::ALL[95], BuiltinId::SharedArrayBufferConstructor);
assert_eq!(BuiltinId::ALL[96], BuiltinId::AtomicsObject);
assert_eq!(BuiltinId::ALL[97], BuiltinId::AsyncIteratorProto);
// 快照经 session 全量构造路径采集,generations 数组维度 = 槽数。
use crate::kernel::{KernelConfig, KernelCore, KernelSession};
+151 -4
View File
@@ -16,7 +16,7 @@ use std::collections::VecDeque;
use oxide_builtins::iterator::make_iter_result;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_runtime_api::NativeResult;
use oxide_runtime_api::{NativeResult, VmHost};
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
@@ -731,6 +731,150 @@ pub(crate) fn async_generator_symbol_async_iterator(vm: &mut Vm, args: &[u8]) ->
NativeResult::Ok(this_val)
}
/// `@@asyncDispose` 结算闭包上存目标能力 promise 的属性名。
const AD_SETTLE_PROMISE_PROP: &str = "__oxide_async_dispose_promise__";
/// `@@asyncDispose` 结算闭包上区分 fulfill/reject 角色的属性名。
const AD_SETTLE_ROLE_PROP: &str = "__oxide_async_dispose_role__";
/// `%AsyncIteratorPrototype%[@@asyncDispose]`:新建能力,取 `this` 的 `return`
/// 方法调用后,把结果经 PromiseResolve 展开、以 undefined 结算能力。
///
/// # 步骤
/// 1. GetMethod(this, "return"):非对象接收者或方法缺失/非对象时按无方法处理。
/// 2. 无方法:能力以 undefined resolve 后立即返回。
/// 3. Call(return, this, « undefined »):调用失败以原异常 reject 能力。
/// 4. 结果为 promise 时注册 unwrap 反应(最终值恒 undefined);非 promise 直接
/// 以 undefined resolve 能力。
///
/// # 边界与前提
/// - 本方法返回的 promise 永不同步抛错:所有异常路径都转为能力 reject。
///
/// # 副作用
/// - 新建能力 promise 与两个结算闭包(登记进 session GC 根)。
/// - 调用用户 `return` 方法(可能执行任意用户代码)。
pub(crate) fn async_iterator_async_dispose(vm: &mut Vm, args: &[u8]) -> NativeResult {
let o = vm.reg(if args.is_empty() { 0 } else { args[0] });
let (promise, _, _) = vm.new_promise_capability();
// GetMethod(O, "return"):完整 [[Get]](触发 getter,getter 抛错经
// IfAbruptRejectPromise 转能力 reject)。
let ret = if o.is_object() {
let si = vm.kernel_core.perm_interner().intern("return").0;
let obj = unsafe { &*o.as_js_object_ptr() };
match vm.ordinary_get(obj, si, o) {
Ok(r) => Some(r),
Err(_) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, "AsyncIterator return getter failed"));
let _ = vm.reject_promise(promise, exc);
return NativeResult::Ok(promise);
}
}
} else {
None
};
let ret = match ret {
Some(r) if r.is_object() => r,
_ => {
let _ = vm.resolve_promise(promise, JsValue::undefined());
return NativeResult::Ok(promise);
}
};
// Call(return, O, « undefined »):调用失败以原异常 reject 能力。
let result = match vm.call_function_sync(ret, o, &[JsValue::undefined()]) {
Ok(r) => r,
Err(_) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, "AsyncIterator return method call failed"));
let _ = vm.reject_promise(promise, exc);
return NativeResult::Ok(promise);
}
};
// PromiseResolve + PerformPromiseThen(resultWrapper, unwrap, undefined, capability):
// unwrap 闭包恒返回 undefined,故能力最终值恒为 undefined。
if vm.is_promise_value(result) {
let unwrap = vm.make_async_dispose_unwrap_fn();
let derived = match vm.perform_promise_then(result, unwrap, JsValue::undefined()) {
Ok(d) => d,
Err(exc) => {
let _ = vm.reject_promise(promise, exc);
return NativeResult::Ok(promise);
}
};
let on_fulfilled = vm.make_async_dispose_settle_fn(promise, false);
let on_rejected = vm.make_async_dispose_settle_fn(promise, true);
let _ = vm.perform_promise_then(derived, on_fulfilled, on_rejected);
} else {
let _ = vm.resolve_promise(promise, JsValue::undefined());
}
NativeResult::Ok(promise)
}
/// unwrap 闭包:忽略反应值,恒返回 undefined(规范 CreateBuiltinFunction(unwrap, 1, "", « »))。
fn async_dispose_unwrap_closure(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::undefined())
}
/// 结算闭包:以反应值结算自身 prop 携带的能力 promise(角色 prop 区分 fulfill/reject)。
fn async_dispose_settle_closure(vm: &mut Vm, args: &[u8]) -> NativeResult {
let callee = vm.reg(254);
if !callee.is_object() {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "settle handler is invalid"));
}
let callee_obj = unsafe { &*callee.as_js_object_ptr() };
let prom_si = vm.kernel_core.perm_interner().intern(AD_SETTLE_PROMISE_PROP).0;
let promise = vm.resolve_property(callee_obj, prom_si).unwrap_or(JsValue::undefined());
let role_si = vm.kernel_core.perm_interner().intern(AD_SETTLE_ROLE_PROP).0;
let is_reject = vm
.resolve_property(callee_obj, role_si)
.is_some_and(oxide_runtime_api::to_boolean);
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if is_reject {
let _ = vm.reject_promise(promise, value);
} else {
let _ = vm.resolve_promise(promise, value);
}
NativeResult::Ok(JsValue::undefined())
}
impl Vm {
/// 构造 `@@asyncDispose` 的 unwrap 闭包(无捕获状态,恒返回 undefined)。
fn make_async_dispose_unwrap_fn(&mut self) -> JsValue {
let fn_proto = self.session.builtin_world().fn_proto_val();
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
func.set_function(true);
// SAFETY: async_dispose_unwrap_closure 是 NativeFn 函数项。
func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_dispose_unwrap_closure as *const ()) }));
func.set_native_arg_count(1);
let ptr = self.alloc_object(func);
self.add_fn_name_length(unsafe { &mut *ptr }, "", 1);
JsValue::from_js_object(ptr)
}
/// 构造 `@@asyncDispose` 结算闭包:捕获能力 promise 与结算角色,
/// 反应触发时以反应值结算能力 promise。
fn make_async_dispose_settle_fn(&mut self, promise: JsValue, reject_role: bool) -> JsValue {
let fn_proto = self.session.builtin_world().fn_proto_val();
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
func.set_function(true);
// SAFETY: async_dispose_settle_closure 是 NativeFn 函数项。
func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_dispose_settle_closure as *const ()) }));
func.set_native_arg_count(1);
let ptr = self.alloc_object(func);
let obj = unsafe { &mut *ptr };
let prom_si = self.kernel_core.perm_interner().intern(AD_SETTLE_PROMISE_PROP).0;
self.set_or_create_prop_value(obj, prom_si, promise);
let role_si = self.kernel_core.perm_interner().intern(AD_SETTLE_ROLE_PROP).0;
self.set_or_create_prop_value(obj, role_si, JsValue::bool(reject_role));
self.add_fn_name_length(obj, "", 1);
JsValue::from_js_object(ptr)
}
}
/// 初始化/重建异步生成器内建对象:`%AsyncGeneratorPrototype%`、
/// `%AsyncGeneratorFunction.prototype%` 与占位 `%AsyncGeneratorFunction%`。
///
@@ -740,10 +884,13 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) {
let sh = vm.kernel_core.shape_forge().as_ref();
let fn_proto_val = vm.session.builtin_world().fn_proto_val();
let world = vm.session.builtin_world();
let object_proto_val = JsValue::from_js_object(vm.session.builtin_world().object_proto.as_ptr() as *mut JsObject);
// %AsyncGeneratorPrototype% 链到 %AsyncIteratorPrototype%(规范原型链),
// 方法 next/return/throw 挂其自身。
let async_iterator_proto_val =
JsValue::from_js_object(world.async_iterator_proto.as_ptr() as *mut JsObject);
// %AsyncGeneratorPrototype%:proto = Object.prototype,方法 next/return/throw。
let mut ag_proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto_val));
// %AsyncGeneratorPrototype%:proto = %AsyncIteratorPrototype%。
let mut ag_proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, async_iterator_proto_val));
// 站点标签:与 Generator 原型的同名方法槽(next/return/throw)区分复用键。
let ag_label = sf.intern("AsyncGeneratorPrototype").0;
oxide_kernel::bind_methods_static!(
+31
View File
@@ -567,6 +567,37 @@ fn bind_iterator_protos(core: &Arc<KernelCore>, session: &KernelSession) {
);
}
// %AsyncIteratorPrototype%:@@asyncIterator(返回 this)与 @@asyncDispose
// (经 return 方法关闭迭代器)。幂等:键槽已存在时跳过。
let aiter_proto_ptr = world.async_iterator_proto.as_ptr() as *mut JsObject;
let aiter_proto = unsafe { &mut *aiter_proto_ptr };
let sym_aiter =
oxide_types::private_key::make_well_known_symbol_key(oxide_types::private_key::WELL_KNOWN_SYMBOL_ASYNC_ITERATOR);
if core.shape_forge().lookup_position(aiter_proto.shape_id(), sym_aiter).is_none() {
bind_well_known_method(
world,
core,
aiter_proto,
oxide_types::private_key::WELL_KNOWN_SYMBOL_ASYNC_ITERATOR,
"[Symbol.asyncIterator]",
crate::async_generator::async_generator_symbol_async_iterator as *const (),
0,
);
}
let sym_adispose =
oxide_types::private_key::make_well_known_symbol_key(oxide_types::private_key::WELL_KNOWN_SYMBOL_ASYNC_DISPOSE);
if core.shape_forge().lookup_position(aiter_proto.shape_id(), sym_adispose).is_none() {
bind_well_known_method(
world,
core,
aiter_proto,
oxide_types::private_key::WELL_KNOWN_SYMBOL_ASYNC_DISPOSE,
"[Symbol.asyncDispose]",
crate::async_generator::async_iterator_async_dispose as *const (),
0,
);
}
// %IteratorHelperPrototype%:wrapper 结果对象的共享原型(链到
// %IteratorPrototype%),绑 next/return/throw 三方法(throw length=1,
// next/return 为 0)+ @@toStringTag 数据属性 "Iterator Helper"。