stage 2 update loop
This commit is contained in:
@@ -88,4 +88,166 @@ class JdtDataFlowModelTest {
|
||||
// Both branches assign "SAME", so it converges to "SAME"
|
||||
assertThat(val).isEqualTo("SAME");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFieldConstantPropagation(@TempDir Path tempDir) throws IOException {
|
||||
Path comFoo = tempDir.resolve("com/foo");
|
||||
Files.createDirectories(comFoo);
|
||||
Files.writeString(comFoo.resolve("Test.java"),
|
||||
"package com.foo;\n" +
|
||||
"public class Test {\n" +
|
||||
" private static final String MY_CONST = \"CONST_VAL\";\n" +
|
||||
" public void run() {\n" +
|
||||
" String a = MY_CONST;\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
|
||||
assertThat(td).isNotNull();
|
||||
|
||||
MethodDeclaration md = td.getMethods()[0];
|
||||
VariableDeclarationStatement aStmt = (VariableDeclarationStatement) md.getBody().statements().get(0);
|
||||
VariableDeclarationFragment aFrag = (VariableDeclarationFragment) aStmt.fragments().get(0);
|
||||
Expression initializer = aFrag.getInitializer();
|
||||
|
||||
DataFlowModel model = new JdtDataFlowModel(context);
|
||||
String val = model.resolveValue(initializer, context);
|
||||
assertThat(val).isEqualTo("CONST_VAL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTernaryExpressionResolution(@TempDir Path tempDir) throws IOException {
|
||||
Path comFoo = tempDir.resolve("com/foo");
|
||||
Files.createDirectories(comFoo);
|
||||
Files.writeString(comFoo.resolve("Test.java"),
|
||||
"package com.foo;\n" +
|
||||
"public class Test {\n" +
|
||||
" public void run(boolean flag) {\n" +
|
||||
" String a = flag ? \"TRUE_VAL\" : \"FALSE_VAL\";\n" +
|
||||
" String b = true ? \"TRUE_VAL\" : \"FALSE_VAL\";\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
|
||||
assertThat(td).isNotNull();
|
||||
|
||||
MethodDeclaration md = td.getMethods()[0];
|
||||
|
||||
// 1. Unresolvable condition (flag)
|
||||
VariableDeclarationStatement aStmt = (VariableDeclarationStatement) md.getBody().statements().get(0);
|
||||
VariableDeclarationFragment aFrag = (VariableDeclarationFragment) aStmt.fragments().get(0);
|
||||
Expression initA = aFrag.getInitializer();
|
||||
|
||||
DataFlowModel model = new JdtDataFlowModel(context);
|
||||
List<Expression> defsA = model.getReachingDefinitions(initA);
|
||||
assertThat(defsA).hasSize(1);
|
||||
assertThat(defsA.get(0)).isInstanceOf(ConditionalExpression.class);
|
||||
|
||||
// 2. Statically resolvable condition (true)
|
||||
VariableDeclarationStatement bStmt = (VariableDeclarationStatement) md.getBody().statements().get(1);
|
||||
VariableDeclarationFragment bFrag = (VariableDeclarationFragment) bStmt.fragments().get(0);
|
||||
Expression initB = bFrag.getInitializer();
|
||||
|
||||
List<Expression> defsB = model.getReachingDefinitions(initB);
|
||||
assertThat(defsB).hasSize(1);
|
||||
assertThat(defsB.get(0)).isInstanceOf(StringLiteral.class);
|
||||
assertThat(((StringLiteral) defsB.get(0)).getLiteralValue()).isEqualTo("TRUE_VAL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStaticFactoryMethodTracking(@TempDir Path tempDir) throws IOException {
|
||||
Path comFoo = tempDir.resolve("com/foo");
|
||||
Files.createDirectories(comFoo);
|
||||
Files.writeString(comFoo.resolve("Test.java"),
|
||||
"package com.foo;\n" +
|
||||
"public class Test {\n" +
|
||||
" public static String create(String value) {\n" +
|
||||
" return value;\n" +
|
||||
" }\n" +
|
||||
" public void run() {\n" +
|
||||
" String a = create(\"FACTORY_VAL\");\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
|
||||
assertThat(td).isNotNull();
|
||||
|
||||
MethodDeclaration runMd = null;
|
||||
for (MethodDeclaration md : td.getMethods()) {
|
||||
if (md.getName().getIdentifier().equals("run")) {
|
||||
runMd = md;
|
||||
}
|
||||
}
|
||||
assertThat(runMd).isNotNull();
|
||||
|
||||
VariableDeclarationStatement aStmt = (VariableDeclarationStatement) runMd.getBody().statements().get(0);
|
||||
VariableDeclarationFragment aFrag = (VariableDeclarationFragment) aStmt.fragments().get(0);
|
||||
Expression initializer = aFrag.getInitializer();
|
||||
|
||||
DataFlowModel model = new JdtDataFlowModel(context);
|
||||
String val = model.resolveValue(initializer, context);
|
||||
assertThat(val).isEqualTo("FACTORY_VAL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoWhileLoopFlow(@TempDir Path tempDir) throws IOException {
|
||||
Path comFoo = tempDir.resolve("com/foo");
|
||||
Files.createDirectories(comFoo);
|
||||
Files.writeString(comFoo.resolve("Test.java"),
|
||||
"package com.foo;\n" +
|
||||
"public class Test {\n" +
|
||||
" public void run(boolean flag) {\n" +
|
||||
" int a = 1;\n" +
|
||||
" do {\n" +
|
||||
" a = 2;\n" +
|
||||
" } while (flag);\n" +
|
||||
" int b = a;\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
|
||||
assertThat(td).isNotNull();
|
||||
|
||||
MethodDeclaration md = td.getMethods()[0];
|
||||
ControlFlowGraph cfg = CfgBuilder.build(md);
|
||||
|
||||
// Find the condition node (flag)
|
||||
ControlFlowGraph.CfgNode condNode = null;
|
||||
for (ControlFlowGraph.CfgNode node : cfg.getNodes()) {
|
||||
if (node.getAstNode() instanceof SimpleName sn && sn.getIdentifier().equals("flag")) {
|
||||
condNode = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertThat(condNode).isNotNull();
|
||||
|
||||
// The condition node should loop back to the statement inside the body (a = 2;)
|
||||
boolean hasLoopBackToBody = false;
|
||||
for (ControlFlowGraph.CfgNode succ : condNode.getSuccessors()) {
|
||||
if (succ.getAstNode() instanceof ExpressionStatement es) {
|
||||
if (es.getExpression() instanceof Assignment assign) {
|
||||
if (assign.getLeftHandSide() instanceof SimpleName sn && sn.getIdentifier().equals("a")) {
|
||||
hasLoopBackToBody = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(hasLoopBackToBody).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,7 +439,7 @@
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payBuilderStatic", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "EventBuilder.buildEvent()",
|
||||
"event" : "new FulfillEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
@@ -447,10 +447,14 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ ]
|
||||
"polymorphicEvents" : [ "OrderEvents.FULFILL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.PAID",
|
||||
"targetState" : "OrderStates.FULFILLED",
|
||||
"event" : "OrderEvents.FULFILL"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
@@ -466,7 +470,7 @@
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.statemachine.polymorphic.PolymorphicController.payBuilderInstance", "click.kamil.examples.statemachine.polymorphic.OrderService.processEvent" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "new EventBuilder().buildInstanceEvent()",
|
||||
"event" : "new CancelEvent()",
|
||||
"className" : "click.kamil.examples.statemachine.polymorphic.OrderService",
|
||||
"methodName" : "processEvent",
|
||||
"sourceFile" : "src/main/java/click/kamil/examples/statemachine/polymorphic/OrderService.java",
|
||||
@@ -474,10 +478,14 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ ]
|
||||
"polymorphicEvents" : [ "OrderEvents.CANCEL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "OrderStates.SUBMITTED",
|
||||
"targetState" : "OrderStates.CANCELED",
|
||||
"event" : "OrderEvents.CANCEL"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
|
||||
Reference in New Issue
Block a user