mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 10:41:11 +08:00
6f70d7d09f
全量 A/B 差分撞出 14 个头独占回归,本提交闭合 13(text-self.js 双树同败归 pre-existing):
数值键 u32 回绕(release 态,debug 为 panic 故单测未现):canonical_index_str/
units 的 v.checked_mul(10)? + d as u32 在乘积合法而和溢出 2^32 时静默回绕
(10 位数字串 "4294967296".."4294967299" 误判整数键 0..3,属性查找回
退、Object.keys 串键),改 checked_mul+checked_add 双段防溢出;str/units 各
钉一枚 2^30-1/2^30/2^32-1/2^32 边界(Group B 5 文件:defineProperty/
defineProperties 2^32 键族 + Array property-cast-number)。
decodeURI/decodeURIComponent 单元域重写(Group A 2 文件):decode_uri_units
按单元扫描(非 % 单元原样透传——孤立 surrogate 往返不再被 lossy 串值面
打碎),%XX 解单元入缓冲;string_arg_units 经 to_units_full 取单元。
动态源编码(eval/Function/$262.evalScript)重设计(Group A 6 文件 + value-u):
source_escape 旧设计把源码域反斜杠恒双写(与 marker 起始区分),但源码即
JS 程序、反斜杠恒为字面量内转义序列起始,双写改变程序语义——
/\u{1d306}/u 变 /\\u{1d306}/u,u 模式撞裸 { 直接 SyntaxError(value-u 回归
根因)。现反斜杠原样透传;孤立 surrogate/FFFD 单元仍以 \\uXXXX 转义文本
注入(Rust str 无法承载裸孤立 surrogate,转义文本是唯一注入形态);
source_escape_to_key 逆路径仅还原注入 marker(\\ud800..\\udfff/\\ufffd
小写转义文本 → FFFD+hex4 池键形态,物化 decode_key 还原原始单元,正则
.source 按原始源返回;用户转义文本 \\u0041/\\u{1d306} 等逐字透传),
\\+marker 拼接形态(数据反斜杠紧邻注入,S7.8.5_A1.4 族 cu=d800 断言)按
前瞻还原 [数据反斜杠, 单元]。source_encoded 标志经 Compiler→Emitter→CompileCtx
(嵌套继承)贯通,仅动态编译入口置位;静态源切片维持 pool_key_plain 不变。
RegExp [[Source]]/[[Flags]] 单元域(regexp 构造器):to_units_full →
new_string_units_owned 入键(旧 lossy to_string 把孤立 surrogate 单元打成
FFFD,src_match 探针 false);regress 编译仍 lossy(既有缺口,另行报告)。
三并钉:source_escape marker 形态 / 反斜杠透传 / decode_key∘to_key∘escape
闭环(含 \\+marker 相邻与 astral 对)。实测:Group A 8/8、Group B 5/5、
value-u 1/1;8178 文件回归子集(13 目录)双树 A/B 差分 HEAD-only=∅、
13 文件转双树同 pass;text-self.js 双树同 compile 败
(requested module export is not exported: default)归 pre-existing;
七 crate 单测 2879/1(唯一失败 compile_class_declaration_emits_constructor
为 HEAD~0 既有,本提交未引入)。
52 lines
2.5 KiB
Rust
52 lines
2.5 KiB
Rust
//! eval 全局函数:非字符串原样返回;字符串按脚本模式动态编译执行。
|
||
//!
|
||
//! # 契约
|
||
//! - 非字符串实参**不** ToString(`eval(new String("1+1"))` 返回对象本身)。
|
||
//! - 字符串经动态编译以 global 为 this 同步调用;编译失败 → SyntaxError;
|
||
//! 运行异常 → 优先重抛原始异常值(`take_uncaught_value`,`eval("throw 1")` 捕获到 `1`)。
|
||
//! - 脚本模式:完成值保留(`eval("1+2")` → 3);var/函数声明落全局对象;let/const 词法隔离。
|
||
|
||
use oxide_runtime_api::{NativeResult, VmHost};
|
||
use oxide_types::object::JsObject;
|
||
use oxide_types::value::JsValue;
|
||
|
||
use crate::error;
|
||
|
||
/// `eval(x)`:非字符串实参原样返回;字符串按脚本模式编译执行。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 实参非字符串 → 原样返回(规范不 ToString)。
|
||
/// 2. 字符串 → 单元序列按源码域转义(`string_forge::source_escape`,孤立
|
||
/// surrogate / FFFD 以 `\uXXXX` 转义文本承载,反斜杠为源码语法字符原样
|
||
/// 透传,可被 oxc 直接词法分析)→ 动态编译为脚本模块(`create_dynamic_script`
|
||
/// 动态路径源契约),
|
||
/// 以 global 为 this 同步调用。
|
||
/// 3. 编译失败 → SyntaxError;运行异常 → 优先重抛原始异常值(`take_uncaught_value`,`eval("throw 1")` 捕获到 `1`)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 脚本模式:var/函数声明落全局对象,完成值保留,let/const 词法隔离。
|
||
/// - 无实参等价于 `eval(undefined)` → undefined。
|
||
pub fn eval<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let arg = if args.len() >= 2 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
if !arg.is_string() {
|
||
return NativeResult::Ok(arg);
|
||
}
|
||
let units = match oxide_runtime_api::to_units_full(arg, vm) {
|
||
Ok(u) => u,
|
||
Err(e) => return NativeResult::Err(error::create_from_text(vm, &e)),
|
||
};
|
||
let code = oxide_kernel::string_forge::source_escape(&units);
|
||
match vm.create_dynamic_script(&code) {
|
||
Ok(func) => {
|
||
let global = JsValue::from_js_object(vm.session().global_object().as_ptr() as *mut JsObject);
|
||
match vm.call_function_sync(func, global, &[]) {
|
||
Ok(val) => NativeResult::Ok(val),
|
||
Err(e) => {
|
||
NativeResult::Err(vm.take_uncaught_value().unwrap_or_else(|| error::create_from_text(vm, &e)))
|
||
}
|
||
}
|
||
}
|
||
Err(e) => NativeResult::Err(error::create_syntax_error(vm, &e)),
|
||
}
|
||
}
|