mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Assert the deep element glue is registered before the executor evaluates
This commit is contained in:
@@ -33,6 +33,20 @@ fn over_budget(used: usize, budget: usize) -> bool {
|
||||
used >= budget / 8 * 7
|
||||
}
|
||||
|
||||
/// The deep element glue is filled by runtime registration: ctors at load
|
||||
/// natively, host-invoked exports on wasm. A lifetime-carrying element whose
|
||||
/// glue never registered takes the shallow clone-out path, which brands its
|
||||
/// arena borrows `'static` for a memo to hold across a reset, so a missed
|
||||
/// registration is caught here rather than at the first capture.
|
||||
fn assert_deep_element_glue() {
|
||||
for (name, registered) in [
|
||||
("graphic", core_types::record::has_deep_element_glue(std::any::TypeId::of::<graphic_types::Graphic<'static>>())),
|
||||
("artboard", core_types::record::has_deep_element_glue(std::any::TypeId::of::<graphic_types::Artboard<'static>>())),
|
||||
] {
|
||||
assert!(registered, "deep element glue for `{name}` is not registered; the host must run `__node_registry_deep_element_{name}` before evaluating");
|
||||
}
|
||||
}
|
||||
|
||||
fn new_arena(capacity: usize) -> Arena {
|
||||
Arena::new(capacity).unwrap_or_else(|| {
|
||||
log::error!("arena generations exhausted; continuing without frame caching");
|
||||
@@ -66,6 +80,7 @@ fn noop_runtime() -> Arc<DynGraphRuntime> {
|
||||
|
||||
impl Default for DynamicExecutor {
|
||||
fn default() -> Self {
|
||||
assert_deep_element_glue();
|
||||
Self {
|
||||
output: Default::default(),
|
||||
tree: Default::default(),
|
||||
@@ -96,6 +111,7 @@ pub struct ResolvedDocumentNodeTypesDelta {
|
||||
|
||||
impl DynamicExecutor {
|
||||
pub fn new(mut proto_network: ProtoNetwork) -> Result<Self, GraphErrors> {
|
||||
assert_deep_element_glue();
|
||||
let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY);
|
||||
typing_context.update(&mut proto_network)?;
|
||||
let output = proto_network.output;
|
||||
|
||||
@@ -515,8 +515,11 @@ where
|
||||
if let Some(deep) = deep_element_glue(std::any::TypeId::of::<T::Static>()) {
|
||||
return unsafe { (deep.clone_out)(ptr) };
|
||||
}
|
||||
// SAFETY: a lifetime-carrying element type registers deep glue, so
|
||||
// this shallow path only erases borrow-free values.
|
||||
// SAFETY: a lifetime-carrying element type registers deep glue, so this
|
||||
// shallow path only erases borrow-free values. The registration is a
|
||||
// whole-program convention rather than something this call can check:
|
||||
// the executor asserts the in-tree types registered before it evaluates,
|
||||
// which is where a missed wasm registration export is caught.
|
||||
Box::new(unsafe { erase_static(read_element::<T>(Rec::new(ptr))) })
|
||||
}
|
||||
unsafe fn repark<T: Clone + Send + Sync + dyn_any::StaticTypeSized>(value: &(dyn std::any::Any + Send + Sync), dst: *mut u8, arena: &crate::arena::Arena) -> Option<()>
|
||||
|
||||
@@ -28,7 +28,7 @@ pub use layout::{
|
||||
ElToken, ElementSpec, ElementWrite, ElementWritePick, ElementWritePickHashed, ElementWritePickPlain, FieldDesc, FieldOffset, FieldWrite, InputReads, Layout, LayoutMeta, RecordLayout, copy_plan,
|
||||
element_dims, element_parked, element_write, element_write_hashed, empty_layout,
|
||||
};
|
||||
pub use owned::{OwnedRecord, deepen_field_value, register_deep_element_clone, register_deep_field_value, replay_field_value};
|
||||
pub use owned::{OwnedRecord, deepen_field_value, has_deep_element_glue, register_deep_element_clone, register_deep_field_value, replay_field_value};
|
||||
pub use promote::{Promotion, assert_promoted, register_element_promote, register_field_promote, register_retained_heap};
|
||||
pub use route::{RecordSource, SourcePlan};
|
||||
pub use run::{Group, GroupItem, RunBuilder, RunColumn, RunView};
|
||||
|
||||
@@ -32,6 +32,13 @@ pub(in crate::record) fn deep_element_glue(type_id: std::any::TypeId) -> Option<
|
||||
DEEP_ELEMENT_CLONES.lock().unwrap().get(&type_id).copied()
|
||||
}
|
||||
|
||||
/// Whether elements of a type registered deep glue. The shallow clone path is
|
||||
/// only sound for types that did not need to, so a host that drives the
|
||||
/// registration itself checks the types it owes before it evaluates anything.
|
||||
pub fn has_deep_element_glue(type_id: std::any::TypeId) -> bool {
|
||||
DEEP_ELEMENT_CLONES.lock().unwrap().contains_key(&type_id)
|
||||
}
|
||||
|
||||
/// Deep-copy overrides for field values whose content borrows the
|
||||
/// evaluation's arena (a graphic list holding native groups), keyed by the
|
||||
/// field's owned value form. Consulted at the persistence seams only:
|
||||
|
||||
Reference in New Issue
Block a user