mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: URI 四函数正确性修复并新增 AsyncIteratorPrototype 原型
This commit is contained in:
@@ -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])?)
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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"。
|
||||
|
||||
Reference in New Issue
Block a user