Skip to content

Commit

Permalink
Allow to build Scala Native on JDK 21 (#3492)
Browse files Browse the repository at this point in the history
* `Thread.Builder.allowSetThreadLocals` didn't made it to stable release, and is only available in JDK 19-20 until --preview flag. Remove it's implementation and tests referencing this no longer existing API
* Update CI to test against JDK 21 instead of JDK 20 with --enable-preview flags
* Don't warn when accessing `Thread.getId()` which is deprecated in latest JDK versions
* Add stubs for JDK 21 Sequential Collections to allow for linkling when using JDK 21+
* CI - Test JDK 21 only on Scala 2.13, compilation fails in Scala 3
  • Loading branch information
WojciechMazur committed Sep 20, 2023
1 parent 5d53d3e commit 4eb7261
Show file tree
Hide file tree
Showing 14 changed files with 125 additions and 90 deletions.
15 changes: 8 additions & 7 deletions .github/workflows/run-jdk-compliance-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ jobs:
matrix:
os: [ubuntu-20.04, macos-11]
scala: [3]
java: [11, 17, 20]
java: [11, 17]
include:
- java: 20
java-options: "--enable-preview"
# Does not compile with Scala 3 yet
- java: 21
scala: 2.13
- java: 17
scala: 2.13
os: ubuntu-20.04
Expand Down Expand Up @@ -55,10 +56,10 @@ jobs:
fail-fast: false
matrix:
scala: [3]
java: [11, 17, 20]
include:
- java: 20
java-options: "--enable-preview"
java: [11, 17]
# Does not compile with Scala 3 yet
- java: 21
scala: 2.13
steps:
# Disable autocrlf setting, otherwise scalalib patches might not be possible to apply
- name: Setup git config
Expand Down
15 changes: 0 additions & 15 deletions javalib/src/main/scala/java/lang/Thread.scala
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,6 @@ object Thread {
// Since JDK 19
trait Builder {

/** Sets whether the thread is allowed to set values for its copy of
* thread-local variables.
*/
def allowSetThreadLocals(allow: scala.Boolean): Builder

/** Returns a ThreadFactory to create threads from the current state of the
* builder.
*/
Expand Down Expand Up @@ -441,11 +436,6 @@ object Thread {
@throws[IllegalArgumentException]("if the stack size is negative")
def stackSize(stackSize: scala.Long): OfPlatform

/** Sets whether the thread is allowed to set values for its copy of
* thread-local variables.
*/
override def allowSetThreadLocals(allow: scala.Boolean): OfPlatform

/** Sets whether the thread inherits the initial values of
* inheritable-thread-local variables from the constructing thread.
*/
Expand All @@ -470,11 +460,6 @@ object Thread {

trait OfVirtual extends Builder {

/** Sets whether the thread is allowed to set values for its copy of
* thread-local variables.
*/
override def allowSetThreadLocals(allow: scala.Boolean): OfVirtual

/** Sets whether the thread inherits the initial values of
* inheritable-thread-local variables from the constructing thread.
*/
Expand Down
7 changes: 0 additions & 7 deletions javalib/src/main/scala/java/lang/ThreadBuilders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ object ThreadBuilders {
self
}

override def allowSetThreadLocals(allow: scala.Boolean): Self = {
val flag = Characteristics.NoThreadLocal
if (allow) characteristics &= ~flag
else characteristics |= flag
self
}

override def inheritInheritableThreadLocals(
inherit: scala.Boolean
): Self = {
Expand Down
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/util/Deque.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package java.util

trait Deque[E] extends Queue[E] {
trait Deque[E] extends Queue[E] with SequencedCollection[E] {
def addFirst(e: E): Unit
def addLast(e: E): Unit
def offerFirst(e: E): Boolean
Expand Down
3 changes: 2 additions & 1 deletion javalib/src/main/scala/java/util/LinkedHashMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class LinkedHashMap[K, V](
initialCapacity: Int,
loadFactor: Float,
accessOrder: Boolean
) extends HashMap[K, V](initialCapacity, loadFactor) {
) extends HashMap[K, V](initialCapacity, loadFactor)
with SequencedMap[K, V] {
self =>

import LinkedHashMap._
Expand Down
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/util/LinkedHashSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import scala.collection.mutable

class LinkedHashSet[E]
extends HashSet[E]
with Set[E]
with SequencedSet[E]
with Cloneable
with Serializable {
def this(initialCapacity: Int, loadFactor: Float) =
Expand Down
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/util/List.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package java.util

import java.util.function.UnaryOperator

trait List[E] extends Collection[E] {
trait List[E] extends SequencedCollection[E] {
def replaceAll(operator: UnaryOperator[E]): Unit = {
val iter = listIterator()
while (iter.hasNext())
Expand Down
27 changes: 27 additions & 0 deletions javalib/src/main/scala/java/util/SequencedCollection.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package java.util

trait SequencedCollection[E /* <: AnyRef */ ] extends Collection[E] {
/* Commented out until we're able to provide reversed views for collections
def reversed(): SequencedCollection[E]
def addFirst(elem: E): Unit = throw new UnsupportedOperationException()
def addLast(elem: E): Unit = throw new UnsupportedOperationException()
def getFirst(): E = this.iterator().next()
def getLast(): E = this.reversed().iterator().next()
def removeFirst(): E = {
val it = this.iterator()
val elem = it.next()
it.remove()
elem
}
def removeLast(): E = {
val it = this.reversed().iterator()
val elem = it.next()
it.remove()
elem
}
*/
}
73 changes: 73 additions & 0 deletions javalib/src/main/scala/java/util/SequencedMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package java.util

trait SequencedMap[K /* <: AnyRef */, V /* <: AnyRef */ ] extends Map[K, V] {
/* Commented out until we're able to provide reversed views for collections
def reversed(): SequencedMap[K, V]
def firstEntry(): Map.Entry[K, V] = {
val it = entrySet().iterator()
if (it.hasNext()) SequencedMap.CopyOfEntry(it.next()) else null
}
def lastEntry(): Map.Entry[K, V] = {
val it = reversed().entrySet().iterator()
if (it.hasNext()) SequencedMap.CopyOfEntry(it.next()) else null
}
def pollFirstEntry(): Map.Entry[K, V] = {
val it = entrySet().iterator()
if (it.hasNext()) {
val entry = SequencedMap.CopyOfEntry(it.next())
it.remove()
entry
} else null
}
def pollLastEntry(): Map.Entry[K, V] = {
val it = this.reversed().entrySet().iterator()
if (it.hasNext()) {
val entry = SequencedMap.CopyOfEntry(it.next())
it.remove()
entry
} else null
}
def putFirst(key: K, value: V): V = throw new UnsupportedOperationException()
def putLast(key: K, value: V): V = throw new UnsupportedOperationException()
def sequencedKeySet(): SequencedSet[K] = ???
def sequencedValues(): SequencedCollection[V] = ???
def sequencedEntrySet(): SequencedSet[Map.Entry[K, V]] = ???
}
private object SequencedMap {
private object CopyOfEntry {
def apply[K /* <: AnyRef */, V /* <: AnyRef */](entry: Map.Entry[K, V]) = {
Objects.requireNonNull(entry)
new CopyOfEntry(
key = entry.getKey(),
value = entry.getValue()
)
}
}
private class CopyOfEntry[K /* <: AnyRef */, V /* <: AnyRef */] private (key: K, value: V)
extends Map.Entry[K, V] {
override def getKey(): K = key
override def getValue(): V = value
override def setValue(value: V): V =
throw new UnsupportedOperationException()
override def equals(o: Any): Boolean = o match {
case entry: Map.Entry[K, V] @unchecked =>
Objects.equals(key, entry.getKey()) &&
Objects.equals(value, entry.getValue())
case _ => false
}
override def hashCode(): Int = {
def hash(obj: Any) = if (obj == null) 0 else obj.##
hash(key) ^ hash(value)
}
override def toString(): String = s"$key=$value"
}
*/
}
9 changes: 9 additions & 0 deletions javalib/src/main/scala/java/util/SequencedSet.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package java.util

trait SequencedSet[E /* <: AnyRef */ ]
extends SequencedCollection[E]
with Set[E] {
/* Commented out until we're able to provide reversed views for collections
override def reversed(): SequencedSet[E]
*/
}
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/util/SortedMap.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package java.util

trait SortedMap[K, V] extends Map[K, V] {
trait SortedMap[K, V] extends Map[K, V] with SequencedMap[K, V] {
def firstKey(): K
def comparator(): Comparator[_ >: K]
def lastKey(): K
Expand Down
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/util/SortedSet.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package java.util

trait SortedSet[E] extends Set[E] {
trait SortedSet[E] extends Set[E] with SequencedSet[E] {
def comparator(): Comparator[_ >: E]
def subSet(fromElement: E, toElement: E): SortedSet[E]
def headSet(toElement: E): SortedSet[E]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import scala.concurrent._
import scala.scalanative.build.Logger
import scala.scalanative.testinterface.NativeRunnerRPC
import scala.scalanative.testinterface.common._
import scala.annotation.nowarn

final class TestAdapter(config: TestAdapter.Config) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,27 +466,6 @@ class ThreadBuilderTestOnJDK19 {
testThreadLocals(builder)
}

@Test def testThreadLocals3(): Unit = {
val builder = Thread.ofPlatform()
// disallow
builder.allowSetThreadLocals(false)
testNoThreadLocals(builder)
// allow
builder.allowSetThreadLocals(true)
testThreadLocals(builder)
}

@Ignore("VirtualThreads unimplemented")
@Test def testThreadLocals4(): Unit = {
val builder = Thread.ofVirtual()
// disallow
builder.allowSetThreadLocals(false)
testNoThreadLocals(builder)
// allow
builder.allowSetThreadLocals(true)
testThreadLocals(builder)
}

private def testInheritedThreadLocals(builder: Thread.Builder): Unit = {
val value = new AnyRef
InheritedLocal.set(value)
Expand Down Expand Up @@ -561,40 +540,6 @@ class ThreadBuilderTestOnJDK19 {
builder.inheritInheritableThreadLocals(true)
testInheritedThreadLocals(builder)
}
@Test def testInheritedThreadLocals3(): Unit = {
val builder = Thread.ofPlatform()
// thread locals not allowed
builder.allowSetThreadLocals(false)
testNoInheritedThreadLocals(builder)
builder.inheritInheritableThreadLocals(false)
testNoInheritedThreadLocals(builder)
builder.inheritInheritableThreadLocals(true)
testNoInheritedThreadLocals(builder)
// thread locals allowed
builder.allowSetThreadLocals(true)
builder.inheritInheritableThreadLocals(false)
testNoInheritedThreadLocals(builder)
builder.inheritInheritableThreadLocals(true)
testInheritedThreadLocals(builder)
}

@Ignore("VirtualThreads unimplemented")
@Test def testInheritedThreadLocals4(): Unit = {
val builder = Thread.ofVirtual()
// thread locals not allowed
builder.allowSetThreadLocals(false)
testNoInheritedThreadLocals(builder)
builder.inheritInheritableThreadLocals(false)
testNoInheritedThreadLocals(builder)
builder.inheritInheritableThreadLocals(true)
testNoInheritedThreadLocals(builder)
// thread locals allowed
builder.allowSetThreadLocals(true)
builder.inheritInheritableThreadLocals(false)
testNoInheritedThreadLocals(builder)
builder.inheritInheritableThreadLocals(true)
testInheritedThreadLocals(builder)
}

@Test def testNulls1(): Unit = {
val builder = Thread.ofPlatform()
Expand Down

0 comments on commit 4eb7261

Please sign in to comment.