diff --git a/build.gradle b/build.gradle index 160cf24a6e..217f71d4b2 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ plugins { id 'eclipse' id 'com.github.ben-manes.versions' version '0.28.0' id 'biz.aQute.bnd.builder' version '5.1.0' + id 'ru.vyarus.animalsniffer' version '1.5.1' } description = 'Mockito mock objects library core API and implementation' @@ -98,6 +99,14 @@ dependencies { testRuntime configurations.compileOnly testUtil sourceSets.test.output + + signature 'org.codehaus.mojo.signature:java18:1.0@signature' + signature 'net.sf.androidscents.signature:android-api-level-24:7.0_r2@signature' +} + +animalsniffer { + sourceSets = [sourceSets.main] + annotation = 'org.mockito.internal.SuppressSignatureCheck' } wrapper { diff --git a/src/main/java/org/mockito/internal/MockedStaticImpl.java b/src/main/java/org/mockito/internal/MockedStaticImpl.java index cb7794cc03..7c3af584eb 100644 --- a/src/main/java/org/mockito/internal/MockedStaticImpl.java +++ b/src/main/java/org/mockito/internal/MockedStaticImpl.java @@ -172,6 +172,6 @@ private void assertNotClosed() { @Override public String toString() { - return "static mock for " + control.getType().getTypeName(); + return "static mock for " + control.getType().getName(); } } diff --git a/src/main/java/org/mockito/internal/SuppressSignatureCheck.java b/src/main/java/org/mockito/internal/SuppressSignatureCheck.java new file mode 100644 index 0000000000..ced5635e0b --- /dev/null +++ b/src/main/java/org/mockito/internal/SuppressSignatureCheck.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2020 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.CLASS) +@Documented +public @interface SuppressSignatureCheck {} 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 0a20882b2c..2d75418e6b 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java @@ -28,6 +28,7 @@ import org.mockito.exceptions.base.MockitoException; import org.mockito.exceptions.base.MockitoInitializationException; import org.mockito.exceptions.misusing.MockitoConfigurationException; +import org.mockito.internal.SuppressSignatureCheck; import org.mockito.internal.configuration.plugins.Plugins; import org.mockito.internal.util.Platform; import org.mockito.internal.util.concurrent.DetachedThreadLocal; @@ -99,6 +100,7 @@ * support this feature. */ @Incubating +@SuppressSignatureCheck public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineMockMaker, Instantiator { @@ -506,7 +508,7 @@ public StaticMockControl createStaticMock( || ClassLoader.class.isAssignableFrom(type)) { throw new MockitoException( "It is not possible to mock static methods of " - + type.getTypeName() + + type.getName() + " to avoid interfering with class loading what leads to infinite loops"); } @@ -534,7 +536,7 @@ public ConstructionMockControl createConstructionMock( } else if (type.isPrimitive() || Modifier.isAbstract(type.getModifiers())) { throw new MockitoException( "It is not possible to construct primitive types or abstract types: " - + type.getTypeName()); + + type.getName()); } bytecodeGenerator.mockClassConstruction(type); @@ -555,7 +557,7 @@ public ConstructionMockControl createConstructionMock( public T newInstance(Class cls) throws InstantiationException { Constructor[] constructors = cls.getDeclaredConstructors(); if (constructors.length == 0) { - throw new InstantiationException(cls.getTypeName() + " does not define a constructor"); + throw new InstantiationException(cls.getName() + " does not define a constructor"); } Constructor selected = constructors[0]; for (Constructor constructor : constructors) { @@ -579,7 +581,7 @@ public T newInstance(Class cls) throws InstantiationException { mockitoConstruction.set(false); } } catch (Exception e) { - throw new InstantiationException("Could not instantiate " + cls.getTypeName(), e); + throw new InstantiationException("Could not instantiate " + cls.getName(), e); } } @@ -754,14 +756,14 @@ private static class InlineConstructionMockContext implements MockedConstruction private static final Map> PRIMITIVES = new HashMap<>(); static { - PRIMITIVES.put(boolean.class.getTypeName(), boolean.class); - PRIMITIVES.put(byte.class.getTypeName(), byte.class); - PRIMITIVES.put(short.class.getTypeName(), short.class); - PRIMITIVES.put(char.class.getTypeName(), char.class); - PRIMITIVES.put(int.class.getTypeName(), int.class); - PRIMITIVES.put(long.class.getTypeName(), long.class); - PRIMITIVES.put(float.class.getTypeName(), float.class); - PRIMITIVES.put(double.class.getTypeName(), double.class); + PRIMITIVES.put(boolean.class.getName(), boolean.class); + PRIMITIVES.put(byte.class.getName(), byte.class); + PRIMITIVES.put(short.class.getName(), short.class); + PRIMITIVES.put(char.class.getName(), char.class); + PRIMITIVES.put(int.class.getName(), int.class); + PRIMITIVES.put(long.class.getName(), long.class); + PRIMITIVES.put(float.class.getName(), float.class); + PRIMITIVES.put(double.class.getName(), double.class); } private int count; @@ -810,7 +812,7 @@ public Constructor constructor() { join( "Could not resolve constructor of type", "", - type.getTypeName(), + type.getName(), "", "with arguments of types", Arrays.toString(parameterTypes)), 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 fc3c69df13..409f58201c 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java @@ -37,6 +37,7 @@ import net.bytebuddy.utility.OpenedClassReader; import net.bytebuddy.utility.RandomString; import org.mockito.exceptions.base.MockitoException; +import org.mockito.internal.SuppressSignatureCheck; import org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher; import org.mockito.internal.util.concurrent.DetachedThreadLocal; import org.mockito.internal.util.concurrent.WeakConcurrentMap; @@ -48,6 +49,7 @@ import static net.bytebuddy.matcher.ElementMatchers.*; import static org.mockito.internal.util.StringUtil.*; +@SuppressSignatureCheck public class InlineBytecodeGenerator implements BytecodeGenerator, ClassFileTransformer { private static final String PRELOAD = "org.mockito.inline.preload"; diff --git a/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java b/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java index c74c79efa6..6be9db8d27 100644 --- a/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java +++ b/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java @@ -9,6 +9,7 @@ import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.implementation.MethodCall; import org.mockito.exceptions.base.MockitoInitializationException; +import org.mockito.internal.SuppressSignatureCheck; import org.mockito.plugins.MemberAccessor; import java.lang.instrument.Instrumentation; @@ -21,6 +22,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named; import static org.mockito.internal.util.StringUtil.join; +@SuppressSignatureCheck class InstrumentationMemberAccessor implements MemberAccessor { private static final Map, Class> WRAPPERS = new HashMap<>(); @@ -67,6 +69,13 @@ class InstrumentationMemberAccessor implements MemberAccessor { "setAccessible", boolean.class)) .onArgument(0) .withArgument(1)) + .method(named("invokeWithArguments")) + .intercept( + MethodCall.invoke( + MethodHandle.class.getMethod( + "invokeWithArguments", Object[].class)) + .onArgument(0) + .withArgument(1)) .make() .load( InstrumentationMemberAccessor.class.getClassLoader(), @@ -144,17 +153,20 @@ public Object newInstance(Constructor constructor, Object... arguments) } assureArguments(constructor, null, null, arguments, constructor.getParameterTypes()); try { - Object module = getModule.bindTo(constructor.getDeclaringClass()).invokeWithArguments(); + Object module = + DISPATCHER.invokeWithArguments( + getModule.bindTo(constructor.getDeclaringClass())); String packageName = constructor.getDeclaringClass().getPackage().getName(); assureOpen(module, packageName); MethodHandle handle = ((MethodHandles.Lookup) - privateLookupIn.invokeExact( + DISPATCHER.invokeWithArguments( + privateLookupIn, constructor.getDeclaringClass(), DISPATCHER.getLookup())) .unreflectConstructor(constructor); try { - return handle.invokeWithArguments(arguments); + return DISPATCHER.invokeWithArguments(handle, arguments); } catch (Throwable t) { throw new InvocationTargetException(t); } @@ -180,19 +192,22 @@ public Object invoke(Method method, Object target, Object... arguments) arguments, method.getParameterTypes()); try { - Object module = getModule.bindTo(method.getDeclaringClass()).invokeWithArguments(); + Object module = + DISPATCHER.invokeWithArguments(getModule.bindTo(method.getDeclaringClass())); String packageName = method.getDeclaringClass().getPackage().getName(); assureOpen(module, packageName); MethodHandle handle = ((MethodHandles.Lookup) - privateLookupIn.invokeExact( - method.getDeclaringClass(), DISPATCHER.getLookup())) + DISPATCHER.invokeWithArguments( + privateLookupIn, + method.getDeclaringClass(), + DISPATCHER.getLookup())) .unreflect(method); if (!Modifier.isStatic(method.getModifiers())) { handle = handle.bindTo(target); } try { - return handle.invokeWithArguments(arguments); + return DISPATCHER.invokeWithArguments(handle, arguments); } catch (Throwable t) { throw new InvocationTargetException(t); } @@ -219,18 +234,21 @@ public Object get(Field field, Object target) { new Object[0], new Class[0]); try { - Object module = getModule.bindTo(field.getDeclaringClass()).invokeWithArguments(); + Object module = + DISPATCHER.invokeWithArguments(getModule.bindTo(field.getDeclaringClass())); String packageName = field.getDeclaringClass().getPackage().getName(); assureOpen(module, packageName); MethodHandle handle = ((MethodHandles.Lookup) - privateLookupIn.invokeExact( - field.getDeclaringClass(), DISPATCHER.getLookup())) + DISPATCHER.invokeWithArguments( + privateLookupIn, + field.getDeclaringClass(), + DISPATCHER.getLookup())) .unreflectGetter(field); if (!Modifier.isStatic(field.getModifiers())) { handle = handle.bindTo(target); } - return handle.invokeWithArguments(); + return DISPATCHER.invokeWithArguments(handle); } catch (Throwable t) { throw new IllegalStateException("Could not read " + field + " on " + target, t); } @@ -246,7 +264,8 @@ public void set(Field field, Object target, Object value) throws IllegalAccessEx new Class[] {field.getType()}); boolean illegalAccess = false; try { - Object module = getModule.bindTo(field.getDeclaringClass()).invokeWithArguments(); + Object module = + DISPATCHER.invokeWithArguments(getModule.bindTo(field.getDeclaringClass())); String packageName = field.getDeclaringClass().getPackage().getName(); assureOpen(module, packageName); // Method handles do not allow setting final fields where setAccessible(true) @@ -268,13 +287,15 @@ public void set(Field field, Object target, Object value) throws IllegalAccessEx try { MethodHandle handle = ((MethodHandles.Lookup) - privateLookupIn.invokeExact( - field.getDeclaringClass(), DISPATCHER.getLookup())) + DISPATCHER.invokeWithArguments( + privateLookupIn, + field.getDeclaringClass(), + DISPATCHER.getLookup())) .unreflectSetter(field); if (!Modifier.isStatic(field.getModifiers())) { handle = handle.bindTo(target); } - handle.invokeWithArguments(value); + DISPATCHER.invokeWithArguments(handle, value); } finally { if (isFinal) { DISPATCHER.setAccessible(field, false); @@ -290,17 +311,18 @@ public void set(Field field, Object target, Object value) throws IllegalAccessEx } private void assureOpen(Object module, String packageName) throws Throwable { - if (!(Boolean) isOpen.invokeWithArguments(module, packageName, DISPATCHER.getModule())) { - redefineModule - .bindTo(INSTRUMENTATION) - .invokeWithArguments( - module, - Collections.emptySet(), - Collections.emptyMap(), - Collections.singletonMap( - packageName, Collections.singleton(DISPATCHER.getModule())), - Collections.emptySet(), - Collections.emptyMap()); + if (!(Boolean) + DISPATCHER.invokeWithArguments( + isOpen, module, packageName, DISPATCHER.getModule())) { + DISPATCHER.invokeWithArguments( + redefineModule.bindTo(INSTRUMENTATION), + module, + Collections.emptySet(), + Collections.emptyMap(), + Collections.singletonMap( + packageName, Collections.singleton(DISPATCHER.getModule())), + Collections.emptySet(), + Collections.emptyMap()); } } @@ -359,5 +381,10 @@ public interface Dispatcher { Object getModule(); void setAccessible(AccessibleObject target, boolean value); + + // Used to avoid invoke/invokeExact being exposed to Android where this class should + // never be loaded. Since the invocation happens from the generated code, the Android + // build pipeline does not fail. + Object invokeWithArguments(MethodHandle handle, Object... arguments) throws Throwable; } }