diff --git a/idealPDS/src/main/java/typestate/ChainingBackwardFlowFunction.java b/idealPDS/src/main/java/typestate/ChainingBackwardFlowFunction.java new file mode 100644 index 00000000..e430e2e4 --- /dev/null +++ b/idealPDS/src/main/java/typestate/ChainingBackwardFlowFunction.java @@ -0,0 +1,65 @@ +/** + * ***************************************************************************** + * Copyright (c) 2018 Fraunhofer IEM, Paderborn, Germany + *
+ * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + *
+ * SPDX-License-Identifier: EPL-2.0 + *
+ * Contributors:
+ * Johannes Spaeth - initial API and implementation
+ * *****************************************************************************
+ */
+package typestate;
+
+import boomerang.flowfunction.DefaultBackwardFlowFunction;
+import boomerang.options.IAllocationSite;
+import boomerang.scope.ControlFlowGraph;
+import boomerang.scope.DeclaredMethod;
+import boomerang.scope.InvokeExpr;
+import boomerang.scope.Statement;
+import boomerang.scope.Val;
+import boomerang.solver.Strategies;
+import boomerang.utils.MethodWrapper;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import sync.pds.solver.nodes.Node;
+import wpds.interfaces.State;
+
+public class ChainingBackwardFlowFunction extends DefaultBackwardFlowFunction {
+
+ private final Collection
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Johannes Spaeth - initial API and implementation
+ * *****************************************************************************
+ */
+package typestate;
+
+import boomerang.flowfunction.DefaultFlowFunctionFactory;
+import boomerang.flowfunction.IBackwardFlowFunction;
+import boomerang.flowfunction.IForwardFlowFunction;
+import boomerang.options.BoomerangOptions;
+import boomerang.scope.FrameworkScope;
+import boomerang.solver.BackwardBoomerangSolver;
+import boomerang.solver.ForwardBoomerangSolver;
+import boomerang.solver.Strategies;
+import boomerang.utils.MethodWrapper;
+import java.util.Collection;
+
+/**
+ * Flow function factory that extends the {@link DefaultFlowFunctionFactory} by adding features to
+ * deal with chained methods when applying the call-to-return flow. Intermediate representations
+ * transform chained method calls into multiple statements and introduce new locals for each
+ * intermediate call. Although the chained calls return the original objects (i.e. an alias), the
+ * default flow function do not find the aliases. The extension in this flow function factory adds
+ * the functionality to collect corresponding aliases, too.
+ *
+ * For example, a program
+ *
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Johannes Spaeth - initial API and implementation
+ * *****************************************************************************
+ */
+package typestate;
+
+import boomerang.ForwardQuery;
+import boomerang.flowfunction.DefaultForwardFlowFunction;
+import boomerang.scope.ControlFlowGraph;
+import boomerang.scope.DeclaredMethod;
+import boomerang.scope.InvokeExpr;
+import boomerang.scope.Statement;
+import boomerang.scope.Val;
+import boomerang.solver.Strategies;
+import boomerang.utils.MethodWrapper;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import sync.pds.solver.nodes.Node;
+import wpds.interfaces.State;
+
+public class ChainingForwardFlowFunction extends DefaultForwardFlowFunction {
+
+ private final Collection
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Johannes Spaeth - initial API and implementation
+ * *****************************************************************************
+ */
+package chains;
+
+public class Chain {
+
+ public Chain chain1() {
+ return this;
+ }
+
+ public Chain chain2() {
+ return this;
+ }
+}
diff --git a/idealPDS/src/test/java/chains/ChainStateMachine.java b/idealPDS/src/test/java/chains/ChainStateMachine.java
new file mode 100644
index 00000000..5a616dcd
--- /dev/null
+++ b/idealPDS/src/test/java/chains/ChainStateMachine.java
@@ -0,0 +1,101 @@
+/**
+ * *****************************************************************************
+ * Copyright (c) 2018 Fraunhofer IEM, Paderborn, Germany
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Johannes Spaeth - initial API and implementation
+ * *****************************************************************************
+ */
+package chains;
+
+import boomerang.WeightedForwardQuery;
+import boomerang.scope.ControlFlowGraph;
+import java.util.Collection;
+import java.util.Collections;
+import typestate.TransitionFunction;
+import typestate.finiteautomata.MatcherTransition;
+import typestate.finiteautomata.State;
+import typestate.finiteautomata.TypeStateMachineWeightFunctions;
+
+public class ChainStateMachine extends TypeStateMachineWeightFunctions {
+
+ public enum States implements State {
+ INIT,
+ CHAIN1,
+ CHAIN2;
+
+ @Override
+ public boolean isErrorState() {
+ return this != CHAIN2;
+ }
+
+ @Override
+ public boolean isInitialState() {
+ return this == INIT;
+ }
+
+ @Override
+ public boolean isAccepting() {
+ return this == CHAIN2;
+ }
+ }
+
+ public ChainStateMachine() {
+ addTransition(
+ new MatcherTransition(
+ States.INIT,
+ ".*chain1.*",
+ MatcherTransition.Parameter.This,
+ States.CHAIN1,
+ MatcherTransition.Type.OnCallToReturn));
+ addTransition(
+ new MatcherTransition(
+ States.CHAIN1,
+ ".chain1.*",
+ MatcherTransition.Parameter.This,
+ States.CHAIN1,
+ MatcherTransition.Type.OnCallToReturn));
+ addTransition(
+ new MatcherTransition(
+ States.CHAIN1,
+ ".*chain2.*",
+ MatcherTransition.Parameter.This,
+ States.CHAIN2,
+ MatcherTransition.Type.OnCallToReturn));
+ addTransition(
+ new MatcherTransition(
+ States.CHAIN2,
+ ".chain1.*",
+ MatcherTransition.Parameter.This,
+ States.CHAIN1,
+ MatcherTransition.Type.OnCallToReturn));
+ addTransition(
+ new MatcherTransition(
+ States.CHAIN2,
+ ".chain2.*",
+ MatcherTransition.Parameter.This,
+ States.CHAIN2,
+ MatcherTransition.Type.OnCallToReturn));
+ }
+
+ @Override
+ public Collection
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Johannes Spaeth - initial API and implementation
+ * *****************************************************************************
+ */
+package chains;
+
+import boomerang.utils.MethodWrapper;
+import java.util.Set;
+import typestate.ChainingFlowFunctionFactory;
+
+public class ChainingTestFlowFunctionFactory extends ChainingFlowFunctionFactory {
+
+ public ChainingTestFlowFunctionFactory() {
+ super(
+ Set.of(
+ new MethodWrapper(Chain.class.getName(), "chain1", Chain.class.getName()),
+ new MethodWrapper(Chain.class.getName(), "chain2", Chain.class.getName())));
+ }
+}
diff --git a/idealPDS/src/test/java/chains/MethodChainingTest.java b/idealPDS/src/test/java/chains/MethodChainingTest.java
new file mode 100644
index 00000000..bd0e9758
--- /dev/null
+++ b/idealPDS/src/test/java/chains/MethodChainingTest.java
@@ -0,0 +1,108 @@
+/**
+ * *****************************************************************************
+ * Copyright (c) 2018 Fraunhofer IEM, Paderborn, Germany
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Johannes Spaeth - initial API and implementation
+ * *****************************************************************************
+ */
+package chains;
+
+import assertions.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import test.IDEALTestRunnerInterceptor;
+import test.TestConfig;
+import test.TestParameters;
+
+@ExtendWith(IDEALTestRunnerInterceptor.class)
+@TestConfig(
+ stateMachine = ChainStateMachine.class,
+ excludedClasses = {Chain.class},
+ flowFunctions = TestConfig.FlowFunctions.CHAINING)
+public class MethodChainingTest {
+
+ @Test
+ @TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
+ public void noChainingTest1() {
+ Chain chain = new Chain();
+ chain.chain1();
+
+ Assertions.mustBeInErrorState(chain);
+ }
+
+ @Test
+ @TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
+ public void chainingTest1() {
+ Chain chain = new Chain();
+ chain.chain1();
+
+ Assertions.mustBeInErrorState(chain);
+ }
+
+ @Test
+ @TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
+ public void noChainingTest2() {
+ Chain chain = new Chain();
+ chain.chain1();
+ chain.chain2();
+
+ Assertions.mustBeInAcceptingState(chain);
+ }
+
+ @Test
+ @TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
+ public void chainingTest2() {
+ Chain chain = new Chain();
+ chain.chain1().chain2();
+
+ Assertions.mustBeInAcceptingState(chain);
+ }
+
+ @Test
+ @TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
+ public void noChainingTest3() {
+ Chain chain = new Chain();
+ chain.chain1();
+ chain.chain1();
+ chain.chain2();
+
+ Assertions.mustBeInAcceptingState(chain);
+ }
+
+ @Test
+ @TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
+ public void chainingTest3() {
+ Chain chain = new Chain();
+ chain.chain1().chain1().chain2();
+
+ Assertions.mustBeInAcceptingState(chain);
+ }
+
+ @Test
+ @TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
+ public void noChainingTest4() {
+ Chain chain = new Chain();
+ chain.chain1();
+ chain.chain1();
+ chain.chain2();
+ chain.chain1();
+
+ Assertions.mustBeInErrorState(chain);
+ }
+
+ @Test
+ @TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
+ public void chainingTest4() {
+ Chain chain = new Chain();
+ chain.chain1().chain1().chain2().chain1();
+
+ Assertions.mustBeInErrorState(chain);
+ }
+}
diff --git a/idealPDS/src/test/java/test/IDEALTestRunnerInterceptor.java b/idealPDS/src/test/java/test/IDEALTestRunnerInterceptor.java
index a9100233..ff3db30f 100644
--- a/idealPDS/src/test/java/test/IDEALTestRunnerInterceptor.java
+++ b/idealPDS/src/test/java/test/IDEALTestRunnerInterceptor.java
@@ -14,6 +14,11 @@
*/
package test;
+import boomerang.flowfunction.DefaultFlowFunctionFactory;
+import boomerang.flowfunction.IFlowFunctionFactory;
+import boomerang.options.BoomerangOptions;
+import boomerang.solver.Strategies;
+import chains.ChainingTestFlowFunctionFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -32,6 +37,7 @@ public class IDEALTestRunnerInterceptor
implements BeforeAllCallback, InvocationInterceptor, AfterEachCallback {
private IDEALTestingFramework testingFramework;
+ private BoomerangOptions options;
@Override
public void beforeAll(ExtensionContext context) {
@@ -78,6 +84,7 @@ public void beforeAll(ExtensionContext context) {
.map(Class::getName)
.collect(Collectors.toList());
testingFramework = new IDEALTestingFramework(stateMachine, includedClasses, excludedClasses);
+ options = createOptions(testConfig);
}
@Override
@@ -97,7 +104,7 @@ public void interceptTestMethod(
+ "' in class '"
+ testClassName
+ "' is not annotated with '"
- + TestConfig.class.getSimpleName()
+ + TestParameters.class.getSimpleName()
+ "'");
}
@@ -111,12 +118,12 @@ public void interceptTestMethod(
testClassName,
testMethodName,
parameters.expectedSeedCount(),
- parameters.expectedAssertionCount());
+ parameters.expectedAssertionCount(),
+ options);
try {
invocation.proceed();
} catch (Throwable ignored) {
-
}
}
@@ -124,4 +131,26 @@ public void interceptTestMethod(
public void afterEach(ExtensionContext context) {
testingFramework.cleanUp();
}
+
+ private BoomerangOptions createOptions(TestConfig config) {
+ IFlowFunctionFactory factory = getFlowFunctionFactory(config.flowFunctions());
+
+ return BoomerangOptions.builder()
+ .withFlowFunctionFactory(factory)
+ .withStaticFieldStrategy(Strategies.StaticFieldStrategy.FLOW_SENSITIVE)
+ .withAnalysisTimeout(-1)
+ .enableAllowMultipleQueries(true)
+ .build();
+ }
+
+ private IFlowFunctionFactory getFlowFunctionFactory(TestConfig.FlowFunctions flowFunctions) {
+ switch (flowFunctions) {
+ case DEFAULT:
+ return new DefaultFlowFunctionFactory();
+ case CHAINING:
+ return new ChainingTestFlowFunctionFactory();
+ default:
+ throw new RuntimeException("Unknown FlowFunctions: " + flowFunctions);
+ }
+ }
}
diff --git a/idealPDS/src/test/java/test/IDEALTestingFramework.java b/idealPDS/src/test/java/test/IDEALTestingFramework.java
index c5a4c4d1..b7234009 100644
--- a/idealPDS/src/test/java/test/IDEALTestingFramework.java
+++ b/idealPDS/src/test/java/test/IDEALTestingFramework.java
@@ -30,7 +30,6 @@
import boomerang.scope.Method;
import boomerang.scope.Statement;
import boomerang.scope.Val;
-import boomerang.solver.Strategies;
import boomerang.utils.MethodWrapper;
import ideal.IDEALAnalysis;
import ideal.IDEALAnalysisDefinition;
@@ -61,7 +60,11 @@ public IDEALTestingFramework(
}
public void analyze(
- String targetClassName, String targetMethodName, int expectedSeeds, int expectedAssertions) {
+ String targetClassName,
+ String targetMethodName,
+ int expectedSeeds,
+ int expectedAssertions,
+ BoomerangOptions options) {
LOGGER.info(
"Running '{}' in class '{}' with {} assertions",
targetMethodName,
@@ -87,7 +90,8 @@ public void analyze(
// Run IDEal
StoreIDEALResultHandler{@code
+ * l.chain().chain();
+ * }
+ *
+ * is transformed into the intermediate representation
+ *
+ * {@code
+ * $s0 = l.chain();
+ * $s1 = $s0.chain();
+ * }
+ *
+ * The extended flow functions make sure to collect $s0 and $s1 as alias s.t. the analysis can
+ * collect the second call to chain().
+ */
+public class ChainingFlowFunctionFactory extends DefaultFlowFunctionFactory {
+
+ private final Collection