com.fitbur.mockito.internal.creation.bytebuddy.MockBytecodeGenerator Maven / Gradle / Ivy
package com.fitbur.mockito.internal.creation.bytebuddy;
import com.fitbur.mockito.bytebuddy.ByteBuddy;
import com.fitbur.mockito.bytebuddy.description.method.MethodDescription;
import com.fitbur.mockito.bytebuddy.description.modifier.SynchronizationState;
import com.fitbur.mockito.bytebuddy.dynamic.DynamicType;
import com.fitbur.mockito.bytebuddy.dynamic.MethodTransformer;
import com.fitbur.mockito.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import com.fitbur.mockito.bytebuddy.dynamic.loading.MultipleParentClassLoader;
import com.fitbur.mockito.bytebuddy.dynamic.scaffold.TypeValidation;
import com.fitbur.mockito.bytebuddy.implementation.FieldAccessor;
import com.fitbur.mockito.bytebuddy.implementation.MethodDelegation;
import com.fitbur.mockito.bytebuddy.implementation.attribute.MethodAttributeAppender;
import com.fitbur.mockito.bytebuddy.matcher.ElementMatcher;
import com.fitbur.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport.CrossClassLoaderSerializableMock;
import com.fitbur.mockito.internal.creation.bytebuddy.MockMethodInterceptor.DispatcherDefaultingToRealMethod;
import com.fitbur.mockito.internal.creation.bytebuddy.MockMethodInterceptor.MockAccess;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Random;
import static com.fitbur.mockito.bytebuddy.description.modifier.Visibility.PRIVATE;
import static com.fitbur.mockito.bytebuddy.implementation.MethodDelegation.to;
import static com.fitbur.mockito.bytebuddy.matcher.ElementMatchers.*;
class MockBytecodeGenerator {
private final ByteBuddy byteBuddy;
private final Random random;
public MockBytecodeGenerator() {
byteBuddy = new ByteBuddy().with(TypeValidation.DISABLED);
random = new Random();
}
public Class extends T> generateMockClass(MockFeatures features) {
DynamicType.Builder builder =
byteBuddy.subclass(features.mockedType)
.name(nameFor(features.mockedType))
.ignoreAlso(isGroovyMethod())
.annotateType(features.mockedType.getAnnotations())
.implement(new ArrayList(features.interfaces))
.method(any())
.intercept(MethodDelegation.to(DispatcherDefaultingToRealMethod.class))
.transform(MethodTransformer.Simple.withModifiers(SynchronizationState.PLAIN))
.attribute(MethodAttributeAppender.ForInstrumentedMethod.INCLUDING_RECEIVER)
.serialVersionUid(42L)
.defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE)
.implement(MockAccess.class)
.intercept(FieldAccessor.ofBeanProperty())
.method(isHashCode())
.intercept(to(MockMethodInterceptor.ForHashCode.class))
.method(isEquals())
.intercept(to(MockMethodInterceptor.ForEquals.class));
if (features.crossClassLoaderSerializable) {
builder = builder.implement(CrossClassLoaderSerializableMock.class)
.intercept(to(MockMethodInterceptor.ForWriteReplace.class));
}
return builder.make()
.load(new MultipleParentClassLoader.Builder()
.append(features.mockedType)
.append(features.interfaces)
.append(MultipleParentClassLoader.class.getClassLoader())
.append(Thread.currentThread().getContextClassLoader())
.build(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
}
private static ElementMatcher isGroovyMethod() {
return isDeclaredBy(named("groovy.lang.GroovyObjectSupport"));
}
// TODO inspect naming strategy (for OSGI, signed package, java.* (and bootstrap classes), etc...)
private String nameFor(Class> type) {
String typeName = type.getName();
if (isComingFromJDK(type)
|| isComingFromSignedJar(type)
|| isComingFromSealedPackage(type)) {
typeName = "codegen." + typeName;
}
return String.format("%s$%s$%d", typeName, "MockitoMock", Math.abs(random.nextInt()));
}
private boolean isComingFromJDK(Class> type) {
// Comes from the manifest entry :
// Implementation-Title: Java Runtime Environment
// This entry is not necessarily present in every jar of the JDK
return type.getPackage() != null && "Java Runtime Environment".equalsIgnoreCase(type.getPackage().getImplementationTitle())
|| type.getName().startsWith("java.")
|| type.getName().startsWith("javax.");
}
private boolean isComingFromSealedPackage(Class> type) {
return type.getPackage() != null && type.getPackage().isSealed();
}
private boolean isComingFromSignedJar(Class> type) {
return type.getSigners() != null;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy