@@ -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(>_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(>, 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,
0 commit comments