-
Bug
-
Resolution: Unresolved
-
P3
-
None
-
21, 25
ADDITIONAL SYSTEM INFORMATION :
I am reporting a bug in the JShell API which is also evident in the JShell command line tool when using the option --execution=local
OS independent; I am using Azul Zulu OpenJDK builds, but I doubt that
matters.
The bug can be reproduced in version 21.0.8, but not version 17.0.16.
A DESCRIPTION OF THE PROBLEM :
When using a local JShell execution environment, an unexpected exception
occurs when executing code which has a conditional assignment of a
derived class based on runtime evaluation. The issue does not occur when
executing with OpenJDK 17, but does with OpenJDK 21. I have included a
hard-coded example to illustrate the problem. Note that the problem can
also be reproduced when executing the same code snippets using "JShell
--execution=local" from the command line. I believe that JShell command
line tool is simply exhibiting the same bug as the code sample I have
included below.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
javac Example.java
java Example
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Print either "TestInterfaceImpl1" or "TestInterfaceImpl2" based on
random clock modulo.
Goodbye
ACTUAL -
Exception in thread "main" java.lang.IllegalArgumentException: Could not
resolve class $JShell$2$TestClass$TestInterfaceImpl1
at
java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.resolve(ClassHierarchyImpl.java:80)
at
java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.isInterface(ClassHierarchyImpl.java:90)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeReferenceFrom(StackMapGenerator.java:1354)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeFrom(StackMapGenerator.java:1322)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.merge(StackMapGenerator.java:1185)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.checkAssignableTo(StackMapGenerator.java:1132)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator.processMethod(StackMapGenerator.java:419)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator.generate(StackMapGenerator.java:313)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator.<init>(StackMapGenerator.java:239)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator.of(StackMapGenerator.java:156)
at
java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.writeBody(DirectCodeBuilder.java:340)
at
java.base/jdk.internal.classfile.impl.UnboundAttribute$AdHocAttribute.writeTo(UnboundAttribute.java:914)
at
java.base/jdk.internal.classfile.impl.AttributeHolder.writeTo(AttributeHolder.java:56)
at
java.base/jdk.internal.classfile.impl.DirectMethodBuilder.writeTo(DirectMethodBuilder.java:150)
at
java.base/jdk.internal.classfile.impl.BufWriterImpl.writeList(BufWriterImpl.java:201)
at
java.base/jdk.internal.classfile.impl.DirectClassBuilder.build(DirectClassBuilder.java:175)
at
java.base/jdk.internal.classfile.Classfile.build(Classfile.java:218)
at
java.base/jdk.internal.classfile.impl.ClassImpl.transform(ClassImpl.java:175)
at
jdk.jshell/jdk.jshell.execution.LocalExecutionControl.instrument(LocalExecutionControl.java:84)
at
jdk.jshell/jdk.jshell.execution.LocalExecutionControl.lambda$load$0(LocalExecutionControl.java:73)
at
java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at
java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
at
java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at
java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at
java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
at
java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at
java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
at
jdk.jshell/jdk.jshell.execution.LocalExecutionControl.load(LocalExecutionControl.java:74)
at jdk.jshell/jdk.jshell.Eval.load(Eval.java:1171)
at
jdk.jshell/jdk.jshell.Eval.lambda$compileAndLoad$29(Eval.java:1115)
at
jdk.jshell/jdk.jshell.TaskFactory.lambda$runTask$4(TaskFactory.java:213)
at
jdk.compiler/com.sun.tools.javac.api.JavacTaskPool.getTask(JavacTaskPool.java:193)
at jdk.jshell/jdk.jshell.TaskFactory.runTask(TaskFactory.java:206)
at jdk.jshell/jdk.jshell.TaskFactory.compile(TaskFactory.java:186)
at jdk.jshell/jdk.jshell.Eval.compileAndLoad(Eval.java:1100)
at jdk.jshell/jdk.jshell.Eval.declare(Eval.java:901)
at jdk.jshell/jdk.jshell.Eval.eval(Eval.java:140)
at jdk.jshell/jdk.jshell.JShell.eval(JShell.java:513)
at Example.main(Example.java:51)
---------- BEGIN SOURCE ----------
// BEGIN CODE BLOCK
--------------------------------------------------------------
/*
Our use of jshell is more involved and manages separate execution
environments.
I was able to reduce the problem to this basic test case, but using
LocalExecutionControlProvider seems to be a trigger.
Instead of using
JShell js = JShell.create();
we use a custom execution environment
LocalExecutionControlProvider lecp = new
LocalExecutionControlProvider();
JShell js = JShell.builder().executionEngine(lecp, null).build();
Test case fails with OpenJDK 21:
-- Note: this is the same code in the js.eval() statements below,
but included here for readability --
public interface TestInterface {
public void bar();
}
public class TestClass {
public void foo() {
TestInterface test;
if (System.currentTimeMillis() % 2 == 0) {
test = new TestInterfaceImpl1();
}
else {
test = new TestInterfaceImpl2();
}
test.bar();
}
private class TestInterfaceImpl1 implements TestInterface {
public void bar() {
System.out.println("TestInterfaceImpl1"); }
}
private class TestInterfaceImpl2 implements TestInterface {
public void bar() {
System.out.println("TestInterfaceImpl2"); }
}
}
TestClass t = new TestClass();
t.foo();
This test case contains conditional assignment of a derived class
based on runtime
evaluation. If the conditional assignment is removed, then the code
executes normally.
Note that this test case works properly when executing with OpenJDK
17, but with
OpenJDK 21 and later the test case fails with the following exception.
Exception in thread "main" java.lang.IllegalArgumentException:
Could not resolve class $JShell$1$TestClass$TestInterfaceImpl1
Note that this can also be reproduced using JShell command line
with "JShell --execution=local"
See additional comments in the code below.
*/
import java.io.Console;
import jdk.jshell.*;
import jdk.jshell.execution.LocalExecutionControlProvider;
class Example {
public static void main(String[] args) {
Console console = System.console();
// Using JShell.create() from the example works correctly,
independent
// of OpenJDK 17 or 21.
// try (JShell js = JShell.create()) {
// We use a multiple local execution environments in our own
process space,
// so JShell.create() is not sufficient.
LocalExecutionControlProvider lecp = new
LocalExecutionControlProvider();
try (JShell js = JShell.builder().executionEngine(lecp,
null).build()) {
js.eval("public interface TestInterface { public void
bar(); }");
js.eval("public class TestClass { public void foo() {
TestInterface test; if (System.currentTimeMillis() % 2 == 0) { test =
new TestInterfaceImpl1(); } else { test = new TestInterfaceImpl2(); }
test.bar(); } private class TestInterfaceImpl1 implements TestInterface
{ public void bar() { System.out.println(\"TestInterfaceImpl1\"); } }
private class TestInterfaceImpl2 implements TestInterface { public void
bar() { System.out.println(\"TestInterfaceImpl2\"); }} } ");
js.eval("TestClass t = new TestClass();");
js.eval("t.foo();");
}
System.out.println("\nGoodbye");
}
}
// END CODE BLOCK
--------------------------------------------------------------
---------- END SOURCE ----------
Workaround:
None that I am aware of except use JDK 17 or earlier instead of JDK 21.
I am reporting a bug in the JShell API which is also evident in the JShell command line tool when using the option --execution=local
OS independent; I am using Azul Zulu OpenJDK builds, but I doubt that
matters.
The bug can be reproduced in version 21.0.8, but not version 17.0.16.
A DESCRIPTION OF THE PROBLEM :
When using a local JShell execution environment, an unexpected exception
occurs when executing code which has a conditional assignment of a
derived class based on runtime evaluation. The issue does not occur when
executing with OpenJDK 17, but does with OpenJDK 21. I have included a
hard-coded example to illustrate the problem. Note that the problem can
also be reproduced when executing the same code snippets using "JShell
--execution=local" from the command line. I believe that JShell command
line tool is simply exhibiting the same bug as the code sample I have
included below.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
javac Example.java
java Example
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Print either "TestInterfaceImpl1" or "TestInterfaceImpl2" based on
random clock modulo.
Goodbye
ACTUAL -
Exception in thread "main" java.lang.IllegalArgumentException: Could not
resolve class $JShell$2$TestClass$TestInterfaceImpl1
at
java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.resolve(ClassHierarchyImpl.java:80)
at
java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.isInterface(ClassHierarchyImpl.java:90)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeReferenceFrom(StackMapGenerator.java:1354)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeFrom(StackMapGenerator.java:1322)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.merge(StackMapGenerator.java:1185)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.checkAssignableTo(StackMapGenerator.java:1132)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator.processMethod(StackMapGenerator.java:419)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator.generate(StackMapGenerator.java:313)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator.<init>(StackMapGenerator.java:239)
at
java.base/jdk.internal.classfile.impl.StackMapGenerator.of(StackMapGenerator.java:156)
at
java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.writeBody(DirectCodeBuilder.java:340)
at
java.base/jdk.internal.classfile.impl.UnboundAttribute$AdHocAttribute.writeTo(UnboundAttribute.java:914)
at
java.base/jdk.internal.classfile.impl.AttributeHolder.writeTo(AttributeHolder.java:56)
at
java.base/jdk.internal.classfile.impl.DirectMethodBuilder.writeTo(DirectMethodBuilder.java:150)
at
java.base/jdk.internal.classfile.impl.BufWriterImpl.writeList(BufWriterImpl.java:201)
at
java.base/jdk.internal.classfile.impl.DirectClassBuilder.build(DirectClassBuilder.java:175)
at
java.base/jdk.internal.classfile.Classfile.build(Classfile.java:218)
at
java.base/jdk.internal.classfile.impl.ClassImpl.transform(ClassImpl.java:175)
at
jdk.jshell/jdk.jshell.execution.LocalExecutionControl.instrument(LocalExecutionControl.java:84)
at
jdk.jshell/jdk.jshell.execution.LocalExecutionControl.lambda$load$0(LocalExecutionControl.java:73)
at
java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at
java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
at
java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at
java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at
java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
at
java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at
java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
at
jdk.jshell/jdk.jshell.execution.LocalExecutionControl.load(LocalExecutionControl.java:74)
at jdk.jshell/jdk.jshell.Eval.load(Eval.java:1171)
at
jdk.jshell/jdk.jshell.Eval.lambda$compileAndLoad$29(Eval.java:1115)
at
jdk.jshell/jdk.jshell.TaskFactory.lambda$runTask$4(TaskFactory.java:213)
at
jdk.compiler/com.sun.tools.javac.api.JavacTaskPool.getTask(JavacTaskPool.java:193)
at jdk.jshell/jdk.jshell.TaskFactory.runTask(TaskFactory.java:206)
at jdk.jshell/jdk.jshell.TaskFactory.compile(TaskFactory.java:186)
at jdk.jshell/jdk.jshell.Eval.compileAndLoad(Eval.java:1100)
at jdk.jshell/jdk.jshell.Eval.declare(Eval.java:901)
at jdk.jshell/jdk.jshell.Eval.eval(Eval.java:140)
at jdk.jshell/jdk.jshell.JShell.eval(JShell.java:513)
at Example.main(Example.java:51)
---------- BEGIN SOURCE ----------
// BEGIN CODE BLOCK
--------------------------------------------------------------
/*
Our use of jshell is more involved and manages separate execution
environments.
I was able to reduce the problem to this basic test case, but using
LocalExecutionControlProvider seems to be a trigger.
Instead of using
JShell js = JShell.create();
we use a custom execution environment
LocalExecutionControlProvider lecp = new
LocalExecutionControlProvider();
JShell js = JShell.builder().executionEngine(lecp, null).build();
Test case fails with OpenJDK 21:
-- Note: this is the same code in the js.eval() statements below,
but included here for readability --
public interface TestInterface {
public void bar();
}
public class TestClass {
public void foo() {
TestInterface test;
if (System.currentTimeMillis() % 2 == 0) {
test = new TestInterfaceImpl1();
}
else {
test = new TestInterfaceImpl2();
}
test.bar();
}
private class TestInterfaceImpl1 implements TestInterface {
public void bar() {
System.out.println("TestInterfaceImpl1"); }
}
private class TestInterfaceImpl2 implements TestInterface {
public void bar() {
System.out.println("TestInterfaceImpl2"); }
}
}
TestClass t = new TestClass();
t.foo();
This test case contains conditional assignment of a derived class
based on runtime
evaluation. If the conditional assignment is removed, then the code
executes normally.
Note that this test case works properly when executing with OpenJDK
17, but with
OpenJDK 21 and later the test case fails with the following exception.
Exception in thread "main" java.lang.IllegalArgumentException:
Could not resolve class $JShell$1$TestClass$TestInterfaceImpl1
Note that this can also be reproduced using JShell command line
with "JShell --execution=local"
See additional comments in the code below.
*/
import java.io.Console;
import jdk.jshell.*;
import jdk.jshell.execution.LocalExecutionControlProvider;
class Example {
public static void main(String[] args) {
Console console = System.console();
// Using JShell.create() from the example works correctly,
independent
// of OpenJDK 17 or 21.
// try (JShell js = JShell.create()) {
// We use a multiple local execution environments in our own
process space,
// so JShell.create() is not sufficient.
LocalExecutionControlProvider lecp = new
LocalExecutionControlProvider();
try (JShell js = JShell.builder().executionEngine(lecp,
null).build()) {
js.eval("public interface TestInterface { public void
bar(); }");
js.eval("public class TestClass { public void foo() {
TestInterface test; if (System.currentTimeMillis() % 2 == 0) { test =
new TestInterfaceImpl1(); } else { test = new TestInterfaceImpl2(); }
test.bar(); } private class TestInterfaceImpl1 implements TestInterface
{ public void bar() { System.out.println(\"TestInterfaceImpl1\"); } }
private class TestInterfaceImpl2 implements TestInterface { public void
bar() { System.out.println(\"TestInterfaceImpl2\"); }} } ");
js.eval("TestClass t = new TestClass();");
js.eval("t.foo();");
}
System.out.println("\nGoodbye");
}
}
// END CODE BLOCK
--------------------------------------------------------------
---------- END SOURCE ----------
Workaround:
None that I am aware of except use JDK 17 or earlier instead of JDK 21.
- relates to
-
JDK-8306560 Add TOOLING.jsh load file
-
- Resolved
-