Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.d/11861-raw-handle-11815-11843.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Fixed

- Root the raw object handles the born-final class prototype builder, the class-object template and the birth-record mint read out of their handles, so those modules carry no raw-handle debt again.
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ unsafe fn birth_record(proto: *const ObjectHeader) -> Option<(u32, u32, u32)> {
/// it was minted from.
#[cold]
#[inline(never)]
unsafe fn mint_birth_record(func_value: f64, proto: *mut ObjectHeader) -> *mut ObjectHeader {
unsafe fn mint_birth_record(func_value: f64, proto: *mut ObjectHeader) -> f64 {
let scope = crate::gc::RuntimeHandleScope::new();
let proto_handle = scope.root_raw_mut_ptr(proto);
let class_id = synthetic_class_id_for_function(func_value);
Expand All @@ -146,7 +146,7 @@ unsafe fn mint_birth_record(func_value: f64, proto: *mut ObjectHeader) -> *mut O
// GC_STORE_AUDIT(POINTER_FREE): a class id and a ShapeId, never a heap
// reference.
(*meta).instance_birth = u64::from(class_id) | u64::from(shape_id) << 32;
obj.get_raw_mut_ptr::<ObjectHeader>()
obj.with_mut_ptr::<ObjectHeader, _>(|o| crate::value::js_nanbox_pointer(o as i64))
}

/// The class `class_id`'s registered prototype moved from `old` to another
Expand Down Expand Up @@ -205,11 +205,12 @@ pub(super) unsafe fn construct_ordinary_compiled_function(
}
None => return None,
};
let obj = match birth_record(proto) {
Some((class_id, shape_id, slots)) => born_from_record(class_id, shape_id, slots),
let instance = match birth_record(proto) {
Some((class_id, shape_id, slots)) => {
crate::value::js_nanbox_pointer(born_from_record(class_id, shape_id, slots) as i64)
}
None => mint_birth_record(func_handle.get_nanbox_f64(), proto),
};
let instance = crate::value::js_nanbox_pointer(obj as i64);
Some(run_constructor_body(
func_handle.get_nanbox_f64(),
instance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ pub(super) fn decl_prototype_born_final(class_id: u32, parent_bits: u64) -> Opti
// no meta record to mirror it in. It is fresh and unobserved, so the link
// is not prototype surgery and invalidates nothing.
let proto = scope.root_raw_mut_ptr(crate::object::js_object_alloc(class_id, count));
if proto.get_raw_mut_ptr::<ObjectHeader>().is_null() {
if proto.with_mut_ptr::<ObjectHeader, _>(|p| p.is_null()) {
return None;
}
let parent_bits = parent.get_heap_word_u64();
Expand All @@ -119,16 +119,18 @@ pub(super) fn decl_prototype_born_final(class_id: u32, parent_bits: u64) -> Opti
);
}
let proto_id = crate::object::shapes::object_proto_id_for(p, parent_bits);
let stamped = crate::object::shapes::stamp_linked_final_shape(
p,
list.get_raw_mut_ptr::<crate::ArrayHeader>(),
count,
proto_id,
parent_bits,
// Slot 0 is `constructor` (a class function object, no lane);
// every method slot names its body.
|slot| slot != 0,
);
let stamped = list.with_mut_ptr::<crate::ArrayHeader, _>(|keys| {
crate::object::shapes::stamp_linked_final_shape(
p,
keys,
count,
proto_id,
parent_bits,
// Slot 0 is `constructor` (a class function object, no lane);
// every method slot names its body.
|slot| slot != 0,
)
});
if stamped {
crate::object::descriptor_state::note_attrs_born_with_keys(p as usize);
}
Expand All @@ -140,11 +142,12 @@ pub(super) fn decl_prototype_born_final(class_id: u32, parent_bits: u64) -> Opti
return None;
}
crate::gc::runtime_shade_external_edge(parent_bits);
let proto = proto.get_raw_mut_ptr::<ObjectHeader>();
super::state::class_decl_prototype_object_root_store(class_id, proto);
proto.with_mut_ptr::<ObjectHeader, _>(|p| {
super::state::class_decl_prototype_object_root_store(class_id, p)
});
#[cfg(test)]
BORN_FINAL_BUILDS.with(|n| n.set(n.get() + 1));
Some(crate::value::js_nanbox_pointer(proto as i64))
Some(proto.with_mut_ptr::<ObjectHeader, _>(|p| crate::value::js_nanbox_pointer(p as i64)))
}

#[cfg(test)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,14 @@ unsafe fn class_evaluation_prototype_value(obj: *const ObjectHeader) -> f64 {
if let Some(parent_proto) = &parent_proto {
if let Some(proto) = class.with_mut_ptr::<ObjectHeader, _>(|class| {
super::class_object_template::prototype_from_template(
&scope,
class,
class_id,
parent_proto.get_heap_word_u64(),
)
}) {
CLASS_EVALUATION_PROTOTYPES_MATERIALIZED
.store(true, std::sync::atomic::Ordering::Relaxed);
let proto = scope.root_raw_mut_ptr(proto);
let proto_value = proto
.with_mut_ptr::<ObjectHeader, _>(|p| crate::value::js_nanbox_pointer(p as i64));
class.with_mut_ptr::<ObjectHeader, _>(|class| {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -692,27 +692,29 @@ pub(crate) unsafe fn evaluation_method_value(class_id: u32, name: &str, class: f
/// in the template's final prototype shape, linked to `parent_proto`. `None`
/// when the template has no recorded prototype shape or that shape names
/// another [[Prototype]].
pub(crate) unsafe fn prototype_from_template(
pub(crate) unsafe fn prototype_from_template<'s>(
out: &'s crate::gc::RuntimeHandleScope,
class: *mut ObjectHeader,
class_id: u32,
parent_proto: u64,
) -> Option<*mut ObjectHeader> {
) -> Option<crate::gc::RuntimeHandle<'s>> {
let cell = template_cell_of(class_id)?;
let (field_count, final_shape, fills, proto_id) = cell.proto_template()?;
if crate::object::shapes::stable_linked_proto_id(class_id, parent_proto) != Some(proto_id) {
return None;
}
#[cfg(test)]
note_template_hit(true);
let scope = crate::gc::RuntimeHandleScope::new();
let class = scope.root_raw_mut_ptr(class);
let parent = scope.root_heap_word_u64(parent_proto);
let proto = scope.root_raw_mut_ptr(crate::object::js_object_alloc(class_id, field_count));
// Every handle lives in the caller's scope: the prototype handle is
// returned, and a handle cannot outlive the scope that rooted it.
let class = out.root_raw_mut_ptr(class);
let parent = out.root_heap_word_u64(parent_proto);
let proto = out.root_raw_mut_ptr(crate::object::js_object_alloc(class_id, field_count));
// The links `class_evaluation_prototype_value` makes, written into the
// prototype's meta record directly: its evaluation (lexical owner) and its
// [[Prototype]], whose identity the final shape already names.
let (meta, _) = proto.across_mut::<ObjectHeader, _>(|| {
crate::object::object_meta_ensure(proto.get_raw_mut_ptr::<ObjectHeader>())
proto.with_mut_ptr::<ObjectHeader, _>(|p| crate::object::object_meta_ensure(p))
});
let owner =
class.with_const_ptr::<ObjectHeader, _>(|c| crate::value::js_nanbox_pointer(c as i64));
Expand Down Expand Up @@ -749,7 +751,7 @@ pub(crate) unsafe fn prototype_from_template(
crate::object::slot_store::store_object_field_slot(proto, slot, bits.to_bits())
});
}
Some(proto.get_raw_mut_ptr::<ObjectHeader>())
Some(proto)
}

/// After `proto`, the prototype object of class object `class` (template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,56 +108,59 @@ fn later_evaluations_are_born_in_the_template_shapes() {
let shape = |h: &crate::gc::RuntimeHandle<'_>| {
h.with_mut_ptr::<ObjectHeader, _>(|o| crate::object::shapes::object_shape_id(o))
};
let c = classes
.iter()
.map(|h| h.get_raw_mut_ptr::<ObjectHeader>())
.collect::<Vec<_>>();
let p = protos
.iter()
.map(|h| h.get_raw_mut_ptr::<ObjectHeader>())
.collect::<Vec<_>>();
let addr =
|h: &crate::gc::RuntimeHandle<'_>| h.with_mut_ptr::<ObjectHeader, _>(|o| o as usize);
let slot_of = |h: &crate::gc::RuntimeHandle<'_>, key: &[u8]| {
h.with_mut_ptr::<ObjectHeader, _>(|o| slot(o, key))
};
assert!(
c[0] != c[1] && p[0] != p[1] && p[1] != p[2],
addr(&classes[0]) != addr(&classes[1])
&& addr(&protos[0]) != addr(&protos[1])
&& addr(&protos[1]) != addr(&protos[2]),
"one object per evaluation"
);
assert_eq!(shape(&classes[0]), shape(&classes[1]));
assert_eq!(shape(&classes[1]), shape(&classes[2]));
assert_eq!(shape(&protos[0]), shape(&protos[1]));
assert_eq!(shape(&protos[1]), shape(&protos[2]));
for i in 0..3 {
let s = slot(c[i], b"s");
assert!(
static_method_value_runs(s, info, c[i]),
"s of evaluation {i} is at home in it"
);
let m = slot(p[i], b"m");
assert!(
static_method_value_runs(m, info, c[i]),
"m of evaluation {i} is at home in it"
);
assert_eq!(
slot(p[i], b"constructor"),
crate::value::js_nanbox_pointer(c[i] as i64).to_bits(),
"prototype {i}'s constructor is its class object"
);
assert_eq!(
super::super::class_registry::class_object_own_field_bytes(
c[i],
super::class_object_props::CLASS_EVALUATION_PROTOTYPE_KEY,
)
.map(f64::to_bits),
Some(crate::value::js_nanbox_pointer(p[i] as i64).to_bits()),
"class object {i} links its own prototype"
);
classes[i].with_mut_ptr::<ObjectHeader, _>(|c| {
protos[i].with_mut_ptr::<ObjectHeader, _>(|p| {
let s = slot(c, b"s");
assert!(
static_method_value_runs(s, info, c),
"s of evaluation {i} is at home in it"
);
let m = slot(p, b"m");
assert!(
static_method_value_runs(m, info, c),
"m of evaluation {i} is at home in it"
);
assert_eq!(
slot(p, b"constructor"),
crate::value::js_nanbox_pointer(c as i64).to_bits(),
"prototype {i}'s constructor is its class object"
);
assert_eq!(
super::super::class_registry::class_object_own_field_bytes(
c,
super::class_object_props::CLASS_EVALUATION_PROTOTYPE_KEY,
)
.map(f64::to_bits),
Some(crate::value::js_nanbox_pointer(p as i64).to_bits()),
"class object {i} links its own prototype"
);
})
});
}
assert_ne!(
slot(c[1], b"s"),
slot(c[2], b"s"),
slot_of(&classes[1], b"s"),
slot_of(&classes[2], b"s"),
"statics are per evaluation"
);
assert_ne!(
slot(p[1], b"m"),
slot(p[2], b"m"),
slot_of(&protos[1], b"m"),
slot_of(&protos[2], b"m"),
"methods are per evaluation"
);
}
Expand Down
Loading