Skip to content
Open
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
34 changes: 24 additions & 10 deletions idealPDS/src/main/java/typestate/TransitionFunctionImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
Expand All @@ -35,15 +36,16 @@
public class TransitionFunctionImpl implements TransitionFunction {

@NonNull private final Multimap<Transition, StatementSequence> stateChangeSequences;
@NonNull private final Statement stateChangeStatement;
@NonNull private final LinkedHashSet<Statement> stateChangeStatements;

public TransitionFunctionImpl(
@NonNull Transition transition, @NonNull Statement stateChangeStatement) {
this.stateChangeSequences =
ImmutableMultimap.of(
transition,
new StatementSequence(new StatementSequence.Entry(stateChangeStatement, transition)));
this.stateChangeStatement = stateChangeStatement;
this.stateChangeStatements = new LinkedHashSet<>();
stateChangeStatements.add(stateChangeStatement);
}

public TransitionFunctionImpl(
Expand All @@ -56,14 +58,23 @@ public TransitionFunctionImpl(
}

this.stateChangeSequences = ImmutableMultimap.copyOf(sequencesMap);
this.stateChangeStatement = stateChangeStatement;
this.stateChangeStatements = new LinkedHashSet<>();
stateChangeStatements.add(stateChangeStatement);
}

public TransitionFunctionImpl(
@NonNull Multimap<Transition, StatementSequence> transitionStatementSequences,
@NonNull Statement stateChangeStatement) {
this.stateChangeSequences = ImmutableMultimap.copyOf(transitionStatementSequences);
this.stateChangeStatement = stateChangeStatement;
this.stateChangeStatements = new LinkedHashSet<>();
stateChangeStatements.add(stateChangeStatement);
}

public TransitionFunctionImpl(
@NonNull Multimap<Transition, StatementSequence> transitionStatementSequences,
@NonNull LinkedHashSet<Statement> stateChangeStatements) {
this.stateChangeSequences = ImmutableMultimap.copyOf(transitionStatementSequences);
this.stateChangeStatements = new LinkedHashSet<>(stateChangeStatements);
}
Comment on lines +73 to 78

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as this is the only ctor using/storing multiple elements it seems to make sense to create a seperate specialized class for it and keep storing the single element here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm what's the benefit of a separate class? The single element case is handled in the ctor above. I could also make this ctor private (if you're worried about the class' API - it would make overriding the combineWith method a bit more cumbersome, though).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

less memory / no linked list overhead - in the cases where we don't need it. I haven't benchmarked it but it seems like a core element that can be generated quite some times.


@NonNull
Expand All @@ -73,7 +84,7 @@ public Multimap<Transition, StatementSequence> getStateChangeSequences() {
}

public Statement getStateChangeStatement() {
return stateChangeStatement;
return stateChangeStatements.iterator().next();
}

@NonNull
Expand Down Expand Up @@ -122,7 +133,7 @@ public Weight extendWith(@NonNull Weight other) {
}
}
}
return new TransitionFunctionImpl(result, func.stateChangeStatement);
return new TransitionFunctionImpl(result, func.getStateChangeStatement());
}

@NonNull
Expand All @@ -146,16 +157,19 @@ public Weight combineWith(@NonNull Weight other) {
transitions.putAll(idTransition, statements);
}

return new TransitionFunctionImpl(transitions, this.stateChangeStatement);
return new TransitionFunctionImpl(transitions, this.getStateChangeStatement());
}

TransitionFunctionImpl func = (TransitionFunctionImpl) other;

Multimap<Transition, StatementSequence> sequences = HashMultimap.create();
sequences.putAll(stateChangeSequences);
sequences.putAll(func.stateChangeSequences);
LinkedHashSet<Statement> mergedStateChangeStatements = new LinkedHashSet<>();
mergedStateChangeStatements.addAll(func.stateChangeStatements);
mergedStateChangeStatements.addAll(stateChangeStatements);

return new TransitionFunctionImpl(sequences, func.stateChangeStatement);
return new TransitionFunctionImpl(sequences, mergedStateChangeStatements);
}

@Override
Expand All @@ -169,11 +183,11 @@ public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
TransitionFunctionImpl that = (TransitionFunctionImpl) o;
return Objects.equals(stateChangeSequences, that.stateChangeSequences)
&& Objects.equals(stateChangeStatement, that.stateChangeStatement);
&& Objects.equals(stateChangeStatements, that.stateChangeStatements);
}

@Override
public int hashCode() {
return Objects.hash(stateChangeSequences, stateChangeStatement);
return Objects.hash(stateChangeSequences, stateChangeStatements);
}
}
57 changes: 57 additions & 0 deletions idealPDS/src/test/java/typestate/TransitionFunctionImplTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* *****************************************************************************
* Copyright (c) 2018 Fraunhofer IEM, Paderborn, Germany
* <p>
* 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.
* <p>
* SPDX-License-Identifier: EPL-2.0
* <p>
* Contributors:
* Johannes Spaeth - initial API and implementation
* *****************************************************************************
*/
package typestate;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;

import boomerang.scope.Method;
import boomerang.scope.Statement;
import boomerang.utils.MethodWrapper;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import test.TestingFramework;
import wpds.impl.Weight;

public class TransitionFunctionImplTest {

@Test
public void testCombineWithCommutative() {
TestingFramework testingFramework = new TestingFramework();
testingFramework.getFrameworkScope(
new MethodWrapper(getClass().getName(), "testCombineWithCommutative"));
Method method = testingFramework.getTestMethod();
Statement first = method.getStatements().get(0);
Statement second = method.getStatements().get(1);
assertNotEquals(first, second);
TransitionFunctionImpl firstTransitionFunction =
new TransitionFunctionImpl(Collections.emptySet(), first);
TransitionFunctionImpl secondTransitionFunction =
new TransitionFunctionImpl(Collections.emptySet(), second);
assertNotEquals(firstTransitionFunction, secondTransitionFunction);
Weight firstSecondResult = firstTransitionFunction.combineWith(secondTransitionFunction);
Weight secondFirstResult = secondTransitionFunction.combineWith(firstTransitionFunction);
assertEquals(firstSecondResult, secondFirstResult);
// ensure that the behavior of getChangeStatement() is as in the
// non-commutative implementation (whether this behavior is "meaningful" is
// debatable, though)
assertEquals(
secondTransitionFunction.getStateChangeStatement(),
((TransitionFunctionImpl) firstSecondResult).getStateChangeStatement());
assertEquals(
firstTransitionFunction.getStateChangeStatement(),
((TransitionFunctionImpl) secondFirstResult).getStateChangeStatement());
}
}
22 changes: 22 additions & 0 deletions idealPDS/src/test/java/typestate/VectorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,26 @@ public void staticAccessTest() {
v.firstElement();
Assertions.mustBeInErrorState(v);
}

public void doSth(Vector<Object> x, Object element) {
if (staticallyUnknown()) {
x.add(element);
} else {
element = x.elementAt(0);
}
}

@Test
@TestParameters(expectedSeedCount = 1, expectedAssertionCount = 1)
public void testNoStackOverflowDueToNonCommutativeCombineWith() {
Vector<Object> x = new Vector<>();
Object element = new Object();
x.add(element);
while (staticallyUnknown()) {
// the method call seems to be crucial for triggering the StackOverflowError
// (inlining the code from doSth does not trigger the error)
doSth(x, element);
}
Assertions.mustBeInAcceptingState(x);
}
}
Loading