mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 全局 var 绑定 0x6F 按 CreateGlobalVarBinding 不更新既有不可写数据描述符,for-in 枚举断言修运算符优先级括号化
This commit is contained in:
@@ -1629,8 +1629,9 @@ impl Emitter {
|
||||
// 函数声明的属性由首个 sub-pass 以函数值覆盖,声明语句保持值更新语义。
|
||||
// builtin 名的全局属性运行期预存(session 绑定):序言值写是单 opcode 值
|
||||
// 混叠,直接写 undefined 会把现存值(NaN 等)抹掉,故以 builtin 镜像槽
|
||||
// (run 起点预载全局属性值)作为写入值——描述符按 CreateGlobalVarBinding
|
||||
// 翻 writable,值幂等保留;裸读走镜像、反射见预存属性。
|
||||
// (run 起点预载全局属性值)作为写入值——CreateGlobalVarBinding 不更新
|
||||
// 既有数据描述符(0x6F 不可写分支 no-op),值幂等保留;裸读走镜像、
|
||||
// 反射见预存属性。
|
||||
let gdi_var_names = self.collect_var_binding_names(&program.body);
|
||||
if !gdi_var_names.is_empty() {
|
||||
let undef_reg = self.emit_undefined(&mut ctx);
|
||||
|
||||
@@ -84,9 +84,9 @@ impl Emitter {
|
||||
ctx.init_var(bi.name.as_str());
|
||||
// 脚本顶层 var:全局对象属性同步绑定当前值(首次声明即 undefined,
|
||||
// 先前已写入则保留写入值,声明不是覆盖性赋值)。被捕获绑定的值
|
||||
// 存在 cell 而非槽,从 cell 同步。builtin 名不同步:描述符翻
|
||||
// writable 已由 GDI 序言以镜像值完成,未初始化的槽/cell 是
|
||||
// undefined,再同步会把现存值(NaN 等)抹掉。
|
||||
// 存在 cell 而非槽,从 cell 同步。builtin 名不同步:CreateGlobalVarBinding
|
||||
// 不更新既有数据描述符,GDI 序言已以镜像值同步(幂等),未初始化
|
||||
// 的槽/cell 是 undefined,再同步会把现存值(NaN 等)抹掉。
|
||||
if ctx.is_global_scope
|
||||
&& matches!(decl.kind, VariableDeclarationKind::Var)
|
||||
&& !CompileCtx::is_known_builtin(bi.name.as_str())
|
||||
|
||||
@@ -277,8 +277,10 @@ impl Vm {
|
||||
/// 定义全局 var 绑定数据属性:rd=全局对象,a=值,b=键。
|
||||
///
|
||||
/// 描述符语义(CreateGlobalVarBinding):
|
||||
/// - 已有可配置属性:保持原 enumerable/configurable、置 writable、更新值——
|
||||
/// 重复声明不漂移既有描述符(内置属性 enumerable 不泄漏、值语义不变)。
|
||||
/// - 已有可配置数据属性且不可写:no-op 返回——规范不更新既有数据描述符
|
||||
/// (writable/enumerable/configurable 与值均保持原样)。
|
||||
/// - 已有可配置属性且可写(数据或 accessor):保 enumerable/configurable、
|
||||
/// 更新值;accessor 形重定义为数据属性(既有行为,不扩面)。
|
||||
/// - 已有不可配置属性:走定义不变量检查(末尾统一路径)——描述符冲突或
|
||||
/// 不可写属性值变更时静默 no-op,否则仅更新值、描述符不变。
|
||||
/// - 属性缺失:新建可写/可枚举/不可配置数据属性。
|
||||
@@ -294,11 +296,15 @@ impl Vm {
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
let value = self.regs[a];
|
||||
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
|
||||
// 已有可配置属性:var 绑定描述符不漂移——保 enumerable/configurable,
|
||||
// 置 writable,更新值(三常量与内置对象声明撞名时值语义按规范落槽)。
|
||||
// 已有可配置属性:CreateGlobalVarBinding 不改既有数据描述符;可写数据
|
||||
// 属性仅更新值(保 e/c),accessor 形重定义为数据属性。
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
if let Some(current) = obj.prop_meta_at(pos) {
|
||||
if current.attributes.configurable() {
|
||||
// 既有数据属性不可写:不更新描述符也不更新值(规范零修改)。
|
||||
if !current.is_accessor && !current.attributes.writable() {
|
||||
return Ok(());
|
||||
}
|
||||
let attrs =
|
||||
PropAttributes::new(true, current.attributes.enumerable(), current.attributes.configurable());
|
||||
let _ = self.define_data_property(obj, prop_name_si, value, attrs);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! 全局三常量(undefined/NaN/Infinity)描述符与 var 声明交互的回归测试。
|
||||
//!
|
||||
//! 覆盖规范全局对象属性描述符 { writable:false, enumerable:false, configurable:true }:
|
||||
//! 描述符读、delete 真删、delete 后镜像裸读、var 无初始化声明的描述符翻转与值
|
||||
//! 保持(NaN/Infinity 不被抹成 undefined)、var 初始化声明的值落槽、枚举面不
|
||||
//! 泄漏、任务只读拦截与 strict TypeError、eval 路径、for-in 枚举面。
|
||||
//! 描述符读、delete 真删、delete 后镜像裸读、var 无初始化声明的描述符不漂移与值
|
||||
//! 保持(writable 不翻位、NaN/Infinity 不被抹成 undefined)、var 初始化声明的值
|
||||
//! 落槽、枚举面不泄漏、任务只读拦截与 strict TypeError、eval 路径、for-in 枚举面。
|
||||
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_parser::Allocator;
|
||||
@@ -72,14 +72,22 @@ fn delete_then_bare_read_via_mirror() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn var_no_init_flips_writable_keeps_value() {
|
||||
// #4:var 无初始化声明——CreateGlobalVarBinding 翻 writable、保 enumerable/
|
||||
// configurable,值保持(三常量现值 undefined,幂等)。
|
||||
run_truthy(
|
||||
"var undefined; var d = Object.getOwnPropertyDescriptor(globalThis, 'undefined'); \
|
||||
d.value === undefined && d.writable === true && d.enumerable === false \
|
||||
&& d.configurable === true",
|
||||
);
|
||||
fn var_no_init_descriptor_unchanged() {
|
||||
// #4:var 无初始化声明——CreateGlobalVarBinding 不更新既有数据描述符
|
||||
// (三常量 writable 保 false 不翻位),值幂等保持 ×3 常量。
|
||||
for name in CONSTANTS {
|
||||
let value_check = match name {
|
||||
"NaN" => "Number.isNaN(d.value)",
|
||||
"Infinity" => "d.value === Infinity",
|
||||
_ => "d.value === undefined",
|
||||
};
|
||||
let src = format!(
|
||||
"var {name}; var d = Object.getOwnPropertyDescriptor(globalThis, '{name}'); \
|
||||
{value_check} && d.writable === false && d.enumerable === false \
|
||||
&& d.configurable === true",
|
||||
);
|
||||
run_truthy(&src);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -156,7 +164,7 @@ fn for_in_does_not_enumerate_three_constants() {
|
||||
// #13:for-in 仅 enumerable 面——三常量 e:false 不泄漏(不变)。
|
||||
run_truthy(
|
||||
"var ks = []; for (var k in globalThis) ks.push(k); \
|
||||
ks.includes('undefined') || ks.includes('NaN') || ks.includes('Infinity') === false",
|
||||
(ks.includes('undefined') || ks.includes('NaN') || ks.includes('Infinity')) === false",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user