Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main/java/com/ibm/cldk/artifacts/DataflowTiers.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ private static void collect(Map<String, JType> types, String source, Map<String,

/** L3: the one literal every reaching definition of {@code var} closes on at {@code useLocalId}. */
static String intra(Owner owner, String useLocalId, String var) {
if (owner == null || owner.callable.getDdg() == null || owner.source == null) {
if (owner == null || var == null || owner.callable.getDdg() == null
|| owner.source == null) {
return null;
}
return IntraTier.reachingLiteral(owner.callable, owner.source, useLocalId, var);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@ void aLocalClosesOverTheDdgAtLevelThree() throws Exception {
assertTrue(r.unresolved.isEmpty());
}

// A dispatcher target that is neither a literal nor a bare identifier (a call expression) used
// to NPE in the dataflow tier: `Site.varName()` returns null for this shape too, and the tier
// dereferenced it unguarded.
@Test
void aCallExpressionTargetStaysNonLiteralInsteadOfCrashing() throws Exception {
ViewDispatches.Result r = run(HEAD
+ " void doGet(HttpServletRequest req, HttpServletResponse res) {\n"
+ " req.getRequestDispatcher(computePage()).forward(req, res);\n"
+ " }\n"
+ " String computePage() { return \"/pages/x.jsp\"; }\n"
+ "}\n", 3);
assertTrue(r.dispatches.isEmpty());
assertEquals("non-literal", only(r).getReason());
}

@Test
void twoDisagreeingDefinitionsStayNonLiteral() throws Exception {
ViewDispatches.Result r = run(HEAD
Expand Down
26 changes: 26 additions & 0 deletions src/test/java/com/ibm/cldk/artifacts/ViewNameDispatchTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ private ViewDispatches.Result run(String controller, String properties, String..
throws Exception {
ServletApiStubs.write(root, "src/main/java/org/springframework/stereotype/Controller.java",
"package org.springframework.stereotype;\npublic @interface Controller {}\n");
ServletApiStubs.write(root,
"src/main/java/org/springframework/web/bind/annotation/RestController.java",
"package org.springframework.web.bind.annotation;\npublic @interface RestController {}\n");
ServletApiStubs.write(root,
"src/main/java/org/springframework/web/bind/annotation/GetMapping.java",
"package org.springframework.web.bind.annotation;\n"
Expand Down Expand Up @@ -136,4 +139,27 @@ void aViewNameWithNoTemplateIsNoSuchArtifact() throws Exception {
assertEquals("missing", r.unresolved.get(0).getTarget());
assertEquals("view-name", r.unresolved.get(0).getVia());
}

// Robot Shop's shipping service: a `@RestController`'s `return String.valueOf(x)` is
// still gated into the view-name tier (the entrypoint check does not yet distinguish
// `@RestController` from `@Controller`) and the target is neither a literal nor a bare
// identifier, so `Site.varName()` is null. That null used to reach `DataflowTiers.intra`
// unguarded and NPE in `IntraTier.reachingLiteral`; it must instead just stay unresolved.
@Test
void aNonIdentifierReturnExpressionStaysNonLiteralInsteadOfCrashing() throws Exception {
ViewDispatches.Result r = run(
"package demo;\n"
+ "import org.springframework.web.bind.annotation.RestController;\n"
+ "import org.springframework.web.bind.annotation.GetMapping;\n"
+ "@RestController\npublic class Home {\n"
+ " @GetMapping(\"/count\") public String count() {\n"
+ " long n = 10;\n"
+ " return String.valueOf(n);\n"
+ " }\n}\n",
null);
assertTrue(r.dispatches.isEmpty());
assertEquals(1, r.unresolved.size());
assertEquals("non-literal", r.unresolved.get(0).getReason());
assertEquals("view-name", r.unresolved.get(0).getVia());
}
}