bug icon indicating copy to clipboard operation
bug copied to clipboard

Inconsistent behavior of -release (JEP 247)

Open szeiger opened this issue 1 year ago • 2 comments

Given these equivalent Scala and Java sources, and compilers running on JDK 17:

$ cat Main.scala
import jdk.jfr.internal.Repository
class Main {}

$ cat Main.java
import jdk.jfr.internal.Repository;
public class Main {}

$ java -version
java version "17.0.11" 2024-04-16 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.11+7.1 (build 17.0.11+7-LTS-jvmci-23.0-b34)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.11+7.1 (build 17.0.11+7-LTS-jvmci-23.0-b34, mixed mode, sharing)

javac does not allow the use of the the internal API, with or without an explicit --release 17 because it always limits access based on modules:

$ javac  Main.java
Main.java:1: error: package jdk.jfr.internal is not visible
import jdk.jfr.internal.Repository;
              ^
  (package jdk.jfr.internal is declared in module jdk.jfr, which does not export it)
1 error

$ javac --release 17  Main.java
Main.java:1: error: package jdk.jfr.internal is not visible
import jdk.jfr.internal.Repository;
              ^
  (package jdk.jfr.internal is declared in module jdk.jfr, which does not export it)
1 error

Without --release the module can be made accessible with --add-exports but this is rejected when used with --release (whether the requested version matches the current JDK version or not):

$ javac --add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED Main.java

$ javac --release 17 --add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED Main.java
error: exporting a package from system module jdk.jfr is not allowed with --release
1 error

$ javac --release 11 --add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED Main.java
error: exporting a package from system module jdk.jfr is not allowed with --release
1 error

Instead of using --release it is possible to manually set both -source and -target version and override the system module path (the module equivalent of the bootclasspath) to get the same effect as --release while allowing the use of --add-exports:

$ javac -source 11 -target 11  --system /Users/stefan.zeiger/.sdkman/candidates/java/11-zulu-local Main.java --add-exports=jdk.jfr/jdk.jfr.internal=ALL-UNNAMED

scalac has several inconsistencies with this scheme:

  • By default scalac is not module-aware. It allows access to private APIs without warnings or errors:

    $ scalac Main.scala
    
  • Setting an explicit release version enforces module access but only if the version is lower than the current JDK version, otherwise it is treated the same as not using modules:

    $ scalac -release:17 Main.scala
    
    $ scalac -release:11 Main.scala
    Main.scala:1: error: object internal is not a member of package jdk.jfr
    import jdk.jfr.internal.Repository
                   ^
    1 error
    
  • There are no equivalents of --add-exports and --system and thus no way to target an older release without enforcing strict module access.

(This is about Scala 2; I tested it with 2.13.14 but AFAICT there are no changes in this area in any recent 2.13 or 2.12 version since the feature was added; I have not tried it with Scala 3)

szeiger avatar Jul 04 '24 13:07 szeiger

The ticket for module support https://github.com/scala/scala-dev/issues/529

som-snytt avatar Jul 04 '24 14:07 som-snytt

Simply adding support for --system without touching any of the other stuff looks straight-forward.

$ /Users/stefan.zeiger/code/scala/build/quick/bin/scalac -target 11 --system /Users/stefan.zeiger/.sdkman/candidates/java/11-zulu-local  Main.scala

It still ignores module access but now you can compile against an older image version like with javac. Since javac does not support the combination of --add-exports and --release it is reasonable (and probably unavoidable) the scalac does the same, so if scalac ever gets full module support, module access will be enforced for the loaded image with or without --system and a new --add-exports option has to be added to allow exceptions.

(My prototype implementation closes the system image after each compilation which is probably not good for performance in long-running compile servers. I'll have to run some benchmarks and possibly add caching.)

szeiger avatar Jul 04 '24 16:07 szeiger