You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Running the released 3.3.0 on daytrader8 resolves 3 of its view dispatches and leaves 17 non-literal. The real mechanism is not a literal on any path: TradeServletAction.requestDispatch(page) and TradeConfigServlet dispatch to TradeConfig.getPage(N), which is return webUI[webInterface][pageNumber] over a static String[][] table of 3 × N page paths (/welcome.jsp / /welcomeImg.jsp / …) indexed by a runtime-selected interface (TradeConfig.java:169-208, 481, 730). Neither the literal tier nor the DDG / call-graph tiers can close it, correctly — no single target exists. The remaining 20 JSPs are therefore unreachable in the graph even though the set of pages the code can reach is fully static.
This issue adds a table tier: a target expression that is a call to a method whose body returns an element of a static String[] / String[][] field with a literal initializer closes on every string literal in that initializer, emitting one J_DISPATCHES_TO per matching artifact with prov: ["table"] — a may-dispatch edge, the first deliberate over-approximation in this pass.
Scope boundary
The table shape only: T.m(args) where every return in m is an array access rooted at a static String[]/String[][] field of T with an array-initializer of string literals, either directly at the dispatch site or as a caller's argument to a parameter the interprocedural tier binds. Not arbitrary constant propagation, not maps, not enums, not switch-returned literals. Not routes: table entries that name no artifact (/app?action=…) are dropped from the edge set, and a table with no matching entry is no-such-artifact. The exactly-one rule stays for literal and dataflow edges; only table edges are many-per-site, and prov says so.
Goals
ViewDispatches table tier at every level, after the literal tier: parse the target expression, resolve the callee by declaring-type simple name + method name within the tree (unique match, else give up), slice the callee's body_span from the module source, require every return to be an array access rooted at one static field, read that field's initializer, collect its string literals.
Interprocedural tier accepts a table-closed argument per caller: a parameter closes when every caller's argument is a literal or a table; the union is the candidate set; prov is ["table"] when any table contributed, else ["dataflow"].
Emit one edge per matched artifact; unresolved when none matches (no-such-artifact, target absent, prov lists the attempted tiers including table).
prov vocabulary on J_DISPATCHES_TO / view_dispatches[] documented as literal | table | dataflow; JSON oracle unchanged (stringList); README and SCHEMA_DECISIONS.md D33.
Caveats and known risks
May-dispatch is an over-approximation by construction: on DayTrader every table site reaches all three interface variants of every page. A consumer wanting "the page this build serves" must filter on prov; the edge does not claim a single path.
The callee is found by simple type name + method name, not by L2 callee (so it works at -a 1); a project with two types of the same simple name both declaring the method gives up rather than picks one.
Body slicing depends on body_span (3.2.0+) and on the module source being captured.
Levels: the tier itself is level-independent; the parameter case still needs -a 4 for the call-graph tier that reaches the callers.
Definition of done
Fixture: Pages.page(i) over {{"/a.jsp","/b.jsp","/x"},{"/aImg.jsp","/bImg.jsp","/x"}} with a.jsp b.jsp aImg.jsp bImg.jsp on disk: req.getRequestDispatcher(Pages.page(1)).include(req,res) at -a 1 yields exactly the four edges, all prov: ["table"], /x contributes nothing; a table naming no file yields one no-such-artifact record with prov containing table.
Parameter case: show(Pages.page(0)) and show("/c.jsp") calling show(String page) at -a 4 yields the four table pages plus c.jsp, prov: ["table"]; at -a 3 the site stays non-literal.
A return that is not an array access, or a field without a literal initializer, does not close (test each).
daytrader8 with the built jar: view_dispatches grows from 3 to the table's reachable pages, every .jsp in webUI that exists under src/main/webapp has an incoming J_DISPATCHES_TO, and the 3 literal edges are unchanged.
Full suite green; -a 1 ⊆ -a 3 ⊆ -a 4 still holds on the fixture.
Plan (optional)
Spec: codellm-devkit/.github
docs/design/specs/2026-09-11-java-view-templates-and-dispatch.md§ 4.5 (amendment on PR codellm-devkit/.github#80). Follows #259 / codeanalyzer-java 3.3.0.Problem
Running the released 3.3.0 on daytrader8 resolves 3 of its view dispatches and leaves 17
non-literal. The real mechanism is not a literal on any path:TradeServletAction.requestDispatch(page)andTradeConfigServletdispatch toTradeConfig.getPage(N), which isreturn webUI[webInterface][pageNumber]over a staticString[][]table of 3 × N page paths (/welcome.jsp//welcomeImg.jsp/ …) indexed by a runtime-selected interface (TradeConfig.java:169-208, 481, 730). Neither the literal tier nor the DDG / call-graph tiers can close it, correctly — no single target exists. The remaining 20 JSPs are therefore unreachable in the graph even though the set of pages the code can reach is fully static.This issue adds a table tier: a target expression that is a call to a method whose body returns an element of a static
String[]/String[][]field with a literal initializer closes on every string literal in that initializer, emitting oneJ_DISPATCHES_TOper matching artifact withprov: ["table"]— a may-dispatch edge, the first deliberate over-approximation in this pass.Scope boundary
The table shape only:
T.m(args)where everyreturninmis an array access rooted at a staticString[]/String[][]field ofTwith an array-initializer of string literals, either directly at the dispatch site or as a caller's argument to a parameter the interprocedural tier binds. Not arbitrary constant propagation, not maps, not enums, notswitch-returned literals. Not routes: table entries that name no artifact (/app?action=…) are dropped from the edge set, and a table with no matching entry isno-such-artifact. The exactly-one rule stays forliteralanddataflowedges; onlytableedges are many-per-site, andprovsays so.Goals
ViewDispatchestable tier at every level, after the literal tier: parse the target expression, resolve the callee by declaring-type simple name + method name within the tree (unique match, else give up), slice the callee'sbody_spanfrom the module source, require everyreturnto be an array access rooted at one static field, read that field'sinitializer, collect its string literals.provis["table"]when any table contributed, else["dataflow"].no-such-artifact,targetabsent,provlists the attempted tiers includingtable).provvocabulary onJ_DISPATCHES_TO/view_dispatches[]documented asliteral | table | dataflow; JSON oracle unchanged (stringList); README andSCHEMA_DECISIONS.mdD33.Caveats and known risks
prov; the edge does not claim a single path.callee(so it works at-a 1); a project with two types of the same simple name both declaring the method gives up rather than picks one.body_span(3.2.0+) and on the modulesourcebeing captured.-a 4for the call-graph tier that reaches the callers.Definition of done
Pages.page(i)over{{"/a.jsp","/b.jsp","/x"},{"/aImg.jsp","/bImg.jsp","/x"}}witha.jsp b.jsp aImg.jsp bImg.jspon disk:req.getRequestDispatcher(Pages.page(1)).include(req,res)at-a 1yields exactly the four edges, allprov: ["table"],/xcontributes nothing; a table naming no file yields oneno-such-artifactrecord withprovcontainingtable.show(Pages.page(0))andshow("/c.jsp")callingshow(String page)at-a 4yields the four table pages plusc.jsp,prov: ["table"]; at-a 3the site staysnon-literal.returnthat is not an array access, or a field without a literal initializer, does not close (test each).view_dispatchesgrows from 3 to the table's reachable pages, every.jspinwebUIthat exists undersrc/main/webapphas an incomingJ_DISPATCHES_TO, and the 3 literal edges are unchanged.-a 1 ⊆ -a 3 ⊆ -a 4still holds on the fixture.