blob: ea51edf780a0878b04558ccc8c127d17416bb559 [file] [log] [blame]
/*
* Copyright (c) 2016 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.creation.bytebuddy;
import net.bytebuddy.TypeCache;
import org.mockito.mock.SerializableMode;
import java.lang.ref.ReferenceQueue;
import java.util.Set;
import java.util.concurrent.Callable;
class TypeCachingBytecodeGenerator extends ReferenceQueue<ClassLoader> implements BytecodeGenerator {
private final Object BOOTSTRAP_LOCK = new Object();
private final BytecodeGenerator bytecodeGenerator;
private final TypeCache<MockitoMockKey> typeCache;
public TypeCachingBytecodeGenerator(BytecodeGenerator bytecodeGenerator, boolean weak) {
this.bytecodeGenerator = bytecodeGenerator;
typeCache = new TypeCache.WithInlineExpunction<MockitoMockKey>(weak ? TypeCache.Sort.WEAK : TypeCache.Sort.SOFT);
}
@SuppressWarnings("unchecked")
@Override
public <T> Class<T> mockClass(final MockFeatures<T> params) {
try {
ClassLoader classLoader = params.mockedType.getClassLoader();
return (Class<T>) typeCache.findOrInsert(classLoader,
new MockitoMockKey(params.mockedType, params.interfaces, params.serializableMode, params.stripAnnotations),
new Callable<Class<?>>() {
@Override
public Class<?> call() throws Exception {
return bytecodeGenerator.mockClass(params);
}
}, BOOTSTRAP_LOCK);
} catch (IllegalArgumentException exception) {
Throwable cause = exception.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw exception;
}
}
}
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;
}
}
}