Details
-
Bug
-
Resolution: Fixed
-
P3
-
15, 16
-
b20
-
b23
-
x86_64
-
windows_10
-
Not verified
Description
ADDITIONAL SYSTEM INFORMATION :
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15+36, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
If B inherits from A, and the two classes are loaded by different class loaders, and both are from unnamed modules.
When B converts a protected method in A to lambda, the lambda will encounter IllegalAccessError when called.
See the recurrence example for more details.
PS: I originally found this issue in IntelliJ IDEA, which is a pretty serious problem.
java.lang.IllegalAccessError: class com.intellij.openapi.roots.ui.configuration.ContentEntriesEditor$$Lambda$5009/0x00000008025e1c68 tried to access protected method 'void com.intellij.openapi.roots.ui.configuration.ModuleElementsEditor.fireConfigurationChanged()' (com.intellij.openapi.roots.ui.configuration.ContentEntriesEditor$$Lambda$5009/0x00000008025e1c68 is in unnamed module of loader com.intellij.ide.plugins.cl.PluginClassLoader @5fe0e8fb; com.intellij.openapi.roots.ui.configuration.ModuleElementsEditor is in unnamed module of loader com.intellij.util.lang.UrlClassLoader @4f47d241)
REGRESSION : Last worked in version 14.0.2
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run example: java Test.java
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Nothing
ACTUAL -
IllegalAccessError
---------- BEGIN SOURCE ----------
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static class A { protected void func() { } }
public static class B extends A { public Runnable get() { return this::func; } }
public static void main(String... args) throws Throwable {
final URL empty[] = { };
final class Loader extends URLClassLoader {
private final Class<?> responsibility;
public Loader(final ClassLoader parent, final Class<?> responsibility) {
super(empty, parent);
this.responsibility = responsibility;
}
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
try {
if (name.equals(responsibility.getName())) {
final byte[] bytes = getBytesFromClass(responsibility);
return defineClass(null, bytes, 0, bytes.length);
}
} catch (IOException e) { throw new RuntimeException(e); }
return super.findClass(name);
}
}
final ClassLoader a = new Loader(null, A.class), b = new Loader(a, B.class);
final Class<?> bClass = b.loadClass(B.class.getName());
final MethodHandle get = MethodHandles.lookup().findVirtual(bClass, "get", MethodType.methodType(Runnable.class));
final Runnable runnable = (Runnable) get.invoke(bClass.getDeclaredConstructor().newInstance());
runnable.run();
}
private static byte[] getBytesFromClass(final Class<?> clazz) throws IOException {
try (final var input = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class")) {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
final byte[] data = new byte[1 << 12];
while ((nRead = input.read(data, 0, data.length)) != -1)
buffer.write(data, 0, nRead);
buffer.flush();
return buffer.toByteArray();
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Back to Java14
FREQUENCY : always
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15+36, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
If B inherits from A, and the two classes are loaded by different class loaders, and both are from unnamed modules.
When B converts a protected method in A to lambda, the lambda will encounter IllegalAccessError when called.
See the recurrence example for more details.
PS: I originally found this issue in IntelliJ IDEA, which is a pretty serious problem.
java.lang.IllegalAccessError: class com.intellij.openapi.roots.ui.configuration.ContentEntriesEditor$$Lambda$5009/0x00000008025e1c68 tried to access protected method 'void com.intellij.openapi.roots.ui.configuration.ModuleElementsEditor.fireConfigurationChanged()' (com.intellij.openapi.roots.ui.configuration.ContentEntriesEditor$$Lambda$5009/0x00000008025e1c68 is in unnamed module of loader com.intellij.ide.plugins.cl.PluginClassLoader @5fe0e8fb; com.intellij.openapi.roots.ui.configuration.ModuleElementsEditor is in unnamed module of loader com.intellij.util.lang.UrlClassLoader @4f47d241)
REGRESSION : Last worked in version 14.0.2
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run example: java Test.java
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Nothing
ACTUAL -
IllegalAccessError
---------- BEGIN SOURCE ----------
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static class A { protected void func() { } }
public static class B extends A { public Runnable get() { return this::func; } }
public static void main(String... args) throws Throwable {
final URL empty[] = { };
final class Loader extends URLClassLoader {
private final Class<?> responsibility;
public Loader(final ClassLoader parent, final Class<?> responsibility) {
super(empty, parent);
this.responsibility = responsibility;
}
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
try {
if (name.equals(responsibility.getName())) {
final byte[] bytes = getBytesFromClass(responsibility);
return defineClass(null, bytes, 0, bytes.length);
}
} catch (IOException e) { throw new RuntimeException(e); }
return super.findClass(name);
}
}
final ClassLoader a = new Loader(null, A.class), b = new Loader(a, B.class);
final Class<?> bClass = b.loadClass(B.class.getName());
final MethodHandle get = MethodHandles.lookup().findVirtual(bClass, "get", MethodType.methodType(Runnable.class));
final Runnable runnable = (Runnable) get.invoke(bClass.getDeclaredConstructor().newInstance());
runnable.run();
}
private static byte[] getBytesFromClass(final Class<?> clazz) throws IOException {
try (final var input = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class")) {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
final byte[] data = new byte[1 << 12];
while ((nRead = input.read(data, 0, data.length)) != -1)
buffer.write(data, 0, nRead);
buffer.flush();
return buffer.toByteArray();
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Back to Java14
FREQUENCY : always