mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
emit/kernel/vm/builtins: 常量池键编码 + 键物化真实串值面 + 16 回归钉——emit 侧字面量/键/模板统一改存无标志键编码:lone_surrogates 字面量与模板 cooked 按 oxc marker 解码还原真单元序列(FFFD+hex4≠fffd→surrogate 单元、FFFD+fffd→FFFD、FFFD+非 hex 防御臂),无标志串 encode_key(encode_utf16()) 入池(BMP 无 FFFD 键恒等 = 现值,池条目零漂移),string_pool 集中收口 9 处入池点;interner string_ptr 由单元物化(含孤立→FlatU16、良形→Flat);键物化面改真实 JS 串值(key_si_to_js_value 经 decode_key,Object.keys/ownKeys/entries/Reflect.ownKeys/json reviver 键同源;整数键反解数字串);批内修复三处子集 A/B 差分撞出回归:replace $n 展开按 spec GetSubstitution 重写(digitCount 2/1、越界回退 1 位、越界含 $0 原文输出,node 13 用例对照钉住)、String 构造器 Symbol 路径按 new.target 分流(String(sym)→描述串、new String(sym)→TypeError,恢复 to_string_for_string_constructor)、padStart/padEnd 未给 fill 时按单空格补(原 lossy 串取 'un' 杂质,pre-existing 一并归零);迭代器 dispose 钉改回真值断言(String(Symbol.dispose) 现合法返回描述串);property-escapes 613/0/0(B167 基线 582/31/0,31 归零)、String 子集 963/250/10 vs BASE 949/264/10(14 修 0 回归)、for-of +1(string-astral-truncated)、五 crate 113 套件 2680/0、clippy/fmt 零新增(JSON 面与全量 test262 收尾在 131.2 后续 commit 闭合)
This commit is contained in:
@@ -8,6 +8,7 @@ description = "OxideJS - AST to IR emit pass"
|
||||
|
||||
[dependencies]
|
||||
oxide_parser = { path = "../oxide_parser" }
|
||||
oxide_kernel = { path = "../oxide_kernel" }
|
||||
oxc_allocator.workspace = true
|
||||
oxide_bytecode = { path = "../oxide_bytecode" }
|
||||
oxide_ir = { path = "../oxide_ir" }
|
||||
|
||||
@@ -74,7 +74,7 @@ impl Emitter {
|
||||
} else {
|
||||
(undef_reg, accessor_reg)
|
||||
};
|
||||
let key_name = self.class_property_name(&method.key)?;
|
||||
let key_name = crate::shared::string_pool::pool_key_property(&method.key)?;
|
||||
let key_idx = ctx.add_constant(Constant::String(key_name));
|
||||
ctx.inst(Inst::define_accessor_attrs(
|
||||
Operand::Reg(home_reg),
|
||||
|
||||
@@ -1876,8 +1876,6 @@ mod tests {
|
||||
assert!(!deletable, "只读三常量名误入可删集:{name}");
|
||||
}
|
||||
}
|
||||
for name in ["$262"] {
|
||||
assert!(!crate::CompileCtx::is_deletable_global_builtin(name), "不可删名误入可删集:{name}");
|
||||
}
|
||||
assert!(!crate::CompileCtx::is_deletable_global_builtin("$262"), "不可删名误入可删集:$262");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,8 @@ impl Emitter {
|
||||
fn emit_string_literal_expression(
|
||||
&self, s: &oxide_parser::StringLiteral, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
let idx = ctx.add_constant(Constant::String(s.value.to_string()));
|
||||
let idx = ctx
|
||||
.add_constant(Constant::String(crate::shared::string_pool::pool_key_marker(&s.value, s.lone_surrogates)));
|
||||
let r = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
|
||||
Ok(r)
|
||||
|
||||
@@ -133,10 +133,13 @@ pub(crate) fn is_array_index_str(s: &str) -> bool {
|
||||
/// (运行期非常量 / 键求值有副作用)与规范数组下标串返回 `None`,维持 DYNAMIC 路径。
|
||||
pub(crate) fn computed_const_key(expr: &Expression) -> Option<String> {
|
||||
let key = match expr {
|
||||
Expression::StringLiteral(s) => s.value.to_string(),
|
||||
Expression::TemplateLiteral(tl) if tl.expressions.is_empty() && tl.quasis.len() == 1 => {
|
||||
tl.quasis[0].value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default()
|
||||
}
|
||||
Expression::StringLiteral(s) => crate::shared::string_pool::pool_key_marker(&s.value, s.lone_surrogates),
|
||||
Expression::TemplateLiteral(tl) if tl.expressions.is_empty() && tl.quasis.len() == 1 => tl.quasis[0]
|
||||
.value
|
||||
.cooked
|
||||
.as_ref()
|
||||
.map(|c| crate::shared::string_pool::pool_key_marker(c, tl.quasis[0].lone_surrogates))
|
||||
.unwrap_or_default(),
|
||||
_ => return None,
|
||||
};
|
||||
if is_array_index_str(&key) {
|
||||
|
||||
@@ -24,7 +24,7 @@ impl Emitter {
|
||||
let ObjectPropertyKind::ObjectProperty(p) = prop else {
|
||||
unreachable!("批量前缀必为 ObjectProperty");
|
||||
};
|
||||
let name = self.class_property_name(&p.key).expect("批量前缀键为静态名");
|
||||
let name = crate::shared::string_pool::pool_key_property(&p.key).expect("批量前缀键为静态名");
|
||||
u32::from(ctx.add_constant(Constant::String(name)))
|
||||
})
|
||||
.collect();
|
||||
@@ -141,7 +141,8 @@ impl Emitter {
|
||||
));
|
||||
}
|
||||
None => {
|
||||
let idx = ctx.add_constant(Constant::String(prop_name.to_string()));
|
||||
let key = crate::shared::string_pool::pool_key_property(&p.key)?;
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
ctx.inst(Inst::define_accessor(
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::Reg(get_reg),
|
||||
@@ -155,7 +156,8 @@ impl Emitter {
|
||||
let key_reg = if computed {
|
||||
self.emit_expression(p.key.to_expression(), ctx)?
|
||||
} else {
|
||||
let idx = ctx.add_constant(Constant::String(prop_name.to_string()));
|
||||
let key = crate::shared::string_pool::pool_key_property(&p.key)?;
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(reg), idx));
|
||||
reg
|
||||
|
||||
@@ -23,12 +23,15 @@ impl Emitter {
|
||||
for quasi in &tt.quasi.quasis {
|
||||
match quasi.value.cooked.as_ref() {
|
||||
Some(c) => {
|
||||
let idx = ctx.add_constant(Constant::String(c.to_string()));
|
||||
let key = crate::shared::string_pool::pool_key_marker(c, quasi.lone_surrogates);
|
||||
let idx = ctx.add_constant(Constant::String(key));
|
||||
cooked_words.push(idx as u32);
|
||||
}
|
||||
None => cooked_words.push(0x8000_0000),
|
||||
}
|
||||
let raw_idx = ctx.add_constant(Constant::String(quasi.value.raw.to_string()));
|
||||
// raw 是源文本(保留转义原文),无 marker 语义,走普通编码。
|
||||
let raw_idx =
|
||||
ctx.add_constant(Constant::String(crate::shared::string_pool::pool_key_plain(&quasi.value.raw)));
|
||||
raw_idxs.push(raw_idx);
|
||||
}
|
||||
let site_no = ctx.next_template_site;
|
||||
|
||||
@@ -20,18 +20,20 @@ impl Emitter {
|
||||
.map(|e| self.emit_expression(e, ctx))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let quasi_const_idxs: Vec<u16> = quasis
|
||||
let mut quasi_keys: Vec<String> = Vec::with_capacity(quasis.len());
|
||||
for q in quasis {
|
||||
quasi_keys.push(match &q.value.cooked {
|
||||
Some(c) => crate::shared::string_pool::pool_key_marker(c, q.lone_surrogates),
|
||||
None => String::new(),
|
||||
});
|
||||
}
|
||||
let quasi_const_idxs: Vec<u16> = quasi_keys
|
||||
.iter()
|
||||
.map(|q| {
|
||||
let s = q.value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default();
|
||||
ctx.add_constant(Constant::String(s))
|
||||
})
|
||||
.map(|s| ctx.add_constant(Constant::String(s.clone())))
|
||||
.collect();
|
||||
|
||||
let total_len_hint: usize = quasis
|
||||
.iter()
|
||||
.map(|q| q.value.cooked.as_ref().map(|c| c.len()).unwrap_or(0))
|
||||
.sum();
|
||||
// 容量提示按单元数口径(键文本经物化解码还原单元序列)。
|
||||
let total_len_hint: usize = quasi_keys.iter().map(|k| oxide_kernel::string_forge::decode_key(k).len()).sum();
|
||||
|
||||
let mut parts = Vec::with_capacity(quasi_const_idxs.len() * 2);
|
||||
let mut expr_iter = expr_regs.iter();
|
||||
|
||||
@@ -267,7 +267,10 @@ impl Emitter {
|
||||
// 只读局部符号表,嵌套函数引用外层绑定会静默回退读全局。
|
||||
PropertyKey::Identifier(_) => self.emit_expression(key.to_expression(), ctx),
|
||||
PropertyKey::StringLiteral(s) => {
|
||||
let key_idx = ctx.add_constant(Constant::String(s.value.to_string()));
|
||||
let key_idx = ctx.add_constant(Constant::String(crate::shared::string_pool::pool_key_marker(
|
||||
&s.value,
|
||||
s.lone_surrogates,
|
||||
)));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx));
|
||||
Ok(key_reg)
|
||||
@@ -294,7 +297,7 @@ impl Emitter {
|
||||
&self, src_reg: u32, key: &PropertyKey, computed: bool, ctx: &mut CompileCtx,
|
||||
) -> Result<(u32, Option<String>, Option<u32>), String> {
|
||||
if !computed {
|
||||
let key_name = self.static_property_name(key)?;
|
||||
let key_name = crate::shared::string_pool::pool_key_property(key)?;
|
||||
let prop_reg = self.emit_object_property_read(src_reg, &key_name, ctx);
|
||||
return Ok((prop_reg, Some(key_name), None));
|
||||
}
|
||||
@@ -302,7 +305,7 @@ impl Emitter {
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(prop_reg), Operand::Reg(src_reg), Operand::None));
|
||||
// 字符串字面量计算键折叠为 IC 静态路径;标识符/数字键走 DYNAMIC。
|
||||
if let PropertyKey::StringLiteral(s) = key {
|
||||
let key_str = s.value.to_string();
|
||||
let key_str = crate::shared::string_pool::pool_key_marker(&s.value, s.lone_surrogates);
|
||||
if !is_array_index_str(&key_str) {
|
||||
let key_idx = ctx.add_constant(Constant::String(key_str));
|
||||
let key_reg = ctx.alloc_reg();
|
||||
|
||||
@@ -59,7 +59,7 @@ impl Emitter {
|
||||
&self, key: &PropertyKey, computed: bool, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
if !computed {
|
||||
let name = self.class_property_name(key)?;
|
||||
let name = crate::shared::string_pool::pool_key_property(key)?;
|
||||
let idx = ctx.add_constant(Constant::String(name));
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(reg), idx));
|
||||
@@ -72,16 +72,6 @@ impl Emitter {
|
||||
self.emit_expression(key.to_expression(), ctx)
|
||||
}
|
||||
|
||||
pub(crate) fn static_property_name(&self, key: &PropertyKey) -> Result<String, String> {
|
||||
match key {
|
||||
PropertyKey::StaticIdentifier(ident) => Ok(ident.name.as_str().to_string()),
|
||||
PropertyKey::Identifier(ident) => Ok(ident.name.as_str().to_string()),
|
||||
PropertyKey::StringLiteral(s) => Ok(s.value.to_string()),
|
||||
PropertyKey::NumericLiteral(n) => Ok(n.value.to_string()),
|
||||
_ => Err("computed destructuring keys not yet supported".into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 从类定义期 computed key 数组按 slot 取键(方法/静态字段在类定义期使用)。
|
||||
pub(crate) fn emit_class_array_key_reg(&self, slot: u8, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
let arr_reg = ctx.class_keys_reg.expect("class computed keys array missing");
|
||||
|
||||
@@ -4,3 +4,4 @@ pub mod binding;
|
||||
pub mod chain;
|
||||
pub mod class_field;
|
||||
pub mod class_key;
|
||||
pub mod string_pool;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
//! 常量池字符串键编码:池统一存无标志键编码(`oxide_kernel::string_forge::encode_key`
|
||||
//! 语义),物化侧(perm_string → `decode_key`)是其精确逆变换。
|
||||
//!
|
||||
//! 两类来源文本:
|
||||
//! - oxc marker 文本(`StringLiteral.value` / `TemplateElement.cooked`):
|
||||
//! `lone_surrogates` 置位时 marker 与键编码同构(FFFD+hex4 = 孤立 surrogate 单元、
|
||||
//! FFFD+fffd = 真实 FFFD),原样即池键;未置位时为普通文本(可含裸 FFFD),
|
||||
//! 须逃逸编码防裸 FFFD 与 surrogate marker 碰撞;
|
||||
//! - 普通文本(标识符 / 数字 / 源文本):非 FFFD 恒等,真实 FFFD 逃逸为 FFFD+"fffd"。
|
||||
|
||||
use oxide_kernel::string_forge::encode_key;
|
||||
use oxide_parser::PropertyKey;
|
||||
|
||||
/// 普通文本 → 池键:真实 FFFD 逃逸为 FFFD+"fffd",其余逐单元恒等。
|
||||
pub(crate) fn pool_key_plain(text: &str) -> String {
|
||||
let units: Vec<u16> = text.encode_utf16().collect();
|
||||
encode_key(&units)
|
||||
}
|
||||
|
||||
/// oxc marker 文本 → 池键,按 `lone_surrogates` 分派(语义见模块头注释)。
|
||||
pub(crate) fn pool_key_marker(text: &str, lone_surrogates: bool) -> String {
|
||||
if lone_surrogates {
|
||||
text.to_string()
|
||||
} else {
|
||||
pool_key_plain(text)
|
||||
}
|
||||
}
|
||||
|
||||
/// 属性键 → 池键:字符串字面量键经 marker 解码,标识符 / 数字键走普通编码。
|
||||
pub(crate) fn pool_key_property(key: &PropertyKey) -> Result<String, String> {
|
||||
match key {
|
||||
PropertyKey::StaticIdentifier(ident) => Ok(pool_key_plain(ident.name.as_str())),
|
||||
PropertyKey::Identifier(ident) => Ok(pool_key_plain(ident.name.as_str())),
|
||||
PropertyKey::StringLiteral(s) => Ok(pool_key_marker(&s.value, s.lone_surrogates)),
|
||||
PropertyKey::NumericLiteral(n) => Ok(pool_key_plain(&n.value.to_string())),
|
||||
PropertyKey::PrivateIdentifier(_) => Err("private class elements not yet supported".into()),
|
||||
_ => Err("unsupported class property key type".into()),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user