mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
Resolve a named read's offset when the graph compiles
The fold already holds the name and the read input's finished layout, so the offset falls out there rather than at construction: `RecordLayout` carries the resolved numbers and `set_layout` copies them into the read slots. Constructors are untouched, and census-marker reads keep their current installation. A read meets the value type the name was written at, so a disagreement between a read here and a write upstream is the same graph error as two writes disagreeing; the one-name-one-type check now spans reads and writes together. An absent attribute stays absent and the read serves the forced default rather than reporting it. `read_attribute` is the catalog's get half, typed and never `Option` at the kernel boundary, with the name declared exactly as the write side declares it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -193,9 +193,7 @@ impl DynamicExecutor {
|
||||
pub fn introspect(&self, node_path: &[NodeId]) -> Result<Arc<dyn std::any::Any + Send + Sync + 'static>, IntrospectError> {
|
||||
let result = self.tree.introspect(node_path)?;
|
||||
if result.downcast_ref::<core_types::context::CtxSnapshot>().is_some() {
|
||||
return self
|
||||
.introspect_with(node_path, graphic_types::boundary::batch_to_legacy)
|
||||
.map(Arc::from);
|
||||
return self.introspect_with(node_path, graphic_types::boundary::batch_to_legacy).map(Arc::from);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
@@ -791,6 +789,71 @@ mod test {
|
||||
assert_eq!(unsafe { layout.rec(&value).read::<&[NodeId]>(offset) }, path.as_slice());
|
||||
}
|
||||
|
||||
/// Reads `read_name` off a record that a write of `write_name` produced.
|
||||
fn read_attribute_network(write_name: &str, read_name: &str, value: TaggedValue) -> ProtoNetwork {
|
||||
ProtoNetwork {
|
||||
stack_need: 0,
|
||||
inputs: vec![],
|
||||
output: NodeId(5),
|
||||
nodes: vec![
|
||||
(NodeId(0), ProtoNode::value(ConstructionArgs::Value(TaggedValue::F64(7.).into()), vec![])),
|
||||
(NodeId(1), string_value(write_name)),
|
||||
(NodeId(2), ProtoNode::value(ConstructionArgs::Value(value.into()), vec![])),
|
||||
(NodeId(3), proto_node("graphic_nodes::graphic::WriteAttributeNode", vec![NodeId(0), NodeId(1), NodeId(2)])),
|
||||
(NodeId(4), string_value(read_name)),
|
||||
(NodeId(5), proto_node("graphic_nodes::graphic::ReadAttributeNode", vec![NodeId(3), NodeId(4)])),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn read_back(network: ProtoNetwork) -> f64 {
|
||||
let executor = build_executor(network);
|
||||
let arena = Arena::new(1 << 12).unwrap();
|
||||
let generations = [];
|
||||
let scope = EvalScope::new(None, None, None, &generations, &arena);
|
||||
let ctx = ContextImpl::root(&scope);
|
||||
let handle = executor.tree().get(NodeId(5)).unwrap();
|
||||
let layout = handle.layout().clone();
|
||||
let edge = handle.duplicate().downcast_record::<f64>().unwrap();
|
||||
let frames = core_types::record::test_frames(executor.tree().stack_need());
|
||||
let GPoll::Final(value) = core_types::record::serve_input(&edge, &ctx, &frames) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
unsafe { core_types::record::read_element::<f64>(layout.rec(&value)) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_named_read_serves_the_written_value() {
|
||||
assert_eq!(read_back(read_attribute_network("novel:count", "novel:count", TaggedValue::F64(2.5))), 2.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_absent_named_read_serves_the_forced_default() {
|
||||
// Nothing upstream writes `novel:absent`, so the read collapses to the
|
||||
// value type's default rather than reporting absence.
|
||||
assert_eq!(read_back(read_attribute_network("novel:count", "novel:absent", TaggedValue::F64(2.5))), 0.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_census_named_read_round_trips_its_written_value() {
|
||||
// A declared name folds onto its census field, so the read resolves
|
||||
// against the same offset the write installed.
|
||||
assert_eq!(read_back(read_attribute_network("opacity", "opacity", TaggedValue::F64(0.5))), 0.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_named_read_disagreeing_with_its_write_is_refused() {
|
||||
// The name is written at a path upstream and read at `f64` here, which
|
||||
// is the one-name-one-type rule spanning a write and a read.
|
||||
let mut network = read_attribute_network("novel:count", "novel:count", TaggedValue::NodeIdPath(vec![NodeId(1)]));
|
||||
network.resolve_types(&node_registry::NODE_REGISTRY).unwrap();
|
||||
let errors = network.compute_layouts().expect_err("a name at two value types must be refused");
|
||||
assert!(
|
||||
errors.iter().any(|error| format!("{:?}", error.error).contains("one name carries one value type")),
|
||||
"the refusal names the one-name-one-type rule, got {errors:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_runtime_attribute_name_is_refused_when_the_graph_compiles() {
|
||||
// The outer node's name comes off another node rather than sitting on
|
||||
|
||||
@@ -7,7 +7,7 @@ use graphene_std::raster::GPU;
|
||||
#[cfg(feature = "gpu")]
|
||||
use graphene_std::SourceId;
|
||||
use graphene_std::raster::{CPU, Raster};
|
||||
use graphene_std::registry::{ConstructionError, SourceHandle, NodeIOTypes, RegistryEntry};
|
||||
use graphene_std::registry::{ConstructionError, NodeIOTypes, RegistryEntry, SourceHandle};
|
||||
#[cfg(feature = "gpu")]
|
||||
use graphene_std::runtime::RuntimeHandle;
|
||||
|
||||
@@ -227,7 +227,9 @@ mod node_registry_macros {
|
||||
let handle = inputs.next().unwrap();
|
||||
let layout = handle.layout().clone();
|
||||
let node = graphene_std::ops::IntoNode::<$to, _, $from>::new(handle.downcast_record::<$from>()?, &layout);
|
||||
Ok(SourceHandle::new_record::<$to>(std::sync::Arc::new(node) as std::sync::Arc<core_types::registry::ErasedRecordNode>))
|
||||
Ok(SourceHandle::new_record::<$to>(
|
||||
std::sync::Arc::new(node) as std::sync::Arc<core_types::registry::ErasedRecordNode>
|
||||
))
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user