Skip to content

Commit 04387ee

Browse files
committed
Implement hoisting, TDZ, and strict mode assignment checks
1 parent 012357a commit 04387ee

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

src/core/eval.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ fn hoist_var_declarations<'gc>(
126126
// We need to wrap it in a slice to recurse
127127
hoist_var_declarations(mc, env, std::slice::from_ref(stmt))?;
128128
}
129+
StatementKind::Export(_, Some(decl)) => {
130+
hoist_var_declarations(mc, env, std::slice::from_ref(decl))?;
131+
}
129132
_ => {}
130133
}
131134
}
@@ -145,6 +148,39 @@ fn hoist_declarations<'gc>(mc: &MutationContext<'gc>, env: &JSObjectDataPtr<'gc>
145148
// 2. Hoist Var declarations (recursively)
146149
hoist_var_declarations(mc, env, statements)?;
147150

151+
// 3. Hoist Lexical declarations (let, const, class) - top-level only, initialize to Uninitialized (TDZ)
152+
for stmt in statements {
153+
match &stmt.kind {
154+
StatementKind::Let(decls) => {
155+
for (name, _) in decls {
156+
env_set(mc, env, name, Value::Uninitialized)?;
157+
}
158+
}
159+
StatementKind::Const(decls) => {
160+
for (name, _) in decls {
161+
env_set(mc, env, name, Value::Uninitialized)?;
162+
}
163+
}
164+
StatementKind::Class(name, ..) => {
165+
env_set(mc, env, name, Value::Uninitialized)?;
166+
}
167+
StatementKind::LetDestructuringArray(pattern, _) | StatementKind::ConstDestructuringArray(pattern, _) => {
168+
let mut names = Vec::new();
169+
collect_names_from_destructuring(pattern, &mut names);
170+
for name in names {
171+
env_set(mc, env, &name, Value::Uninitialized)?;
172+
}
173+
}
174+
StatementKind::LetDestructuringObject(pattern, _) | StatementKind::ConstDestructuringObject(pattern, _) => {
175+
let mut names = Vec::new();
176+
collect_names_from_object_destructuring(pattern, &mut names);
177+
for name in names {
178+
env_set(mc, env, &name, Value::Uninitialized)?;
179+
}
180+
}
181+
_ => {}
182+
}
183+
}
148184
Ok(())
149185
}
150186

@@ -811,7 +847,11 @@ fn evaluate_var<'gc>(_mc: &MutationContext<'gc>, env: &JSObjectDataPtr<'gc>, nam
811847
let mut current_opt = Some(*env);
812848
while let Some(current_env) = current_opt {
813849
if let Some(val_ptr) = env_get(&current_env, name) {
814-
return Ok(val_ptr.borrow().clone());
850+
let val = val_ptr.borrow().clone();
851+
if let Value::Uninitialized = val {
852+
return Err(raise_reference_error!(format!("Cannot access '{}' before initialization", name)));
853+
}
854+
return Ok(val);
815855
}
816856
current_opt = current_env.borrow().prototype;
817857
}

src/core/value.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,10 @@ pub fn env_set_recursive<'gc>(mc: &MutationContext<'gc>, env: &JSObjectDataPtr<'
421421
if let Some(parent_rc) = parent_opt {
422422
current = parent_rc;
423423
} else {
424-
return env_set(mc, env, key, val);
424+
// Reached global scope (or end of chain) and variable not found.
425+
// In strict mode, this is a ReferenceError.
426+
// Since our engine is strict-only, we should error here instead of creating a global.
427+
return Err(crate::raise_reference_error!(format!("{} is not defined", key)));
425428
}
426429
}
427430
}

0 commit comments

Comments
 (0)