/
TypeCachingBytecodeGenerator.java
123 lines (106 loc) · 3.98 KB
/
TypeCachingBytecodeGenerator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
* Copyright (c) 2016 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.creation.bytebuddy;
import java.lang.ref.ReferenceQueue;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.bytebuddy.TypeCache;
import org.mockito.mock.SerializableMode;
import org.mockito.stubbing.Answer;
class TypeCachingBytecodeGenerator extends ReferenceQueue<ClassLoader>
implements BytecodeGenerator {
private static final Object BOOTSTRAP_LOCK = new Object();
private final BytecodeGenerator bytecodeGenerator;
private final TypeCache<MockitoMockKey> typeCache;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public TypeCachingBytecodeGenerator(BytecodeGenerator bytecodeGenerator, boolean weak) {
this.bytecodeGenerator = bytecodeGenerator;
typeCache =
new TypeCache.WithInlineExpunction<>(
weak ? TypeCache.Sort.WEAK : TypeCache.Sort.SOFT);
}
@SuppressWarnings("unchecked")
@Override
public <T> Class<T> mockClass(final MockFeatures<T> params) {
lock.readLock().lock();
try {
ClassLoader classLoader = params.mockedType.getClassLoader();
return (Class<T>)
typeCache.findOrInsert(
classLoader,
new MockitoMockKey(
params.mockedType,
params.interfaces,
params.serializableMode,
params.stripAnnotations),
() -> bytecodeGenerator.mockClass(params),
BOOTSTRAP_LOCK);
} catch (IllegalArgumentException exception) {
Throwable cause = exception.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw exception;
}
} finally {
lock.readLock().unlock();
}
}
@Override
public void mockClassStatic(Class<?> type) {
bytecodeGenerator.mockClassStatic(type);
}
@Override
public void mockClassConstruction(Class<?> type) {
bytecodeGenerator.mockClassConstruction(type);
}
@Override
public void clearAllCaches() {
lock.writeLock().lock();
try {
typeCache.clear();
bytecodeGenerator.clearAllCaches();
} finally {
lock.writeLock().unlock();
}
}
private static class MockitoMockKey extends TypeCache.SimpleKey {
private final SerializableMode serializableMode;
private final boolean stripAnnotations;
private MockitoMockKey(
Class<?> type,
Set<Class<?>> additionalType,
SerializableMode serializableMode,
boolean stripAnnotations) {
super(type, additionalType);
this.serializableMode = serializableMode;
this.stripAnnotations = stripAnnotations;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
if (!super.equals(object)) {
return false;
}
MockitoMockKey that = (MockitoMockKey) object;
return stripAnnotations == that.stripAnnotations
&& serializableMode.equals(that.serializableMode);
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (stripAnnotations ? 1 : 0);
result = 31 * result + serializableMode.hashCode();
return result;
}
}
}