Skip to content

Commit 41a3010

Browse files
DIvanov503Tim Van Den Broecke
authored andcommitted
Allow selecting which element to remove in LimitedHashMap
1 parent 75f2a30 commit 41a3010

13 files changed

+225
-52
lines changed

base/src/main/java/proguard/analysis/cpa/defaults/LimitedHashMap.java

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,63 +20,123 @@
2020

2121
import java.util.HashMap;
2222
import java.util.Map;
23-
import proguard.analysis.cpa.jvm.util.TriPredicate;
23+
import java.util.Optional;
24+
import java.util.function.BiFunction;
25+
import java.util.function.Function;
26+
import proguard.analysis.cpa.util.TriFunction;
2427

2528
/**
26-
* This {@link LimitedHashMap} is a {@link HashMap} which limits its content based on the tripredicate {@code limitReached}.
29+
* This {@link LimitedHashMap} is a {@link HashMap} which limits its content based on the function {@code removeElement}.
30+
* {@code removeElement} determines whether the map limit is reached. If it returns an empty value, the map behaves as usual.
31+
* Otherwise, the returned key is removed from the map.
2732
*
2833
* @author Dmitry Ivanov
2934
*/
3035
public class LimitedHashMap<K, V>
3136
extends HashMap<K, V>
3237
{
3338

34-
protected TriPredicate<LimitedHashMap<K, V>, K, V> limitReached;
39+
protected TriFunction<LimitedHashMap<K, V>, K, V, Optional<K>> removeElement;
3540

3641
/**
3742
* Create an empty limited hash map.
3843
*
39-
* @param limitReached whether the size limit of the map is reached and no further mappings can be added
44+
* @param removeElement determines whether the map limit is reached
45+
* if it returns an empty value, the map behaves as usual
46+
* otherwise, the returned key is removed from the map
4047
*/
41-
public LimitedHashMap(TriPredicate<LimitedHashMap<K, V>, K, V> limitReached)
48+
public LimitedHashMap(TriFunction<LimitedHashMap<K, V>, K, V, Optional<K>> removeElement)
4249
{
43-
this.limitReached = limitReached;
50+
this.removeElement = removeElement;
4451
}
4552

4653
/**
4754
* Create an empty limited map with reserved initial capacity.
4855
*
4956
* @param initialCapacity the initial capacity of the hash table
50-
* @param limitReached whether the size limit of the map is reached and no further mappings can be added
57+
* @param removeElement determines whether the map limit is reached
58+
* if it returns an empty value, the map behaves as usual
59+
* otherwise, the returned key is removed from the map
5160
*/
52-
public LimitedHashMap(int initialCapacity, TriPredicate<LimitedHashMap<K, V>, K, V> limitReached)
61+
public LimitedHashMap(int initialCapacity, TriFunction<LimitedHashMap<K, V>, K, V, Optional<K>> removeElement)
5362
{
5463
super(initialCapacity);
55-
this.limitReached = limitReached;
64+
this.removeElement = removeElement;
5665
}
5766

5867
/**
5968
* Create a limited map from another map and a tripredicate.
6069
*
61-
* @param m map which elements are used for initialization
62-
* @param limitReached whether the size limit of the map is reached and no further mappings can be added
70+
* @param m map which elements are used for initialization
71+
* @param removeElement determines whether the map limit is reached
72+
* if it returns an empty value, the map behaves as usual
73+
* otherwise, the returned key is removed from the map
6374
*/
64-
public LimitedHashMap(Map<? extends K, ? extends V> m, TriPredicate<LimitedHashMap<K, V>, K, V> limitReached) {
75+
public LimitedHashMap(Map<? extends K, ? extends V> m, TriFunction<LimitedHashMap<K, V>, K, V, Optional<K>> removeElement) {
6576
super(m);
66-
this.limitReached = limitReached;
77+
this.removeElement = removeElement;
6778
}
6879

6980
// implementations for HashMap
7081

7182
@Override
7283
public V put(K key, V value)
7384
{
74-
return limitReached.test(this, key, value) ? get(key) : super.put(key, value);
85+
V oldValue = super.put(key, value);
86+
removeElement.apply(this, key, value).ifPresent(this::remove);
87+
return oldValue;
7588
}
7689

7790
@Override
7891
public void putAll(Map<? extends K, ? extends V> m)
7992
{
8093
m.entrySet().forEach((e -> put(e.getKey(), e.getValue())));
8194
}
95+
96+
@Override
97+
public V putIfAbsent(K key, V value)
98+
{
99+
V oldValue = super.putIfAbsent(key, value);
100+
if (oldValue == null)
101+
{
102+
Optional.ofNullable(get(key))
103+
.flatMap(v -> removeElement.apply(this, key, v))
104+
.ifPresent(this::remove);
105+
}
106+
return oldValue;
107+
}
108+
109+
@Override
110+
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
111+
{
112+
V oldValue = get(key);
113+
V newValue = super.computeIfAbsent(key, mappingFunction);
114+
if (oldValue == null)
115+
{
116+
Optional.ofNullable(newValue)
117+
.flatMap(v -> removeElement.apply(this, key, v))
118+
.ifPresent(this::remove);
119+
}
120+
return newValue;
121+
}
122+
123+
@Override
124+
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
125+
{
126+
V newValue = super.compute(key, remappingFunction);
127+
Optional.ofNullable(newValue)
128+
.flatMap(v -> removeElement.apply(this, key, v))
129+
.ifPresent(this::remove);
130+
return newValue;
131+
}
132+
133+
@Override
134+
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
135+
{
136+
V newValue = super.merge(key, value, remappingFunction);
137+
Optional.ofNullable(newValue)
138+
.flatMap(v -> removeElement.apply(this, key, v))
139+
.ifPresent(this::remove);
140+
return newValue;
141+
}
82142
}

base/src/main/java/proguard/analysis/cpa/defaults/LimitedHashMapAbstractState.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
package proguard.analysis.cpa.defaults;
2020

2121
import java.util.Map;
22-
import proguard.analysis.cpa.jvm.util.TriPredicate;
22+
import java.util.Optional;
23+
import proguard.analysis.cpa.util.TriFunction;
24+
import proguard.analysis.cpa.util.TriPredicate;
2325

2426
/**
2527
* This {@link LimitedHashMapAbstractState} represents a limited map to {@link LatticeAbstractState}s with the semilattice operators lifted to the map.
@@ -34,41 +36,47 @@ public class LimitedHashMapAbstractState<KeyT, AbstractSpaceT extends LatticeAbs
3436
/**
3537
* Create an empty limited hash map abstract state.
3638
*
37-
* @param limitReached whether the size limit of the map is reached and no further mappings can be added
39+
* @param removeElement determines whether the map limit is reached
40+
* if it returns an empty value, the map behaves as usual
41+
* otherwise, the returned key is removed from the map
3842
*/
39-
public LimitedHashMapAbstractState(TriPredicate<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT> limitReached)
43+
public LimitedHashMapAbstractState(TriFunction<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT, Optional<KeyT>> removeElement)
4044
{
41-
super(limitReached);
45+
super(removeElement);
4246
}
4347

4448
/**
4549
* Create an empty limited hash map abstract state with reserved initial capacity.
4650
*
4751
* @param initialCapacity the initial capacity of the hash table
48-
* @param limitReached whether the size limit of the map is reached and no further mappings can be added
52+
* @param removeElement determines whether the map limit is reached
53+
* if it returns an empty value, the map behaves as usual
54+
* otherwise, the returned key is removed from the map
4955
*
5056
*/
51-
public LimitedHashMapAbstractState(int initialCapacity, TriPredicate<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT> limitReached)
57+
public LimitedHashMapAbstractState(int initialCapacity, TriFunction<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT, Optional<KeyT>> removeElement)
5258
{
53-
super(initialCapacity, limitReached);
59+
super(initialCapacity, removeElement);
5460
}
5561

5662
/**
5763
* Create a hash map abstract state from another map.
5864
*
59-
* @param m map which elements are used for initialization
60-
* @param limitReached whether the size limit of the map is reached and no further mappings can be added
65+
* @param m map which elements are used for initialization
66+
* @param removeElement determines whether the map limit is reached
67+
* if it returns an empty value, the map behaves as usual
68+
* otherwise, the returned key is removed from the map
6169
*/
62-
public LimitedHashMapAbstractState(Map<? extends KeyT, ? extends AbstractSpaceT> m, TriPredicate<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT> limitReached) {
63-
super(m, limitReached);
70+
public LimitedHashMapAbstractState(Map<? extends KeyT, ? extends AbstractSpaceT> m, TriFunction<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT, Optional<KeyT>> removeElement) {
71+
super(m, removeElement);
6472
}
6573

6674
// implementations for AbstractState
6775

6876
@Override
6977
public LimitedHashMapAbstractState<KeyT, AbstractSpaceT> copy()
7078
{
71-
return new LimitedHashMapAbstractState<>(this, limitReached);
79+
return new LimitedHashMapAbstractState<>(this, removeElement);
7280
}
7381

7482
}

base/src/main/java/proguard/analysis/cpa/jvm/state/heap/tree/JvmTreeHeapFollowerAbstractState.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public JvmTreeHeapFollowerAbstractState<StateT> copy()
207207
.collect(Collectors.toMap(Entry::getKey,
208208
e -> e.getValue().copy(),
209209
HeapNode::join,
210-
() -> new HashMapAbstractState<>(referenceToNode))),
210+
heapMapAbstractStateFactory::createMapAbstractState)),
211211
heapMapAbstractStateFactory,
212212
heapNodeMapAbstractStateFactory);
213213
}

base/src/main/java/proguard/analysis/cpa/state/LimitedHashMapAbstractStateFactory.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@
1818

1919
package proguard.analysis.cpa.state;
2020

21-
import proguard.analysis.cpa.defaults.HashMapAbstractState;
21+
import java.util.Optional;
2222
import proguard.analysis.cpa.defaults.LatticeAbstractState;
2323
import proguard.analysis.cpa.defaults.LimitedHashMap;
2424
import proguard.analysis.cpa.defaults.LimitedHashMapAbstractState;
25-
import proguard.analysis.cpa.defaults.MapAbstractState;
26-
import proguard.analysis.cpa.jvm.util.TriPredicate;
25+
import proguard.analysis.cpa.util.TriFunction;
2726

2827
/**
2928
* This interface contains a method creating a fresh instance of {@link LimitedHashMapAbstractState}.
@@ -34,18 +33,18 @@ public class LimitedHashMapAbstractStateFactory<KeyT, AbstractSpaceT extends Lat
3433
implements MapAbstractStateFactory<KeyT, AbstractSpaceT>
3534
{
3635

37-
private final TriPredicate<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT> limitReached;
36+
private final TriFunction<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT, Optional<KeyT>> removeElement;
3837

39-
public LimitedHashMapAbstractStateFactory(TriPredicate<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT> limitReached)
38+
public LimitedHashMapAbstractStateFactory(TriFunction<LimitedHashMap<KeyT, AbstractSpaceT>, KeyT, AbstractSpaceT, Optional<KeyT>> removeElement)
4039
{
41-
this.limitReached = limitReached;
40+
this.removeElement = removeElement;
4241
}
4342

4443
// implementations for MapAbstractStateFactory
4544

4645
@Override
4746
public LimitedHashMapAbstractState<KeyT, AbstractSpaceT> createMapAbstractState()
4847
{
49-
return new LimitedHashMapAbstractState<>(limitReached);
48+
return new LimitedHashMapAbstractState<>(removeElement);
5049
}
5150
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* ProGuardCORE -- library to process Java bytecode.
3+
*
4+
* Copyright (c) 2002-2022 Guardsquare NV
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package proguard.analysis.cpa.util;
20+
21+
/**
22+
* A function taking three parameters.
23+
*
24+
* @author Dmitry Ivanov
25+
*/
26+
@FunctionalInterface
27+
public interface TriFunction<T, U, V, R>
28+
{
29+
30+
/**
31+
* Returns the result of the function on given arguments
32+
*/
33+
R apply(T t, U u, V v);
34+
}

base/src/main/java/proguard/analysis/cpa/jvm/util/TriPredicate.java renamed to base/src/main/java/proguard/analysis/cpa/util/TriPredicate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* limitations under the License.
1717
*/
1818

19-
package proguard.analysis.cpa.jvm.util;
19+
package proguard.analysis.cpa.util;
2020

2121
import java.util.Objects;
2222

base/src/test/kotlin/proguard/analysis/cpa/HeapOperatorsTest.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import proguard.analysis.cpa.state.MapAbstractStateFactory
3131
import proguard.classfile.MethodSignature
3232
import proguard.testutils.ClassPoolBuilder
3333
import proguard.testutils.JavaSource
34+
import java.util.Optional
3435
import kotlin.reflect.full.memberProperties
3536
import kotlin.reflect.jvm.isAccessible
3637

@@ -100,17 +101,17 @@ class HeapOperatorsTest : FreeSpec({
100101
listOf(
101102
HashMapAbstractStateFactory.getInstance(),
102103
DifferentialMapAbstractStateFactory<String, TaintAbstractState> { false },
103-
LimitedHashMapAbstractStateFactory { _, _, _ -> false }
104+
LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() }
104105
).forEach { staticFieldMapAbstractStateFactory ->
105106
listOf<Pair<MapAbstractStateFactory<Reference, HeapNode<SetAbstractState<Reference>>>, MapAbstractStateFactory<Reference, HeapNode<TaintAbstractState>>>>(
106107
Pair(HashMapAbstractStateFactory.getInstance(), HashMapAbstractStateFactory.getInstance()),
107108
Pair(DifferentialMapAbstractStateFactory { false }, DifferentialMapAbstractStateFactory { false }),
108-
Pair(LimitedHashMapAbstractStateFactory { _, _, _ -> false }, LimitedHashMapAbstractStateFactory { _, _, _ -> false })
109+
Pair(LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() }, LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() })
109110
).forEach { (principalHeapMapAbstractStateFactory, followerHeapMapAbstractStateFactory) ->
110111
listOf<Pair<MapAbstractStateFactory<String, SetAbstractState<Reference>>, MapAbstractStateFactory<String, TaintAbstractState>>>(
111112
Pair(HashMapAbstractStateFactory.getInstance(), HashMapAbstractStateFactory.getInstance()),
112113
Pair(DifferentialMapAbstractStateFactory { false }, DifferentialMapAbstractStateFactory { false }),
113-
Pair(LimitedHashMapAbstractStateFactory { _, _, _ -> false }, LimitedHashMapAbstractStateFactory { _, _, _ -> false })
114+
Pair(LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() }, LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() })
114115
).forEach { (principalHeapNodeMapAbstractStateFactory, followerHeapNodeMapAbstractStateFactory) ->
115116
val testNameSuffix =
116117
"""

base/src/test/kotlin/proguard/analysis/cpa/JvmTaintTreeHeapFollowerAbstractStateTest.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import proguard.analysis.cpa.state.LimitedHashMapAbstractStateFactory
3636
import proguard.analysis.cpa.state.MapAbstractStateFactory
3737
import proguard.testutils.ClassPoolBuilder
3838
import proguard.testutils.JavaSource
39+
import java.util.Optional
3940

4041
@Ignored
4142
class JvmTaintTreeHeapFollowerAbstractStateTest : FreeSpec({
@@ -150,17 +151,17 @@ class JvmTaintTreeHeapFollowerAbstractStateTest : FreeSpec({
150151
listOf(
151152
HashMapAbstractStateFactory.getInstance(),
152153
DifferentialMapAbstractStateFactory<String, TaintAbstractState> { false },
153-
LimitedHashMapAbstractStateFactory { _, _, _ -> false }
154+
LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() }
154155
).forEach { staticFieldMapAbstractStateFactory ->
155156
listOf<Pair<MapAbstractStateFactory<Reference, HeapNode<SetAbstractState<Reference>>>, MapAbstractStateFactory<Reference, HeapNode<TaintAbstractState>>>>(
156157
Pair(HashMapAbstractStateFactory.getInstance(), HashMapAbstractStateFactory.getInstance()),
157158
Pair(DifferentialMapAbstractStateFactory { false }, DifferentialMapAbstractStateFactory { false }),
158-
Pair(LimitedHashMapAbstractStateFactory { _, _, _ -> false }, LimitedHashMapAbstractStateFactory { _, _, _ -> false })
159+
Pair(LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() }, LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() })
159160
).forEach { (principalHeapMapAbstractStateFactory, followerHeapMapAbstractStateFactory) ->
160161
listOf<Pair<MapAbstractStateFactory<String, SetAbstractState<Reference>>, MapAbstractStateFactory<String, TaintAbstractState>>>(
161162
Pair(HashMapAbstractStateFactory.getInstance(), HashMapAbstractStateFactory.getInstance()),
162163
Pair(DifferentialMapAbstractStateFactory { false }, DifferentialMapAbstractStateFactory { false }),
163-
Pair(LimitedHashMapAbstractStateFactory { _, _, _ -> false }, LimitedHashMapAbstractStateFactory { _, _, _ -> false })
164+
Pair(LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() }, LimitedHashMapAbstractStateFactory { _, _, _ -> Optional.empty() })
164165
).forEach { (principalHeapNodeMapAbstractStateFactory, followerHeapNodeMapAbstractStateFactory) ->
165166
listOf(false, true).forEach { reduceHeap ->
166167
listOf(-1, 0).forEach { stackDepth ->

0 commit comments

Comments
 (0)