Skip to content

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

@david-waltermire

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
    No fields configured for issues without a type.

    Projects

    Status
    To Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions