-
Notifications
You must be signed in to change notification settings - Fork 520
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Java release option #2825
Use Java release option #2825
Conversation
Seems like fs2 could benefit from this? |
Scala 3Example 1package io.vasilev
import java.lang.invoke.{MethodHandles, VarHandle}
object VarHandles {
@volatile private[this] var n: Int = 0
private[this] final val N: VarHandle = MethodHandles.lookup().findVarHandle(classOf[VarHandles.type], "n", classOf[Int])
}
scalac io/vasilev/VarHandles.scala --release 8
-- [E006] Not Found Error: io/vasilev/VarHandles.scala:9:29 --------------------
9 | private[this] final val N: VarHandle = MethodHandles.lookup().findVarHandle(classOf[VarHandles.type], "n", classOf[Int])
| ^^^^^^^^^
| Not found: type VarHandle
longer explanation available when compiling with `-explain`
-- [E008] Not Found Error: io/vasilev/VarHandles.scala:9:64 --------------------
9 | private[this] final val N: VarHandle = MethodHandles.lookup().findVarHandle(classOf[VarHandles.type], "n", classOf[Int])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|value findVarHandle is not a member of java.lang.invoke.MethodHandles#Lookup
2 errors found Example 2package io.vasilev
import java.nio.ByteBuffer
object Buffers {
private[this] final val buffer: ByteBuffer = ByteBuffer.allocate(16)
val flipped: ByteBuffer = buffer.flip()
} scalac io/vasilev/Buffers.scala --release 8
-- [E007] Type Mismatch Error: io/vasilev/Buffers.scala:8:39 -------------------
8 | val flipped: ByteBuffer = buffer.flip()
| ^^^^^^^^^^^^^
| Found: java.nio.Buffer
| Required: java.nio.ByteBuffer
longer explanation available when compiling with `-explain`
1 error found Example 3package io.vasilev
import java.nio.ByteBuffer
object Buffers {
private[this] final val buffer: ByteBuffer = ByteBuffer.allocate(16)
buffer.flip()
}
scalac io/vasilev/Buffers.scala
javap -p -c io.vasilev.Buffers\$
Compiled from "Buffers.scala"
public final class io.vasilev.Buffers$ implements java.io.Serializable {
public static final io.vasilev.Buffers$ MODULE$;
private io.vasilev.Buffers$();
Code:
0: aload_0
1: invokespecial #13 // Method java/lang/Object."<init>":()V
4: return
public static {};
Code:
0: new #2 // class io/vasilev/Buffers$
3: dup
4: invokespecial #16 // Method "<init>":()V
7: putstatic #18 // Field MODULE$:Lio/vasilev/Buffers$;
10: bipush 16
12: invokestatic #24 // Method java/nio/ByteBuffer.allocate:(I)Ljava/nio/ByteBuffer;
15: astore_0
16: aload_0
17: invokevirtual #28 // Method java/nio/ByteBuffer.flip:()Ljava/nio/ByteBuffer; <----- Notice the new JVM signature that only works on JDK 9+
20: pop
21: return
private java.lang.Object writeReplace();
Code:
0: new #34 // class scala/runtime/ModuleSerializationProxy
3: dup
4: ldc #2 // class io/vasilev/Buffers$
6: invokespecial #37 // Method scala/runtime/ModuleSerializationProxy."<init>":(Ljava/lang/Class;)V
9: areturn
}
scalac io/vasilev/Buffers.scala -release 8
javap -p -c io.vasilev.Buffers\$
Compiled from "Buffers.scala"
public final class io.vasilev.Buffers$ implements java.io.Serializable {
public static final io.vasilev.Buffers$ MODULE$;
private io.vasilev.Buffers$();
Code:
0: aload_0
1: invokespecial #13 // Method java/lang/Object."<init>":()V
4: return
public static {};
Code:
0: new #2 // class io/vasilev/Buffers$
3: dup
4: invokespecial #16 // Method "<init>":()V
7: putstatic #18 // Field MODULE$:Lio/vasilev/Buffers$;
10: bipush 16
12: invokestatic #24 // Method java/nio/ByteBuffer.allocate:(I)Ljava/nio/ByteBuffer;
15: astore_0
16: aload_0
17: invokevirtual #28 // Method java/nio/ByteBuffer.flip:()Ljava/nio/Buffer; <------ Notice the old JVM signature that works on JDK 8
20: pop
21: return
private java.lang.Object writeReplace();
Code:
0: new #34 // class scala/runtime/ModuleSerializationProxy
3: dup
4: ldc #2 // class io/vasilev/Buffers$
6: invokespecial #37 // Method scala/runtime/ModuleSerializationProxy."<init>":(Ljava/lang/Class;)V
9: areturn
} |
And with this, I will be deleting JDK 8 from my machine. |
I don’t think this helps with fs2 where we use newer JDK APIs but want a JAR that’s 8 compatible. It should work for scodec though. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is super impressive! Two concerns. First, can we sanity-check the ByteBuffer
scenario on Scala 2 as well as Scala 3? Or did you already do that and I missed it? Second, I'd rather see this done as a one-off autoplugin rather than a settings
val like this.
build.sbt
Outdated
@@ -255,6 +255,32 @@ val jsProjects: Seq[ProjectReference] = | |||
val undocumentedRefs = | |||
jsProjects ++ Seq[ProjectReference](benchmarks, example.jvm, tests.jvm, tests.js) | |||
|
|||
lazy val releaseSettings = Seq( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be achieved more easily by tossing a one-off autoplugin into project/
build.sbt
Outdated
.jvmSettings( | ||
javacOptions ++= Seq("-source", "1.8", "-target", "1.8") | ||
javacOptions ++= { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO we should add these to the aforementioned autoplugin and apply them to all projects just so we don't accidentally forget to add them in the future.
@djspiewak Please read the first post carefully. ByteBuffer is covered. |
@vasilmkd I was mostly concerned about Scala 2, since the OP output looks more like Scala 3's error messages and they have two different backends. If both have been sanity checked though I'm very strongly 👍 |
I'll post Scala 2 too. |
Scala 2Example 1package io.vasilev
import java.lang.invoke.{ MethodHandles, VarHandle }
object VarHandles {
@volatile private[this] var n: Int = 0
private[this] final val N: VarHandle = MethodHandles.lookup().findVarHandle(classOf[VarHandles.type], "n", classOf[Int])
} scalac io/vasilev/VarHandles.scala --release 8
io/vasilev/VarHandles.scala:3: error: object VarHandle is not a member of package invoke
import java.lang.invoke.{ MethodHandles, VarHandle }
^
io/vasilev/VarHandles.scala:9: error: not found: type VarHandle
private[this] final val N: VarHandle = MethodHandles.lookup().findVarHandle(classOf[VarHandles.type], "n", classOf[Int])
^
io/vasilev/VarHandles.scala:9: error: value findVarHandle is not a member of java.lang.invoke.MethodHandles.Lookup
private[this] final val N: VarHandle = MethodHandles.lookup().findVarHandle(classOf[VarHandles.type], "n", classOf[Int])
^
3 errors Example 2package io.vasilev
import java.nio.ByteBuffer
object Buffers {
private[this] final val buffer: ByteBuffer = ByteBuffer.allocate(16)
val flipped: ByteBuffer = buffer.flip()
} scalac io/vasilev/Buffers.scala --release 8
io/vasilev/Buffers.scala:8: error: type mismatch;
found : java.nio.Buffer
required: java.nio.ByteBuffer
val flipped: ByteBuffer = buffer.flip()
^
1 error Example 3package io.vasilev
import java.nio.ByteBuffer
object Buffers {
private[this] final val buffer: ByteBuffer = ByteBuffer.allocate(16)
buffer.flip()
}
scalac io/vasilev/Buffers.scala
javap -p -c io.vasilev.Buffers\$
Compiled from "Buffers.scala"
public final class io.vasilev.Buffers$ {
public static final io.vasilev.Buffers$ MODULE$;
private static final java.nio.ByteBuffer buffer;
public static {};
Code:
0: new #2 // class io/vasilev/Buffers$
3: dup
4: invokespecial #14 // Method "<init>":()V
7: putstatic #16 // Field MODULE$:Lio/vasilev/Buffers$;
10: bipush 16
12: invokestatic #22 // Method java/nio/ByteBuffer.allocate:(I)Ljava/nio/ByteBuffer;
15: putstatic #24 // Field buffer:Ljava/nio/ByteBuffer;
18: getstatic #24 // Field buffer:Ljava/nio/ByteBuffer;
21: invokevirtual #28 // Method java/nio/ByteBuffer.flip:()Ljava/nio/ByteBuffer; <------- notice the new JDK 9+ method with overloaded return type, which cannot be called on JDK 8.
24: pop
25: return
private io.vasilev.Buffers$();
Code:
0: aload_0
1: invokespecial #29 // Method java/lang/Object."<init>":()V
4: return
}
scalac io/vasilev/Buffers.scala -release 8
javap -p -c io.vasilev.Buffers\$
Compiled from "Buffers.scala"
public final class io.vasilev.Buffers$ {
public static final io.vasilev.Buffers$ MODULE$;
private static final java.nio.ByteBuffer buffer;
public static {};
Code:
0: new #2 // class io/vasilev/Buffers$
3: dup
4: invokespecial #14 // Method "<init>":()V
7: putstatic #16 // Field MODULE$:Lio/vasilev/Buffers$;
10: bipush 16
12: invokestatic #22 // Method java/nio/ByteBuffer.allocate:(I)Ljava/nio/ByteBuffer;
15: putstatic #24 // Field buffer:Ljava/nio/ByteBuffer;
18: getstatic #24 // Field buffer:Ljava/nio/ByteBuffer;
21: invokevirtual #28 // Method java/nio/ByteBuffer.flip:()Ljava/nio/Buffer; <------ notice that scalac used the JDK 8 compatible method
24: pop
25: return
private io.vasilev.Buffers$();
Code:
0: aload_0
1: invokespecial #29 // Method java/lang/Object."<init>":()V
4: return
} |
Honestly outstanding! I think this means I can delete Java 8 |
cough sbt-typelevel cough 😝 |
:-D It's on my "to peruse" list. Still digging out from under everything else for the time being though. |
// The release flag controls what JVM platform APIs are called. We only want JDK 8 APIs | ||
// in order to maintain compability with JDK 8. | ||
val releaseFlag = | ||
if (version.startsWith("1.8")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's factor this out as it is repeated:
val isJdk8: Boolean = System.getProperty("java.version").startsWith("1.8")
Superseded by #2857. |
Example 1
The easy example first, APIs that don't exist on JDK 8 (
VarHandle
):Compiling:
Example 2
Our favorite example,
ByteBuffer#flip()
on JDK 9+ overridesBuffer#flip()
with a more specific type.On JDK 8 we have:
Buffer.flip()
has the JVM signaturejava/nio/Buffer.flip:()Ljava/nio/Buffer
ByteBuffer.flip()
has the JVM signaturejava/nio/ByteBuffer.flip:()L/java/nio/Buffer
On JDK 9+ we have:
Buffer.flip()
has the same JVM signaturejava/nio/Buffer.flip:()Ljava/nio/Buffer
ByteBuffer.flip()
has the new JVM signaturejava/nio/ByteBuffer.flip:()L/java/nio/ByteBuffer
and thus cannot be called on JDK 8.Let's examine the output of
javac --relase 8
in this case, when we have an explicit return type in the code.io/vasilev/Buffers.java:10: error: incompatible types: Buffer cannot be converted to ByteBuffer ByteBuffer flipped = buffer.flip(); ^ 1 error
Example 3
Final example, what if we do not have an explicit return type and let Java figure it out. In this case we have to examine the produced bytecode, because
javac
won't complain in this case, because we have a method in JDK 8 that syntactically matches the code.--release 8
option.--release 8
option.