mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
perf: 字符串域加模板折叠为单字符串常量
This commit is contained in:
@@ -1,18 +1,29 @@
|
||||
//! 双常量折叠:两操作数均为字面量且通过语义守卫时,编译期折成单常量,
|
||||
//! 常量折叠:操作数均为字面量且通过语义守卫时,编译期折成单常量,
|
||||
//! 发射单条 `LOAD_CONST`。折值编码逐运算符镜像运行时指令的产物编码,
|
||||
//! 保证物化后与运行时执行结果逐字节一致。
|
||||
//!
|
||||
//! 三条语义守卫:任一操作数为 BigInt 字面量不折(BigInt 混合运行期抛
|
||||
//! 两个域并列:数值域(`literal_constant`/`fold_binary`,两操作数形态,
|
||||
//! 守卫排除字符串与 BigInt)与字符串域(`literal_units`/`fold_string_chain`/
|
||||
//! `fold_template`,Addition 链与全字面量模板,单元口径与运行期
|
||||
//! `push_units_to` 逐段一致)。
|
||||
//!
|
||||
//! 数值域语义守卫:任一操作数为 BigInt 字面量不折(BigInt 混合运行期抛
|
||||
//! TypeError,BigInt 与 BigInt 有独立指令族);`DIV`/`MOD` 除数为零不折、
|
||||
//! `EXP` 指数为负不折(保持运行时指令为除零/负指数编码的唯一来源)。
|
||||
//! 字符串字面量不折(字符串域是独立任务)。
|
||||
//! 字符串域语义守卫:Addition 链须至少一个字符串字面量(全数字链落回
|
||||
//! 数值域与 `CONCAT_N`,两域零重叠);模板须全部 cooked 为 Some。
|
||||
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_parser::{BinaryOperator, Expression, UnaryOperator};
|
||||
use oxide_runtime_api::{abstract_eq_primitives, relational_compare, strict_equality, to_int32, to_number, to_uint32};
|
||||
use oxide_runtime_api::{
|
||||
abstract_eq_primitives, bigint_to_string, js_number_to_string, relational_compare, strict_equality, 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;
|
||||
|
||||
/// 字面量表达式提取常量:剥括号与一元负号后,数字(`is_int_literal` 分派
|
||||
/// `Int`/`Number`)、布尔、null 三类;BigInt 字面量、字符串字面量、RegExp 与
|
||||
@@ -186,3 +197,87 @@ fn relational(l: &Constant, r: &Constant, swap: bool, negate: bool) -> Option<Co
|
||||
let cmp = relational_compare(a, b).unwrap_or(false);
|
||||
Some(Constant::Boolean(if negate { !cmp } else { cmp }))
|
||||
}
|
||||
|
||||
/// 可折叠字面量的单元序列(剥括号与一元负号链):字符串字面量经
|
||||
/// `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)))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! 逻辑与 `??` 用短路跳转,复合求值利用 `is_side_effect_free` 优化。
|
||||
//! 函数:`emit_operator` 及各类 `emit_*_expression`。
|
||||
|
||||
mod fold;
|
||||
pub(crate) mod fold;
|
||||
|
||||
use crate::{is_side_effect_free, BinaryOperator, CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
@@ -50,6 +50,14 @@ impl Emitter {
|
||||
let mut operands = Vec::new();
|
||||
collect_add_operands(&bin.left, &mut operands);
|
||||
operands.push(&bin.right);
|
||||
// 字符串域链折叠:全操作数为可折叠字面量且至少一个字符串字面量时,
|
||||
// 编译期折单字符串常量(单元口径见 fold.rs),发射单条 LOAD_CONST。
|
||||
if let Some(folded) = fold::fold_string_chain(&operands) {
|
||||
let idx = ctx.add_constant(folded);
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
|
||||
return Ok(r);
|
||||
}
|
||||
// 上限防寄存器压力:CONCAT_N 单点读全部操作数(须同时存活),超长链回退
|
||||
// 左结合 ADD(寄存器随链复用,纯表达式寄存器占用有界)。
|
||||
if operands.len() >= 3 && operands.len() <= MAX_CONCAT_N_OPERANDS {
|
||||
|
||||
@@ -10,6 +10,14 @@ impl Emitter {
|
||||
pub(crate) fn emit_template_literal_expression(
|
||||
&self, tl: &oxide_parser::TemplateLiteral, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
// 全字面量模板折叠:全部内插表达式为可折叠字面量且全部 cooked 为 Some 时,
|
||||
// 编译期折单字符串常量,发射单条 LOAD_CONST(单元口径见 fold.rs)。
|
||||
if let Some(folded) = crate::expr::operator::fold::fold_template(tl) {
|
||||
let idx = ctx.add_constant(folded);
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
|
||||
return Ok(r);
|
||||
}
|
||||
let r = ctx.alloc_reg();
|
||||
let quasis = &tl.quasis;
|
||||
let expressions = &tl.expressions;
|
||||
|
||||
Reference in New Issue
Block a user