Skip to content

Commit ecb7580

Browse files
committed
Fix getter arguments, eval var configurable, class strictness, Object.assign Throw=true
- Getter arguments: Provide empty arguments object in call_accessor for Value::Getter so sloppy-mode getters have 'arguments' defined (fixes 4 Iterator.zip/zipKeyed padding-iteration tests) - Direct eval var configurable: Pass is_eval=true in IsDirectEval hoist path so global var bindings are created as configurable per EvalDeclarationInstantiation §19.2.1.3 - Class body strictness: Set class_env to strict mode in create_class_object per §14.6 (class bodies are always strict code) - Object.assign Throw=true: Enforce strict mode in eval.rs Object.assign inline implementation so [[Set]] failures on non-extensible/sealed targets throw TypeError per §22.1.2.1 step 5.e.iv.2
1 parent b076ec0 commit ecb7580

File tree

2 files changed

+72
-45
lines changed

2 files changed

+72
-45
lines changed

src/core/eval.rs

Lines changed: 70 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2839,8 +2839,10 @@ pub fn evaluate_statements_with_labels<'gc>(
28392839
env_set_strictness(mc, &eval_lex_env, true)?;
28402840
}
28412841

2842-
// Hoist var/function declarations to caller's env (skip lexicals)
2843-
hoist_declarations(mc, env, statements, true, false)?;
2842+
// Hoist var/function declarations to caller's env (skip lexicals).
2843+
// Pass is_eval=true so global var bindings are created as
2844+
// configurable per EvalDeclarationInstantiation §19.2.1.3.
2845+
hoist_declarations(mc, env, statements, true, true)?;
28442846

28452847
// Hoist let/const/class into the fresh eval lex env
28462848
for stmt in statements {
@@ -13150,63 +13152,79 @@ pub fn evaluate_call_dispatch<'gc>(
1315013152
},
1315113153
};
1315213154

13153-
for src_expr in eval_args.iter().skip(1) {
13154-
if matches!(src_expr, Value::Undefined | Value::Null) {
13155-
continue;
13156-
}
13157-
13158-
let source_obj = match src_expr {
13159-
Value::Object(o) => *o,
13160-
other => match crate::js_class::handle_object_constructor(mc, std::slice::from_ref(other), env)? {
13161-
Value::Object(o) => o,
13162-
_ => return Err(raise_type_error!("Cannot convert value to object").into()),
13163-
},
13164-
};
13165-
13166-
let ordered = if let Some(proxy_cell) = crate::core::slot_get(&source_obj, &InternalSlot::Proxy)
13167-
&& let Value::Proxy(proxy) = &*proxy_cell.borrow()
13168-
{
13169-
crate::js_proxy::proxy_own_keys(mc, proxy)?
13170-
} else {
13171-
crate::core::ordinary_own_property_keys_mc(mc, &source_obj)?
13172-
};
13155+
// Object.assign uses [[Set]] with Throw=true (§22.1.2.1 step 5.e.iv.2),
13156+
// so property-set failures must always throw TypeError.
13157+
let was_strict = crate::core::env_get_strictness(env);
13158+
if !was_strict {
13159+
crate::core::env_set_strictness(mc, env, true)?;
13160+
}
1317313161

13174-
for key in ordered {
13175-
if key == "__proto__".into() {
13162+
let assign_result: Result<Value<'gc>, EvalError<'gc>> = (|| {
13163+
for src_expr in eval_args.iter().skip(1) {
13164+
if matches!(src_expr, Value::Undefined | Value::Null) {
1317613165
continue;
1317713166
}
1317813167

13179-
let is_enumerable = if let Some(proxy_cell) = crate::core::slot_get(&source_obj, &InternalSlot::Proxy)
13168+
let source_obj = match src_expr {
13169+
Value::Object(o) => *o,
13170+
other => match crate::js_class::handle_object_constructor(mc, std::slice::from_ref(other), env)? {
13171+
Value::Object(o) => o,
13172+
_ => return Err(raise_type_error!("Cannot convert value to object").into()),
13173+
},
13174+
};
13175+
13176+
let ordered = if let Some(proxy_cell) = crate::core::slot_get(&source_obj, &InternalSlot::Proxy)
1318013177
&& let Value::Proxy(proxy) = &*proxy_cell.borrow()
1318113178
{
13182-
match crate::js_proxy::proxy_get_own_property_is_enumerable(mc, proxy, &key)? {
13183-
Some(en) => en,
13184-
None => continue,
13185-
}
13179+
crate::js_proxy::proxy_own_keys(mc, proxy)?
1318613180
} else {
13187-
if crate::core::get_own_property(&source_obj, &key).is_none() {
13181+
crate::core::ordinary_own_property_keys_mc(mc, &source_obj)?
13182+
};
13183+
13184+
for key in ordered {
13185+
if key == "__proto__".into() {
1318813186
continue;
1318913187
}
13190-
source_obj.borrow().is_enumerable(&key)
13191-
};
1319213188

13193-
if !is_enumerable {
13194-
continue;
13195-
}
13189+
let is_enumerable = if let Some(proxy_cell) = crate::core::slot_get(&source_obj, &InternalSlot::Proxy)
13190+
&& let Value::Proxy(proxy) = &*proxy_cell.borrow()
13191+
{
13192+
match crate::js_proxy::proxy_get_own_property_is_enumerable(mc, proxy, &key)? {
13193+
Some(en) => en,
13194+
None => continue,
13195+
}
13196+
} else {
13197+
if crate::core::get_own_property(&source_obj, &key).is_none() {
13198+
continue;
13199+
}
13200+
source_obj.borrow().is_enumerable(&key)
13201+
};
1319613202

13197-
let prop_value = crate::core::get_property_with_accessors(mc, env, &source_obj, &key)?;
13198-
crate::core::set_property_with_accessors(
13199-
mc,
13200-
env,
13201-
&target_obj,
13202-
key,
13203-
&prop_value,
13204-
Some(&Value::Object(target_obj)),
13205-
)?;
13203+
if !is_enumerable {
13204+
continue;
13205+
}
13206+
13207+
let prop_value = crate::core::get_property_with_accessors(mc, env, &source_obj, &key)?;
13208+
crate::core::set_property_with_accessors(
13209+
mc,
13210+
env,
13211+
&target_obj,
13212+
key,
13213+
&prop_value,
13214+
Some(&Value::Object(target_obj)),
13215+
)?;
13216+
}
1320613217
}
13218+
13219+
Ok(Value::Object(target_obj))
13220+
})();
13221+
13222+
// Restore env strictness
13223+
if !was_strict {
13224+
crate::core::env_set_strictness(mc, env, was_strict)?;
1320713225
}
1320813226

13209-
Ok(Value::Object(target_obj))
13227+
assign_result
1321013228
} else {
1321113229
Ok(crate::js_object::handle_object_method(mc, suffix, eval_args, env)?)
1321213230
}
@@ -21122,6 +21140,13 @@ pub(crate) fn call_accessor<'gc>(
2112221140
call_env.borrow_mut(mc).prototype = Some(*captured_env);
2112321141
call_env.borrow_mut(mc).is_function_scope = true;
2112421142
object_set_key_value(mc, &call_env, "this", &Value::Object(*receiver))?;
21143+
// Getters are ordinary functions — provide an `arguments` object
21144+
// (empty, since getters receive no arguments) for sloppy-mode code.
21145+
{
21146+
let args_obj = crate::core::new_js_object_data(mc);
21147+
object_set_key_value(mc, &args_obj, "length", &Value::Number(0.0))?;
21148+
object_set_key_value(mc, &call_env, "arguments", &Value::Object(args_obj))?;
21149+
}
2112521150
// If the getter carried a home object, propagate it into call env so `super` resolves
2112621151
call_env.borrow_mut(mc).set_home_object(home_opt.clone());
2112721152
let body_clone = body.clone();

src/js_class.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,8 @@ pub(crate) fn create_class_object<'gc>(
22692269
// Create class environment for private names
22702270
let class_env = new_js_object_data(mc);
22712271
class_env.borrow_mut(mc).prototype = Some(*env);
2272+
// Class bodies are always strict (§14.6 — A ClassBody is always strict code)
2273+
crate::core::env_set_strictness(mc, &class_env, true)?;
22722274

22732275
if !name.is_empty() {
22742276
object_set_key_value(mc, &class_env, name, &Value::Uninitialized)?;

0 commit comments

Comments
 (0)