Skip to content

Commit 16475a1

Browse files
ssrliveCopilot
andcommitted
Fix descriptor-aware property regressions
- preserve global descriptor attributes during eval/global writeback - materialize function and closure descriptor reads for super/property access - fix object literal __proto__ semantics for special form, shorthand, and accessors - use descriptor-aware prototype/species lookups for BigInt, DataView, and TypedArray setup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9e488e4 commit 16475a1

6 files changed

Lines changed: 254 additions & 53 deletions

File tree

src/core/compiler.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5012,12 +5012,12 @@ impl<'gc> Compiler<'gc> {
50125012
if let Some(ip) = effective_ip {
50135013
self.chunk.fn_names.entry(ip).or_insert_with(|| key_name.clone());
50145014
}
5015-
let idx = self.chunk.add_constant(Value::String(s.clone()));
5016-
self.chunk.write_opcode(if is_proto_colon {
5017-
Opcode::SetProperty
5015+
let idx = if is_proto_colon {
5016+
self.chunk.add_constant(Value::from("__own_data___proto__"))
50185017
} else {
5019-
Opcode::InitProperty
5020-
});
5018+
self.chunk.add_constant(Value::String(s.clone()))
5019+
};
5020+
self.chunk.write_opcode(Opcode::InitProperty);
50215021
self.chunk.write_u16(idx);
50225022
self.chunk.write_opcode(Opcode::Pop);
50235023
continue;

src/core/vm.rs

Lines changed: 160 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13044,7 +13044,53 @@ impl<'gc> VM<'gc> {
1304413044
if own_is_data && !is_frozen && !is_readonly {
1304513045
drop(borrow);
1304613046
let mut b = map.borrow_mut(ctx);
13047-
if key == "__proto__" {
13047+
if let Some(base_key) = key.strip_prefix(GETTER_PREFIX) {
13048+
if base_key == "__proto__" {
13049+
let attrs = match b.get(OWN_DUNDER_PROTO_DATA_KEY) {
13050+
Some(Value::Property { attrs, .. }) => *attrs,
13051+
Some(_) => attrs_from_legacy_map(&b, "__proto__"),
13052+
None => PropAttrs::EC,
13053+
};
13054+
let current_setter = match b.get(OWN_DUNDER_PROTO_DATA_KEY) {
13055+
Some(Value::Property { setter: Some(setter), .. }) => Some((**setter).clone()),
13056+
_ => None,
13057+
};
13058+
b.insert(
13059+
OWN_DUNDER_PROTO_DATA_KEY.to_string(),
13060+
Value::Property {
13061+
value: None,
13062+
getter: Some(Box::new(val.clone())),
13063+
setter: current_setter.map(Box::new),
13064+
attrs,
13065+
},
13066+
);
13067+
} else {
13068+
set_getter(&mut b, base_key, val.clone());
13069+
}
13070+
} else if let Some(base_key) = key.strip_prefix(SETTER_PREFIX) {
13071+
if base_key == "__proto__" {
13072+
let attrs = match b.get(OWN_DUNDER_PROTO_DATA_KEY) {
13073+
Some(Value::Property { attrs, .. }) => *attrs,
13074+
Some(_) => attrs_from_legacy_map(&b, "__proto__"),
13075+
None => PropAttrs::EC,
13076+
};
13077+
let current_getter = match b.get(OWN_DUNDER_PROTO_DATA_KEY) {
13078+
Some(Value::Property { getter: Some(getter), .. }) => Some((**getter).clone()),
13079+
_ => None,
13080+
};
13081+
b.insert(
13082+
OWN_DUNDER_PROTO_DATA_KEY.to_string(),
13083+
Value::Property {
13084+
value: None,
13085+
getter: current_getter.map(Box::new),
13086+
setter: Some(Box::new(val.clone())),
13087+
attrs,
13088+
},
13089+
);
13090+
} else {
13091+
set_setter(&mut b, base_key, val.clone());
13092+
}
13093+
} else if key == "__proto__" {
1304813094
b.insert(OWN_DUNDER_PROTO_DATA_KEY.to_string(), val.clone());
1304913095
} else if own_is_inline_data {
1305013096
if let Some(Value::Property { value, .. }) = b.get_mut(key) {
@@ -13161,9 +13207,51 @@ impl<'gc> VM<'gc> {
1316113207
} else {
1316213208
let mut b = map.borrow_mut(ctx);
1316313209
if let Some(base_key) = key.strip_prefix(GETTER_PREFIX) {
13164-
set_getter(&mut b, base_key, val.clone());
13210+
if base_key == "__proto__" {
13211+
let attrs = match b.get(OWN_DUNDER_PROTO_DATA_KEY) {
13212+
Some(Value::Property { attrs, .. }) => *attrs,
13213+
Some(_) => attrs_from_legacy_map(&b, "__proto__"),
13214+
None => PropAttrs::EC,
13215+
};
13216+
let current_setter = match b.get(OWN_DUNDER_PROTO_DATA_KEY) {
13217+
Some(Value::Property { setter: Some(setter), .. }) => Some((**setter).clone()),
13218+
_ => None,
13219+
};
13220+
b.insert(
13221+
OWN_DUNDER_PROTO_DATA_KEY.to_string(),
13222+
Value::Property {
13223+
value: None,
13224+
getter: Some(Box::new(val.clone())),
13225+
setter: current_setter.map(Box::new),
13226+
attrs,
13227+
},
13228+
);
13229+
} else {
13230+
set_getter(&mut b, base_key, val.clone());
13231+
}
1316513232
} else if let Some(base_key) = key.strip_prefix(SETTER_PREFIX) {
13166-
set_setter(&mut b, base_key, val.clone());
13233+
if base_key == "__proto__" {
13234+
let attrs = match b.get(OWN_DUNDER_PROTO_DATA_KEY) {
13235+
Some(Value::Property { attrs, .. }) => *attrs,
13236+
Some(_) => attrs_from_legacy_map(&b, "__proto__"),
13237+
None => PropAttrs::EC,
13238+
};
13239+
let current_getter = match b.get(OWN_DUNDER_PROTO_DATA_KEY) {
13240+
Some(Value::Property { getter: Some(getter), .. }) => Some((**getter).clone()),
13241+
_ => None,
13242+
};
13243+
b.insert(
13244+
OWN_DUNDER_PROTO_DATA_KEY.to_string(),
13245+
Value::Property {
13246+
value: None,
13247+
getter: current_getter.map(Box::new),
13248+
setter: Some(Box::new(val.clone())),
13249+
attrs,
13250+
},
13251+
);
13252+
} else {
13253+
set_setter(&mut b, base_key, val.clone());
13254+
}
1316713255
} else if key == "__proto__" {
1316813256
b.insert(OWN_DUNDER_PROTO_DATA_KEY.to_string(), val.clone());
1316913257
} else {
@@ -13721,8 +13809,13 @@ impl<'gc> VM<'gc> {
1372113809
let props = self.get_fn_props(ctx, *ip, *arity);
1372213810
// For VmClosure with overlay, check overlay for setter/getter first
1372313811
let overlay = self.get_closure_overlay(obj);
13812+
let current_desc = overlay
13813+
.as_ref()
13814+
.and_then(|o| desc_from_legacy_map(&o.borrow(), key))
13815+
.or_else(|| desc_from_legacy_map(&props.borrow(), key));
1372413816
// Check for setter accessor
1372513817
let setter_fn = overlay
13818+
.as_ref()
1372613819
.and_then(|o| lookup_setter(&o.borrow(), key).cloned())
1372713820
.or_else(|| lookup_setter(&props.borrow(), key).cloned());
1372813821
if let Some(sf) = setter_fn {
@@ -13754,9 +13847,31 @@ impl<'gc> VM<'gc> {
1375413847
}
1375513848
// Write to per-closure overlay when available, otherwise shared fn_props
1375613849
if let Some(o) = overlay {
13757-
o.borrow_mut(ctx).insert(key.to_string(), val.clone());
13850+
let mut borrow = o.borrow_mut(ctx);
13851+
if let Some(base_key) = key.strip_prefix(GETTER_PREFIX) {
13852+
set_getter(&mut borrow, base_key, val.clone());
13853+
} else if let Some(base_key) = key.strip_prefix(SETTER_PREFIX) {
13854+
set_setter(&mut borrow, base_key, val.clone());
13855+
} else if let Some(desc) = current_desc
13856+
&& let PropKind::Data(_) = desc.kind
13857+
{
13858+
PropDesc::data(val.clone(), desc.attrs).write_to_legacy_map(&mut borrow, key);
13859+
} else {
13860+
borrow.insert(key.to_string(), val.clone());
13861+
}
1375813862
} else {
13759-
props.borrow_mut(ctx).insert(key.to_string(), val.clone());
13863+
let mut borrow = props.borrow_mut(ctx);
13864+
if let Some(base_key) = key.strip_prefix(GETTER_PREFIX) {
13865+
set_getter(&mut borrow, base_key, val.clone());
13866+
} else if let Some(base_key) = key.strip_prefix(SETTER_PREFIX) {
13867+
set_setter(&mut borrow, base_key, val.clone());
13868+
} else if let Some(desc) = current_desc
13869+
&& let PropKind::Data(_) = desc.kind
13870+
{
13871+
PropDesc::data(val.clone(), desc.attrs).write_to_legacy_map(&mut borrow, key);
13872+
} else {
13873+
borrow.insert(key.to_string(), val.clone());
13874+
}
1376013875
}
1376113876
Ok(val.clone())
1376213877
} else {
@@ -14544,8 +14659,9 @@ impl<'gc> VM<'gc> {
1454414659
if has_setter(&borrow, key) && !borrow.contains_key(&getter_key) {
1454514660
return Value::Undefined;
1454614661
}
14662+
let materialized = self.materialize_property_read_value(ctx, receiver, val);
1454714663
if key == "prototype"
14548-
&& let Value::VmObject(proto_obj) = val.clone()
14664+
&& let Value::VmObject(proto_obj) = materialized.clone()
1454914665
{
1455014666
drop(borrow);
1455114667
let needs_update = {
@@ -14564,9 +14680,9 @@ impl<'gc> VM<'gc> {
1456414680
return Value::VmObject(proto_obj);
1456514681
}
1456614682
return if let Some(rid) = realm_id {
14567-
self.remap_cross_realm_value(ctx, val, rid)
14683+
self.remap_cross_realm_value(ctx, materialized, rid)
1456814684
} else {
14569-
val
14685+
materialized
1457014686
};
1457114687
}
1457214688
if let Some(gf) = borrow.get(&getter_key).cloned() {
@@ -14606,8 +14722,9 @@ impl<'gc> VM<'gc> {
1460614722
if lookup_in(&setter_key).is_some() && lookup_in(&getter_key).is_none() {
1460714723
return Value::Undefined;
1460814724
}
14725+
let materialized = self.materialize_property_read_value(ctx, receiver, val);
1460914726
if key == "prototype"
14610-
&& let Value::VmObject(proto_obj) = val.clone()
14727+
&& let Value::VmObject(proto_obj) = materialized.clone()
1461114728
{
1461214729
let needs_update = {
1461314730
let proto_borrow = proto_obj.borrow();
@@ -14624,7 +14741,7 @@ impl<'gc> VM<'gc> {
1462414741
}
1462514742
return Value::VmObject(proto_obj);
1462614743
}
14627-
return val;
14744+
return materialized;
1462814745
}
1462914746
if let Some(gf) = lookup_in(&getter_key) {
1463014747
return self.invoke_getter_with_receiver(ctx, &gf, receiver);
@@ -15991,7 +16108,7 @@ impl<'gc> VM<'gc> {
1599116108
write_attrs_to_legacy_map(&mut array_buffer_map, "prototype", PropAttrs::empty());
1599216109
let array_buffer_ctor = Value::VmObject(new_gc_cell_ptr(ctx, array_buffer_map));
1599316110
if let Value::VmObject(ctor_obj) = &array_buffer_ctor
15994-
&& let Some(Value::VmObject(proto_obj)) = ctor_obj.borrow().get("prototype").cloned()
16111+
&& let Some(Value::VmObject(proto_obj)) = own_data_from_legacy_map(&ctor_obj.borrow(), "prototype")
1599516112
{
1599616113
proto_obj
1599716114
.borrow_mut(ctx)
@@ -16047,7 +16164,7 @@ impl<'gc> VM<'gc> {
1604716164
write_attrs_to_legacy_map(&mut shared_array_buffer_map, "prototype", PropAttrs::empty());
1604816165
let sab_ctor = Value::VmObject(new_gc_cell_ptr(ctx, shared_array_buffer_map));
1604916166
if let Value::VmObject(ctor_obj) = &sab_ctor
16050-
&& let Some(Value::VmObject(proto_obj)) = ctor_obj.borrow().get("prototype").cloned()
16167+
&& let Some(Value::VmObject(proto_obj)) = own_data_from_legacy_map(&ctor_obj.borrow(), "prototype")
1605116168
{
1605216169
proto_obj.borrow_mut(ctx).insert("constructor".to_string(), sab_ctor.clone());
1605316170
mark_nonenumerable(&mut proto_obj.borrow_mut(ctx), "constructor");
@@ -17311,7 +17428,6 @@ impl<'gc> VM<'gc> {
1731117428
async_gen_ctor.insert("name".to_string(), Value::from("AsyncGeneratorFunction"));
1731217429
write_attrs_to_legacy_map(&mut async_gen_ctor, "name", PropAttrs::CONFIGURABLE);
1731317430
write_attrs_to_legacy_map(&mut async_gen_ctor, "length", PropAttrs::CONFIGURABLE);
17314-
write_attrs_to_legacy_map(&mut async_gen_ctor, "prototype", PropAttrs::empty());
1731517431
async_gen_ctor.insert("__async_generator_function_constructor__".to_string(), Value::Boolean(true));
1731617432

1731717433
if let Some(Value::VmObject(function_ctor)) = self.globals.get("Function")
@@ -17323,9 +17439,9 @@ impl<'gc> VM<'gc> {
1732317439
let async_gen_ctor_val = Value::VmObject(new_gc_cell_ptr(ctx, async_gen_ctor));
1732417440
let async_gen_fn_proto_val = Value::VmObject(new_gc_cell_ptr(ctx, async_gen_fn_proto));
1732517441
if let Value::VmObject(ctor_obj) = &async_gen_ctor_val {
17326-
ctor_obj
17327-
.borrow_mut(ctx)
17328-
.insert("prototype".to_string(), async_gen_fn_proto_val.clone());
17442+
let mut ctor_obj = ctor_obj.borrow_mut(ctx);
17443+
ctor_obj.insert("prototype".to_string(), async_gen_fn_proto_val.clone());
17444+
write_attrs_to_legacy_map(&mut ctor_obj, "prototype", PropAttrs::empty());
1732917445
}
1733017446
if let Value::VmObject(proto_obj) = &async_gen_fn_proto_val {
1733117447
let mut pb = proto_obj.borrow_mut(ctx);
@@ -17442,9 +17558,6 @@ impl<'gc> VM<'gc> {
1744217558

1744317559
// %GeneratorPrototype% — shared prototype for generator iterator objects
1744417560
let mut gen_proto = IndexMap::new();
17445-
mark_nonenumerable(&mut gen_proto, "next");
17446-
mark_nonenumerable(&mut gen_proto, "return");
17447-
mark_nonenumerable(&mut gen_proto, "throw");
1744817561
gen_proto.insert("@@sym:4".to_string(), Value::from("Generator"));
1744917562
write_attrs_to_legacy_map(&mut gen_proto, "@@sym:4", PropAttrs::CONFIGURABLE);
1745017563
// Per spec: %GeneratorPrototype% [[Prototype]] = %IteratorPrototype%
@@ -17467,6 +17580,9 @@ impl<'gc> VM<'gc> {
1746717580
gp.insert("return".to_string(), return_fn);
1746817581
gp.insert("throw".to_string(), throw_fn);
1746917582
gp.insert("@@sym:1".to_string(), iter_fn);
17583+
mark_nonenumerable(&mut gp, "next");
17584+
mark_nonenumerable(&mut gp, "return");
17585+
mark_nonenumerable(&mut gp, "throw");
1747017586
mark_nonenumerable(&mut gp, "@@sym:1");
1747117587
}
1747217588
self.generator_prototype = Value::VmObject(gen_proto_ptr);
@@ -21443,7 +21559,11 @@ impl<'gc> VM<'gc> {
2144321559
.collect();
2144421560
let mut gt_borrow = gt.borrow_mut(ctx);
2144521561
for (k, v) in public_globals {
21446-
gt_borrow.insert(k, v);
21562+
if let Some(existing_desc) = desc_from_legacy_map(&gt_borrow, &k) {
21563+
PropDesc::data(v, existing_desc.attrs).write_to_legacy_map(&mut gt_borrow, &k);
21564+
} else {
21565+
gt_borrow.insert(k, v);
21566+
}
2144721567
}
2144821568
}
2144921569
eval_vm.script_path = self.current_source_path().map(str::to_owned).or_else(|| self.script_path.clone());
@@ -21688,15 +21808,13 @@ impl<'gc> VM<'gc> {
2168821808
// For direct eval inside a function, var declarations stay in
2168921809
// the function scope and should not leak to globalThis.
2169021810
let in_fn_scope = self.direct_eval && !self.frames.is_empty();
21691-
// CreateGlobalFunctionBinding semantics (§8.1.1.4.18)
21692-
// For eval function declarations, update property descriptors:
21693-
// if existing property is configurable, set writable + enumerable
21694-
if chunk.fn_declared_globals.contains(k) && !in_fn_scope {
21811+
if !in_fn_scope {
2169521812
let mut gt = self.global_this.borrow_mut(ctx);
21696-
gt.insert(k.clone(), adjusted);
21697-
} else if !in_fn_scope {
21698-
// Normal variable: just mirror onto globalThis
21699-
self.global_this.borrow_mut(ctx).insert(k.clone(), adjusted);
21813+
if let Some(existing_desc) = desc_from_legacy_map(&gt, k) {
21814+
PropDesc::data(adjusted, existing_desc.attrs).write_to_legacy_map(&mut gt, k);
21815+
} else {
21816+
gt.insert(k.clone(), adjusted);
21817+
}
2170021818
}
2170121819
}
2170221820
}
@@ -23758,8 +23876,20 @@ impl<'gc> VM<'gc> {
2375823876
if key == "__proto__" {
2375923877
// __proto__ data is stored under OWN_DUNDER_PROTO_DATA_KEY
2376023878
if let Some(val) = borrow.get(OWN_DUNDER_PROTO_DATA_KEY) {
23761-
let a = attrs_from_legacy_map(&borrow, &key);
23762-
return self.make_descriptor_object_from_desc(ctx, &PropDesc::data(val.clone(), a));
23879+
let desc = match val {
23880+
Value::Property { getter, setter, attrs, .. } => PropDesc {
23881+
kind: PropKind::Accessor {
23882+
get: getter.as_ref().map(|g| (**g).clone()),
23883+
set: setter.as_ref().map(|s| (**s).clone()),
23884+
},
23885+
attrs: *attrs,
23886+
},
23887+
other => {
23888+
let a = attrs_from_legacy_map(&borrow, &key);
23889+
PropDesc::data(other.clone(), a)
23890+
}
23891+
};
23892+
return self.make_descriptor_object_from_desc(ctx, &desc);
2376323893
}
2376423894
// Fall back to accessor lookup only (Value::Property or __get_/__set_).
2376523895
// Plain data at "__proto__" is the internal [[Prototype]] slot,

src/core/vm/bigint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ impl<'gc> VM<'gc> {
109109
pub(super) fn bigint_init_prototype(&mut self, ctx: &GcContext<'gc>) {
110110
let object_proto = if let Some(Value::VmObject(o)) = self.globals.get("Object").and_then(|v| {
111111
if let Value::VmObject(obj) = v {
112-
obj.borrow().get("prototype").cloned()
112+
own_data_from_legacy_map(&obj.borrow(), "prototype")
113113
} else {
114114
None
115115
}

0 commit comments

Comments
 (0)