Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8366926

Unexpected exception occurs when executing code in a "local" JShell environment

XMLWordPrintable

    • 21
    • generic
    • generic

      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.

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: