1use std::collections::{HashMap, HashSet};
8
9use xfa_dom_resolver::data_dom::{DataDom, DataNodeId};
10use xfa_dom_resolver::som::{
11 parse_som, resolve_data_path, SomExpression, SomIndex, SomRoot, SomSelector,
12};
13use xfa_layout_engine::form::{FormNodeId, FormNodeType, FormTree, GroupKind};
14
15use super::RuntimeMetadata;
16
17pub const MAX_MUTATIONS_PER_DOC: usize = 4096;
19pub const MAX_INSTANCES_PER_SUBFORM: u32 = 256;
21pub const MAX_ITEMS_PER_LISTBOX: u32 = 4096;
23pub const MAX_RESOLVE_CALLS_PER_SCRIPT: u32 = 1024;
25pub const MAX_RESOLVE_RESULTS: usize = 256;
27pub const MAX_SOM_DEPTH: usize = 16;
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct MutationLogEntry {
33 pub node_id: FormNodeId,
35 pub script_idx: usize,
37 pub before: String,
39 pub after: String,
41}
42
43#[derive(Debug)]
45pub struct HostBindings {
46 form: *mut FormTree,
47 root_id: FormNodeId,
48 current_id: Option<FormNodeId>,
49 current_activity: Option<String>,
50 current_script_idx: usize,
51 next_script_idx: usize,
52 generation: u64,
53 mutation_log: Vec<MutationLogEntry>,
54 mutation_count_this_doc: usize,
55 resolve_count_this_script: u32,
56 metadata: RuntimeMetadata,
57 static_page_count: u32,
58 zero_instance_runs: HashMap<(FormNodeId, String), u64>,
59 data_dom: Option<*const DataDom>,
63 presave_gate: bool,
72 captured_occur_mutations: Vec<(FormNodeId, String, i64)>,
77 som_fail_log: Vec<crate::dynamic::SomFailEntry>,
80 instance_write_log: Vec<crate::dynamic::InstanceWriteEntry>,
83 declared_subform_names: HashSet<String>,
92}
93
94impl Default for HostBindings {
95 fn default() -> Self {
96 Self {
97 form: std::ptr::null_mut(),
98 root_id: FormNodeId(0),
99 current_id: None,
100 current_activity: None,
101 current_script_idx: 0,
102 next_script_idx: 0,
103 generation: 0,
104 mutation_log: Vec::new(),
105 mutation_count_this_doc: 0,
106 resolve_count_this_script: 0,
107 metadata: RuntimeMetadata::default(),
108 static_page_count: 0,
109 zero_instance_runs: HashMap::new(),
110 data_dom: None,
111 presave_gate: false,
112 captured_occur_mutations: Vec::new(),
113 som_fail_log: Vec::new(),
114 instance_write_log: Vec::new(),
115 declared_subform_names: HashSet::new(),
116 }
117 }
118}
119
120impl HostBindings {
121 pub fn new() -> Self {
123 Self::default()
124 }
125
126 pub fn set_form_handle(&mut self, form: *mut FormTree, root_id: FormNodeId) {
128 self.form = form;
129 self.root_id = root_id;
130 if form.is_null() {
131 self.current_id = None;
132 self.current_activity = None;
133 self.current_script_idx = 0;
134 self.zero_instance_runs.clear();
135 }
136 }
137
138 pub fn reset_per_document(&mut self) {
147 self.form = std::ptr::null_mut();
148 self.root_id = FormNodeId(0);
149 self.current_id = None;
150 self.current_activity = None;
151 self.current_script_idx = 0;
152 self.next_script_idx = 0;
153 self.generation = self.generation.wrapping_add(1);
154 self.mutation_log.clear();
155 self.mutation_count_this_doc = 0;
156 self.resolve_count_this_script = 0;
157 self.metadata = RuntimeMetadata::default();
158 self.static_page_count = 0;
159 self.zero_instance_runs.clear();
160 self.captured_occur_mutations.clear();
161 self.presave_gate = false;
167 }
168
169 pub fn set_data_handle(&mut self, dom: *const DataDom) {
173 self.data_dom = Some(dom);
174 }
175
176 pub fn set_declared_subform_names(&mut self, names: HashSet<String>) {
181 self.declared_subform_names = names;
182 }
183
184 pub fn is_declared_absent_node(&self, name: &str) -> bool {
193 self.declared_subform_names.contains(name)
194 }
195
196 pub fn reset_per_script(&mut self, current_id: FormNodeId, activity: Option<&str>) {
198 self.current_id = Some(current_id);
199 self.current_activity = activity.map(str::to_string);
200 self.current_script_idx = self.next_script_idx;
201 self.next_script_idx = self.next_script_idx.saturating_add(1);
202 self.resolve_count_this_script = 0;
203 }
204
205 pub fn set_static_page_count(&mut self, page_count: u32) {
207 self.static_page_count = page_count;
208 }
209
210 pub fn set_presave_gate(&mut self, enabled: bool) {
216 self.presave_gate = enabled;
217 }
218
219 #[doc(hidden)]
221 pub fn presave_gate(&self) -> bool {
222 self.presave_gate
223 }
224
225 pub fn generation(&self) -> u64 {
228 self.generation
229 }
230
231 pub fn current_node(&self) -> Option<FormNodeId> {
233 self.current_id
234 }
235
236 pub fn node_is_container(&mut self, node_id: FormNodeId, generation: u64) -> bool {
242 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
243 if !self.handle_is_live(node_id, generation) {
244 return false;
245 }
246 let Some(form) = self.form_ref() else {
247 return false;
248 };
249 is_instance_node(&form.get(node_id).node_type)
250 }
251
252 pub fn parent_of_node(&mut self, node_id: FormNodeId, generation: u64) -> Option<FormNodeId> {
259 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
260 if !self.handle_is_live(node_id, generation) {
261 return None;
262 }
263 let form = self.form_ref()?;
264 if self.root_id.0 >= form.nodes.len() {
265 return None;
266 }
267 let parents = build_parent_map(form, self.root_id);
268 parents.get(&node_id).copied()
269 }
270
271 pub fn take_metadata(&mut self) -> RuntimeMetadata {
273 std::mem::take(&mut self.metadata)
274 }
275
276 pub fn take_diag_logs(&mut self) -> crate::js_runtime::RuntimeDiagLogs {
278 crate::js_runtime::RuntimeDiagLogs {
279 som_fail_log: std::mem::take(&mut self.som_fail_log),
280 instance_write_log: std::mem::take(&mut self.instance_write_log),
281 }
282 }
283
284 pub fn mutation_log(&self) -> &[MutationLogEntry] {
286 &self.mutation_log
287 }
288
289 pub fn get_raw_value(&mut self, node_id: FormNodeId, generation: u64) -> Option<String> {
292 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
293 if !self.handle_is_live(node_id, generation) {
294 return None;
295 }
296 let Some(form) = self.form_ref() else {
297 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
298 return None;
299 };
300 match &form.get(node_id).node_type {
301 FormNodeType::Field { value } => Some(value.clone()),
302 _ => None,
303 }
304 }
305
306 pub fn node_name(&self, node_id: FormNodeId, generation: u64) -> Option<String> {
309 if !self.handle_is_live(node_id, generation) {
310 return None;
311 }
312 let form = self.form_ref()?;
313 Some(form.get(node_id).name.clone())
314 }
315
316 pub fn occur_resolve(&mut self, node_id: FormNodeId, generation: u64) -> bool {
319 self.metadata.occur_lookups_total = self.metadata.occur_lookups_total.saturating_add(1);
320 let live = self.handle_is_live(node_id, generation);
321 if live {
322 self.metadata.occur_lookup_successes =
323 self.metadata.occur_lookup_successes.saturating_add(1);
324 } else {
325 self.metadata.occur_lookup_failures =
326 self.metadata.occur_lookup_failures.saturating_add(1);
327 }
328 live
329 }
330
331 pub fn occur_read(&mut self, node_id: FormNodeId, generation: u64, prop: &str) -> i64 {
335 self.metadata.occur_property_reads = self.metadata.occur_property_reads.saturating_add(1);
336 if !self.handle_is_live(node_id, generation) {
337 return -1;
338 }
339 let Some(form) = self.form_ref() else {
340 return -1;
341 };
342 let occur = &form.get(node_id).occur;
343 match prop {
344 "min" => occur.min as i64,
345 "max" => occur.max.map(|m| m as i64).unwrap_or(-1),
346 "initial" => occur.initial as i64,
347 _ => -1,
348 }
349 }
350
351 pub fn occur_capture(
357 &mut self,
358 node_id: FormNodeId,
359 generation: u64,
360 prop: &str,
361 _value: i64,
362 ) -> bool {
363 let _ = generation;
364 self.metadata.occur_property_writes = self.metadata.occur_property_writes.saturating_add(1);
365 match prop {
366 "min" => {
367 self.metadata.occur_min_writes = self.metadata.occur_min_writes.saturating_add(1)
368 }
369 "max" => {
370 self.metadata.occur_max_writes = self.metadata.occur_max_writes.saturating_add(1)
371 }
372 _ => {}
373 }
374 self.metadata.occur_mutations_captured =
375 self.metadata.occur_mutations_captured.saturating_add(1);
376 if prop == "min" || prop == "max" {
380 self.captured_occur_mutations
381 .push((node_id, prop.to_string(), _value));
382 }
383 true
384 }
385
386 pub fn take_occur_mutations(&mut self) -> Vec<(FormNodeId, String, i64)> {
390 std::mem::take(&mut self.captured_occur_mutations)
391 }
392
393 pub fn set_raw_value(&mut self, node_id: FormNodeId, value: String, generation: u64) -> bool {
395 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
396 if !self.write_activity_allowed()
397 || !self.handle_is_live(node_id, generation)
398 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
399 {
400 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
401 return false;
402 }
403
404 let Some((before, after)) = self.write_field_value(node_id, value) else {
405 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
406 return false;
407 };
408
409 self.mutation_log.push(MutationLogEntry {
410 node_id,
411 script_idx: self.current_script_idx,
412 before,
413 after,
414 });
415 self.metadata.mutations = self.metadata.mutations.saturating_add(1);
416 self.mutation_count_this_doc = self.mutation_count_this_doc.saturating_add(1);
417 true
418 }
419
420 fn note_som(&mut self, resolved: bool, path: &str) {
426 self.metadata.som_lookups_total = self.metadata.som_lookups_total.saturating_add(1);
427 if resolved {
428 self.metadata.som_lookup_successes =
429 self.metadata.som_lookup_successes.saturating_add(1);
430 } else {
431 self.metadata.som_lookup_failures = self.metadata.som_lookup_failures.saturating_add(1);
432 if path == "occur" || path.starts_with("occur.") || path.starts_with("occur[") {
433 self.metadata.som_occur_path_refs =
434 self.metadata.som_occur_path_refs.saturating_add(1);
435 }
436 }
437 }
438
439 pub fn resolve_node(&mut self, path: &str) -> Option<FormNodeId> {
441 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
442 let nodes = match self.resolve_path(path) {
443 ResolveOutcome::Ok(nodes) => {
444 self.note_som(true, path);
445 nodes
446 }
447 ResolveOutcome::NoMatch => {
448 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
449 self.note_som(false, path);
450 self.debug_log_resolve_miss("resolve_node:NoMatch", path);
451 return None;
452 }
453 ResolveOutcome::BindingError => {
454 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
455 self.debug_log_resolve_miss("resolve_node:BindingError", path);
456 return None;
457 }
458 };
459
460 let Some(form) = self.form_ref() else {
461 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
462 return None;
463 };
464 let found = nodes
465 .into_iter()
466 .find(|node_id| matches!(form.get(*node_id).node_type, FormNodeType::Field { .. }));
467 if found.is_none() {
468 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
469 self.debug_log_resolve_miss("resolve_node:NoField", path);
470 }
471 found
472 }
473
474 pub fn resolve_nodes(&mut self, path: &str) -> Vec<FormNodeId> {
477 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
478 let nodes = match self.resolve_path(path) {
479 ResolveOutcome::Ok(nodes) => nodes,
480 ResolveOutcome::NoMatch => {
481 self.debug_log_resolve_miss("resolve_nodes:NoMatch", path);
482 return Vec::new();
483 }
484 ResolveOutcome::BindingError => {
485 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
486 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
487 self.debug_log_resolve_miss("resolve_nodes:BindingError", path);
488 return Vec::new();
489 }
490 };
491
492 let Some(form) = self.form_ref() else {
493 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
494 return Vec::new();
495 };
496 nodes
497 .into_iter()
498 .filter(|node_id| matches!(form.get(*node_id).node_type, FormNodeType::Field { .. }))
499 .take(MAX_RESOLVE_RESULTS)
500 .collect()
501 }
502
503 pub fn resolve_implicit(&mut self, current_id: FormNodeId, name: &str) -> Option<FormNodeId> {
510 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
511 match self.resolve_implicit_inner(current_id, name) {
512 ResolveOutcome::Ok(nodes) => {
513 self.note_som(true, name);
514 nodes.into_iter().next()
515 }
516 ResolveOutcome::NoMatch => {
517 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
518 self.note_som(false, name);
519 self.debug_log_resolve_miss("resolve_implicit:NoMatch", name);
520 None
521 }
522 ResolveOutcome::BindingError => {
523 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
524 None
525 }
526 }
527 }
528
529 pub fn resolve_implicit_candidates(
535 &mut self,
536 current_id: FormNodeId,
537 name: &str,
538 ) -> Vec<FormNodeId> {
539 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
540 match self.resolve_implicit_inner(current_id, name) {
541 ResolveOutcome::Ok(nodes) => {
542 self.note_som(true, name);
543 nodes
544 }
545 ResolveOutcome::NoMatch => {
546 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
547 self.note_som(false, name);
548 self.debug_log_resolve_miss("resolve_implicit_candidates:NoMatch", name);
549 Vec::new()
550 }
551 ResolveOutcome::BindingError => {
552 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
553 Vec::new()
554 }
555 }
556 }
557
558 pub fn resolve_implicit_candidates_quiet(
565 &mut self,
566 current_id: FormNodeId,
567 name: &str,
568 ) -> Vec<FormNodeId> {
569 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
570 match self.resolve_implicit_inner(current_id, name) {
571 ResolveOutcome::Ok(nodes) => nodes,
572 ResolveOutcome::NoMatch => Vec::new(),
573 ResolveOutcome::BindingError => {
574 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
575 Vec::new()
576 }
577 }
578 }
579
580 pub fn resolve_child(&mut self, parent_id: FormNodeId, name: &str) -> Option<FormNodeId> {
582 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
583 match self.resolve_child_inner(parent_id, name) {
584 ResolveOutcome::Ok(nodes) => {
585 self.note_som(true, name);
586 nodes.into_iter().next()
587 }
588 ResolveOutcome::NoMatch => {
589 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
590 self.note_som(false, name);
591 self.debug_log_resolve_miss("resolve_child:NoMatch", name);
592 None
593 }
594 ResolveOutcome::BindingError => {
595 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
596 None
597 }
598 }
599 }
600
601 pub fn resolve_child_candidates(
608 &mut self,
609 parent_ids: &[FormNodeId],
610 name: &str,
611 ) -> Vec<FormNodeId> {
612 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
613 match self.resolve_child_candidates_inner(parent_ids, name) {
614 ResolveOutcome::Ok(nodes) => {
615 self.note_som(true, name);
616 nodes
617 }
618 ResolveOutcome::NoMatch => {
619 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
620 self.note_som(false, name);
621 self.debug_log_resolve_miss("resolve_child_candidates:NoMatch", name);
622 Vec::new()
623 }
624 ResolveOutcome::BindingError => {
625 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
626 Vec::new()
627 }
628 }
629 }
630
631 pub fn resolve_child_candidates_quiet(
637 &mut self,
638 parent_ids: &[FormNodeId],
639 name: &str,
640 ) -> Vec<FormNodeId> {
641 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
642 match self.resolve_child_candidates_inner(parent_ids, name) {
643 ResolveOutcome::Ok(nodes) => nodes,
644 ResolveOutcome::NoMatch => Vec::new(),
645 ResolveOutcome::BindingError => {
646 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
647 Vec::new()
648 }
649 }
650 }
651
652 pub fn resolve_scoped_candidates(
658 &mut self,
659 scope_ids: &[FormNodeId],
660 name: &str,
661 ) -> Vec<FormNodeId> {
662 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
663 match self.resolve_scoped_candidates_inner(scope_ids, name) {
664 ResolveOutcome::Ok(nodes) => {
665 self.note_som(true, name);
666 nodes
667 }
668 ResolveOutcome::NoMatch => {
669 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
670 self.note_som(false, name);
671 self.debug_log_resolve_miss("resolve_scoped_candidates:NoMatch", name);
672 Vec::new()
673 }
674 ResolveOutcome::BindingError => {
675 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
676 Vec::new()
677 }
678 }
679 }
680
681 pub fn resolve_implicit_candidates_hinted(
694 &mut self,
695 current_id: FormNodeId,
696 name: &str,
697 next_hint: &str,
698 ) -> Vec<FormNodeId> {
699 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
700 let name = name.trim();
701 let hint = next_hint.trim();
702 if name.is_empty() || !self.consume_resolve_call() {
703 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
704 return Vec::new();
705 }
706 let Some(form) = self.form_ref() else {
707 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
708 return Vec::new();
709 };
710 if current_id.0 >= form.nodes.len() || self.root_id.0 >= form.nodes.len() {
711 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
712 return Vec::new();
713 }
714 let parents = build_parent_map(form, self.root_id);
715 let baseline = match resolve_implicit_candidates_in_scope(form, &parents, current_id, name)
716 {
717 ResolveOutcome::Ok(nodes) => nodes,
718 ResolveOutcome::NoMatch => {
719 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
720 self.debug_log_resolve_miss("resolve_implicit_candidates_hinted:NoMatch", name);
721 return Vec::new();
722 }
723 ResolveOutcome::BindingError => {
724 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
725 return Vec::new();
726 }
727 };
728 if hint.is_empty() {
729 return baseline;
730 }
731 let baseline_hit: Vec<FormNodeId> = baseline
732 .iter()
733 .copied()
734 .filter(|n| subtree_contains_name(form, *n, hint, MAX_SOM_DEPTH))
735 .collect();
736 if !baseline_hit.is_empty() {
737 return baseline_hit;
738 }
739 let mut widened: Vec<FormNodeId> = Vec::new();
743 let mut scope = Some(current_id);
744 let mut depth = 0usize;
745 while let Some(scope_id) = scope {
746 if depth > MAX_SOM_DEPTH || scope_id.0 >= form.nodes.len() {
747 break;
748 }
749 let mut candidates =
750 collect_named_descendant_candidates(form, scope_id, name, MAX_SOM_DEPTH);
751 order_candidates(form, &mut candidates);
752 for node_id in candidates {
753 if !widened.contains(&node_id)
754 && subtree_contains_name(form, node_id, hint, MAX_SOM_DEPTH)
755 {
756 widened.push(node_id);
757 if widened.len() >= MAX_RESOLVE_CANDIDATES {
758 return widened;
759 }
760 }
761 }
762 scope = parents.get(&scope_id).copied();
763 depth += 1;
764 }
765 if widened.is_empty() {
766 baseline
767 } else {
768 widened
769 }
770 }
771
772 pub fn resolve_child_candidates_hinted(
779 &mut self,
780 parent_ids: &[FormNodeId],
781 name: &str,
782 next_hint: &str,
783 ) -> Vec<FormNodeId> {
784 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
785 let candidates = match self.resolve_child_candidates_inner(parent_ids, name) {
786 ResolveOutcome::Ok(nodes) => nodes,
787 ResolveOutcome::NoMatch => {
788 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
789 return Vec::new();
790 }
791 ResolveOutcome::BindingError => {
792 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
793 return Vec::new();
794 }
795 };
796 self.apply_lookahead_filter(candidates, next_hint)
797 }
798
799 fn apply_lookahead_filter(
800 &self,
801 candidates: Vec<FormNodeId>,
802 next_hint: &str,
803 ) -> Vec<FormNodeId> {
804 let hint = next_hint.trim();
805 if hint.is_empty() || candidates.len() <= 1 {
806 return candidates;
807 }
808 let Some(form) = self.form_ref() else {
809 return candidates;
810 };
811 let filtered: Vec<FormNodeId> = candidates
812 .iter()
813 .copied()
814 .filter(|node_id| subtree_contains_name(form, *node_id, hint, MAX_SOM_DEPTH))
815 .collect();
816 if filtered.is_empty() {
817 candidates
818 } else {
819 filtered
820 }
821 }
822
823 pub fn resolve_with_full_chain(
849 &mut self,
850 parent_ids: &[FormNodeId],
851 chain: &[String],
852 implicit_origin: Option<FormNodeId>,
853 ) -> Vec<FormNodeId> {
854 self.resolve_with_full_chain_impl(parent_ids, chain, implicit_origin, false)
855 }
856
857 pub fn resolve_with_full_chain_strict(
861 &mut self,
862 parent_ids: &[FormNodeId],
863 chain: &[String],
864 implicit_origin: Option<FormNodeId>,
865 ) -> Vec<FormNodeId> {
866 self.resolve_with_full_chain_impl(parent_ids, chain, implicit_origin, true)
867 }
868
869 fn resolve_with_full_chain_impl(
870 &mut self,
871 parent_ids: &[FormNodeId],
872 chain: &[String],
873 implicit_origin: Option<FormNodeId>,
874 strict: bool,
875 ) -> Vec<FormNodeId> {
876 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
877 if !self.consume_resolve_call() {
878 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
879 return Vec::new();
880 }
881 let Some(form) = self.form_ref() else {
882 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
883 return Vec::new();
884 };
885 if self.root_id.0 >= form.nodes.len() {
886 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
887 return Vec::new();
888 }
889 if parent_ids
890 .iter()
891 .any(|node_id| node_id.0 >= form.nodes.len())
892 {
893 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
894 return Vec::new();
895 }
896 if chain.is_empty() {
897 return parent_ids.to_vec();
898 }
899 if chain.len() > MAX_SOM_DEPTH {
900 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
901 return Vec::new();
902 }
903 for seg in chain {
904 if seg.trim().is_empty() {
905 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
906 return Vec::new();
907 }
908 }
909
910 let parents = build_parent_map(form, self.root_id);
911
912 let entry_name = chain[0].trim();
914 let initial: Vec<FormNodeId> = if parent_ids.is_empty() {
915 let Some(origin) = implicit_origin else {
916 return Vec::new();
917 };
918 if origin.0 >= form.nodes.len() {
919 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
920 return Vec::new();
921 }
922 match resolve_implicit_candidates_in_scope(form, &parents, origin, entry_name) {
923 ResolveOutcome::Ok(nodes) => nodes,
924 ResolveOutcome::NoMatch => return Vec::new(),
925 ResolveOutcome::BindingError => {
926 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
927 return Vec::new();
928 }
929 }
930 } else {
931 let mut direct = Vec::new();
936 for &parent_id in parent_ids {
937 for &child_id in &form.get(parent_id).children {
938 if form.get(child_id).name == entry_name {
939 push_unique_candidate(&mut direct, child_id);
940 if direct.len() >= MAX_RESOLVE_CANDIDATES {
941 break;
942 }
943 }
944 }
945 if direct.len() >= MAX_RESOLVE_CANDIDATES {
946 break;
947 }
948 }
949 if !direct.is_empty() {
950 direct
951 } else {
952 let mut descendants = Vec::new();
953 for &parent_id in parent_ids {
954 let mut local = collect_named_descendant_candidates(
955 form,
956 parent_id,
957 entry_name,
958 MAX_SOM_DEPTH,
959 );
960 order_candidates(form, &mut local);
961 for node_id in local {
962 push_unique_candidate(&mut descendants, node_id);
963 if descendants.len() >= MAX_RESOLVE_CANDIDATES {
964 break;
965 }
966 }
967 if descendants.len() >= MAX_RESOLVE_CANDIDATES {
968 break;
969 }
970 }
971 descendants
972 }
973 };
974
975 if initial.is_empty() {
976 return Vec::new();
977 }
978
979 let mut layer = initial;
983 for seg in chain.iter().skip(1) {
984 let seg_name = seg.trim();
985 let mut next_layer: Vec<FormNodeId> = Vec::new();
986 for &node_id in &layer {
987 let mut found_any = false;
989 for &child_id in &form.get(node_id).children {
990 if form.get(child_id).name == seg_name {
991 push_unique_candidate(&mut next_layer, child_id);
992 found_any = true;
993 if next_layer.len() >= MAX_RESOLVE_CANDIDATES {
994 break;
995 }
996 }
997 }
998 if !found_any {
999 if let ResolveOutcome::Ok(scoped) =
1002 resolve_implicit_candidates_in_scope(form, &parents, node_id, seg_name)
1003 {
1004 for cand in scoped {
1005 push_unique_candidate(&mut next_layer, cand);
1006 if next_layer.len() >= MAX_RESOLVE_CANDIDATES {
1007 break;
1008 }
1009 }
1010 }
1011 }
1012 if next_layer.len() >= MAX_RESOLVE_CANDIDATES {
1013 break;
1014 }
1015 }
1016 if next_layer.is_empty() {
1017 if strict {
1024 return Vec::new();
1025 }
1026 return layer;
1027 }
1028 layer = next_layer;
1029 }
1030 layer
1031 }
1032
1033 pub fn instance_count(&mut self, parent_id: FormNodeId) -> u32 {
1035 self.instance_count_inner(parent_id, None)
1036 }
1037
1038 pub fn instance_count_for_handle(&mut self, parent_id: FormNodeId, generation: u64) -> u32 {
1040 self.instance_count_inner(parent_id, Some(generation))
1041 }
1042
1043 pub fn instance_index(&mut self, node_id: FormNodeId) -> u32 {
1045 self.instance_index_inner(node_id, None)
1046 }
1047
1048 pub fn instance_index_for_handle(&mut self, node_id: FormNodeId, generation: u64) -> u32 {
1050 self.instance_index_inner(node_id, Some(generation))
1051 }
1052
1053 pub fn has_zero_instance_run(
1058 &mut self,
1059 parent_id: FormNodeId,
1060 generation: u64,
1061 name: &str,
1062 ) -> bool {
1063 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1064 let name = name.trim();
1065 if name.is_empty() || !self.consume_resolve_call() {
1066 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1067 return false;
1068 }
1069 let Some(form) = self.form_ref() else {
1070 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1071 return false;
1072 };
1073 if generation != self.generation || parent_id.0 >= form.nodes.len() {
1074 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1075 return false;
1076 }
1077 self.zero_instance_runs
1078 .contains_key(&(parent_id, name.to_string()))
1079 }
1080
1081 #[allow(clippy::result_unit_err)]
1084 pub fn instance_set(&mut self, parent_id: FormNodeId, n: u32) -> Result<u32, ()> {
1085 self.instance_set_inner(parent_id, None, n)
1086 }
1087
1088 #[allow(clippy::result_unit_err)]
1090 pub fn instance_set_for_handle(
1091 &mut self,
1092 parent_id: FormNodeId,
1093 generation: u64,
1094 n: u32,
1095 ) -> Result<u32, ()> {
1096 self.instance_set_inner(parent_id, Some(generation), n)
1097 }
1098
1099 #[allow(clippy::result_unit_err)]
1101 pub fn instance_add(&mut self, parent_id: FormNodeId) -> Result<FormNodeId, ()> {
1102 self.instance_add_inner(parent_id, None)
1103 }
1104
1105 #[allow(clippy::result_unit_err)]
1107 pub fn instance_add_for_handle(
1108 &mut self,
1109 parent_id: FormNodeId,
1110 generation: u64,
1111 ) -> Result<FormNodeId, ()> {
1112 self.instance_add_inner(parent_id, Some(generation))
1113 }
1114
1115 #[allow(clippy::result_unit_err)]
1117 pub fn instance_remove(&mut self, parent_id: FormNodeId, index: u32) -> Result<(), ()> {
1118 self.instance_remove_inner(parent_id, None, index)
1119 }
1120
1121 #[allow(clippy::result_unit_err)]
1123 pub fn instance_remove_for_handle(
1124 &mut self,
1125 parent_id: FormNodeId,
1126 generation: u64,
1127 index: u32,
1128 ) -> Result<(), ()> {
1129 self.instance_remove_inner(parent_id, Some(generation), index)
1130 }
1131
1132 #[allow(clippy::result_unit_err)]
1134 pub fn list_clear(&mut self, field_id: FormNodeId) -> Result<(), ()> {
1135 self.list_clear_inner(field_id, None)
1136 }
1137
1138 #[allow(clippy::result_unit_err)]
1140 pub fn list_clear_for_handle(
1141 &mut self,
1142 field_id: FormNodeId,
1143 generation: u64,
1144 ) -> Result<(), ()> {
1145 self.list_clear_inner(field_id, Some(generation))
1146 }
1147
1148 #[allow(clippy::result_unit_err)]
1150 pub fn list_add(
1151 &mut self,
1152 field_id: FormNodeId,
1153 display: String,
1154 save: Option<String>,
1155 ) -> Result<(), ()> {
1156 self.list_add_inner(field_id, None, display, save)
1157 }
1158
1159 #[allow(clippy::result_unit_err)]
1161 pub fn list_add_for_handle(
1162 &mut self,
1163 field_id: FormNodeId,
1164 generation: u64,
1165 display: String,
1166 save: Option<String>,
1167 ) -> Result<(), ()> {
1168 self.list_add_inner(field_id, Some(generation), display, save)
1169 }
1170
1171 pub fn bound_item_for_handle(
1182 &mut self,
1183 field_id: FormNodeId,
1184 generation: u64,
1185 display_value: String,
1186 ) -> String {
1187 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1188 if !self.handle_is_live(field_id, generation) {
1189 return display_value;
1190 }
1191 let Some(form) = self.form_ref() else {
1192 return display_value;
1193 };
1194 if !matches!(form.get(field_id).node_type, FormNodeType::Field { .. }) {
1195 return display_value;
1196 }
1197 let meta = form.meta(field_id);
1198 for (display, save) in &meta.runtime_listbox_items {
1199 if display == &display_value {
1200 return save.clone();
1201 }
1202 }
1203 for (idx, display) in meta.display_items.iter().enumerate() {
1204 if display == &display_value {
1205 return meta
1206 .save_items
1207 .get(idx)
1208 .cloned()
1209 .unwrap_or_else(|| display_value.clone());
1210 }
1211 }
1212 display_value
1213 }
1214
1215 pub fn num_pages(&mut self) -> u32 {
1217 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1218 self.static_page_count
1219 }
1220
1221 pub fn metadata_binding_error(&mut self) {
1223 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1224 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1225 }
1226
1227 pub fn metadata_resolve_failure(&mut self) {
1229 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1230 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
1231 }
1232
1233 pub fn metadata_unsupported_host_call(&mut self) {
1240 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1241 self.metadata.unsupported_host_calls =
1242 self.metadata.unsupported_host_calls.saturating_add(1);
1243 }
1244
1245 pub fn metadata_probe_skip(&mut self) {
1250 self.metadata.probe_skips = self.metadata.probe_skips.saturating_add(1);
1251 }
1252
1253 pub fn metadata_som_data_root_hit(&mut self) {
1255 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1256 self.metadata.som_data_root_hits = self.metadata.som_data_root_hits.saturating_add(1);
1257 }
1258
1259 pub fn metadata_som_items_path_hit(&mut self) {
1261 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1262 self.metadata.som_items_path_hits = self.metadata.som_items_path_hits.saturating_add(1);
1263 }
1264
1265 fn consume_resolve_call(&mut self) -> bool {
1266 if self.resolve_count_this_script >= MAX_RESOLVE_CALLS_PER_SCRIPT {
1267 return false;
1268 }
1269 self.resolve_count_this_script = self.resolve_count_this_script.saturating_add(1);
1270 true
1271 }
1272
1273 fn resolve_path(&mut self, path: &str) -> ResolveOutcome {
1274 if !self.consume_resolve_call() {
1275 return ResolveOutcome::BindingError;
1276 }
1277
1278 let Some(form) = self.form_ref() else {
1279 return ResolveOutcome::BindingError;
1280 };
1281 let Some(current_id) = self.current_id else {
1282 return ResolveOutcome::BindingError;
1283 };
1284 if current_id.0 >= form.nodes.len() || self.root_id.0 >= form.nodes.len() {
1285 return ResolveOutcome::BindingError;
1286 }
1287
1288 let normalized = normalize_resolve_path(path.trim());
1289 let mut expr = match parse_som(&normalized) {
1290 Ok(expr) => expr,
1291 Err(_) => return ResolveOutcome::BindingError,
1292 };
1293 if expr.segments.len() > MAX_SOM_DEPTH {
1294 return ResolveOutcome::BindingError;
1295 }
1296 if matches!(
1297 expr.segments.last().map(|segment| &segment.selector),
1298 Some(SomSelector::Name(name)) if name == "rawValue"
1299 ) {
1300 expr.segments.pop();
1301 }
1302
1303 let parents = build_parent_map(form, self.root_id);
1304 let resolver = HostSomResolver {
1305 form,
1306 root_id: self.root_id,
1307 parents: &parents,
1308 current_id,
1309 };
1310 match resolver.resolve_expression(&expr) {
1311 Some(nodes) if !nodes.is_empty() => ResolveOutcome::Ok(nodes),
1312 _ => ResolveOutcome::NoMatch,
1313 }
1314 }
1315
1316 fn resolve_implicit_inner(&mut self, current_id: FormNodeId, name: &str) -> ResolveOutcome {
1317 let name = name.trim();
1318 if name.is_empty() || !self.consume_resolve_call() {
1319 return ResolveOutcome::BindingError;
1320 }
1321
1322 let Some(form) = self.form_ref() else {
1323 return ResolveOutcome::BindingError;
1324 };
1325 if current_id.0 >= form.nodes.len() || self.root_id.0 >= form.nodes.len() {
1326 return ResolveOutcome::BindingError;
1327 }
1328
1329 let parents = build_parent_map(form, self.root_id);
1330 resolve_implicit_candidates_in_scope(form, &parents, current_id, name)
1331 }
1332
1333 fn resolve_child_inner(&mut self, parent_id: FormNodeId, name: &str) -> ResolveOutcome {
1334 self.resolve_child_candidates_inner(&[parent_id], name)
1335 }
1336
1337 fn resolve_child_candidates_inner(
1338 &mut self,
1339 parent_ids: &[FormNodeId],
1340 name: &str,
1341 ) -> ResolveOutcome {
1342 let name = name.trim();
1343 if name.is_empty() || !self.consume_resolve_call() {
1344 return ResolveOutcome::BindingError;
1345 }
1346
1347 let Some(form) = self.form_ref() else {
1348 return ResolveOutcome::BindingError;
1349 };
1350 if parent_ids.is_empty()
1351 || parent_ids
1352 .iter()
1353 .any(|node_id| node_id.0 >= form.nodes.len())
1354 {
1355 return ResolveOutcome::BindingError;
1356 }
1357
1358 let mut direct = Vec::new();
1359 for &parent_id in parent_ids {
1360 for &child_id in &form.get(parent_id).children {
1361 if form.get(child_id).name == name {
1362 push_unique_candidate(&mut direct, child_id);
1363 if direct.len() >= MAX_RESOLVE_CANDIDATES {
1364 return ResolveOutcome::Ok(direct);
1365 }
1366 }
1367 }
1368 }
1369 if !direct.is_empty() {
1370 return ResolveOutcome::Ok(direct);
1371 }
1372
1373 let mut descendants = Vec::new();
1374 for &parent_id in parent_ids {
1375 let mut local =
1376 collect_named_descendant_candidates(form, parent_id, name, MAX_SOM_DEPTH);
1377 order_candidates(form, &mut local);
1378 for node_id in local {
1379 push_unique_candidate(&mut descendants, node_id);
1380 if descendants.len() >= MAX_RESOLVE_CANDIDATES {
1381 return ResolveOutcome::Ok(descendants);
1382 }
1383 }
1384 }
1385 if descendants.is_empty() {
1386 ResolveOutcome::NoMatch
1387 } else {
1388 ResolveOutcome::Ok(descendants)
1389 }
1390 }
1391
1392 fn resolve_scoped_candidates_inner(
1393 &mut self,
1394 scope_ids: &[FormNodeId],
1395 name: &str,
1396 ) -> ResolveOutcome {
1397 let name = name.trim();
1398 if name.is_empty() || !self.consume_resolve_call() {
1399 return ResolveOutcome::BindingError;
1400 }
1401
1402 let Some(form) = self.form_ref() else {
1403 return ResolveOutcome::BindingError;
1404 };
1405 if scope_ids.is_empty()
1406 || self.root_id.0 >= form.nodes.len()
1407 || scope_ids
1408 .iter()
1409 .any(|node_id| node_id.0 >= form.nodes.len())
1410 {
1411 return ResolveOutcome::BindingError;
1412 }
1413
1414 let parents = build_parent_map(form, self.root_id);
1415 let mut out = Vec::new();
1416 for &scope_id in scope_ids {
1417 if let ResolveOutcome::Ok(nodes) =
1418 resolve_implicit_candidates_in_scope(form, &parents, scope_id, name)
1419 {
1420 for node_id in nodes {
1421 push_unique_candidate(&mut out, node_id);
1422 if out.len() >= MAX_RESOLVE_CANDIDATES {
1423 return ResolveOutcome::Ok(out);
1424 }
1425 }
1426 }
1427 }
1428 if out.is_empty() {
1429 ResolveOutcome::NoMatch
1430 } else {
1431 ResolveOutcome::Ok(out)
1432 }
1433 }
1434
1435 fn instance_count_inner(&mut self, parent_id: FormNodeId, generation: Option<u64>) -> u32 {
1436 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1437 let Some(run) = self.read_instance_run(parent_id, generation) else {
1438 return 0;
1439 };
1440 run.nodes.len() as u32
1441 }
1442
1443 fn instance_index_inner(&mut self, node_id: FormNodeId, generation: Option<u64>) -> u32 {
1444 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1445 let Some(run) = self.read_instance_run(node_id, generation) else {
1446 return 0;
1447 };
1448 run.nodes
1449 .iter()
1450 .position(|candidate| *candidate == node_id)
1451 .unwrap_or(0) as u32
1452 }
1453
1454 fn instance_set_inner(
1455 &mut self,
1456 parent_id: FormNodeId,
1457 generation: Option<u64>,
1458 n: u32,
1459 ) -> Result<u32, ()> {
1460 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1461 if !self.write_activity_allowed()
1462 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1463 || !self.consume_resolve_call()
1464 {
1465 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1466 return Err(());
1467 }
1468
1469 let Some(run) = self.live_instance_run(parent_id, generation) else {
1470 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1471 return Err(());
1472 };
1473 let Some(target_count) = self.clamped_instance_count(run.prototype_id, n) else {
1474 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1475 return Err(());
1476 };
1477
1478 let target_count = target_count as usize;
1479 let prototype_id = run.prototype_id;
1480 let parent_id = run.parent_id;
1481 let first_pos = run.first_position;
1482 let Some(prototype_name) = self
1483 .form_ref()
1484 .and_then(|form| form.nodes.get(prototype_id.0))
1485 .map(|node| node.name.clone())
1486 else {
1487 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1488 return Err(());
1489 };
1490 let remove_ids = run.nodes;
1491 let old_count_set = remove_ids.len();
1492 let parent_name_set = self
1494 .form_ref()
1495 .and_then(|form| form.nodes.get(parent_id.0))
1496 .map(|n| n.name.clone())
1497 .unwrap_or_default();
1498
1499 let mut new_ids = Vec::with_capacity(target_count);
1500 if target_count > 0 {
1501 self.normalize_instance_occurrence(prototype_id);
1502 new_ids.push(prototype_id);
1503 for _ in 1..target_count {
1504 let cloned_id = self.clone_subtree(prototype_id)?;
1505 new_ids.push(cloned_id);
1506 }
1507 }
1508
1509 let Some(form) = self.form_mut() else {
1510 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1511 return Err(());
1512 };
1513 let parent = form.get_mut(parent_id);
1514 parent
1515 .children
1516 .retain(|child_id| !remove_ids.contains(child_id));
1517 let insert_pos = first_pos.min(parent.children.len());
1518 for (offset, node_id) in new_ids.iter().copied().enumerate() {
1519 parent.children.insert(insert_pos + offset, node_id);
1520 }
1521
1522 let key = (parent_id, prototype_name.clone());
1523 if target_count == 0 {
1524 self.zero_instance_runs.insert(key, self.generation);
1525 } else {
1526 self.zero_instance_runs.remove(&key);
1527 }
1528 self.record_instance_write();
1529 self.record_instance_write_detail(
1530 parent_id,
1531 &parent_name_set,
1532 &prototype_name,
1533 old_count_set,
1534 target_count,
1535 );
1536 Ok(target_count as u32)
1537 }
1538
1539 fn instance_add_inner(
1540 &mut self,
1541 parent_id: FormNodeId,
1542 generation: Option<u64>,
1543 ) -> Result<FormNodeId, ()> {
1544 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1545 if !self.write_activity_allowed()
1546 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1547 || !self.consume_resolve_call()
1548 {
1549 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1550 return Err(());
1551 }
1552
1553 let Some(run) = self.live_instance_run(parent_id, generation) else {
1554 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1555 return Err(());
1556 };
1557 let zero_key = self
1558 .form_ref()
1559 .and_then(|form| form.nodes.get(run.prototype_id.0))
1560 .map(|node| (run.parent_id, node.name.clone()));
1561 let add_proto_name = self
1563 .form_ref()
1564 .and_then(|form| form.nodes.get(run.prototype_id.0))
1565 .map(|n| n.name.clone())
1566 .unwrap_or_default();
1567 let add_parent_name = self
1568 .form_ref()
1569 .and_then(|form| form.nodes.get(run.parent_id.0))
1570 .map(|n| n.name.clone())
1571 .unwrap_or_default();
1572 let add_old_count = run.nodes.len();
1573 let add_parent_id = run.parent_id;
1574 let Some(max_allowed) = self.max_instances_for(run.prototype_id) else {
1575 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1576 return Err(());
1577 };
1578 if run.nodes.len() as u32 >= max_allowed {
1579 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1580 return Err(());
1581 }
1582
1583 let cloned_id = self.clone_subtree(run.prototype_id)?;
1584 let Some(form) = self.form_mut() else {
1585 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1586 return Err(());
1587 };
1588 form.get_mut(run.parent_id)
1589 .children
1590 .insert(run.last_position + 1, cloned_id);
1591
1592 if let Some(key) = zero_key {
1593 self.zero_instance_runs.remove(&key);
1594 }
1595 self.record_instance_write();
1596 self.record_instance_write_detail(
1597 add_parent_id,
1598 &add_parent_name,
1599 &add_proto_name,
1600 add_old_count,
1601 add_old_count + 1,
1602 );
1603 Ok(cloned_id)
1604 }
1605
1606 fn instance_remove_inner(
1607 &mut self,
1608 parent_id: FormNodeId,
1609 generation: Option<u64>,
1610 index: u32,
1611 ) -> Result<(), ()> {
1612 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1613 if !self.write_activity_allowed()
1614 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1615 || !self.consume_resolve_call()
1616 {
1617 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1618 return Err(());
1619 }
1620
1621 let Some(run) = self.live_instance_run(parent_id, generation) else {
1622 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1623 return Err(());
1624 };
1625 let zero_key = self
1626 .form_ref()
1627 .and_then(|form| form.nodes.get(run.prototype_id.0))
1628 .map(|node| (run.parent_id, node.name.clone()));
1629 let rm_proto_name = self
1631 .form_ref()
1632 .and_then(|form| form.nodes.get(run.prototype_id.0))
1633 .map(|n| n.name.clone())
1634 .unwrap_or_default();
1635 let rm_parent_name = self
1636 .form_ref()
1637 .and_then(|form| form.nodes.get(run.parent_id.0))
1638 .map(|n| n.name.clone())
1639 .unwrap_or_default();
1640 let rm_old_count = run.nodes.len();
1641 let rm_parent_id = run.parent_id;
1642 let min_allowed = self
1643 .form_ref()
1644 .and_then(|form| form.nodes.get(run.prototype_id.0))
1645 .map(|node| node.occur.min)
1646 .unwrap_or(1);
1647 if run.nodes.len() as u32 <= min_allowed {
1648 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1649 return Err(());
1650 }
1651 let Some(remove_position) = run.positions.get(index as usize).copied() else {
1652 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1653 return Err(());
1654 };
1655
1656 let Some(form) = self.form_mut() else {
1657 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1658 return Err(());
1659 };
1660 form.get_mut(run.parent_id).children.remove(remove_position);
1661
1662 if let Some(key) = zero_key {
1663 if run.nodes.len() == 1 {
1664 self.zero_instance_runs.insert(key, self.generation);
1665 } else {
1666 self.zero_instance_runs.remove(&key);
1667 }
1668 }
1669 self.record_instance_write();
1670 self.record_instance_write_detail(
1671 rm_parent_id,
1672 &rm_parent_name,
1673 &rm_proto_name,
1674 rm_old_count,
1675 rm_old_count.saturating_sub(1),
1676 );
1677 Ok(())
1678 }
1679
1680 fn read_instance_run(
1681 &mut self,
1682 node_id: FormNodeId,
1683 generation: Option<u64>,
1684 ) -> Option<InstanceRun> {
1685 if !self.consume_resolve_call() {
1686 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1687 return None;
1688 }
1689 let Some(form) = self.form_ref() else {
1690 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1691 return None;
1692 };
1693 if generation.is_some_and(|value| value != self.generation) {
1694 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1695 return None;
1696 }
1697 if node_id.0 >= form.nodes.len() || self.root_id.0 >= form.nodes.len() {
1698 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1699 return None;
1700 }
1701
1702 let parents = build_parent_map(form, self.root_id);
1703 if !parents.contains_key(&node_id) {
1704 if node_id == self.root_id {
1705 return Some(InstanceRun {
1706 parent_id: node_id,
1707 positions: vec![0],
1708 nodes: vec![node_id],
1709 prototype_id: node_id,
1710 first_position: 0,
1711 last_position: 0,
1712 });
1713 }
1714 return None;
1715 }
1716
1717 build_instance_run(form, &parents, node_id)
1718 }
1719
1720 fn live_instance_run(
1721 &self,
1722 node_id: FormNodeId,
1723 generation: Option<u64>,
1724 ) -> Option<InstanceRun> {
1725 let form = self.form_ref()?;
1726 if generation.is_some_and(|value| value != self.generation)
1727 || node_id.0 >= form.nodes.len()
1728 || self.root_id.0 >= form.nodes.len()
1729 || !is_instance_node(&form.get(node_id).node_type)
1730 {
1731 return None;
1732 }
1733 let parents = build_parent_map(form, self.root_id);
1734 build_instance_run(form, &parents, node_id)
1735 }
1736
1737 fn clamped_instance_count(&self, prototype_id: FormNodeId, requested: u32) -> Option<u32> {
1738 let min_allowed = self.form_ref()?.get(prototype_id).occur.min;
1739 let max_allowed = self.max_instances_for(prototype_id)?;
1740 if min_allowed > max_allowed {
1741 return None;
1742 }
1743 Some(requested.clamp(min_allowed, max_allowed))
1744 }
1745
1746 fn max_instances_for(&self, prototype_id: FormNodeId) -> Option<u32> {
1747 let occur = &self.form_ref()?.get(prototype_id).occur;
1748 let max = occur.max.unwrap_or(u32::MAX);
1749 Some(max.min(MAX_INSTANCES_PER_SUBFORM))
1750 }
1751
1752 fn clone_subtree(&mut self, source_id: FormNodeId) -> Result<FormNodeId, ()> {
1753 let (mut new_node, mut new_meta, child_ids) = {
1754 let Some(form) = self.form_ref() else {
1755 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1756 return Err(());
1757 };
1758 if source_id.0 >= form.nodes.len() {
1759 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1760 return Err(());
1761 }
1762 (
1763 form.get(source_id).clone(),
1764 form.meta(source_id).clone(),
1765 form.get(source_id).children.clone(),
1766 )
1767 };
1768
1769 let mut new_children = Vec::with_capacity(child_ids.len());
1770 for child_id in child_ids {
1771 new_children.push(self.clone_subtree(child_id)?);
1772 }
1773 new_node.children = new_children;
1774 new_node.occur.initial = 1;
1777 new_meta.xfa_id = None;
1778
1779 let Some(form) = self.form_mut() else {
1780 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1781 return Err(());
1782 };
1783 Ok(form.add_node_with_meta(new_node, new_meta))
1784 }
1785
1786 fn normalize_instance_occurrence(&mut self, node_id: FormNodeId) {
1787 if let Some(form) = self.form_mut() {
1788 if let Some(node) = form.nodes.get_mut(node_id.0) {
1789 node.occur.initial = 1;
1792 }
1793 }
1794 }
1795
1796 fn record_instance_write(&mut self) {
1797 self.metadata.instance_writes = self.metadata.instance_writes.saturating_add(1);
1798 self.mutation_count_this_doc = self.mutation_count_this_doc.saturating_add(1);
1799 }
1800
1801 fn record_instance_write_detail(
1805 &mut self,
1806 parent_id: FormNodeId,
1807 parent_name: &str,
1808 proto_name: &str,
1809 old_count: usize,
1810 new_count: usize,
1811 ) {
1812 if !crate::dynamic::runtime_diag_enabled() || self.instance_write_log.len() >= 200 {
1813 return;
1814 }
1815 let parent_node_id = parent_id.0;
1816 self.instance_write_log
1817 .push(crate::dynamic::InstanceWriteEntry {
1818 script_idx: self.current_script_idx,
1819 activity: self.current_activity.as_deref().unwrap_or("").to_string(),
1820 parent_node_id,
1821 parent_node_name: parent_name.to_string(),
1822 prototype_node_name: proto_name.to_string(),
1823 old_count,
1824 new_count,
1825 });
1826 }
1827
1828 fn record_list_write(&mut self) {
1829 self.metadata.list_writes = self.metadata.list_writes.saturating_add(1);
1830 self.mutation_count_this_doc = self.mutation_count_this_doc.saturating_add(1);
1831 }
1832
1833 fn list_clear_inner(
1834 &mut self,
1835 field_id: FormNodeId,
1836 generation: Option<u64>,
1837 ) -> Result<(), ()> {
1838 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1839 if !self.write_activity_allowed()
1840 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1841 || !self.consume_resolve_call()
1842 {
1843 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1844 return Err(());
1845 }
1846 let current_generation = self.generation;
1847 let Some(form) = self.form_mut() else {
1848 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1849 return Err(());
1850 };
1851 if generation.is_some_and(|value| value != current_generation)
1852 || field_id.0 >= form.nodes.len()
1853 {
1854 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1855 return Err(());
1856 }
1857 if !matches!(form.get(field_id).node_type, FormNodeType::Field { .. }) {
1858 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1859 return Err(());
1860 }
1861 form.meta_mut(field_id).runtime_listbox_items.clear();
1862 self.record_list_write();
1863 Ok(())
1864 }
1865
1866 fn list_add_inner(
1867 &mut self,
1868 field_id: FormNodeId,
1869 generation: Option<u64>,
1870 display: String,
1871 save: Option<String>,
1872 ) -> Result<(), ()> {
1873 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1874 if !self.write_activity_allowed()
1875 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1876 || !self.consume_resolve_call()
1877 {
1878 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1879 return Err(());
1880 }
1881 let current_generation = self.generation;
1882 let Some(form) = self.form_mut() else {
1883 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1884 return Err(());
1885 };
1886 if generation.is_some_and(|value| value != current_generation)
1887 || field_id.0 >= form.nodes.len()
1888 {
1889 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1890 return Err(());
1891 }
1892 if !matches!(form.get(field_id).node_type, FormNodeType::Field { .. }) {
1893 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1894 return Err(());
1895 }
1896 let meta = form.meta_mut(field_id);
1897 if meta.runtime_listbox_items.len() >= MAX_ITEMS_PER_LISTBOX as usize {
1898 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1899 return Err(());
1900 }
1901 let save_value = save.unwrap_or_else(|| display.clone());
1902 meta.runtime_listbox_items.push((display, save_value));
1903 self.record_list_write();
1904 Ok(())
1905 }
1906
1907 fn write_activity_allowed(&self) -> bool {
1908 let base = matches!(
1909 self.current_activity.as_deref(),
1910 Some("initialize")
1911 | Some("calculate")
1912 | Some("validate")
1913 | Some("docReady")
1914 | Some("layoutReady")
1915 );
1916 if base {
1917 return true;
1918 }
1919 self.presave_gate && matches!(self.current_activity.as_deref(), Some("preSave"))
1925 }
1926
1927 fn handle_is_live(&self, node_id: FormNodeId, generation: u64) -> bool {
1928 generation == self.generation
1929 && self
1930 .form_ref()
1931 .is_some_and(|form| node_id.0 < form.nodes.len())
1932 }
1933
1934 fn write_field_value(
1935 &mut self,
1936 node_id: FormNodeId,
1937 value: String,
1938 ) -> Option<(String, String)> {
1939 let form = self.form_mut()?;
1940 let node = form.nodes.get_mut(node_id.0)?;
1941 let FormNodeType::Field { value: field_value } = &mut node.node_type else {
1942 return None;
1943 };
1944 let before = field_value.clone();
1945 *field_value = value;
1946 Some((before, field_value.clone()))
1947 }
1948
1949 fn form_ref(&self) -> Option<&FormTree> {
1950 if self.form.is_null() {
1951 None
1952 } else {
1953 unsafe { self.form.as_ref() }
1957 }
1958 }
1959
1960 fn form_mut(&mut self) -> Option<&mut FormTree> {
1961 if self.form.is_null() {
1962 None
1963 } else {
1964 unsafe { self.form.as_mut() }
1967 }
1968 }
1969
1970 fn data_dom_ref(&self) -> Option<&DataDom> {
1972 self.data_dom.map(|ptr| unsafe { &*ptr })
1976 }
1977
1978 pub fn data_children(&mut self, raw_id: usize) -> Vec<usize> {
1981 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1982 let result = {
1984 let Some(dom) = self.data_dom_ref() else {
1985 return Vec::new();
1986 };
1987 let id = DataNodeId::from_raw(raw_id);
1988 if dom.get(id).is_none() {
1989 return Vec::new();
1990 }
1991 dom.children(id)
1992 .iter()
1993 .take(MAX_RESOLVE_RESULTS)
1994 .map(|c| c.as_raw())
1995 .collect::<Vec<_>>()
1996 };
1997 self.metadata.data_reads = self.metadata.data_reads.saturating_add(1);
1998 result
1999 }
2000
2001 pub fn data_value(&mut self, raw_id: usize) -> Option<String> {
2004 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2005 let result = {
2006 let dom = self.data_dom_ref()?;
2007 let id = DataNodeId::from_raw(raw_id);
2008 dom.get(id)?;
2009 dom.value(id).ok().map(|s| s.to_owned())
2010 };
2011 if result.is_some() {
2012 self.metadata.data_reads = self.metadata.data_reads.saturating_add(1);
2013 }
2014 result
2015 }
2016
2017 pub fn data_child_by_name(&mut self, parent_raw: usize, name: &str) -> Option<usize> {
2020 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2021 let result = {
2022 let dom = self.data_dom_ref()?;
2023 let parent = DataNodeId::from_raw(parent_raw);
2024 dom.get(parent)?;
2025 dom.children_by_name(parent, name)
2026 .into_iter()
2027 .next()
2028 .map(|id| id.as_raw())
2029 };
2030 if result.is_some() {
2031 self.metadata.data_reads = self.metadata.data_reads.saturating_add(1);
2032 }
2033 result
2034 }
2035
2036 pub fn data_bound_record(
2039 &mut self,
2040 form_node_id: FormNodeId,
2041 generation: u64,
2042 ) -> Option<usize> {
2043 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2044 if generation != self.generation {
2045 return None;
2046 }
2047 let form = self.form_ref()?;
2048 if form_node_id.0 >= form.nodes.len() {
2049 return None;
2050 }
2051 form.meta(form_node_id).bound_data_node
2052 }
2053
2054 pub fn get_display_items(&mut self, field_id: FormNodeId, generation: u64) -> Vec<String> {
2066 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2067 if !self.handle_is_live(field_id, generation) {
2068 return Vec::new();
2069 }
2070 let Some(form) = self.form_ref() else {
2071 return Vec::new();
2072 };
2073 if !matches!(form.get(field_id).node_type, FormNodeType::Field { .. }) {
2074 return Vec::new();
2075 }
2076 let meta = form.meta(field_id);
2077 if !meta.runtime_listbox_items.is_empty() {
2078 return meta
2079 .runtime_listbox_items
2080 .iter()
2081 .map(|(display, _save)| display.clone())
2082 .collect();
2083 }
2084 meta.display_items.clone()
2085 }
2086
2087 pub fn data_resolve_node(&mut self, path: &str) -> Option<usize> {
2091 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2092 if !self.consume_resolve_call() {
2093 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2094 return None;
2095 }
2096 {
2098 let dom = self.data_dom_ref()?;
2099 resolve_data_path(dom, path, None)
2100 .ok()?
2101 .into_iter()
2102 .next()
2103 .map(|id| id.as_raw())
2104 }
2105 }
2106
2107 pub fn data_resolve_nodes(&mut self, path: &str) -> Vec<usize> {
2110 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2111 if !self.consume_resolve_call() {
2112 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2113 return Vec::new();
2114 }
2115 {
2116 let Some(dom) = self.data_dom_ref() else {
2117 return Vec::new();
2118 };
2119 resolve_data_path(dom, path, None)
2120 .unwrap_or_default()
2121 .into_iter()
2122 .take(MAX_RESOLVE_RESULTS)
2123 .map(|id| id.as_raw())
2124 .collect()
2125 }
2126 }
2127}
2128
2129enum ResolveOutcome {
2130 Ok(Vec<FormNodeId>),
2131 NoMatch,
2132 BindingError,
2133}
2134
2135#[derive(Debug, Clone)]
2136struct InstanceRun {
2137 parent_id: FormNodeId,
2138 positions: Vec<usize>,
2139 nodes: Vec<FormNodeId>,
2140 prototype_id: FormNodeId,
2141 first_position: usize,
2142 last_position: usize,
2143}
2144
2145fn build_instance_run(
2146 form: &FormTree,
2147 parents: &HashMap<FormNodeId, FormNodeId>,
2148 node_id: FormNodeId,
2149) -> Option<InstanceRun> {
2150 let parent_id = parents.get(&node_id).copied()?;
2151 let name = form.get(node_id).name.clone();
2152 let parent = form.get(parent_id);
2153 let mut positions = Vec::new();
2154 let mut nodes = Vec::new();
2155 for (position, child_id) in parent.children.iter().copied().enumerate() {
2156 if form.get(child_id).name == name {
2157 positions.push(position);
2158 nodes.push(child_id);
2159 }
2160 }
2161 if !nodes.contains(&node_id) {
2162 return None;
2163 }
2164 Some(InstanceRun {
2165 parent_id,
2166 prototype_id: nodes[0],
2167 first_position: positions[0],
2168 last_position: *positions.last()?,
2169 positions,
2170 nodes,
2171 })
2172}
2173
2174fn is_instance_node(node_type: &FormNodeType) -> bool {
2175 matches!(
2176 node_type,
2177 FormNodeType::Root
2178 | FormNodeType::Subform
2179 | FormNodeType::Area
2180 | FormNodeType::ExclGroup
2181 | FormNodeType::SubformSet
2182 )
2183}
2184
2185impl HostBindings {
2186 fn debug_log_resolve_miss(&mut self, kind: &str, path: &str) {
2193 if std::env::var("XFA_JS_DEBUG").ok().as_deref() == Some("1") {
2194 eprintln!("XFA_JS_DEBUG {kind} path={path:?}");
2195 }
2196 if crate::dynamic::runtime_diag_enabled() && self.som_fail_log.len() < 200 {
2197 self.som_fail_log.push(crate::dynamic::SomFailEntry {
2198 path: path.to_string(),
2199 kind: kind.to_string(),
2200 script_idx: self.current_script_idx,
2201 activity: self.current_activity.as_deref().unwrap_or("").to_string(),
2202 });
2203 }
2204 }
2205}
2206
2207fn normalize_resolve_path(path: &str) -> String {
2208 if let Some(rest) = path.strip_prefix("this.") {
2209 format!("$.{rest}")
2210 } else if path == "this" {
2211 "$".to_string()
2212 } else {
2213 path.to_string()
2214 }
2215}
2216
2217struct HostSomResolver<'a> {
2218 form: &'a FormTree,
2219 root_id: FormNodeId,
2220 parents: &'a HashMap<FormNodeId, FormNodeId>,
2221 current_id: FormNodeId,
2222}
2223
2224impl HostSomResolver<'_> {
2225 fn resolve_expression(&self, expr: &SomExpression) -> Option<Vec<FormNodeId>> {
2226 match expr.root {
2227 SomRoot::Data | SomRoot::Record | SomRoot::Template => None,
2228 SomRoot::CurrentContainer => {
2229 if expr.segments.is_empty() {
2230 Some(vec![self.current_id])
2231 } else {
2232 Some(self.follow_absolute(vec![self.current_id], &expr.segments))
2233 }
2234 }
2235 SomRoot::Form => {
2236 if expr.segments.is_empty() {
2237 Some(vec![self.root_id])
2238 } else {
2239 Some(self.follow_absolute(vec![self.root_id], &expr.segments))
2240 }
2241 }
2242 SomRoot::Xfa => {
2243 let segments = strip_xfa_form_prefix(&expr.segments);
2244 if segments.is_empty() {
2245 Some(vec![self.root_id])
2246 } else {
2247 Some(self.follow_absolute(vec![self.root_id], segments))
2248 }
2249 }
2250 SomRoot::Unqualified => {
2251 if expr.segments.is_empty() {
2252 Some(vec![self.current_id])
2253 } else {
2254 Some(self.follow_unqualified(&expr.segments))
2255 }
2256 }
2257 }
2258 }
2259
2260 fn follow_absolute(
2261 &self,
2262 mut current: Vec<FormNodeId>,
2263 segments: &[xfa_dom_resolver::som::SomSegment],
2264 ) -> Vec<FormNodeId> {
2265 for (idx, segment) in segments.iter().enumerate() {
2266 let allow_self = idx == 0;
2267 current = current
2268 .into_iter()
2269 .flat_map(|node_id| self.step_from_node(node_id, segment, allow_self))
2270 .collect();
2271 if current.is_empty() {
2272 break;
2273 }
2274 }
2275 current
2276 }
2277
2278 fn follow_unqualified(
2279 &self,
2280 segments: &[xfa_dom_resolver::som::SomSegment],
2281 ) -> Vec<FormNodeId> {
2282 let Some((first, rest)) = segments.split_first() else {
2283 return vec![self.current_id];
2284 };
2285
2286 let mut scope = Some(self.current_id);
2287 while let Some(scope_id) = scope {
2288 let anchors: Vec<_> = descendants_inclusive(self.form, scope_id)
2289 .into_iter()
2290 .filter(|node_id| self.node_matches_segment(*node_id, first))
2291 .collect();
2292 let matched = self.follow_remaining(anchors, rest);
2293 if !matched.is_empty() {
2294 return matched;
2295 }
2296 scope = self.parents.get(&scope_id).copied();
2297 }
2298
2299 let anchors: Vec<_> = descendants_inclusive(self.form, self.root_id)
2300 .into_iter()
2301 .filter(|node_id| self.node_matches_segment(*node_id, first))
2302 .collect();
2303 self.follow_remaining(anchors, rest)
2304 }
2305
2306 fn follow_remaining(
2307 &self,
2308 mut current: Vec<FormNodeId>,
2309 segments: &[xfa_dom_resolver::som::SomSegment],
2310 ) -> Vec<FormNodeId> {
2311 for segment in segments {
2312 current = current
2313 .into_iter()
2314 .flat_map(|node_id| self.step_from_node(node_id, segment, false))
2315 .collect();
2316 if current.is_empty() {
2317 break;
2318 }
2319 }
2320 current
2321 }
2322
2323 fn step_from_node(
2324 &self,
2325 node_id: FormNodeId,
2326 segment: &xfa_dom_resolver::som::SomSegment,
2327 allow_self: bool,
2328 ) -> Vec<FormNodeId> {
2329 if let SomSelector::Name(name) = &segment.selector {
2330 if name == ".." {
2331 if let Some(&parent_id) = self.parents.get(&node_id) {
2332 return apply_index_to_single(parent_id, segment.index);
2333 }
2334 return Vec::new();
2335 }
2336 }
2337
2338 if allow_self && self.node_matches_selector(node_id, &segment.selector) {
2339 return apply_index_to_single(node_id, segment.index);
2340 }
2341
2342 let matches: Vec<_> = self
2343 .form
2344 .get(node_id)
2345 .children
2346 .iter()
2347 .copied()
2348 .filter(|child_id| self.node_matches_selector(*child_id, &segment.selector))
2349 .collect();
2350
2351 apply_index(matches, segment.index)
2352 }
2353
2354 fn node_matches_segment(
2355 &self,
2356 node_id: FormNodeId,
2357 segment: &xfa_dom_resolver::som::SomSegment,
2358 ) -> bool {
2359 if !self.node_matches_selector(node_id, &segment.selector) {
2360 return false;
2361 }
2362
2363 match segment.index {
2364 SomIndex::All => true,
2365 SomIndex::None => self.sibling_position(node_id, &segment.selector) == Some(0),
2366 SomIndex::Specific(idx) => {
2367 self.sibling_position(node_id, &segment.selector) == Some(idx)
2368 }
2369 }
2370 }
2371
2372 fn sibling_position(&self, node_id: FormNodeId, selector: &SomSelector) -> Option<usize> {
2373 let Some(parent_id) = self.parents.get(&node_id).copied() else {
2374 return self.node_matches_selector(node_id, selector).then_some(0);
2375 };
2376
2377 self.form
2378 .get(parent_id)
2379 .children
2380 .iter()
2381 .copied()
2382 .filter(|candidate| self.node_matches_selector(*candidate, selector))
2383 .position(|candidate| candidate == node_id)
2384 }
2385
2386 fn node_matches_selector(&self, node_id: FormNodeId, selector: &SomSelector) -> bool {
2387 match selector {
2388 SomSelector::Name(name) => self.form.get(node_id).name == *name,
2389 SomSelector::Class(class_name) => self.node_matches_class(node_id, class_name),
2390 SomSelector::AllChildren => true,
2391 }
2392 }
2393
2394 fn node_matches_class(&self, node_id: FormNodeId, class_name: &str) -> bool {
2395 let class_name = class_name.to_ascii_lowercase();
2396 match class_name.as_str() {
2397 "subform" => matches!(
2398 self.form.get(node_id).node_type,
2399 FormNodeType::Root | FormNodeType::Subform
2400 ),
2401 "pageset" => matches!(self.form.get(node_id).node_type, FormNodeType::PageSet),
2402 "pagearea" => matches!(
2403 self.form.get(node_id).node_type,
2404 FormNodeType::PageArea { .. }
2405 ),
2406 "field" => matches!(self.form.get(node_id).node_type, FormNodeType::Field { .. }),
2407 "draw" => matches!(
2408 self.form.get(node_id).node_type,
2409 FormNodeType::Draw(_) | FormNodeType::Image { .. }
2410 ),
2411 "exclgroup" => self.form.meta(node_id).group_kind == GroupKind::ExclusiveChoice,
2412 _ => false,
2413 }
2414 }
2415}
2416
2417fn strip_xfa_form_prefix(
2418 segments: &[xfa_dom_resolver::som::SomSegment],
2419) -> &[xfa_dom_resolver::som::SomSegment] {
2420 match segments.first() {
2421 Some(segment)
2422 if matches!(&segment.selector, SomSelector::Name(name) if name == "form")
2423 && matches!(segment.index, SomIndex::None) =>
2424 {
2425 &segments[1..]
2426 }
2427 _ => segments,
2428 }
2429}
2430
2431fn apply_index(matches: Vec<FormNodeId>, index: SomIndex) -> Vec<FormNodeId> {
2432 match index {
2433 SomIndex::None => matches.into_iter().take(1).collect(),
2434 SomIndex::Specific(idx) => matches.get(idx).copied().into_iter().collect(),
2435 SomIndex::All => matches,
2436 }
2437}
2438
2439fn apply_index_to_single(node_id: FormNodeId, index: SomIndex) -> Vec<FormNodeId> {
2440 match index {
2441 SomIndex::None | SomIndex::Specific(0) | SomIndex::All => vec![node_id],
2442 SomIndex::Specific(_) => Vec::new(),
2443 }
2444}
2445
2446fn descendants_inclusive(form: &FormTree, root_id: FormNodeId) -> Vec<FormNodeId> {
2447 let mut out = Vec::new();
2448 collect_descendants(form, root_id, &mut out);
2449 out
2450}
2451
2452fn collect_descendants(form: &FormTree, node_id: FormNodeId, out: &mut Vec<FormNodeId>) {
2453 out.push(node_id);
2454 for &child_id in &form.get(node_id).children {
2455 collect_descendants(form, child_id, out);
2456 }
2457}
2458
2459const MAX_RESOLVE_CANDIDATES: usize = 32;
2464
2465fn collect_named_descendant_candidates(
2485 form: &FormTree,
2486 scope_id: FormNodeId,
2487 name: &str,
2488 max_depth: usize,
2489) -> Vec<FormNodeId> {
2490 let mut candidates: Vec<FormNodeId> = Vec::new();
2491 find_named_descendant_inner(form, scope_id, name, 0, max_depth, &mut candidates);
2492 candidates
2493}
2494
2495fn order_candidates(form: &FormTree, candidates: &mut [FormNodeId]) {
2496 candidates.sort_by(|left, right| {
2506 let left_is_field = matches!(form.get(*left).node_type, FormNodeType::Field { .. });
2507 let right_is_field = matches!(form.get(*right).node_type, FormNodeType::Field { .. });
2508 match (left_is_field, right_is_field) {
2509 (true, false) => std::cmp::Ordering::Less,
2510 (false, true) => std::cmp::Ordering::Greater,
2511 (true, true) => std::cmp::Ordering::Equal,
2512 (false, false) => form
2513 .get(*right)
2514 .children
2515 .len()
2516 .cmp(&form.get(*left).children.len()),
2517 }
2518 });
2519}
2520
2521fn push_unique_candidate(candidates: &mut Vec<FormNodeId>, node_id: FormNodeId) {
2522 if candidates.len() < MAX_RESOLVE_CANDIDATES && !candidates.contains(&node_id) {
2523 candidates.push(node_id);
2524 }
2525}
2526
2527fn resolve_implicit_candidates_in_scope(
2528 form: &FormTree,
2529 parents: &HashMap<FormNodeId, FormNodeId>,
2530 current_id: FormNodeId,
2531 name: &str,
2532) -> ResolveOutcome {
2533 let mut scope = Some(current_id);
2538 let mut depth = 0usize;
2539 while let Some(scope_id) = scope {
2540 if depth > MAX_SOM_DEPTH {
2541 return ResolveOutcome::BindingError;
2542 }
2543 if scope_id.0 >= form.nodes.len() {
2544 return ResolveOutcome::BindingError;
2545 }
2546 let mut candidates =
2547 collect_named_descendant_candidates(form, scope_id, name, MAX_SOM_DEPTH);
2548 if !candidates.is_empty() {
2549 order_candidates(form, &mut candidates);
2550 if !form.get(candidates[0]).children.is_empty() {
2551 return ResolveOutcome::Ok(candidates);
2552 }
2553 }
2554 scope = parents.get(&scope_id).copied();
2555 depth += 1;
2556 }
2557
2558 let mut scope = Some(current_id);
2561 let mut depth = 0usize;
2562 while let Some(scope_id) = scope {
2563 if depth > MAX_SOM_DEPTH {
2564 return ResolveOutcome::BindingError;
2565 }
2566 if scope_id.0 >= form.nodes.len() {
2567 return ResolveOutcome::BindingError;
2568 }
2569 if form.get(scope_id).name == name {
2570 return ResolveOutcome::Ok(vec![scope_id]);
2571 }
2572 scope = parents.get(&scope_id).copied();
2573 depth += 1;
2574 }
2575
2576 let mut scope = Some(current_id);
2578 let mut depth = 0usize;
2579 while let Some(scope_id) = scope {
2580 if depth > MAX_SOM_DEPTH {
2581 return ResolveOutcome::BindingError;
2582 }
2583 if scope_id.0 >= form.nodes.len() {
2584 return ResolveOutcome::BindingError;
2585 }
2586 let mut candidates =
2587 collect_named_descendant_candidates(form, scope_id, name, MAX_SOM_DEPTH);
2588 if !candidates.is_empty() {
2589 order_candidates(form, &mut candidates);
2590 return ResolveOutcome::Ok(candidates);
2591 }
2592 scope = parents.get(&scope_id).copied();
2593 depth += 1;
2594 }
2595
2596 ResolveOutcome::NoMatch
2597}
2598
2599fn subtree_contains_name(
2606 form: &FormTree,
2607 node_id: FormNodeId,
2608 name: &str,
2609 max_depth: usize,
2610) -> bool {
2611 if node_id.0 >= form.nodes.len() {
2612 return false;
2613 }
2614 subtree_contains_name_inner(form, node_id, name, 0, max_depth)
2615}
2616
2617fn subtree_contains_name_inner(
2618 form: &FormTree,
2619 node_id: FormNodeId,
2620 name: &str,
2621 depth: usize,
2622 max_depth: usize,
2623) -> bool {
2624 if depth >= max_depth {
2625 return false;
2626 }
2627 for &child_id in &form.get(node_id).children {
2628 if form.get(child_id).name == name {
2629 return true;
2630 }
2631 if subtree_contains_name_inner(form, child_id, name, depth + 1, max_depth) {
2632 return true;
2633 }
2634 }
2635 false
2636}
2637
2638fn find_named_descendant_inner(
2639 form: &FormTree,
2640 node_id: FormNodeId,
2641 name: &str,
2642 depth: usize,
2643 max_depth: usize,
2644 candidates: &mut Vec<FormNodeId>,
2645) {
2646 if depth >= max_depth || candidates.len() >= MAX_RESOLVE_CANDIDATES {
2647 return;
2648 }
2649 for &child_id in &form.get(node_id).children {
2650 if candidates.len() >= MAX_RESOLVE_CANDIDATES {
2651 return;
2652 }
2653 if form.get(child_id).name == name {
2654 candidates.push(child_id);
2655 } else {
2659 find_named_descendant_inner(form, child_id, name, depth + 1, max_depth, candidates);
2660 }
2661 }
2662}
2663
2664fn build_parent_map(form: &FormTree, root_id: FormNodeId) -> HashMap<FormNodeId, FormNodeId> {
2665 let mut parents = HashMap::new();
2666 populate_parent_map(form, root_id, &mut parents);
2667 parents
2668}
2669
2670fn populate_parent_map(
2671 form: &FormTree,
2672 node_id: FormNodeId,
2673 parents: &mut HashMap<FormNodeId, FormNodeId>,
2674) {
2675 for &child_id in &form.get(node_id).children {
2676 parents.insert(child_id, node_id);
2677 populate_parent_map(form, child_id, parents);
2678 }
2679}
2680
2681#[cfg(test)]
2682mod tests {
2683 use super::*;
2684 use xfa_layout_engine::form::{FormNode, Occur};
2685 use xfa_layout_engine::text::FontMetrics;
2686 use xfa_layout_engine::types::{BoxModel, LayoutStrategy};
2687
2688 fn add_node(tree: &mut FormTree, name: &str, node_type: FormNodeType) -> FormNodeId {
2689 tree.add_node(FormNode {
2690 name: name.to_string(),
2691 node_type,
2692 box_model: BoxModel::default(),
2693 layout: LayoutStrategy::TopToBottom,
2694 children: Vec::new(),
2695 occur: Occur::once(),
2696 font: FontMetrics::default(),
2697 calculate: None,
2698 validate: None,
2699 column_widths: Vec::new(),
2700 col_span: 1,
2701 })
2702 }
2703
2704 #[test]
2705 fn raw_value_get_set_and_generation_guard() {
2706 let mut tree = FormTree::new();
2707 let root = add_node(&mut tree, "root", FormNodeType::Root);
2708 let field = add_node(
2709 &mut tree,
2710 "Field1",
2711 FormNodeType::Field {
2712 value: "old".to_string(),
2713 },
2714 );
2715 tree.get_mut(root).children = vec![field];
2716
2717 let mut host = HostBindings::new();
2718 host.reset_per_document();
2719 host.set_form_handle(&mut tree as *mut FormTree, root);
2720 host.reset_per_script(field, Some("calculate"));
2721 let generation = host.generation();
2722
2723 assert_eq!(
2724 host.get_raw_value(field, generation),
2725 Some("old".to_string())
2726 );
2727 assert!(host.set_raw_value(field, "new".to_string(), generation));
2728 assert_eq!(
2729 host.get_raw_value(field, generation),
2730 Some("new".to_string())
2731 );
2732
2733 host.reset_per_document();
2734 assert_eq!(host.get_raw_value(field, generation), None);
2735 }
2736
2737 #[test]
2738 fn multi_segment_som_chain_lookahead() {
2739 let mut tree = FormTree::new();
2745 let root = add_node(&mut tree, "root", FormNodeType::Root);
2746 let f = add_node(&mut tree, "F", FormNodeType::Subform);
2747 let p1_empty = add_node(&mut tree, "P1", FormNodeType::Subform);
2748 let stub = add_node(&mut tree, "Stub", FormNodeType::Subform);
2749 let p1_rich = add_node(&mut tree, "P1", FormNodeType::Subform);
2750 let x = add_node(
2751 &mut tree,
2752 "X",
2753 FormNodeType::Field {
2754 value: "answer".to_string(),
2755 },
2756 );
2757 tree.get_mut(p1_empty).children = vec![stub];
2758 tree.get_mut(p1_rich).children = vec![x];
2759 tree.get_mut(f).children = vec![p1_empty, p1_rich];
2760 tree.get_mut(root).children = vec![f];
2761
2762 let mut host = HostBindings::new();
2763 host.reset_per_document();
2764 host.set_form_handle(&mut tree as *mut FormTree, root);
2765 host.reset_per_script(root, Some("calculate"));
2766
2767 let plain = host.resolve_child_candidates(&[f], "P1");
2769 assert!(plain.contains(&p1_empty) && plain.contains(&p1_rich));
2770
2771 let hinted = host.resolve_child_candidates_hinted(&[f], "P1", "X");
2774 assert_eq!(hinted, vec![p1_rich]);
2775
2776 let hinted_none = host.resolve_child_candidates_hinted(&[f], "P1", "Nope");
2779 assert!(hinted_none.contains(&p1_empty) && hinted_none.contains(&p1_rich));
2780
2781 let implicit = host.resolve_implicit_candidates_hinted(root, "P1", "X");
2784 assert_eq!(implicit, vec![p1_rich]);
2785 }
2786
2787 #[test]
2788 fn resolve_node_rejects_over_depth() {
2789 let mut tree = FormTree::new();
2790 let root = add_node(&mut tree, "root", FormNodeType::Root);
2791 let field = add_node(
2792 &mut tree,
2793 "Field1",
2794 FormNodeType::Field {
2795 value: String::new(),
2796 },
2797 );
2798 tree.get_mut(root).children = vec![field];
2799
2800 let mut host = HostBindings::new();
2801 host.reset_per_document();
2802 host.set_form_handle(&mut tree as *mut FormTree, root);
2803 host.reset_per_script(root, Some("calculate"));
2804 let long_path = (0..=MAX_SOM_DEPTH)
2805 .map(|idx| format!("n{idx}"))
2806 .collect::<Vec<_>>()
2807 .join(".");
2808
2809 assert_eq!(host.resolve_node(&long_path), None);
2810 assert_eq!(host.take_metadata().binding_errors, 1);
2811 }
2812
2813 fn build_chain_disambiguation_tree() -> (
2822 FormTree,
2823 FormNodeId, FormNodeId, FormNodeId, ) {
2827 let mut tree = FormTree::new();
2828 let root = add_node(&mut tree, "root", FormNodeType::Root);
2829
2830 let a1 = add_node(&mut tree, "A", FormNodeType::Subform);
2831 let b1 = add_node(&mut tree, "B", FormNodeType::Subform);
2832 tree.get_mut(b1).children = vec![];
2833 tree.get_mut(a1).children = vec![b1];
2834
2835 let a2 = add_node(&mut tree, "A", FormNodeType::Subform);
2836 let b2 = add_node(&mut tree, "B", FormNodeType::Subform);
2837 let c2 = add_node(&mut tree, "C", FormNodeType::Subform);
2838 let x2 = add_node(
2839 &mut tree,
2840 "X",
2841 FormNodeType::Field {
2842 value: "winner".to_string(),
2843 },
2844 );
2845 tree.get_mut(c2).children = vec![x2];
2846 tree.get_mut(b2).children = vec![c2];
2847 tree.get_mut(a2).children = vec![b2];
2848
2849 let a3 = add_node(&mut tree, "A", FormNodeType::Subform);
2850 let b3 = add_node(&mut tree, "B", FormNodeType::Subform);
2851 let z3 = add_node(
2852 &mut tree,
2853 "Z",
2854 FormNodeType::Field {
2855 value: "decoy".to_string(),
2856 },
2857 );
2858 tree.get_mut(b3).children = vec![z3];
2859 tree.get_mut(a3).children = vec![b3];
2860
2861 tree.get_mut(root).children = vec![a1, a2, a3];
2862 (tree, root, a2, x2)
2863 }
2864
2865 #[test]
2866 fn full_chain_implicit_pins_winning_branch() {
2867 let (mut tree, root, _a2, x2) = build_chain_disambiguation_tree();
2871 let mut host = HostBindings::new();
2872 host.reset_per_document();
2873 host.set_form_handle(&mut tree as *mut FormTree, root);
2874 host.reset_per_script(root, Some("calculate"));
2875
2876 let chain = vec![
2877 "A".to_string(),
2878 "B".to_string(),
2879 "C".to_string(),
2880 "X".to_string(),
2881 ];
2882 let result = host.resolve_with_full_chain(&[], &chain, Some(root));
2883 assert_eq!(result, vec![x2], "chain must pin onto the X under A2");
2884 }
2885
2886 #[test]
2887 fn full_chain_child_entry_uses_parent_ids() {
2888 let (mut tree, root, _a2, x2) = build_chain_disambiguation_tree();
2892 let mut host = HostBindings::new();
2893 host.reset_per_document();
2894 host.set_form_handle(&mut tree as *mut FormTree, root);
2895 host.reset_per_script(root, Some("calculate"));
2896
2897 let chain = vec![
2898 "A".to_string(),
2899 "B".to_string(),
2900 "C".to_string(),
2901 "X".to_string(),
2902 ];
2903 let result = host.resolve_with_full_chain(&[root], &chain, None);
2904 assert_eq!(result, vec![x2]);
2905 }
2906
2907 #[test]
2908 fn full_chain_dead_end_returns_deepest_layer() {
2909 let (mut tree, root, _a2, _x2) = build_chain_disambiguation_tree();
2913 let mut host = HostBindings::new();
2914 host.reset_per_document();
2915 host.set_form_handle(&mut tree as *mut FormTree, root);
2916 host.reset_per_script(root, Some("calculate"));
2917
2918 let chain = vec![
2919 "A".to_string(),
2920 "B".to_string(),
2921 "C".to_string(),
2922 "Bogus".to_string(),
2923 ];
2924 let result = host.resolve_with_full_chain(&[], &chain, Some(root));
2925 assert_eq!(result.len(), 1);
2928 }
2929
2930 #[test]
2931 fn full_chain_no_match_returns_empty() {
2932 let (mut tree, root, _a2, _x2) = build_chain_disambiguation_tree();
2934 let mut host = HostBindings::new();
2935 host.reset_per_document();
2936 host.set_form_handle(&mut tree as *mut FormTree, root);
2937 host.reset_per_script(root, Some("calculate"));
2938
2939 let chain = vec!["Nope".to_string(), "X".to_string()];
2940 let result = host.resolve_with_full_chain(&[], &chain, Some(root));
2941 assert!(result.is_empty());
2942 }
2943
2944 #[test]
2945 fn full_chain_rejects_overdepth_and_empty_segments() {
2946 let (mut tree, root, _a2, _x2) = build_chain_disambiguation_tree();
2947 let mut host = HostBindings::new();
2948 host.reset_per_document();
2949 host.set_form_handle(&mut tree as *mut FormTree, root);
2950 host.reset_per_script(root, Some("calculate"));
2951
2952 let overlong: Vec<String> = (0..=MAX_SOM_DEPTH).map(|i| format!("S{i}")).collect();
2953 let result = host.resolve_with_full_chain(&[], &overlong, Some(root));
2954 assert!(result.is_empty());
2955 let with_empty = vec!["A".to_string(), "".to_string()];
2957 let result = host.resolve_with_full_chain(&[], &with_empty, Some(root));
2958 assert!(result.is_empty());
2959 }
2960
2961 #[test]
2962 fn full_chain_empty_chain_returns_parents() {
2963 let (mut tree, root, _a2, _x2) = build_chain_disambiguation_tree();
2967 let mut host = HostBindings::new();
2968 host.reset_per_document();
2969 host.set_form_handle(&mut tree as *mut FormTree, root);
2970 host.reset_per_script(root, Some("calculate"));
2971
2972 let result = host.resolve_with_full_chain(&[root], &[], None);
2973 assert_eq!(result, vec![root]);
2974 }
2975}