JDK-8360194 : Order of module imports for JEP-494 can cause array variables to be null
  • Type: Bug
  • Component: tools
  • Sub-Component: jshell
  • Affected Version: 23,24
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2025-06-22
  • Updated: 2025-06-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 26
26Unresolved
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
java version "24.0.1" 2025-04-15
Java(TM) SE Runtime Environment Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmci-b01)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmci-b01, mixed mode, sharing)

macOS: 15.5 (24F74)


A DESCRIPTION OF THE PROBLEM :
When using local execution mode and a --startup option, jshell does not recognise prior declared reference type variables, in combination with the use of JSR-494: Module Import Declarations: https://openjdk.org/jeps/494

Primitive types and primitive arrays are recognised however.



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a file called test.jsh with the following content

```jsh
String[] arr = {"one"};

import module java.base;
import static java.util.stream.Collectors.*;

System.out.println(arr);

/exit
```

Execute the above with the following:

jshell --enable-preview -J--enable-preview --startup=DEFAULT --execution=local ./test.jsh


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Array reference should be printed
ACTUAL -
null is produced

The behaviour can be corrected by changing the order of the imports so the module import is after the static import.


Comments
I just realized the module imports need to get a similar handling to the imports-on-demand, and potentially re-define snippets (e.g. to detect conflicts, or to fix unresolvable errors in snippets). Compare these three sessions: --- single import jshell> void test(JButton b) {} | created method test(JButton), however, it cannot be referenced until class JButton is declared jshell> test(null) | Error: | cannot find symbol | symbol: method test(<nulltype>) | test(null) | ^--^ jshell> import javax.swing.JButton; jshell> test(null) --- --- import on demand jshell> void test(JButton b) {} | created method test(JButton), however, it cannot be referenced until class JButton is declared jshell> test(null) | Error: | cannot find symbol | symbol: method test(<nulltype>) | test(null) | ^--^ jshell> import javax.swing.*; jshell> test(null) --- --- module import jshell> void test(JButton b) {} | created method test(JButton), however, it cannot be referenced until class JButton is declared jshell> test(null) | Error: | cannot find symbol | symbol: method test(<nulltype>) | test(null) | ^--^ jshell> import module java.desktop; jshell> test(null) | Error: | cannot find symbol | symbol: method test(<nulltype>) | test(null) | ^--^ ---
27-06-2025

Christian, I wonder if you could try to take a look on whether we could reduce the snippet redefinition for import-on-demand, depending on the names that could potentially be imported by the import. Please see the link above to see where that happens.
27-06-2025

This is a consequence of a combination of many small behaviors in JShell: 1. JShell in some cases re-defines snippets - this happens e.g. when a snippet depends on a class, and the class is (potentially) changed. This behavior is, by itself, fairly central to JShell and is hard or maybe impossible to change. 2. When redefining snippets, the snippets are not re-run. This is particularly important for the variable declarations, when the variable is re-declared, but its initializer is not re-run. It is not clear if this can be changed. 3. When processing imports-on-demand, JShell will redefine snippets that depend on any element, regardless whether element of such name is potentially imported by the import. This could potentially be improved by only redefining snippets that could be affected by the import-on-demand. 4. JShell has two directions in which it avoids complete re-definition: a) normally, when using the remote agent, it uses JDI to redefine the bytecode of the appropriate snippet class. If this passes, existing instances are preserved. But this is (obviously) impossible for `--execution=local`. b) if the before and after redefinition bytecodes are exactly the same, there's no re-definition. The issue in this particular case is that the classfile before the `import module` is not marked as preview, and the redefined class after the `import module` is marked as preview, so the snippet is redefined, leading to the observed behavior. The good news is that the specific problem above is gone in JDK 25, as module imports are now finalized, so using a module import will not force the classfile to be marked as a preview. The bad news is that I don't think we can completely prevent a situation like this. Snippet re-definition is fairly central to JShell, and re-running variable initializers would be highly problematic. The only potential improvement here that comes to mind would be to add a setting that would optionally disable the re-definition (maybe re-definition of existing snippets), but that feels a bit problematic as well. The thing that probably could be improved would be to reduce the snippet re-definition for import-on-demands based on the names that could potentially be introduce by the import-on-demand here: https://github.com/openjdk/jdk/blob/839cede1a46b05d27abeaffbbd82c241910035cd/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java#L130 (because it is obvious that `import static java.util.stream.Collectors.*;` cannot change the resolution of `String`, as there's no static member of `Collectors` named `String`.
27-06-2025

The observations on Windows 11: JDK 23: Failed, null returned. JDK 24: Failed. Impact -> M (Somewhere in-between the extremes) Likelihood -> L (Preview features) Workaround -> M (Somewhere in-between the extremes) Priority -> P4
23-06-2025