mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: var 解构声明叶名递归预声明入槽
This commit is contained in:
Generated
+18
-18
@@ -1095,7 +1095,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_builtins"
|
name = "oxide_builtins"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -1120,7 +1120,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_bytecode"
|
name = "oxide_bytecode"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1128,7 +1128,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cfg"
|
name = "oxide_cfg"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_emit",
|
"oxide_emit",
|
||||||
@@ -1139,7 +1139,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cli"
|
name = "oxide_cli"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@@ -1159,7 +1159,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_code_cache"
|
name = "oxide_code_cache"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru",
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
@@ -1168,7 +1168,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_compiler"
|
name = "oxide_compiler"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1187,7 +1187,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_dce"
|
name = "oxide_dce"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1201,7 +1201,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_emit"
|
name = "oxide_emit"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
@@ -1216,7 +1216,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_ir"
|
name = "oxide_ir"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1225,7 +1225,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_kernel"
|
name = "oxide_kernel"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_liveness"
|
name = "oxide_liveness"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1252,7 +1252,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_log"
|
name = "oxide_log"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
@@ -1261,7 +1261,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_parser"
|
name = "oxide_parser"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
@@ -1276,7 +1276,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_regalloc"
|
name = "oxide_regalloc"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1292,7 +1292,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_runtime_api"
|
name = "oxide_runtime_api"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -1304,7 +1304,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_test262"
|
name = "oxide_test262"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_types"
|
name = "oxide_types"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
@@ -1333,7 +1333,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_vm"
|
name = "oxide_vm"
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0"
|
version = "0.6.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -66,6 +66,39 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 递归预声明 var 解构 pattern 内的全部绑定名(数组/对象/默认值/rest 叶名),
|
||||||
|
/// 使解构声明的叶名进入函数/全局 var 预声明集合,入口实例化得以到达它们。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - 叶名走 `predeclare_var_name` 漏斗,保留顶层内置名镜像槽特判,与单绑定
|
||||||
|
/// 名行为一致;同 scope 既有 var 同名时经 `declare_initialized` 幂等复用槽。
|
||||||
|
fn predeclare_var_pattern(&self, pattern: &BindingPattern, ctx: &mut CompileCtx) {
|
||||||
|
match pattern {
|
||||||
|
BindingPattern::BindingIdentifier(bi) => {
|
||||||
|
self.predeclare_var_name(bi.name.as_str(), ctx);
|
||||||
|
}
|
||||||
|
BindingPattern::ArrayPattern(ap) => {
|
||||||
|
for e in ap.elements.iter().flatten() {
|
||||||
|
self.predeclare_var_pattern(e, ctx);
|
||||||
|
}
|
||||||
|
if let Some(rest) = &ap.rest {
|
||||||
|
self.predeclare_var_pattern(&rest.argument, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BindingPattern::ObjectPattern(op) => {
|
||||||
|
for prop in &op.properties {
|
||||||
|
self.predeclare_var_pattern(&prop.value, ctx);
|
||||||
|
}
|
||||||
|
if let Some(rest) = &op.rest {
|
||||||
|
self.predeclare_var_pattern(&rest.argument, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BindingPattern::AssignmentPattern(ap) => {
|
||||||
|
self.predeclare_var_pattern(&ap.left, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 预声明语句列表中的全部 `var` 绑定,使先发的提升函数声明能解析它们。
|
/// 预声明语句列表中的全部 `var` 绑定,使先发的提升函数声明能解析它们。
|
||||||
/// 顶层 `var` 名在编译闭包捕获它的函数体时必须可见。
|
/// 顶层 `var` 名在编译闭包捕获它的函数体时必须可见。
|
||||||
pub(crate) fn predeclare_var_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
pub(crate) fn predeclare_var_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
|
||||||
@@ -76,9 +109,7 @@ impl Emitter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for d in &decl.declarations {
|
for d in &decl.declarations {
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
self.predeclare_var_pattern(&d.id, ctx);
|
||||||
self.predeclare_var_name(bi.name.as_str(), ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::BlockStatement(bs) => self.predeclare_var_declarations(&bs.body, ctx),
|
Statement::BlockStatement(bs) => self.predeclare_var_declarations(&bs.body, ctx),
|
||||||
@@ -98,9 +129,7 @@ impl Emitter {
|
|||||||
if let Some(oxide_parser::ForStatementInit::VariableDeclaration(decl)) = &fs.init {
|
if let Some(oxide_parser::ForStatementInit::VariableDeclaration(decl)) = &fs.init {
|
||||||
if matches!(decl.kind, VariableDeclarationKind::Var) {
|
if matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||||
for d in &decl.declarations {
|
for d in &decl.declarations {
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
self.predeclare_var_pattern(&d.id, ctx);
|
||||||
self.predeclare_var_name(bi.name.as_str(), ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,9 +142,7 @@ impl Emitter {
|
|||||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(decl) = &fi.left {
|
if let oxide_parser::ForStatementLeft::VariableDeclaration(decl) = &fi.left {
|
||||||
if matches!(decl.kind, VariableDeclarationKind::Var) {
|
if matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||||
for d in &decl.declarations {
|
for d in &decl.declarations {
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
self.predeclare_var_pattern(&d.id, ctx);
|
||||||
self.predeclare_var_name(bi.name.as_str(), ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,9 +152,7 @@ impl Emitter {
|
|||||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(decl) = &fo.left {
|
if let oxide_parser::ForStatementLeft::VariableDeclaration(decl) = &fo.left {
|
||||||
if matches!(decl.kind, VariableDeclarationKind::Var) {
|
if matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||||
for d in &decl.declarations {
|
for d in &decl.declarations {
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
self.predeclare_var_pattern(&d.id, ctx);
|
||||||
self.predeclare_var_name(bi.name.as_str(), ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,9 +184,7 @@ impl Emitter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for d in &decl.declarations {
|
for d in &decl.declarations {
|
||||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
self.predeclare_var_pattern(&d.id, ctx);
|
||||||
self.predeclare_var_name(bi.name.as_str(), ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
//! for-in/for-of 解构 `var` 头叶名预声明:解构声明的绑定名须全部进入
|
||||||
|
//! 函数/全局 var 预声明集合,入口实例化到达叶名,零迭代后读见 undefined,
|
||||||
|
//! 不得误写外层同名继承槽、不得取到寄存器残留。
|
||||||
|
//!
|
||||||
|
//! 覆盖:顶层误写主形、外层函数域同名、for-of/for-in 主形与孪生、同 scope
|
||||||
|
//! 复用绑定、1 迭代覆写、默认值叶、嵌套叶、捕获叶、顶层形、数组/对象 rest、
|
||||||
|
//! 深层嵌套叶。
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use oxide_compiler::compiler::Compiler;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
use oxide_vm::vm::Vm;
|
||||||
|
|
||||||
|
fn eval(source: &str) -> Result<JsValue, String> {
|
||||||
|
let allocator = oxide_parser::Allocator::default();
|
||||||
|
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
|
||||||
|
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
vm.run(&Arc::new(module))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn truthy(source: &str) {
|
||||||
|
let result = eval(source).unwrap_or_else(|e| panic!("{source} -> {e}"));
|
||||||
|
assert!(result.as_bool(), "{source} -> 期望 true,得 {:?}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn top_level_same_name_not_clobbered() {
|
||||||
|
// 主红形:函数内解构 var 头叶名与顶层 var 同名,零迭代后顶层值不被误写。
|
||||||
|
truthy("var x = 5; function f(){ for (var {x} of []) {} } f(); x === 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn outer_function_scope_same_name_is_undefined() {
|
||||||
|
// 外层函数域同名(仅内层声明、未赋值):叶名建本地槽,读见 undefined。
|
||||||
|
truthy("function f(){ function g(){ var x = 5; } for (var {x} of []) {} return typeof x } f() === 'undefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_of_dstr_object_head_zero_iterations_is_undefined() {
|
||||||
|
// for-of 对象解构头零迭代:叶名本地槽入口实例化,读见 undefined。
|
||||||
|
truthy("function f(){ for (var {x} of []) {} return typeof x } f() === 'undefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_in_dstr_array_head_zero_iterations_is_undefined() {
|
||||||
|
// for-in 数组解构头零迭代(空可枚举):孪生形,读见 undefined。
|
||||||
|
truthy("function f(){ for (var [x] in {}) {} return typeof x } f() === 'undefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn same_scope_reuse_binding_keeps_value() {
|
||||||
|
// 同 scope 既有 var 同名:叶名经幂等复用同槽,声明点值保留。
|
||||||
|
truthy("function f(){ var x = 5; for (var {x} of []) {} return typeof x } f() === 'number'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_iteration_overwrites_entry_init() {
|
||||||
|
// 1 迭代覆写:绑定点写入覆盖入口 undefined,读见迭代值。
|
||||||
|
truthy("function f(){ for (var {x} of [{x:1}]) {} return typeof x } f() === 'number'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_value_leaf_is_undefined() {
|
||||||
|
// 默认值叶零迭代:默认值不触发,读见 undefined。
|
||||||
|
truthy("function f(){ for (var {x = 1} of []) {} return typeof x } f() === 'undefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_leaf_is_undefined() {
|
||||||
|
// 嵌套对象解构叶(递归深度):读见 undefined。
|
||||||
|
truthy("function f(){ for (var {a: {b}} of []) {} return typeof b } f() === 'undefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn captured_leaf_is_undefined() {
|
||||||
|
// 捕获叶:闭包经 cell 入口实例化,读见 undefined。
|
||||||
|
truthy(
|
||||||
|
"function f(){ for (var {x} of []) {} var g = function(){ return typeof x }; return g() } f() === 'undefined'",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn top_level_dstr_head_not_clobbered() {
|
||||||
|
// 顶层形:顶层解构 var 头零迭代,顶层值保留。
|
||||||
|
truthy("var x = 5; for (var {x} of []) {} x === 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_rest_leaf_is_undefined() {
|
||||||
|
// 数组解构 rest 叶:元素与 rest 均见 undefined。
|
||||||
|
truthy("function f(){ for (var [a, ...b] of []) {} return typeof a + typeof b } f() === 'undefinedundefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_rest_leaf_is_undefined() {
|
||||||
|
// 对象解构 rest 叶:具名与 rest 均见 undefined。
|
||||||
|
truthy("function f(){ for (var {x, ...r} of []) {} return typeof x + typeof r } f() === 'undefinedundefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deep_nested_array_leaf_is_undefined() {
|
||||||
|
// 深层嵌套数组解构叶:内外叶均见 undefined。
|
||||||
|
truthy("function f(){ for (var [[a], b] of []) {} return typeof a + typeof b } f() === 'undefinedundefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_in_object_head_zero_iterations_is_undefined() {
|
||||||
|
// for-in 对象解构头零迭代(空可枚举):读见 undefined。
|
||||||
|
truthy("function f(){ for (var {x} in {}) {} return typeof x } f() === 'undefined'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn for_of_array_head_zero_iterations_is_undefined() {
|
||||||
|
// for-of 数组解构头零迭代:读见 undefined。
|
||||||
|
truthy("function f(){ for (var [x] of []) {} return typeof x } f() === 'undefined'");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user