1 /* 2 * Copyright (c) 2016 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.creation.bytebuddy; 6 7 import net.bytebuddy.ByteBuddy; 8 import net.bytebuddy.description.method.MethodDescription; 9 import net.bytebuddy.description.modifier.SynchronizationState; 10 import net.bytebuddy.description.modifier.Visibility; 11 import net.bytebuddy.dynamic.DynamicType; 12 import net.bytebuddy.dynamic.loading.MultipleParentClassLoader; 13 import net.bytebuddy.dynamic.scaffold.TypeValidation; 14 import net.bytebuddy.implementation.FieldAccessor; 15 import net.bytebuddy.implementation.Implementation; 16 import net.bytebuddy.matcher.ElementMatcher; 17 import org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport.CrossClassLoaderSerializableMock; 18 import org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.DispatcherDefaultingToRealMethod; 19 import org.mockito.mock.SerializableMode; 20 21 import java.io.IOException; 22 import java.io.ObjectInputStream; 23 import java.lang.reflect.Type; 24 import java.util.ArrayList; 25 import java.util.Random; 26 27 import static java.lang.Thread.currentThread; 28 import static net.bytebuddy.description.modifier.Visibility.PRIVATE; 29 import static net.bytebuddy.dynamic.Transformer.ForMethod.withModifiers; 30 import static net.bytebuddy.implementation.MethodDelegation.to; 31 import static net.bytebuddy.implementation.attribute.MethodAttributeAppender.ForInstrumentedMethod.INCLUDING_RECEIVER; 32 import static net.bytebuddy.matcher.ElementMatchers.*; 33 34 class SubclassBytecodeGenerator implements BytecodeGenerator { 35 36 private final SubclassLoader loader; 37 38 private final ByteBuddy byteBuddy; 39 private final Random random; 40 41 private final Implementation readReplace; 42 private final ElementMatcher<? super MethodDescription> matcher; 43 44 public SubclassBytecodeGenerator() { 45 this(new SubclassInjectionLoader()); 46 } 47 48 public SubclassBytecodeGenerator(SubclassLoader loader) { 49 this(loader, null, any()); 50 } 51 52 public SubclassBytecodeGenerator(Implementation readReplace, ElementMatcher<? super MethodDescription> matcher) { 53 this(new SubclassInjectionLoader(), readReplace, matcher); 54 } 55 56 protected SubclassBytecodeGenerator(SubclassLoader loader, Implementation readReplace, ElementMatcher<? super MethodDescription> matcher) { 57 this.loader = loader; 58 this.readReplace = readReplace; 59 this.matcher = matcher; 60 byteBuddy = new ByteBuddy().with(TypeValidation.DISABLED); 61 random = new Random(); 62 } 63 64 @Override 65 public <T> Class<? extends T> mockClass(MockFeatures<T> features) { 66 DynamicType.Builder<T> builder = 67 byteBuddy.subclass(features.mockedType) 68 .name(nameFor(features.mockedType)) 69 .ignoreAlso(isGroovyMethod()) 70 .annotateType(features.mockedType.getAnnotations()) 71 .implement(new ArrayList<Type>(features.interfaces)) 72 .method(matcher) 73 .intercept(to(DispatcherDefaultingToRealMethod.class)) 74 .transform(withModifiers(SynchronizationState.PLAIN)) 75 .attribute(INCLUDING_RECEIVER) 76 .method(isHashCode()) 77 .intercept(to(MockMethodInterceptor.ForHashCode.class)) 78 .method(isEquals()) 79 .intercept(to(MockMethodInterceptor.ForEquals.class)) 80 .serialVersionUid(42L) 81 .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) 82 .implement(MockAccess.class) 83 .intercept(FieldAccessor.ofBeanProperty()); 84 if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) { 85 builder = builder.implement(CrossClassLoaderSerializableMock.class) 86 .intercept(to(MockMethodInterceptor.ForWriteReplace.class)); 87 } 88 if (readReplace != null) { 89 builder = builder.defineMethod("readObject", void.class, Visibility.PRIVATE) 90 .withParameters(ObjectInputStream.class) 91 .throwing(ClassNotFoundException.class, IOException.class) 92 .intercept(readReplace); 93 } 94 return builder.make() 95 .load(new MultipleParentClassLoader.Builder() 96 .append(features.mockedType) 97 .append(features.interfaces) 98 .append(currentThread().getContextClassLoader()) 99 .append(MockAccess.class, DispatcherDefaultingToRealMethod.class) 100 .append(MockMethodInterceptor.class, 101 MockMethodInterceptor.ForHashCode.class, 102 MockMethodInterceptor.ForEquals.class).build(MockMethodInterceptor.class.getClassLoader()), 103 loader.getStrategy(features.mockedType)) 104 .getLoaded(); 105 } 106 107 private static ElementMatcher<MethodDescription> isGroovyMethod() { 108 return isDeclaredBy(named("groovy.lang.GroovyObjectSupport")); 109 } 110 111 // TODO inspect naming strategy (for OSGI, signed package, java.* (and bootstrap classes), etc...) 112 private String nameFor(Class<?> type) { 113 String typeName = type.getName(); 114 if (isComingFromJDK(type) 115 || isComingFromSignedJar(type) 116 || isComingFromSealedPackage(type)) { 117 typeName = "codegen." + typeName; 118 } 119 return String.format("%s$%s$%d", typeName, "MockitoMock", Math.abs(random.nextInt())); 120 } 121 122 private boolean isComingFromJDK(Class<?> type) { 123 // Comes from the manifest entry : 124 // Implementation-Title: Java Runtime Environment 125 // This entry is not necessarily present in every jar of the JDK 126 return type.getPackage() != null && "Java Runtime Environment".equalsIgnoreCase(type.getPackage().getImplementationTitle()) 127 || type.getName().startsWith("java.") 128 || type.getName().startsWith("javax."); 129 } 130 131 private boolean isComingFromSealedPackage(Class<?> type) { 132 return type.getPackage() != null && type.getPackage().isSealed(); 133 } 134 135 private boolean isComingFromSignedJar(Class<?> type) { 136 return type.getSigners() != null; 137 } 138 } 139