Skip to content

Lift the Java port-edge gate on slice_forward/paths_between/flows_to_call/flows_to_argument (codeanalyzer-java 3.0.3) #354

Open

Description

@rahlk

Is your feature request related to a problem? Please describe.

The four interprocedural value verbs are refused on a Java graph, behind a probe for whether any dependence edge touches an SDG port. The refusal was correct: codeanalyzer-java emitted the port vertices and the param_in/param_out/summary lattice between them, but no ddg edge ever touched a port, so slice_forward from a parameter returned the seed alone, paths_between returned no paths, and flows_to_call/flows_to_argument returned False for every input — an empty answer indistinguishable from "no flow exists".

codeanalyzer-java 3.0.3 fixes it (codeanalyzer-java#227, PR codeanalyzer-java#230). The analyzer now emits @formal_in:k → use, return → @formal_out, statement → <call>/actual_in:i and <call>/actual_out → statement, so the port layer is joined to the statement graph in both directions. The same release also drops ddg edges whose endpoint is not a body node (codeanalyzer-java#228), which removes the only measured disagreement between analysis.json and the Neo4j projection.

Measured on daytrader8 at -a 4 with 3.0.3: 5,108 ddg edges touch a port (0 before), formal_in ports with out-degree zero fall from 1,176 to 140 (all 140 are parameters no statement reads), 0 dangling endpoints, and JSON and graph both report 10,493 ddg edges.

Describe the solution you'd like

  • Pin at codeanalyzer-java==3.0.3; the probe passes without a special case
  • slice_forward from a parameter, paths_between, flows_to_call and flows_to_argument answer on a Java graph, over both the JSON and the Neo4j backend
  • A test that would have failed before: an end-to-end witness that a caller's argument reaches a callee's parameter across at least two frames

Describe alternatives you've considered

Not stated in the original issue.

Additional context

Scope boundary

In scope: move the codeanalyzer-java pin to 3.0.3, confirm the port-edge probe passes on its own, remove the refusal so the four verbs answer on Java, and regenerate any committed Java -a 4 fixtures (ddg roughly doubles).

Out of scope: the analyzer-side work (shipped); the wheel/JVM uptake (#339); the pin-vs-releases/latest release mechanism (#336).

Caveats and known risks

  • The attachment of actual_in/actual_out is deliberately coarse: every actual of a call site is fed by the one statement containing it, not by the reaching definition of that particular argument. Paths are complete; per-argument precision needs the statement's own incoming edges. Do not assert a tighter shape than the analyzer promises.
  • Under --l3-engine wala there are no @entry edges to mirror, so formal_in ports stay unattached on that engine. If the SDK ever selects that engine, the probe must keep gating there.
  • 140 formal_in ports still have out-degree zero on daytrader8 — unread parameters. A probe asserting "every port is attached" would fail on correct output.

Definition of done

  • The four verbs return non-empty, correct answers on a Java fixture where a value crosses two call boundaries, and the same query over Neo4j agrees with the JSON backend
  • The gate/probe code path that refused them is gone or narrowed to the wala-engine case, with a test covering the reason it stays

Activity

  1. rahlk commented on Sep 7, 2026

    @rahlk
    CollaboratorAuthor

    Verified against real 3.0.3 output ahead of the work. Three things to record, one of which changes a line in the definition of done.

    The probe lifts on its own — it should be kept, not removed. Ran the four verbs through JCodeanalyzer over a whole-project -a 4 daytrader8 payload produced by 3.0.3, with no SDK change of any kind:

    3.0.2 3.0.3
    _ports_carry_dependence False True
    slice_forward('orderID') refused 45 nodes, complete
    flows_to_call → completeOrder refused True
    flows_to_argument('orderID') refused True
    paths_between across the two refused 10 paths

    The shortest cross-callable path is 21 hops and alternates the four vocabularies the surface promises — data, argument, return, control — so the witness the third goal asks for exists in the fixture already.

    The probe asks the data whether ports carry dependence, never the analyzer version. That is what makes it lift by itself, and it is also why deleting it would be a regression rather than a cleanup: the Neo4j floor is 3.0.1, so a graph emitted by 3.0.1 or 3.0.2 is still attachable and still needs the refusal, and the --l3-engine wala case in the caveats needs it too. Suggest the last DoD line becomes: the probe is kept, a test pins that it refuses on a 3.0.2-shaped graph and answers on a 3.0.3-shaped one, and no version literal appears in the decision.

    A narrow guard is worth adding while here. The local backend computes the probe as "some formal_in has an outgoing edge" without requiring that edge's target to exist as a body node; the Neo4j backend requires a materialised node at both ends. Those two definitions can disagree on output carrying dangling endpoints — which 3.0.2 had and 3.0.3 fixed, so the divergence is unobservable today and was unobservable before only because the out-degree was uniformly zero. One dst in nodes clause closes it.

    The committed fixtures move much less than "roughly doubles". That figure is the whole-project number; on the two committed files the change is surgical:

    a1 (L1) a4 (L4)
    analyzer stamp 3.0.2 → 3.0.3 3.0.2 → 3.0.3
    ddg 0 → 0 1,491 → 2,358
    cdg / cfg / summary / call_graph / param_in / param_out / artifacts / callables / units all unchanged all unchanged

    Every one of a4's 867 new edges is an ssa port crossing; points-to stays at 320, self-loops stay at 20, and dangling endpoints are 0 in both releases on this fixture — so #228's fix is not observable here, only on the whole project. Regenerated copies of both are ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions