Skip to content

Commit b2439e2

Browse files
authored
Merge pull request #1 from sjrd/initial-implementation
Initial implementation.
2 parents 9b377b4 + 2d3059d commit b2439e2

File tree

9 files changed

+344
-0
lines changed

9 files changed

+344
-0
lines changed

.github/workflows/ci.yml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: CI
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
branches:
8+
- main
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
scalaversion: ["2.11.12", "2.12.13", "2.13.5"]
16+
steps:
17+
- uses: actions/checkout@v2
18+
- uses: olafurpg/setup-scala@v10
19+
with:
20+
java-version: "[email protected]"
21+
- uses: coursier/cache-action@v5
22+
- uses: actions/checkout@v2
23+
with:
24+
repository: scala-js/scala-js
25+
ref: e38b86aef95ac074fe84e7fda3e22e08a723a91b
26+
path: ./scalajs-nightly
27+
- name: publishLocal Scala.js nightly
28+
run: |
29+
sbt ";ir2_12/publishLocal;linkerInterface2_12/publishLocal;linker2_12/publishLocal;testAdapter2_12/publishLocal;sbtPlugin/publishLocal"
30+
sbt ";compiler2_11/publishLocal;library2_11/publishLocal;testInterface2_11/publishLocal;testBridge2_11/publishLocal;jUnitRuntime2_11/publishLocal;jUnitPlugin2_11/publishLocal"
31+
sbt ";compiler2_12/publishLocal;library2_12/publishLocal;testInterface2_12/publishLocal;testBridge2_12/publishLocal;jUnitRuntime2_12/publishLocal;jUnitPlugin2_12/publishLocal"
32+
sbt ";compiler2_13/publishLocal;library2_13/publishLocal;testInterface2_13/publishLocal;testBridge2_13/publishLocal;jUnitRuntime2_13/publishLocal;jUnitPlugin2_13/publishLocal"
33+
working-directory: ./scalajs-nightly
34+
- name: Test JVM
35+
run: sbt "++${{ matrix.scalaversion }}" testSuiteJVM/test
36+
- name: Test JS
37+
run: sbt "++${{ matrix.scalaversion }}" testSuiteJS/test
38+
- name: Doc generation
39+
run: sbt "++${{ matrix.scalaversion }}" root/doc
40+
- name: Header check
41+
run: sbt "++${{ matrix.scalaversion }}" root/headerCheck testSuiteJVM/test:headerCheck testSuiteJS/test:headerCheck

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target/

build.sbt

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
inThisBuild(Def.settings(
2+
crossScalaVersions := Seq("2.12.13", "2.11.12", "2.13.5"),
3+
scalaVersion := crossScalaVersions.value.head,
4+
5+
version := "1.0.0-SNAPSHOT",
6+
organization := "org.scala-js",
7+
scalacOptions ++= Seq(
8+
"-encoding", "utf-8",
9+
"-deprecation",
10+
"-feature",
11+
"-Xfatal-warnings",
12+
),
13+
14+
// Licensing
15+
homepage := Some(url("https://github.com/scala-js/scala-js-weakreferences")),
16+
startYear := Some(2013),
17+
licenses += (("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0"))),
18+
scmInfo := Some(ScmInfo(
19+
url("https://github.com/scala-js/scala-js-weakreferences"),
20+
"scm:git:[email protected]:scala-js/scala-js-weakreferences.git",
21+
Some("scm:git:[email protected]:scala-js/scala-js-weakreferences.git"))),
22+
23+
// Publishing
24+
publishMavenStyle := true,
25+
publishTo := {
26+
val nexus = "https://oss.sonatype.org/"
27+
if (version.value.endsWith("-SNAPSHOT"))
28+
Some("snapshots" at nexus + "content/repositories/snapshots")
29+
else
30+
Some("releases" at nexus + "service/local/staging/deploy/maven2")
31+
},
32+
pomExtra := (
33+
<developers>
34+
<developer>
35+
<id>sjrd</id>
36+
<name>Sébastien Doeraene</name>
37+
<url>https://github.com/sjrd/</url>
38+
</developer>
39+
<developer>
40+
<id>gzm0</id>
41+
<name>Tobias Schlatter</name>
42+
<url>https://github.com/gzm0/</url>
43+
</developer>
44+
</developers>
45+
),
46+
pomIncludeRepository := { _ => false },
47+
))
48+
49+
val commonSettings = Def.settings(
50+
// sbt-header configuration
51+
headerLicense := Some(HeaderLicense.Custom(
52+
s"""scalajs-weakreferences (${homepage.value.get})
53+
|
54+
|Copyright EPFL.
55+
|
56+
|Licensed under Apache License 2.0
57+
|(https://www.apache.org/licenses/LICENSE-2.0).
58+
|
59+
|See the NOTICE file distributed with this work for
60+
|additional information regarding copyright ownership.
61+
|""".stripMargin
62+
)),
63+
)
64+
65+
lazy val root: Project = project.in(file("."))
66+
.enablePlugins(ScalaJSPlugin)
67+
.settings(commonSettings: _*)
68+
.settings(
69+
name := "scalajs-weakreferences",
70+
Compile / packageBin / mappings ~= {
71+
_.filter(!_._2.endsWith(".class"))
72+
},
73+
exportJars := true,
74+
)
75+
76+
lazy val testSuite = crossProject(JSPlatform, JVMPlatform)
77+
.in(file("test-suite"))
78+
.jsConfigure(_.enablePlugins(ScalaJSJUnitPlugin))
79+
.settings(commonSettings: _*)
80+
.settings(
81+
testOptions += Tests.Argument(TestFramework("com.novocode.junit.JUnitFramework"), "-v", "-a"),
82+
)
83+
.jsConfigure(_.dependsOn(root))
84+
.jvmSettings(
85+
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test",
86+
)

project/build.properties

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=1.5.2

project/plugins.sbt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.6.0-SNAPSHOT")
2+
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0")
3+
4+
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.6.0")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* scalajs-weakreferences (https://github.com/scala-js/scala-js-weakreferences)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package java.lang.ref
14+
15+
import scala.scalajs.js
16+
17+
/* The JavaDoc says that the methods are concrete in `Reference`, and not
18+
* overridden in `WeakReference`. To mimic this setup, and since
19+
* `WeakReference` is the only implemented subclass, we actually implement the
20+
* behavior of `WeakReference` in `Reference`.
21+
*/
22+
abstract class Reference[T] private[ref] (referent: T, queue: ReferenceQueue[_ >: T]) {
23+
private[this] var weakRef = new js.WeakRef(referent)
24+
private[ref] var enqueued: Boolean = false
25+
26+
if (queue != null)
27+
queue.register(this, referent)
28+
29+
def get(): T =
30+
if (weakRef == null) null.asInstanceOf[T]
31+
else weakRef.deref().getOrElse(null.asInstanceOf[T])
32+
33+
def clear(): Unit = {
34+
if (queue != null)
35+
queue.unregister(this)
36+
weakRef = null
37+
}
38+
39+
def isEnqueued(): Boolean =
40+
enqueued
41+
42+
def enqueue(): Boolean = {
43+
if (queue != null && queue.enqueue(this)) {
44+
queue.unregister(this)
45+
true
46+
} else {
47+
false
48+
}
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* scalajs-weakreferences (https://github.com/scala-js/scala-js-weakreferences)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package java.lang.ref
14+
15+
import scala.scalajs.js
16+
17+
class ReferenceQueue[T] {
18+
/** The "enqueued" References.
19+
*
20+
* Despite the name, this is used more as a stack (LIFO) than as a queue
21+
* (FIFO). The JavaDoc of `ReferenceQueue` does not actually prescribe FIFO
22+
* ordering, and experimentation shows that the JVM implementation does not
23+
* guarantee that ordering.
24+
*/
25+
private[this] val enqueuedRefs = js.Array[Reference[_ <: T]]()
26+
27+
private[this] val finalizationRegistry = {
28+
new js.FinalizationRegistry[T, Reference[_ <: T], Reference[_ <: T]]({
29+
(ref: Reference[_ <: T]) => enqueue(ref)
30+
})
31+
}
32+
33+
private[ref] def register(ref: Reference[_ <: T], referent: T): Unit =
34+
finalizationRegistry.register(referent, ref, ref)
35+
36+
private[ref] def unregister(ref: Reference[_ <: T]): Unit =
37+
finalizationRegistry.unregister(ref)
38+
39+
private[ref] def enqueue(ref: Reference[_ <: T]): Boolean = {
40+
if (ref.enqueued) {
41+
false
42+
} else {
43+
ref.enqueued = true
44+
enqueuedRefs.push(ref)
45+
true
46+
}
47+
}
48+
49+
def poll(): Reference[_ <: T] = {
50+
if (enqueuedRefs.length == 0)
51+
null
52+
else
53+
enqueuedRefs.pop()
54+
}
55+
56+
// Not implemented because they have a blocking contract:
57+
//def remove(timeout: Long): Reference[_ <: T] = ???
58+
//def remove(): Reference[_ <: T] = ???
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* scalajs-weakreferences (https://github.com/scala-js/scala-js-weakreferences)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package java.lang.ref
14+
15+
class WeakReference[T](referent: T, q: ReferenceQueue[_ >: T])
16+
extends Reference[T](referent, q) {
17+
18+
def this(referent: T) = this(referent, null)
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* scalajs-weakreferences (https://github.com/scala-js/scala-js-weakreferences)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package org.scalajs.testsuite.javalib.lang.ref
14+
15+
import java.lang.ref._
16+
import java.lang.StringBuilder
17+
18+
import org.junit.Test
19+
import org.junit.Assert._
20+
21+
class WeakReferenceTest {
22+
@Test def allOperationsWithoutQueue(): Unit = {
23+
val obj = new StringBuilder("foo")
24+
val ref = new WeakReference(obj)
25+
assertSame(obj, ref.get())
26+
assertEquals(false, ref.enqueue())
27+
assertEquals(false, ref.isEnqueued())
28+
ref.clear()
29+
assertNull(ref.get())
30+
assertEquals(false, ref.isEnqueued())
31+
}
32+
33+
@Test def explicitEnqueue(): Unit = {
34+
val obj = new StringBuilder("foo")
35+
val queue = new ReferenceQueue[Any]
36+
val ref = new WeakReference(obj, queue)
37+
assertSame(obj, ref.get())
38+
assertNull(queue.poll())
39+
assertFalse(ref.isEnqueued())
40+
assertTrue(ref.enqueue())
41+
assertTrue(ref.isEnqueued())
42+
assertSame(ref, queue.poll())
43+
assertNull(queue.poll())
44+
assertSame(obj, ref.get())
45+
assertFalse(ref.enqueue())
46+
}
47+
48+
@Test def clearDoesNotEnqueue(): Unit = {
49+
val obj = new StringBuilder("foo")
50+
val queue = new ReferenceQueue[Any]
51+
val ref = new WeakReference(obj, queue)
52+
assertSame(obj, ref.get())
53+
assertNull(queue.poll())
54+
assertFalse(ref.isEnqueued())
55+
ref.clear()
56+
assertNull(ref.get())
57+
assertFalse(ref.isEnqueued())
58+
assertNull(queue.poll())
59+
60+
// Can still enqueue after that
61+
assertTrue(ref.enqueue())
62+
assertTrue(ref.isEnqueued())
63+
assertSame(ref, queue.poll())
64+
}
65+
66+
@Test def twoWeakReferencesOverTheSameObjectInQueue(): Unit = {
67+
val obj = new StringBuilder("foo")
68+
val queue = new ReferenceQueue[Any]
69+
val ref1 = new WeakReference(obj, queue)
70+
val ref2 = new WeakReference(obj, queue)
71+
assertSame(obj, ref1.get())
72+
assertSame(obj, ref2.get())
73+
74+
ref1.enqueue()
75+
ref2.enqueue()
76+
val polled1 = queue.poll()
77+
val polled2 = queue.poll()
78+
assertTrue(polled1 == ref1 || polled1 == ref2)
79+
assertTrue(polled2 == ref1 || polled2 == ref2)
80+
assertNotSame(polled1, polled2)
81+
assertNull(queue.poll())
82+
}
83+
}

0 commit comments

Comments
 (0)