Files
OxideJS-New/crates/oxide_emit/src/expr/operator/fold.rs
T

420 lines
19 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 常量折叠:操作数均为字面量且通过语义守卫时,编译期折成单常量,
//! 发射单条 `LOAD_CONST`。折值编码逐运算符镜像运行时指令的产物编码,
//! 保证物化后与运行时执行结果逐字节一致。
//!
//! 三个域并列:数值域(`literal_constant`/`fold_binary`,两操作数形态,
//! 守卫排除字符串与 BigInt)、字符串域(`literal_units`/`fold_string_chain`/
//! `fold_template`,Addition 链与全字面量模板,单元口径与运行期
//! `push_units_to` 逐段一致)、一元/逻辑/条件域(`literal_constant_wide`/
//! `fold_unary`/`fold_logical`/`fold_conditional`,全字面量操作数折单常量)。
//!
//! 数值域语义守卫:任一操作数为 BigInt 字面量不折(BigInt 混合运行期抛
//! TypeError,BigInt 与 BigInt 有独立指令族);`DIV`/`MOD` 除数为零不折、
//! `EXP` 指数为负不折(保持运行时指令为除零/负指数编码的唯一来源)。
//! 字符串域语义守卫:Addition 链须至少一个字符串字面量(全数字链落回
//! 数值域与 `CONCAT_N`,两域零重叠);模板须全部 cooked 为 Some。
//! 一元/逻辑/条件域语义守卫:`+` 作用 BigInt 字面量不折(运行期抛
//! TypeError,保持运行时指令为该异常的唯一来源)。
use oxide_bytecode::module::Constant;
use oxide_parser::{BinaryOperator, Expression, LogicalOperator, UnaryOperator};
use oxide_runtime_api::{
abstract_eq_primitives, bigint_to_string, js_number_to_string, relational_compare, strict_equality, to_boolean,
to_int32, to_number, to_uint32,
};
use oxide_types::string_forge::{decode_key, encode_key};
use oxide_types::value::JsValue;
use crate::is_int_literal;
use crate::shared::string_pool::{pool_key_marker, pool_key_plain};
/// 字面量表达式提取常量:剥括号与一元负号后,数字(`is_int_literal` 分派
/// `Int`/`Number`)、布尔、null 三类;BigInt 字面量、字符串字面量、RegExp 与
/// 非字面量一律 `None`。
///
/// 一元负号只沿 `UnaryNegation` 链剥(含嵌套负号与括号),其余一元运算符
/// (`!`/`typeof`/`void`/`~`/`+`/`delete`)终止剥壳不折。
pub(crate) fn literal_constant(expr: &Expression) -> Option<Constant> {
let (inner, negated) = peel_negation(expr);
match inner {
Expression::NumericLiteral(n) => {
let value = if negated { -n.value } else { n.value };
if is_int_literal(value) {
Some(Constant::Int(value as i32))
} else {
Some(Constant::Number(value))
}
}
Expression::BooleanLiteral(b) if !negated => Some(Constant::Boolean(b.value)),
Expression::NullLiteral(_) if !negated => Some(Constant::Null),
_ => None,
}
}
/// 剥括号与一元负号链:返回内层表达式与负号层数奇偶(奇数层取负)。
fn peel_negation<'a>(expr: &'a Expression<'a>) -> (&'a Expression<'a>, bool) {
let mut e = expr;
let mut negated = false;
loop {
while let Expression::ParenthesizedExpression(p) = e {
e = &p.expression;
}
if let Expression::UnaryExpression(u) = e {
if u.operator == UnaryOperator::UnaryNegation {
negated = !negated;
e = &u.argument;
continue;
}
}
break;
}
(e, negated)
}
/// 常量转 `JsValue`(纯 NaN-box 构造,零分配):`Int`/`Number`/`Boolean`/`Null`
/// 四臂,其余臂不可达(BigInt/String/Undefined 已被守卫排除)。
fn const_to_jsvalue(c: &Constant) -> JsValue {
match c {
Constant::Int(i) => JsValue::int(*i),
Constant::Number(d) => JsValue::float(*d),
Constant::Boolean(b) => JsValue::bool(*b),
Constant::Null => JsValue::null(),
_ => unreachable!("折叠操作数守卫已排除 BigInt/String/Undefined"),
}
}
/// 常量的数值(`Int`/`Number`/`Boolean`/`Null` 均走 `ToNumber` 口径)。
fn const_to_number(c: &Constant) -> f64 {
to_number(const_to_jsvalue(c))
}
/// 字面量表达式提取常量(宽口径):窄口径三臂加 BigInt 字面量臂,供一元/逻辑/
/// 条件折叠识别 BigInt 字面量(`+` 的 TypeError 守卫与 `-`/`~` 的 BigInt 折值
/// 都须知道操作数是 BigInt)。
///
/// 负零字面量(奇数层负号作用于 0 字面量)保 `Number(-0.0)` 不经 Int 臂,防
/// 符号丢失(`+`/`-` 折叠产物经 `Object.is` 可观察)。BigInt 字面量解析与
/// 字面量发射器同口径,解析失败返回 `None` 不折。
pub(crate) fn literal_constant_wide(expr: &Expression) -> Option<Constant> {
let (inner, negated) = peel_negation(expr);
match inner {
Expression::NumericLiteral(n) => {
// 负零守卫:奇数层负号作用于 0 字面量保 Number(-0.0),Int 臂会丢符号。
if negated && n.value == 0.0 {
return Some(Constant::Number(-0.0));
}
let value = if negated { -n.value } else { n.value };
if is_int_literal(value) {
Some(Constant::Int(value as i32))
} else {
Some(Constant::Number(value))
}
}
Expression::BooleanLiteral(b) if !negated => Some(Constant::Boolean(b.value)),
Expression::NullLiteral(_) if !negated => Some(Constant::Null),
Expression::BigIntLiteral(b) => {
let value = parse_bigint_literal(b)?;
Some(if negated { Constant::BigInt(-value) } else { Constant::BigInt(value) })
}
_ => None,
}
}
/// BigInt 字面量解析:与字面量发射器同口径(i128 快路径失败回退十进制
/// `parse_bytes`,解析失败返回 `None` 不折)。
fn parse_bigint_literal(b: &oxide_parser::BigIntLiteral) -> Option<num_bigint::BigInt> {
if let Ok(v) = b.value.parse::<i128>() {
Some(num_bigint::BigInt::from(v))
} else {
num_bigint::BigInt::parse_bytes(b.value.as_bytes(), 10)
}
}
/// 常量的布尔值(`ToBoolean` 口径):BigInt 臂直接判零,其余经
/// `const_to_jsvalue` 加 `to_boolean`。
fn const_to_boolean(c: &Constant) -> bool {
if let Constant::BigInt(b) = c {
return *b != num_bigint::BigInt::from(0);
}
to_boolean(const_to_jsvalue(c))
}
/// 双常量折叠分派:按操作码计算折值,守卫命中返回 `None`。
///
/// 折值编码逐运算符镜像运行时产物:
/// - `ADD`:int+int 经 `checked_add` 在 i32 内保 `Int`,溢出升 `Number`(镜像
/// `int_add`);其余组合 f64 加(镜像 `dispatch_add` 的 `to_number` 路径)
/// - `SUB`/`MUL`/`DIV`/`MOD`:恒 `Number`(镜像 `binary_arith!` 的 `as f64`)
/// - `EXP`:恒 `Number` 的 `powf`(镜像 `exp_result`)
/// - `BIT_AND`/`BIT_OR`/`BIT_XOR`/`SHL`/`SHR`:`Int`(镜像 `to_int32`)
/// - `USHR`:结果 ≤ i32::MAX 保 `Int`,否则升 `Number`(镜像 `write_ushr_result`)
/// - `LT`/`GT`/`LTE`/`GTE`:`Boolean`(镜像 `relational_compare`,GT/LTE 交换
/// 操作数,LTE/GTE 取反,`None` 归 false)
/// - `EQ`/`NEQ`:`Boolean`(镜像 `abstract_eq` 原始值核心);
/// `STRICT_EQ`/`STRICT_NEQ`:`Boolean`(镜像 `strict_equality`)
///
/// 守卫:`DIV`/`MOD` 除数为零(`Int(0)` 与 `Number(±0.0)` 均判零)、`EXP`
/// 指数为负(`Int`/`Number` 负值均判负)返回 `None`,落回运行时路径。
pub(crate) fn fold_binary(op: BinaryOperator, l: &Constant, r: &Constant) -> Option<Constant> {
match op {
BinaryOperator::Addition => {
// int+int:checked_add 在 i32 内保 Int,溢出升 f64(镜像 int_add)。
if let (Constant::Int(a), Constant::Int(b)) = (l, r) {
return a
.checked_add(*b)
.map(Constant::Int)
.or(Some(Constant::Number(*a as f64 + *b as f64)));
}
Some(Constant::Number(const_to_number(l) + const_to_number(r)))
}
BinaryOperator::Subtraction
| BinaryOperator::Multiplication
| BinaryOperator::Division
| BinaryOperator::Remainder => {
// 除零守卫:Int(0) 与 Number(±0.0) 均判零(v == 0.0 对 -0.0 亦真)。
if matches!(op, BinaryOperator::Division | BinaryOperator::Remainder) && const_to_number(r) == 0.0 {
return None;
}
// 恒 f64(镜像 binary_arith! 宏的 as f64)。
let (ln, rn) = (const_to_number(l), const_to_number(r));
Some(match op {
BinaryOperator::Subtraction => Constant::Number(ln - rn),
BinaryOperator::Multiplication => Constant::Number(ln * rn),
BinaryOperator::Division => Constant::Number(ln / rn),
_ => Constant::Number(ln % rn),
})
}
BinaryOperator::Exponential => {
// 负指数守卫:Int 与 Number 负值均判负。
if const_to_number(r) < 0.0 {
return None;
}
// 恒 f64 powf(镜像 exp_result)。
Some(Constant::Number(const_to_number(l).powf(const_to_number(r))))
}
BinaryOperator::BitwiseAnd | BinaryOperator::BitwiseOR | BinaryOperator::BitwiseXOR => {
let (a, b) = (to_int32(const_to_jsvalue(l)), to_int32(const_to_jsvalue(r)));
Some(match op {
BinaryOperator::BitwiseAnd => Constant::Int(a & b),
BinaryOperator::BitwiseOR => Constant::Int(a | b),
_ => Constant::Int(a ^ b),
})
}
BinaryOperator::ShiftLeft | BinaryOperator::ShiftRight => {
let a = to_int32(const_to_jsvalue(l));
let shift = to_uint32(const_to_jsvalue(r)) & 0x1F;
Some(match op {
BinaryOperator::ShiftLeft => Constant::Int(a << shift),
_ => Constant::Int(a >> shift),
})
}
BinaryOperator::ShiftRightZeroFill => {
let a = to_int32(const_to_jsvalue(l)) as u32;
let shift = to_uint32(const_to_jsvalue(r)) & 0x1F;
let result = a >> shift;
// 结果 ≤ i32::MAX 保 Int,否则升 Number(镜像 write_ushr_result)。
if result <= i32::MAX as u32 {
Some(Constant::Int(result as i32))
} else {
Some(Constant::Number(result as f64))
}
}
BinaryOperator::LessThan => relational(l, r, false, false),
BinaryOperator::GreaterThan => relational(l, r, true, false),
BinaryOperator::LessEqualThan => relational(l, r, true, true),
BinaryOperator::GreaterEqualThan => relational(l, r, false, true),
BinaryOperator::Equality => {
Some(Constant::Boolean(abstract_eq_primitives(const_to_jsvalue(l), const_to_jsvalue(r))))
}
BinaryOperator::Inequality => {
Some(Constant::Boolean(!abstract_eq_primitives(const_to_jsvalue(l), const_to_jsvalue(r))))
}
BinaryOperator::StrictEquality => {
Some(Constant::Boolean(strict_equality(const_to_jsvalue(l), const_to_jsvalue(r))))
}
BinaryOperator::StrictInequality => {
Some(Constant::Boolean(!strict_equality(const_to_jsvalue(l), const_to_jsvalue(r))))
}
// IN/INSTANCEOF 是对象操作,不折。
BinaryOperator::In | BinaryOperator::Instanceof => None,
}
}
/// 关系比较折叠:镜像运行时 dispatch 口径(GT/LTE 交换操作数,LTE/GTE 取反,
/// `None` 归 false)。
fn relational(l: &Constant, r: &Constant, swap: bool, negate: bool) -> Option<Constant> {
let (a, b) = if swap {
(const_to_jsvalue(r), const_to_jsvalue(l))
} else {
(const_to_jsvalue(l), const_to_jsvalue(r))
};
let cmp = relational_compare(a, b).unwrap_or(false);
Some(Constant::Boolean(if negate { !cmp } else { cmp }))
}
/// 一元折叠分派:按操作符计算折值,守卫命中返回 `None`。
///
/// 折值编码逐运算符镜像运行时产物:
/// - `LogicalNot`:`Boolean`(镜像 `NOT` 的 `to_boolean` 取反)
/// - `UnaryNegation`:BigInt 操作数折 `BigInt`,其余恒 `Number`(镜像
/// `NEG` 的 `JsValue::float`,不保 `Int`)
/// - `UnaryPlus`:BigInt 操作数不折(运行期抛 TypeError,保持运行时指令为
/// 该异常的唯一来源);其余恒 `Number`
/// - `BitwiseNot`:BigInt 操作数折 `BigInt`,其余恒 `Int`(镜像 `to_int32`)
/// - `Typeof`:`Int`/`Number` 折 "number"、`BigInt` 折 "bigint"、`Boolean` 折
/// "boolean"、`Null` 折 "object"(结果字符串经 `pool_key_plain` 入池,逐类型
/// 镜像运行时 `TYPEOF_TEXTS` 分派)
/// - `Void`:恒 `Undefined`
/// - `Delete`:引用语义非常量,不折(入口 Delete 特判先行,此臂为防御)
pub(crate) fn fold_unary(op: UnaryOperator, c: &Constant) -> Option<Constant> {
match op {
UnaryOperator::LogicalNot => Some(Constant::Boolean(!const_to_boolean(c))),
UnaryOperator::UnaryNegation => match c {
Constant::BigInt(b) => Some(Constant::BigInt(-b.clone())),
_ => Some(Constant::Number(-const_to_number(c))),
},
UnaryOperator::UnaryPlus => {
// BigInt 守卫:运行期抛 TypeError,不折。
if matches!(c, Constant::BigInt(_)) {
return None;
}
Some(Constant::Number(const_to_number(c)))
}
UnaryOperator::BitwiseNot => match c {
Constant::BigInt(b) => Some(Constant::BigInt(!b.clone())),
_ => Some(Constant::Int(!to_int32(const_to_jsvalue(c)))),
},
UnaryOperator::Typeof => {
// 逐类型镜像运行时 `TYPEOF_TEXTS` 分派:BigInt 归 "bigint"
// (独立于 Int/Double 的 "number"),Boolean 归 "boolean",Null 归 "object"。
let text = match c {
Constant::Int(_) | Constant::Number(_) => "number",
Constant::BigInt(_) => "bigint",
Constant::Boolean(_) => "boolean",
Constant::Null => "object",
_ => return None,
};
Some(Constant::String(pool_key_plain(text)))
}
UnaryOperator::Void => Some(Constant::Undefined),
UnaryOperator::Delete => None,
}
}
/// 逻辑折叠分派:结果恒为某一操作数的原样拷贝(无类型转换,任何字面量类型
/// 均安全,含 BigInt)。
pub(crate) fn fold_logical(op: LogicalOperator, l: &Constant, r: &Constant) -> Option<Constant> {
Some(match op {
LogicalOperator::And => {
if const_to_boolean(l) {
r.clone()
} else {
l.clone()
}
}
LogicalOperator::Or => {
if const_to_boolean(l) {
l.clone()
} else {
r.clone()
}
}
LogicalOperator::Coalesce => {
if matches!(l, Constant::Null | Constant::Undefined) {
r.clone()
} else {
l.clone()
}
}
})
}
/// 条件折叠分派:结果恒为 test 的布尔值选定的分支常量(原样拷贝)。
pub(crate) fn fold_conditional(test: &Constant, cons: &Constant, alt: &Constant) -> Option<Constant> {
Some(if const_to_boolean(test) { cons.clone() } else { alt.clone() })
}
/// 可折叠字面量的单元序列(剥括号与一元负号链):字符串字面量经
/// `pool_key_marker` 加 `decode_key` 还原单元(与字面量发射器同一池键函数);
/// 数字字面量按 `is_int_literal` 分派 i32 itoa 或 `js_number_to_string` 后
/// UTF-16 编码;BigInt 字面量解析后 `bigint_to_string`(i128 快路径失败回退
/// 十进制 `parse_bytes`,解析失败返回 `None` 不折);布尔与 null 为
/// "true"/"false"/"null"。
///
/// 其余形态(标识符/调用/RegExp/嵌套二元等)返回 `None`。
pub(crate) fn literal_units(expr: &Expression) -> Option<Vec<u16>> {
let (inner, negated) = peel_negation(expr);
match inner {
Expression::StringLiteral(s) if !negated => Some(decode_key(&pool_key_marker(&s.value, s.lone_surrogates))),
Expression::NumericLiteral(n) => {
let value = if negated { -n.value } else { n.value };
let text = if is_int_literal(value) {
(value as i32).to_string()
} else {
js_number_to_string(value)
};
Some(text.encode_utf16().collect())
}
Expression::BigIntLiteral(b) => {
let value = if let Ok(v) = b.value.parse::<i128>() {
num_bigint::BigInt::from(v)
} else {
num_bigint::BigInt::parse_bytes(b.value.as_bytes(), 10)?
};
let value = if negated { -value } else { value };
Some(bigint_to_string(&value).encode_utf16().collect())
}
Expression::BooleanLiteral(b) if !negated => {
let text = if b.value { "true" } else { "false" };
Some(text.encode_utf16().collect())
}
Expression::NullLiteral(_) if !negated => Some("null".encode_utf16().collect()),
_ => None,
}
}
/// 判定操作数是否为字符串字面量(剥括号):规范判定整条 `+` 链为字符串
/// 拼接的条件。
fn is_string_literal(expr: &Expression) -> bool {
let mut e = expr;
while let Expression::ParenthesizedExpression(p) = e {
e = &p.expression;
}
matches!(e, Expression::StringLiteral(_))
}
/// Addition 链字符串域折叠:全操作数经 `literal_units` 可定值且至少一个是
/// 字符串字面量时,拼接各段单元经 `encode_key` 回池键形态,返回
/// `Constant::String`;全数字链(无字符串字面量)返回 `None` 落回 `CONCAT_N`。
pub(crate) fn fold_string_chain(operands: &[&Expression]) -> Option<Constant> {
// 字符串域守卫置于首行:至少一个字符串字面量,全数字链留给数值域。
if !operands.iter().any(|op| is_string_literal(op)) {
return None;
}
let mut units: Vec<u16> = Vec::new();
for op in operands {
units.extend(literal_units(op)?);
}
Some(Constant::String(encode_key(&units)))
}
/// 模板字面量折叠:全部 quasi 的 cooked 为 Some 且全部内插表达式经
/// `literal_units` 可定值时,按 quasi 与表达式交替序拼接单元经 `encode_key`
/// 回池键形态,返回 `Constant::String`;任一 cooked 为 None(非法转义,
/// 解析期 SyntaxError 的防御路径)返回 `None` 落回 `TEMPLATE_STR`。
pub(crate) fn fold_template(tl: &oxide_parser::TemplateLiteral) -> Option<Constant> {
// cooked 守卫置于首行:任一段为 None 时整模板不折。
if !tl.quasis.iter().all(|q| q.value.cooked.is_some()) {
return None;
}
let mut units: Vec<u16> = Vec::new();
for (i, q) in tl.quasis.iter().enumerate() {
let cooked = q.value.cooked.as_ref().expect("守卫已确认全部 cooked 为 Some");
units.extend(decode_key(&pool_key_marker(cooked, q.lone_surrogates)));
if let Some(expr) = tl.expressions.get(i) {
units.extend(literal_units(expr)?);
}
}
Some(Constant::String(encode_key(&units)))
}