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

Issues with JShell when using "local" execution engine

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P4 P4
    • 22
    • 22
    • tools
    • b27
    • generic
    • generic

      There are a few bugs (or "opportunities for improvement") in JShell's support
      for "local" execution control.

      Background
      ----------

      JShell utilizes two distinct "class lookup" functions. First, it
      looks up clases when compiling snippets. This happens indirectly
      via the Java compiler using its JavaFileSystem interface. Secondly,
      JShell executes code by supplying an execution engine with class
      files and asking it to invoke methods. The execution engine therefore
      performs a class lookup function when it links and loads these
      classes. The compilation happens on the local machine while the
      execution happens either locally or remotely.

      For everything to run smoothly, both of these lookup functions must
      be working for whatever class(es) you want to name in JShell input.

      Local execution is implemented by LocalExecutionControl and provided
      by the LocalExecutionControlProvider under the name "local". In
      turn, LocalExecutionControl uses a LoaderDelegate to manage class
      loading. The default DefaultLoaderDelegate uses a custom subclass of
      URLClassLoader (called RemoteClassLoader). This class loader has
      two functions: (a) keep track of snippet classes generated by JShell,
      and (b) find classes for the execution engine (normal class loader
      function).

      Issue #1
      --------

      The RemoteClassLoader is hard-wired with the system class loader
      as its parent loader. This means that in non-trivial class loading
      scenarios, such as when running in web servlet container, the
      application's classes are not visible to the local execution engine.
      As a result, it's impossible to resolve any of these classes.

      Issue #2
      --------

      JShell has a "--class-path" parameter that is supposed to handle
      properly configuring *both* lookup functions, i.e., compilation and
      execution. Internally, this is done by (a) configuring the compiler's
      class path, and (b) passing this flag to the execution engine as
      a "remove VM option".

      However, the "local" execution engine ignores the remote VM options.

      Here's a simple demonstration:

          $ ls classes/test/Foo.class
          classes/test/Foo.class
          $ jshell --execution=local --class-path classes
          | Welcome to JShell -- Version 20.0.1
          | For an introduction type: /help intro

          jshell> Class.forName("test.Foo");
          | Exception java.lang.ClassNotFoundException: test.Foo
          | at URLClassLoader.findClass (URLClassLoader.java:445)
          | at DefaultLoaderDelegate$RemoteClassLoader.findClass (DefaultLoaderDelegate.java:154)
          | at ClassLoader.loadClass (ClassLoader.java:588)
          | at ClassLoader.loadClass (ClassLoader.java:521)
          | at Class.forName0 (Native Method)
          | at Class.forName (Class.java:391)
          | at Class.forName (Class.java:382)
          | at (#1:1)

          jshell> Thread.currentThread().getContextClassLoader();
          $2 ==> jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader@45283ce2

          jshell> Arrays.asList(((URLClassLoader)$2).getURLs())
          $3 ==> []

          jshell> new File("classes/test/Foo.class").exists()
          $4 ==> true

      Issue #3
      --------

      JShell defines a LoaderDelegate interface, and provides a perfectly
      reasonable default implementation in DefaultLoaderDelegate, but
      makes it impossible for anyone wishing to customize LocalExecutionControl
      to leverage any of the existing DefaultLoaderDelegate implementation,
      because the class is not public. Also, this class is non-trivial and so
      duplicating its tricky class loading function is tedious and error-prone.

      There's no reason not to make DefaultLoaderDelegate a public class,
      because (a) it adds no API surface beyond what is already defined
      by the already-public LoaderDelegate interface, and (b) its current
      behavior is already being relied upon (it's always used), so by
      making it public no additional dependencies or expectations would
      be created beyond those that already exist.

            acobbs Archie Cobbs
            acobbs Archie Cobbs
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: