Skip to content

Module-walk mode: declared model-return-type for instance-resolving functions #706

Description

@david-waltermire

Summary

Expressions evaluated against an IModuleNodeItem graph (rather than an instance document) have no reliable way to produce a definition-level result when they pass through functions that exist to load or dereference external instance data (fn:doc, oscal:resolve-reference, oscal:resolve-profile, fn:resolve-uri). As a consequence, let statements and constraint expressions that use these functions either throw InvalidTypeFunctionException (FOTY0013) during module walking or cannot be resolved to a specific global definition.

Example

At module walk, OSCAL's external constraints declare let expressions such as:

<let var="all-imports"
     expression="import-component-definition ! recurse-depth(
       'doc(resolve-uri(Q{http://csrc.nist.gov/ns/oscal/1.0}resolve-reference(@href)))/component-definition')"/>

The @href flag on a definition node has no typed value, so atomization throws FOTY0013 before resolve-reference even runs. recurse-depth has no way to know what kind of node doc(...)/component-definition would produce, so it cannot navigate the module graph to the matching global assembly definition.

Root cause

fn:doc, oscal:resolve-reference, oscal:resolve-profile, and fn:resolve-uri do not declare a model-level return type. Without a declared target type, module-walking consumers (visitors, recurse-depth, constraint targets) cannot resolve an instance-resolving chain to the corresponding global definition in the module graph.

Proposed direction

  1. Add a model-return-type declaration to IFunction (e.g. a named NCName target, a sentinel such as module-root-wildcard / pass-through, or a function of arg types to return type).
  2. Add a module-walk flag to DynamicContext (analogous to disablePredicateEvaluation).
  3. In module-walk mode, functions with a declared model-return-type resolve to the corresponding global definition; functions without one return an empty sequence and emit a one-time info-level log per function so authors know to declare a model-return-type.
  4. Let recurse-depth (and similar) consult the declared model-return-type of the compiled inner expression instead of attempting to load documents.
  5. Update affected declarations in metaschema-java core (FnDoc, FnResolveUri, MpRecurseDepth) and in liboscal-java (ResolveReference, ResolveProfile).

Context

Lazy let evaluation in AllowedValueCollectingNodeItemVisitor (separate PR) sidesteps the symptom for list-allowed-values because OSCAL's problematic lets are referenced only by <index> and <expect> constraints, not by <allowed-values>. This issue tracks the root-cause fix so any visitor or constraint consumer walking the module graph behaves correctly regardless of which constraint kind references the let.

Scope

  • metaschema-java/coreIFunction, DynamicContext, function implementations (FnDoc, FnResolveUri, MpRecurseDepth), atomization on no-data node items.
  • liboscal-javaResolveReference, ResolveProfile declarations.
  • Warrants a PRD given cross-cutting impact and constraint-author-visible changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    To Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions