diff --git a/node-graph/libraries/graphic-types/src/graphic.rs b/node-graph/libraries/graphic-types/src/graphic.rs index a4a2488a0e..2a8e000ad9 100644 --- a/node-graph/libraries/graphic-types/src/graphic.rs +++ b/node-graph/libraries/graphic-types/src/graphic.rs @@ -1307,10 +1307,55 @@ fn deep_repark_graphic_list(value: &dyn core_types::list::AnyAttributeValue, are Some(Some(Box::new(Some(list)))) } +/// The promote for graphic-list fields, the deep field glue's third half: the +/// owned halves copy content groups out to the owned form and replay them back, +/// while this maps the content transient-to-persistent in one pass. +/// +/// THE TWO-LEVEL SHARING LAW: the field's own header is not provenance-shared. +/// It moves where the content is group-free, since the payload then owns all of +/// its content and the transient arena confirms the reference is its own park, +/// and otherwise it clones into a fresh persistent park. One level inside, a +/// content group's interior is arena-resident and its provenance is decidable, +/// so it takes [`map_groups_to_persistent`]'s Cow dispatch: an interior the +/// persistent region already holds is shared pointer for pointer. +/// +/// # Safety +/// `src` must point at a live parked graphic-list field, and `dst` at the field +/// slot the promoted reference is written to. +unsafe fn promote_graphic_list(src: *const u8, dst: *mut u8, promotion: &core_types::record::Promotion<'_>) -> Option<()> { + // SAFETY: the caller's contract; the slot holds one optional reference. + let Some(list) = (unsafe { src.cast::>>>().read() }) else { + // SAFETY: as above, into the promoted image's own field slot. + unsafe { dst.cast::>>>().write(None) }; + return Some(()); + }; + if !list_contains_groups(list) { + // SAFETY: a group-free list owns all of its content, and the arena + // declines a reference that is not a park of its own. + if let Some(moved) = unsafe { promotion.move_park::>>(std::ptr::from_ref(list).cast(), 0) } { + // SAFETY: the move published a live list in the persistent region. + unsafe { dst.cast::>>>().write(Some(&*moved)) }; + return Some(()); + } + } + let mut promoted = list.clone(); + for element in promoted.iter_element_values_mut() { + *element = map_groups_to_persistent(element, promotion)?; + } + // SAFETY: every borrow the clone carried was replaced by persistent content + // above, so the erased form outlives the evaluation. + let promoted = unsafe { core_types::record::erase_static(promoted) }; + let (parked, _) = promotion.persistent().alloc(promoted)?; + // SAFETY: the slot holds one optional reference. + unsafe { dst.cast::>>>().write(Some(parked)) }; + Some(()) +} + const _: () = { fn register_all() { core_types::record::register_deep_element_clone::(deep_clone_graphic, deep_repark_graphic); core_types::record::register_deep_field_value::>>(deep_clone_graphic_list, deep_repark_graphic_list); + core_types::record::register_field_promote::>>>(promote_graphic_list); core_types::record::register_element_promote::(promote_graphic); core_types::record::register_retained_heap::(|value| value.downcast_ref::().map_or(0, graphic_retained_heap)); core_types::record::register_retained_heap::(|value| value.downcast_ref::().map_or(0, vector_retained_heap));