Repository: mockito/mockito
Issue: Swallowed illegal access exceptions make it harder to debug modularity-related issues

### Issue Summary

In FieldReader and ReflectionMemberAccessor, if an IllegalAccessException is thrown, it will be suppressed.

At the very least, Mockito should include the original exception. When testing a modular application, it is common to have packages encapsulated by default.

### Stacktrace

In FieldReader, the IllegalAccessException is caught. The exception is re-thrown but the cause is lost.

https://github.com/mockito/mockito/blob/ea000f72444ed4c7e409441aedc3021951b89660/src/main/java/org/mockito/internal/util/reflection/FieldReader.java#L28-L35

Using a debugger, the IllegalAccessException can be observed: https://pastebin.com/GsnU29QV

### Problematic Code

1. Use `module-info` in the project and don't open the package under test to mockito.

2. Use the `MockitoExtension` for JUnit:

```java
@ExtendWith(MockitoExtension.class)
public class MyClassTest {

  private final MyType mock;

  public MyClassTest(@Mock MyType mock) {
    this.mock = mock;
  }
}
```

### Environment
Mockito-Junit-Jupiter 3.9.0
Zulu16.28+11-CA (build 16+36)


Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldReader.java b/src/main/java/org/mockito/internal/util/reflection/FieldReader.java
index e202c714a0..6c3c120ee9 100644
--- a/src/main/java/org/mockito/internal/util/reflection/FieldReader.java
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldReader.java
@@ -30,7 +30,7 @@ public Object read() {
             return accessor.get(field, target);
         } catch (Exception e) {
             throw new MockitoException(
-                    "Cannot read state from field: " + field + ", on instance: " + target);
+                    "Cannot read state from field: " + field + ", on instance: " + target, e);
         }
     }
 }


Test patch:
diff --git a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java
index 6382a253b8..55923e77b5 100644
--- a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java
+++ b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java
@@ -5,6 +5,8 @@
 package org.mockito.moduletest;
 
 import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.exceptions.base.MockitoException;
 import org.mockito.internal.util.reflection.ModuleMemberAccessor;
 import org.mockito.internal.util.reflection.ReflectionMemberAccessor;
 
@@ -68,4 +70,25 @@ public void cannot_access_non_opened_module_with_reflection_member_accessor() th
             Thread.currentThread().setContextClassLoader(contextLoader);
         }
     }
+
+    @Test
+    public void cannot_read_unopened_private_field_but_exception_includes_cause() throws Exception {
+        Path jar = modularJar(true, true, false, true);
+        ModuleLayer layer = layer(jar, true, true);
+
+        ClassLoader loader = layer.findLoader("mockito.test");
+        Class<?> type = loader.loadClass("sample.MyCallable");
+
+        @SuppressWarnings("unchecked")
+        Callable<String> testInstance = (Callable<String>) type.getDeclaredConstructor().newInstance();
+        try {
+            Mockito.mockitoSession()
+                .initMocks(testInstance)
+                .startMocking();
+            fail("Expected MockitoException caused by IllegalAccessException");
+        } catch (MockitoException ex) {
+            assertThat(ex.getCause()).isInstanceOf(IllegalAccessException.class);
+        }
+    }
+
 }
diff --git a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleUtil.java b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleUtil.java
index 49632fd71d..14838043c4 100644
--- a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleUtil.java
+++ b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleUtil.java
@@ -6,6 +6,7 @@
 
 import net.bytebuddy.ByteBuddy;
 import net.bytebuddy.description.modifier.Visibility;
+import net.bytebuddy.dynamic.DynamicType;
 import net.bytebuddy.implementation.FixedValue;
 import net.bytebuddy.jar.asm.ClassWriter;
 import net.bytebuddy.jar.asm.ModuleVisitor;
@@ -30,23 +31,31 @@
 public class ModuleUtil {
 
     public static Path modularJar(boolean isPublic, boolean isExported, boolean isOpened) throws IOException {
+        return modularJar(isPublic, isExported, isOpened, false);
+    }
+    public static Path modularJar(boolean isPublic, boolean isExported, boolean isOpened, boolean addField) throws IOException {
         Path jar = Files.createTempFile("sample-module", ".jar");
         try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(jar))) {
             out.putNextEntry(new JarEntry("module-info.class"));
             out.write(moduleInfo(isExported, isOpened));
             out.closeEntry();
             out.putNextEntry(new JarEntry("sample/MyCallable.class"));
-            out.write(type(isPublic));
+            out.write(type(isPublic, addField));
             out.closeEntry();
         }
         return jar;
     }
 
-    private static byte[] type(boolean isPublic) {
-        return new ByteBuddy()
+    private static byte[] type(boolean isPublic, boolean addField) {
+        DynamicType.Builder<?> typeBuilder = new ByteBuddy()
             .subclass(Callable.class)
             .name("sample.MyCallable")
-            .merge(isPublic ? Visibility.PUBLIC : Visibility.PACKAGE_PRIVATE)
+            .merge(isPublic ? Visibility.PUBLIC : Visibility.PACKAGE_PRIVATE);
+        if (addField) {
+            typeBuilder = typeBuilder
+                .defineField("runnable", Runnable.class, Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL);
+        }
+        return typeBuilder
             .method(named("call"))
             .intercept(FixedValue.value("foo"))
             .make()