diff options
author | Garfield Tan <xutan@google.com> | 2019-09-10 10:59:56 -0700 |
---|---|---|
committer | Garfield Tan <xutan@google.com> | 2019-09-10 15:27:13 -0700 |
commit | 49ce6eaa609cd48b55ae0387dbeb32ee8761bc63 (patch) | |
tree | bc8b6ddef81b0693a169e714db156fcb1378371b /src/main/java/org/mockito/internal/creation | |
parent | c8600e6ec88a49c46f00d772e24c0139738a1020 (diff) | |
download | mockito-49ce6eaa609cd48b55ae0387dbeb32ee8761bc63.tar.gz |
Revert "Update to Mockito v2.25.7."
This reverts commit e14b62991dfb625a81997be77825455fdce66ebd.
Bug: 140773999
Test: Builds.
Change-Id: I299dabdf4fdb30845de3415ed8b2f727c5011098
Diffstat (limited to 'src/main/java/org/mockito/internal/creation')
13 files changed, 130 insertions, 546 deletions
diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java index 50134b4..ca67730 100644 --- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java +++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java @@ -11,7 +11,6 @@ import org.mockito.internal.util.Checks; import org.mockito.internal.util.MockCreationValidator; import org.mockito.internal.util.MockNameImpl; import org.mockito.listeners.InvocationListener; -import org.mockito.listeners.StubbingLookupListener; import org.mockito.listeners.VerificationStartedListener; import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; @@ -20,17 +19,17 @@ import org.mockito.stubbing.Answer; import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; -import static java.util.Arrays.asList; import static org.mockito.internal.exceptions.Reporter.defaultAnswerDoesNotAcceptNullParameter; import static org.mockito.internal.exceptions.Reporter.extraInterfacesAcceptsOnlyInterfaces; import static org.mockito.internal.exceptions.Reporter.extraInterfacesDoesNotAcceptNullParameters; import static org.mockito.internal.exceptions.Reporter.extraInterfacesRequiresAtLeastOneInterface; +import static org.mockito.internal.exceptions.Reporter.invocationListenersRequiresAtLeastOneListener; import static org.mockito.internal.exceptions.Reporter.methodDoesNotAcceptParameter; -import static org.mockito.internal.exceptions.Reporter.requiresAtLeastOneListener; import static org.mockito.internal.util.collections.Sets.newSet; @SuppressWarnings("unchecked") @@ -155,7 +154,7 @@ public class MockSettingsImpl<T> extends CreationSettings<T> implements MockSett } List<Object> resultArgs = new ArrayList<Object>(constructorArgs.length + 1); resultArgs.add(outerClassInstance); - resultArgs.addAll(asList(constructorArgs)); + resultArgs.addAll(Arrays.asList(constructorArgs)); return resultArgs.toArray(new Object[constructorArgs.length + 1]); } @@ -174,23 +173,17 @@ public class MockSettingsImpl<T> extends CreationSettings<T> implements MockSett @Override public MockSettings invocationListeners(InvocationListener... listeners) { + if (listeners == null || listeners.length == 0) { + throw invocationListenersRequiresAtLeastOneListener(); + } addListeners(listeners, invocationListeners, "invocationListeners"); return this; } - @Override - public MockSettings stubbingLookupListeners(StubbingLookupListener... listeners) { - addListeners(listeners, stubbingLookupListeners, "stubbingLookupListeners"); - return this; - } - - static <T> void addListeners(T[] listeners, List<T> container, String method) { + private static <T> void addListeners(T[] listeners, List<T> container, String method) { if (listeners == null) { throw methodDoesNotAcceptParameter(method, "null vararg array."); } - if (listeners.length == 0) { - throw requiresAtLeastOneListener(method); - } for (T listener : listeners) { if (listener == null) { throw methodDoesNotAcceptParameter(method, "null listeners."); @@ -214,8 +207,13 @@ public class MockSettingsImpl<T> extends CreationSettings<T> implements MockSett return false; } + @Override + public List<InvocationListener> getInvocationListeners() { + return this.invocationListeners; + } + public boolean hasInvocationListeners() { - return !getInvocationListeners().isEmpty(); + return !invocationListeners.isEmpty(); } @Override diff --git a/src/main/java/org/mockito/internal/creation/SuspendMethod.java b/src/main/java/org/mockito/internal/creation/SuspendMethod.java index 42ceac6..018bc50 100644 --- a/src/main/java/org/mockito/internal/creation/SuspendMethod.java +++ b/src/main/java/org/mockito/internal/creation/SuspendMethod.java @@ -11,18 +11,12 @@ import java.util.Arrays; * See <a href="https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#continuation-passing-style">Design docs for details</a>. */ public class SuspendMethod { - private static final String KOTLIN_EXPERIMENTAL_CONTINUATION = "kotlin.coroutines.experimental.Continuation"; - private static final String KOTLIN_CONTINUATION = "kotlin.coroutines.Continuation"; + private static final String KOTLIN_CONTINUATION = "kotlin.coroutines.experimental.Continuation"; public static Class<?>[] trimSuspendParameterTypes(Class<?>[] parameterTypes) { int n = parameterTypes.length; - if (n > 0 && isContinuationType(parameterTypes[n - 1])) + if (n > 0 && parameterTypes[n - 1].getName().equals(KOTLIN_CONTINUATION)) return Arrays.copyOf(parameterTypes, n - 1); return parameterTypes; } - - private static boolean isContinuationType(Class<?> parameterType) { - String name = parameterType.getName(); - return name.equals(KOTLIN_CONTINUATION) || name.equals(KOTLIN_EXPERIMENTAL_CONTINUATION); - } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java index e99b915..42f10ce 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java @@ -14,7 +14,6 @@ import org.mockito.internal.util.Platform; import org.mockito.internal.util.concurrent.WeakConcurrentMap; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; -import org.mockito.plugins.InlineMockMaker; import java.io.File; import java.io.FileOutputStream; @@ -26,8 +25,6 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; -import javax.tools.ToolProvider; - import static org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.EXCLUDES; import static org.mockito.internal.util.StringUtil.join; @@ -90,7 +87,7 @@ import static org.mockito.internal.util.StringUtil.join; * support this feature. */ @Incubating -public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineMockMaker { +public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker { private static final Instrumentation INSTRUMENTATION; @@ -112,7 +109,7 @@ public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineM boot.deleteOnExit(); JarOutputStream outputStream = new JarOutputStream(new FileOutputStream(boot)); try { - String source = "org/mockito/internal/creation/bytebuddy/inject/MockMethodDispatcher"; + String source = "org/mockito/internal/creation/bytebuddy/MockMethodDispatcher"; InputStream inputStream = InlineByteBuddyMockMaker.class.getClassLoader().getResourceAsStream(source + ".raw"); if (inputStream == null) { throw new IllegalStateException(join( @@ -137,7 +134,13 @@ public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineM } instrumentation.appendToBootstrapClassLoaderSearch(new JarFile(boot)); try { - Class.forName("org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher", false, null); + Class<?> dispatcher = Class.forName("org.mockito.internal.creation.bytebuddy.MockMethodDispatcher"); + if (dispatcher.getClassLoader() != null) { + throw new IllegalStateException(join( + "The MockMethodDispatcher must not be loaded manually but must be injected into the bootstrap class loader.", + "", + "The dispatcher class was already loaded by: " + dispatcher.getClassLoader())); + } } catch (ClassNotFoundException cnfe) { throw new IllegalStateException(join( "Mockito failed to inject the MockMethodDispatcher class into the bootstrap class loader", @@ -167,7 +170,7 @@ public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineM if (INITIALIZATION_ERROR != null) { throw new MockitoInitializationException(join( "Could not initialize inline Byte Buddy mock maker. (This mock maker is not supported on Android.)", - ToolProvider.getSystemJavaCompiler() == null ? "Are you running a JRE instead of a JDK? The inline mock maker needs to be run on a JDK.\n" : "", + "", Platform.describe()), INITIALIZATION_ERROR); } bytecodeGenerator = new TypeCachingBytecodeGenerator(new InlineBytecodeGenerator(INSTRUMENTATION, mocks), true); @@ -273,16 +276,6 @@ public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineM } @Override - public void clearMock(Object mock) { - mocks.remove(mock); - } - - @Override - public void clearAllMocks() { - mocks.clear(); - } - - @Override public TypeMockability isTypeMockable(final Class<?> type) { return new TypeMockability() { @Override diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java index e160e3c..64139c2 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java @@ -26,17 +26,17 @@ import net.bytebuddy.pool.TypePool; import net.bytebuddy.utility.OpenedClassReader; import net.bytebuddy.utility.RandomString; import org.mockito.exceptions.base.MockitoException; -import org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher; import org.mockito.internal.util.concurrent.WeakConcurrentMap; import org.mockito.internal.util.concurrent.WeakConcurrentSet; import org.mockito.mock.SerializableMode; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; -import java.util.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import static net.bytebuddy.implementation.MethodDelegation.withDefaultConfiguration; import static net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFixedValue.OfConstant.of; @@ -60,12 +60,14 @@ public class InlineBytecodeGenerator implements BytecodeGenerator, ClassFileTran String.class)); private final Instrumentation instrumentation; + private final ByteBuddy byteBuddy; + private final WeakConcurrentSet<Class<?>> mocked; + private final BytecodeGenerator subclassEngine; - private final AsmVisitorWrapper mockTransformer; - private final Method getModule, canRead, redefineModule; + private final AsmVisitorWrapper mockTransformer; private volatile Throwable lastException; @@ -96,20 +98,6 @@ public class InlineBytecodeGenerator implements BytecodeGenerator, ClassFileTran Advice.withCustomMapping() .bind(MockMethodAdvice.Identifier.class, identifier) .to(MockMethodAdvice.ForEquals.class)); - Method getModule, canRead, redefineModule; - try { - getModule = Class.class.getMethod("getModule"); - canRead = getModule.getReturnType().getMethod("canRead", getModule.getReturnType()); - redefineModule = Instrumentation.class.getMethod("redefineModule", - getModule.getReturnType(), Set.class, Map.class, Map.class, Set.class, Map.class); - } catch (Exception ignored) { - getModule = null; - canRead = null; - redefineModule = null; - } - this.getModule = getModule; - this.canRead = canRead; - this.redefineModule = redefineModule; MockMethodDispatcher.set(identifier, new MockMethodAdvice(mocks, identifier)); instrumentation.addTransformer(this, true); } @@ -170,7 +158,6 @@ public class InlineBytecodeGenerator implements BytecodeGenerator, ClassFileTran } while (type != null); if (!types.isEmpty()) { try { - assureCanReadMockito(types); instrumentation.retransformClasses(types.toArray(new Class<?>[types.size()])); Throwable throwable = lastException; if (throwable != null) { @@ -191,32 +178,6 @@ public class InlineBytecodeGenerator implements BytecodeGenerator, ClassFileTran } } - private void assureCanReadMockito(Set<Class<?>> types) { - if (redefineModule == null) { - return; - } - Set<Object> modules = new HashSet<Object>(); - try { - Object target = getModule.invoke(Class.forName("org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher", false, null)); - for (Class<?> type : types) { - Object module = getModule.invoke(type); - if (!modules.contains(module) && !(Boolean) canRead.invoke(module, target)) { - modules.add(module); - } - } - for (Object module : modules) { - redefineModule.invoke(instrumentation, module, Collections.singleton(target), - Collections.emptyMap(), Collections.emptyMap(), Collections.emptySet(), Collections.emptyMap()); - } - } catch (Exception e) { - throw new IllegalStateException(join("Could not adjust module graph to make the mock instance dispatcher visible to some classes", - "", - "At least one of those modules: " + modules + " is not reading the unnamed module of the bootstrap loader", - "Without such a read edge, the classes that are redefined to become mocks cannot access the mock dispatcher.", - "To circumvent this, Mockito attempted to add a read edge to this module what failed for an unexpected reason"), e); - } - } - private <T> void checkSupportedCombination(boolean subclassingRequired, MockFeatures<T> features) { if (subclassingRequired && !features.mockedType.isArray() diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java index 828b848..f39a1a2 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java @@ -12,7 +12,6 @@ import net.bytebuddy.implementation.bind.annotation.Argument; import net.bytebuddy.implementation.bind.annotation.This; import net.bytebuddy.implementation.bytecode.assign.Assigner; import org.mockito.exceptions.base.MockitoException; -import org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher; import org.mockito.internal.debugging.LocationImpl; import org.mockito.internal.exceptions.stacktrace.ConditionalStackTraceFilter; import org.mockito.internal.invocation.RealMethod; @@ -36,7 +35,7 @@ import java.util.concurrent.Callable; public class MockMethodAdvice extends MockMethodDispatcher { - private final WeakConcurrentMap<Object, MockMethodInterceptor> interceptors; + final WeakConcurrentMap<Object, MockMethodInterceptor> interceptors; private final String identifier; diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java index e57a82e..9066927 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java @@ -46,11 +46,13 @@ public class MockMethodInterceptor implements Serializable { Method invokedMethod, Object[] arguments, RealMethod realMethod) throws Throwable { - return doIntercept(mock, - invokedMethod, - arguments, - realMethod, - new LocationImpl()); + return doIntercept( + mock, + invokedMethod, + arguments, + realMethod, + new LocationImpl() + ); } Object doIntercept(Object mock, @@ -106,11 +108,11 @@ public class MockMethodInterceptor implements Serializable { return superCall.call(); } return interceptor.doIntercept( - mock, - invokedMethod, - arguments, - new RealMethod.FromCallable(superCall) - ); + mock, + invokedMethod, + arguments, + new RealMethod.FromCallable(superCall) + ); } @SuppressWarnings("unused") @@ -124,11 +126,11 @@ public class MockMethodInterceptor implements Serializable { return stubValue; } return interceptor.doIntercept( - mock, - invokedMethod, - arguments, - RealMethod.IsIllegal.INSTANCE - ); + mock, + invokedMethod, + arguments, + RealMethod.IsIllegal.INSTANCE + ); } } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/ModuleHandler.java b/src/main/java/org/mockito/internal/creation/bytebuddy/ModuleHandler.java deleted file mode 100644 index 5be0b95..0000000 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/ModuleHandler.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * 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.ByteBuddy; -import net.bytebuddy.description.modifier.Ownership; -import net.bytebuddy.description.modifier.Visibility; -import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; -import net.bytebuddy.implementation.Implementation; -import net.bytebuddy.implementation.MethodCall; -import net.bytebuddy.implementation.StubMethod; -import org.mockito.codegen.InjectionBase; -import org.mockito.exceptions.base.MockitoException; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Random; - -import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer; -import static org.mockito.internal.util.StringUtil.join; - -abstract class ModuleHandler { - - abstract boolean isOpened(Class<?> source, Class<?> target); - - abstract boolean canRead(Class<?> source, Class<?> target); - - abstract boolean isExported(Class<?> source); - - abstract boolean isExported(Class<?> source, Class<?> target); - - abstract Class<?> injectionBase(ClassLoader classLoader, String tyoeName); - - abstract void adjustModuleGraph(Class<?> source, Class<?> target, boolean export, boolean read); - - static ModuleHandler make(ByteBuddy byteBuddy, SubclassLoader loader, Random random) { - try { - return new ModuleSystemFound( - byteBuddy, loader, random - ); - } catch (Exception ignored) { - return new NoModuleSystemFound(); - } - } - - private static class ModuleSystemFound extends ModuleHandler { - - private final ByteBuddy byteBuddy; - private final SubclassLoader loader; - private final Random random; - - private final int injectonBaseSuffix; - - private final Method getModule, isOpen, isExported, isExportedUnqualified, canRead, addExports, addReads, addOpens, forName; - - private ModuleSystemFound(ByteBuddy byteBuddy, SubclassLoader loader, Random random) throws Exception { - this.byteBuddy = byteBuddy; - this.loader = loader; - this.random = random; - injectonBaseSuffix = Math.abs(random.nextInt()); - Class<?> moduleType = Class.forName("java.lang.Module"); - getModule = Class.class.getMethod("getModule"); - isOpen = moduleType.getMethod("isOpen", String.class, moduleType); - isExported = moduleType.getMethod("isExported", String.class, moduleType); - isExportedUnqualified = moduleType.getMethod("isExported", String.class); - canRead = moduleType.getMethod("canRead", moduleType); - addExports = moduleType.getMethod("addExports", String.class, moduleType); - addReads = moduleType.getMethod("addReads", moduleType); - addOpens = moduleType.getMethod("addOpens", String.class, moduleType); - forName = Class.class.getMethod("forName", String.class); - } - - @Override - boolean isOpened(Class<?> source, Class<?> target) { - if (source.getPackage() == null) { - return true; - } - return (Boolean) invoke(isOpen, invoke(getModule, source), source.getPackage().getName(), invoke(getModule, target)); - } - - @Override - boolean canRead(Class<?> source, Class<?> target) { - return (Boolean) invoke(canRead, invoke(getModule, source), invoke(getModule, target)); - } - - @Override - boolean isExported(Class<?> source) { - if (source.getPackage() == null) { - return true; - } - return (Boolean) invoke(isExportedUnqualified, invoke(getModule, source), source.getPackage().getName()); - } - - @Override - boolean isExported(Class<?> source, Class<?> target) { - if (source.getPackage() == null) { - return true; - } - return (Boolean) invoke(isExported, invoke(getModule, source), source.getPackage().getName(), invoke(getModule, target)); - } - - @Override - Class<?> injectionBase(ClassLoader classLoader, String typeName) { - String packageName = typeName.substring(0, typeName.lastIndexOf('.')); - if (classLoader == InjectionBase.class.getClassLoader() && InjectionBase.class.getPackage().getName().equals(packageName)) { - return InjectionBase.class; - } else { - synchronized (this) { - String name; - int suffix = injectonBaseSuffix; - do { - name = packageName + "." + InjectionBase.class.getSimpleName() + "$" + suffix++; - try { - Class<?> type = Class.forName(name, false, classLoader); - // The injected type must be defined in the class loader that is target of the injection. Otherwise, - // the class's unnamed module would differ from the intended module. To avoid conflicts, we increment - // the suffix until we hit a class with a known name and generate one if it does not exist. - if (type.getClassLoader() == classLoader) { - return type; - } - } catch (ClassNotFoundException ignored) { - break; - } - } while (true); - return byteBuddy.subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS) - .name(name) - .make() - .load(classLoader, loader.resolveStrategy(InjectionBase.class, classLoader, false)) - .getLoaded(); - } - } - } - - @Override - void adjustModuleGraph(Class<?> source, Class<?> target, boolean export, boolean read) { - boolean needsExport = export && !isExported(source, target); - boolean needsRead = read && !canRead(source, target); - if (!needsExport && !needsRead) { - return; - } - ClassLoader classLoader = source.getClassLoader(); - if (classLoader == null) { - throw new MockitoException(join("Cannot adjust module graph for modules in the bootstrap loader", - "", - source + " is declared by the bootstrap loader and cannot be adjusted", - "Requires package export to " + target + ": " + needsExport, - "Requires adjusted reading of " + target + ": " + needsRead)); - } - boolean targetVisible = classLoader == target.getClassLoader(); - while (!targetVisible && classLoader != null) { - classLoader = classLoader.getParent(); - targetVisible = classLoader == target.getClassLoader(); - } - MethodCall targetLookup; - Implementation.Composable implementation; - if (targetVisible) { - targetLookup = MethodCall.invoke(getModule).onMethodCall(MethodCall.invoke(forName).with(target.getName())); - implementation = StubMethod.INSTANCE; - } else { - Class<?> intermediate; - Field field; - try { - intermediate = byteBuddy.subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS) - .name(String.format("%s$%d", "org.mockito.codegen.MockitoTypeCarrier", Math.abs(random.nextInt()))) - .defineField("mockitoType", Class.class, Visibility.PUBLIC, Ownership.STATIC) - .make() - .load(source.getClassLoader(), loader.resolveStrategy(source, source.getClassLoader(), false)) - .getLoaded(); - field = intermediate.getField("mockitoType"); - field.set(null, target); - } catch (Exception e) { - throw new MockitoException(join("Could not create a carrier for making the Mockito type visible to " + source, - "", - "This is required to adjust the module graph to enable mock creation"), e); - } - targetLookup = MethodCall.invoke(getModule).onField(field); - implementation = MethodCall.invoke(getModule).onMethodCall(MethodCall.invoke(forName).with(intermediate.getName())); - } - MethodCall sourceLookup = MethodCall.invoke(getModule).onMethodCall(MethodCall.invoke(forName).with(source.getName())); - if (needsExport) { - implementation = implementation.andThen(MethodCall.invoke(addExports) - .onMethodCall(sourceLookup) - .with(target.getPackage().getName()) - .withMethodCall(targetLookup)); - } - if (needsRead) { - implementation = implementation.andThen(MethodCall.invoke(addReads) - .onMethodCall(sourceLookup) - .withMethodCall(targetLookup)); - } - try { - Class.forName(byteBuddy.subclass(Object.class) - .name(String.format("%s$%s$%d", source.getName(), "MockitoModuleProbe", Math.abs(random.nextInt()))) - .invokable(isTypeInitializer()).intercept(implementation) - .make() - .load(source.getClassLoader(), loader.resolveStrategy(source, source.getClassLoader(), false)) - .getLoaded() - .getName(), true, source.getClassLoader()); - } catch (Exception e) { - throw new MockitoException(join("Could not force module adjustment of the module of " + source, - "", - "This is required to adjust the module graph to enable mock creation"), e); - } - } - - private static Object invoke(Method method, Object target, Object... args) { - try { - return method.invoke(target, args); - } catch (Exception e) { - throw new MockitoException(join("Could not invoke " + method + " using reflection", - "", - "Mockito attempted to interact with the Java module system but an unexpected method behavior was encountered"), e); - } - } - } - - private static class NoModuleSystemFound extends ModuleHandler { - - @Override - boolean isOpened(Class<?> source, Class<?> target) { - return true; - } - - @Override - boolean canRead(Class<?> source, Class<?> target) { - return true; - } - - @Override - boolean isExported(Class<?> source) { - return true; - } - - @Override - boolean isExported(Class<?> source, Class<?> target) { - return true; - } - - @Override - Class<?> injectionBase(ClassLoader classLoader, String tyoeName) { - return InjectionBase.class; - } - - @Override - void adjustModuleGraph(Class<?> source, Class<?> target, boolean export, boolean read) { - // empty - } - } -} diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java index 093978d..b659c73 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java @@ -15,7 +15,6 @@ import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.attribute.MethodAttributeAppender; import net.bytebuddy.matcher.ElementMatcher; -import org.mockito.codegen.InjectionBase; import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport.CrossClassLoaderSerializableMock; import org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.DispatcherDefaultingToRealMethod; @@ -27,9 +26,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; import java.util.Random; import static java.lang.Thread.currentThread; @@ -45,9 +41,10 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { private static final String CODEGEN_PACKAGE = "org.mockito.codegen."; private final SubclassLoader loader; - private final ModuleHandler handler; + private final ByteBuddy byteBuddy; private final Random random; + private final Implementation readReplace; private final ElementMatcher<? super MethodDescription> matcher; @@ -74,128 +71,95 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { this.matcher = matcher; byteBuddy = new ByteBuddy().with(TypeValidation.DISABLED); random = new Random(); - handler = ModuleHandler.make(byteBuddy, loader, random); } @Override public <T> Class<? extends T> mockClass(MockFeatures<T> features) { - ClassLoader classLoader = new MultipleParentClassLoader.Builder() - .appendMostSpecific(getAllTypes(features.mockedType)) - .appendMostSpecific(features.interfaces) - .appendMostSpecific(currentThread().getContextClassLoader()) - .appendMostSpecific(MockAccess.class) - .build(); - - // If Mockito does not need to create a new class loader and if a mock is not based on a JDK type, we attempt - // to define the mock class in the user runtime package to allow for mocking package private types and methods. - // This also requires that we are able to access the package of the mocked class either by override or explicit - // privilege given by the target package being opened to Mockito. - boolean localMock = classLoader == features.mockedType.getClassLoader() - && features.serializableMode != SerializableMode.ACROSS_CLASSLOADERS - && !isComingFromJDK(features.mockedType) - && (loader.isDisrespectingOpenness() || handler.isOpened(features.mockedType, MockAccess.class)); - String typeName; - if (localMock || loader instanceof MultipleParentClassLoader && !isComingFromJDK(features.mockedType)) { - typeName = features.mockedType.getName(); - } else { - typeName = InjectionBase.class.getPackage().getName() + "." + features.mockedType.getSimpleName(); - } - String name = String.format("%s$%s$%d", typeName, "MockitoMock", Math.abs(random.nextInt())); - - if (localMock) { - handler.adjustModuleGraph(features.mockedType, MockAccess.class, false, true); - for (Class<?> iFace : features.interfaces) { - handler.adjustModuleGraph(iFace, features.mockedType, true, false); - handler.adjustModuleGraph(features.mockedType, iFace, false, true); - } - } else { - boolean exported = handler.isExported(features.mockedType); - Iterator<Class<?>> it = features.interfaces.iterator(); - while (exported && it.hasNext()) { - exported = handler.isExported(it.next()); - } - // We check if all mocked types are exported without qualification to avoid generating a hook type. - // unless this is necessary. We expect this to be the case for most mocked types what makes this a - // worthy performance optimization. - if (exported) { - assertVisibility(features.mockedType); - for (Class<?> iFace : features.interfaces) { - assertVisibility(iFace); - } - } else { - Class<?> hook = handler.injectionBase(classLoader, typeName); - assertVisibility(features.mockedType); - handler.adjustModuleGraph(features.mockedType, hook, true, false); - for (Class<?> iFace : features.interfaces) { - assertVisibility(iFace); - handler.adjustModuleGraph(iFace, hook, true, false); - } - } - } - - DynamicType.Builder<T> builder = byteBuddy.subclass(features.mockedType) - .name(name) - .ignoreAlso(isGroovyMethod()) - .annotateType(features.stripAnnotations - ? new Annotation[0] - : features.mockedType.getAnnotations()) - .implement(new ArrayList<Type>(features.interfaces)) - .method(matcher) - .intercept(dispatcher) - .transform(withModifiers(SynchronizationState.PLAIN)) - .attribute(features.stripAnnotations - ? MethodAttributeAppender.NoOp.INSTANCE - : INCLUDING_RECEIVER) - .method(isHashCode()) - .intercept(hashCode) - .method(isEquals()) - .intercept(equals) - .serialVersionUid(42L) - .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) - .implement(MockAccess.class) - .intercept(FieldAccessor.ofBeanProperty()); + String name = nameFor(features.mockedType); + DynamicType.Builder<T> builder = + byteBuddy.subclass(features.mockedType) + .name(name) + .ignoreAlso(isGroovyMethod()) + .annotateType(features.stripAnnotations + ? new Annotation[0] + : features.mockedType.getAnnotations()) + .implement(new ArrayList<Type>(features.interfaces)) + .method(matcher) + .intercept(dispatcher) + .transform(withModifiers(SynchronizationState.PLAIN)) + .attribute(features.stripAnnotations + ? MethodAttributeAppender.NoOp.INSTANCE + : INCLUDING_RECEIVER) + .method(isHashCode()) + .intercept(hashCode) + .method(isEquals()) + .intercept(equals) + .serialVersionUid(42L) + .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) + .implement(MockAccess.class) + .intercept(FieldAccessor.ofBeanProperty()); if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) { builder = builder.implement(CrossClassLoaderSerializableMock.class) - .intercept(writeReplace); + .intercept(writeReplace); } if (readReplace != null) { builder = builder.defineMethod("readObject", void.class, Visibility.PRIVATE) - .withParameters(ObjectInputStream.class) - .throwing(ClassNotFoundException.class, IOException.class) - .intercept(readReplace); + .withParameters(ObjectInputStream.class) + .throwing(ClassNotFoundException.class, IOException.class) + .intercept(readReplace); } - if (name.startsWith(CODEGEN_PACKAGE) || classLoader instanceof MultipleParentClassLoader) { + ClassLoader classLoader = new MultipleParentClassLoader.Builder() + .append(features.mockedType) + .append(features.interfaces) + .append(currentThread().getContextClassLoader()) + .append(MockAccess.class, DispatcherDefaultingToRealMethod.class) + .append(MockMethodInterceptor.class, + MockMethodInterceptor.ForHashCode.class, + MockMethodInterceptor.ForEquals.class).build(MockMethodInterceptor.class.getClassLoader()); + if (classLoader != features.mockedType.getClassLoader()) { + assertVisibility(features.mockedType); + for (Class<?> iFace : features.interfaces) { + assertVisibility(iFace); + } builder = builder.ignoreAlso(isPackagePrivate() .or(returns(isPackagePrivate())) .or(hasParameters(whereAny(hasType(isPackagePrivate()))))); } return builder.make() - .load(classLoader, loader.resolveStrategy(features.mockedType, classLoader, localMock)) - .getLoaded(); - } - - private <T> Collection<Class<? super T>> getAllTypes(Class<T> type) { - Collection<Class<? super T>> supertypes = new LinkedList<Class<? super T>>(); - supertypes.add(type); - Class<? super T> superType = type; - while (superType != null) { - supertypes.add(superType); - superType = superType.getSuperclass(); - } - return supertypes; + .load(classLoader, loader.resolveStrategy(features.mockedType, classLoader, name.startsWith(CODEGEN_PACKAGE))) + .getLoaded(); } private static ElementMatcher<MethodDescription> 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_PACKAGE + type.getSimpleName(); + } + 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."); + || 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; } private static void assertVisibility(Class<?> type) { diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java index 89b0a30..454dd8e 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java @@ -25,7 +25,7 @@ class SubclassInjectionLoader implements SubclassLoader { private final SubclassLoader loader; SubclassInjectionLoader() { - if (!Boolean.getBoolean("org.mockito.internal.noUnsafeInjection") && ClassInjector.UsingReflection.isAvailable()) { + if (!Boolean.getBoolean("org.mockito.internal.simulateJava11") && ClassInjector.UsingReflection.isAvailable()) { this.loader = new WithReflection(); } else if (ClassInjector.UsingLookup.isAvailable()) { this.loader = tryLookup(); @@ -49,13 +49,8 @@ class SubclassInjectionLoader implements SubclassLoader { private static class WithReflection implements SubclassLoader { @Override - public boolean isDisrespectingOpenness() { - return true; - } - - @Override - public ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean localMock) { - return ClassLoadingStrategy.Default.INJECTION.with(localMock ? mockedType.getProtectionDomain() : InjectionBase.class.getProtectionDomain()); + public ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean codegen) { + return ClassLoadingStrategy.Default.INJECTION.with(codegen ? InjectionBase.class.getProtectionDomain() : mockedType.getProtectionDomain()); } } @@ -74,13 +69,12 @@ class SubclassInjectionLoader implements SubclassLoader { } @Override - public boolean isDisrespectingOpenness() { - return false; - } - - @Override - public ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean localMock) { - if (localMock) { + public ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean codegen) { + if (codegen) { + return ClassLoadingStrategy.UsingLookup.of(codegenLookup); + } else if (classLoader != mockedType.getClassLoader()) { + return ClassLoadingStrategy.Default.WRAPPER.with(mockedType.getProtectionDomain()); + } else { try { Object privateLookup; try { @@ -102,21 +96,12 @@ class SubclassInjectionLoader implements SubclassLoader { exception )); } - } else if (classLoader == InjectionBase.class.getClassLoader()) { - return ClassLoadingStrategy.UsingLookup.of(codegenLookup); - } else { - return ClassLoadingStrategy.Default.WRAPPER.with(mockedType.getProtectionDomain()); } } } @Override - public boolean isDisrespectingOpenness() { - return loader.isDisrespectingOpenness(); - } - - @Override - public ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean localMock) { - return loader.resolveStrategy(mockedType, classLoader, localMock); + public ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean codegen) { + return loader.resolveStrategy(mockedType, classLoader, codegen); } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassLoader.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassLoader.java index 011504e..194c282 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassLoader.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassLoader.java @@ -12,19 +12,12 @@ import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; public interface SubclassLoader { /** - * Checks if this loader does not require a module to be open. - * - * @return {@code true} if this loader is not constraint to a target module being opened for loading a class. - */ - boolean isDisrespectingOpenness(); - - /** * Resolves a class loading strategy. * * @param mockedType The type being mocked. * @param classLoader The class loader being used. - * @param localMock {@code true} if the mock is loaded within the runtime package of the mocked type. + * @param codegen {@code true} if the mock is loaded in the {@code org.mockito.codegen} package. * @return An appropriate class loading strategy. */ - ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean localMock); + ClassLoadingStrategy<ClassLoader> resolveStrategy(Class<?> mockedType, ClassLoader classLoader, boolean codegen); } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/inject/MockMethodDispatcher.java b/src/main/java/org/mockito/internal/creation/bytebuddy/inject/MockMethodDispatcher.java deleted file mode 100644 index 12e30db..0000000 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/inject/MockMethodDispatcher.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.creation.bytebuddy.inject; - -import java.lang.reflect.Method; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -public abstract class MockMethodDispatcher { - - private static final ConcurrentMap<String, MockMethodDispatcher> INSTANCE = new ConcurrentHashMap<String, MockMethodDispatcher>(); - - public static MockMethodDispatcher get(String identifier, Object mock) { - if (mock == INSTANCE) { // Avoid endless loop if ConcurrentHashMap was redefined to check for being a mock. - return null; - } else { - return INSTANCE.get(identifier); - } - } - - public static void set(String identifier, MockMethodDispatcher dispatcher) { - INSTANCE.putIfAbsent(identifier, dispatcher); - } - - public abstract Callable<?> handle(Object instance, Method origin, Object[] arguments) throws Throwable; - - public abstract boolean isMock(Object instance); - - public abstract boolean isMocked(Object instance); - - public abstract boolean isOverridden(Object instance, Method origin); -} diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/inject/package-info.java b/src/main/java/org/mockito/internal/creation/bytebuddy/inject/package-info.java deleted file mode 100644 index 5abed05..0000000 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/inject/package-info.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2007 Mockito contributors - * This program is made available under the terms of the MIT License. - */ - -/** - * Internal classes, not to be used by clients. Intended for injection into the bootstrap class loader. - * - * Subject to change at any time without notice. - */ -package org.mockito.internal.creation.bytebuddy.inject; diff --git a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java index 3b45592..03afd80 100644 --- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java +++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java @@ -4,8 +4,8 @@ */ package org.mockito.internal.creation.settings; +import org.mockito.internal.listeners.StubbingLookupListener; import org.mockito.listeners.InvocationListener; -import org.mockito.listeners.StubbingLookupListener; import org.mockito.listeners.VerificationStartedListener; import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; @@ -18,7 +18,6 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; public class CreationSettings<T> implements MockCreationSettings<T>, Serializable { private static final long serialVersionUID = -6789800638070123629L; @@ -31,11 +30,7 @@ public class CreationSettings<T> implements MockCreationSettings<T>, Serializabl protected MockName mockName; protected SerializableMode serializableMode = SerializableMode.NONE; protected List<InvocationListener> invocationListeners = new ArrayList<InvocationListener>(); - - //Other listeners in this class may also need concurrency-safe implementation. However, no issue was reported about it. - // If we do it, we need to understand usage patterns and choose the right concurrent implementation. - protected List<StubbingLookupListener> stubbingLookupListeners = new CopyOnWriteArrayList<StubbingLookupListener>(); - + protected final List<StubbingLookupListener> stubbingLookupListeners = new ArrayList<StubbingLookupListener>(); protected List<VerificationStartedListener> verificationStartedListeners = new LinkedList<VerificationStartedListener>(); protected boolean stubOnly; protected boolean stripAnnotations; @@ -48,7 +43,6 @@ public class CreationSettings<T> implements MockCreationSettings<T>, Serializabl @SuppressWarnings("unchecked") public CreationSettings(CreationSettings copy) { - //TODO can we have a reflection test here? We had a couple of bugs here in the past. this.typeToMock = copy.typeToMock; this.extraInterfaces = copy.extraInterfaces; this.name = copy.name; @@ -57,14 +51,12 @@ public class CreationSettings<T> implements MockCreationSettings<T>, Serializabl this.mockName = copy.mockName; this.serializableMode = copy.serializableMode; this.invocationListeners = copy.invocationListeners; - this.stubbingLookupListeners = copy.stubbingLookupListeners; this.verificationStartedListeners = copy.verificationStartedListeners; this.stubOnly = copy.stubOnly; this.useConstructor = copy.isUsingConstructor(); this.outerClassInstance = copy.getOuterClassInstance(); this.constructorArgs = copy.getConstructorArgs(); this.lenient = copy.lenient; - this.stripAnnotations = copy.stripAnnotations; } @Override |