-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
TypeCachingBytecodeGenerator.java
129 lines (113 loc) · 4.56 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
124
125
126
127
128
129
/*
* 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.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.bytebuddy.TypeCache;
import org.mockito.mock.SerializableMode;
class TypeCachingBytecodeGenerator extends ReferenceQueue<ClassLoader>
implements BytecodeGenerator {
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 {
Class<T> mockedType = params.mockedType;
ClassLoader classLoader = mockedType.getClassLoader();
return (Class<T>)
typeCache.findOrInsert(
classLoader,
new MockitoMockKey(
mockedType,
params.interfaces,
params.serializableMode,
params.stripAnnotations),
() -> bytecodeGenerator.mockClass(params),
/*
* Only lock on the main mockType, instead of a global lock.
* But the mockType class lock, will still ensure that a MockitoMockKey will never be created twice.
*
* #3035: Excessive locking in TypeCachingBytecodeGenerator#BOOTSTRAP_LOCK
*
* Note: This lock is still a bit more than needed, if e.g. the same type is mocked with
* and without interfaces at the same time. But this can be neglected for simplicity.
*/
mockedType);
} 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;
}
}
}