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 8c748a535a..89acec934b 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java @@ -31,7 +31,6 @@ import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; -import java.lang.instrument.UnmodifiableClassException; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import java.util.Arrays; @@ -41,6 +40,7 @@ import static net.bytebuddy.implementation.MethodDelegation.withDefaultConfiguration; import static net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFixedValue.OfConstant.of; import static net.bytebuddy.matcher.ElementMatchers.*; +import static org.mockito.internal.util.StringUtil.join; public class InlineBytecodeGenerator implements BytecodeGenerator, ClassFileTransformer { @@ -68,6 +68,8 @@ public class InlineBytecodeGenerator implements BytecodeGenerator, ClassFileTran private final BytecodeGenerator subclassEngine; + private volatile Throwable lastException; + public InlineBytecodeGenerator(Instrumentation instrumentation, WeakConcurrentMap mocks) { this.instrumentation = instrumentation; byteBuddy = new ByteBuddy() @@ -113,11 +115,21 @@ private void triggerRetransformation(MockFeatures features) { if (!types.isEmpty()) { try { instrumentation.retransformClasses(types.toArray(new Class[types.size()])); - } catch (UnmodifiableClassException exception) { + Throwable throwable = lastException; + if (throwable != null) { + throw new IllegalStateException(join("Byte Buddy could not instrument all classes within the mock's type hierarchy", + "", + "This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:", + " - Compiled by older versions of scalac", + " - Classes that are part of the Android distribution"), throwable); + } + } catch (Exception exception) { for (Class failed : types) { mocked.remove(failed); } throw new MockitoException("Could not modify all classes " + types, exception); + } finally { + lastException = null; } } } @@ -169,6 +181,7 @@ public byte[] transform(ClassLoader loader, .make() .getBytes(); } catch (Throwable throwable) { + lastException = throwable; return null; } }