diff --git a/javalib/src/main/scala/java/io/BufferedReader.scala b/javalib/src/main/scala/java/io/BufferedReader.scala
index 3257c5b9d1..4cb0afdd32 100644
--- a/javalib/src/main/scala/java/io/BufferedReader.scala
+++ b/javalib/src/main/scala/java/io/BufferedReader.scala
@@ -16,7 +16,9 @@ class BufferedReader(in: Reader, sz: Int) extends Reader {
def this(in: Reader) = this(in, 4096)
- private[this] var buf = new Array[Char](sz)
+ // Workaround 2.11 with no-specialization ; buf should be initialized on the same line
+ private[this] var buf: Array[Char] = null
+ buf = new Array[Char](sz)
/** Last valid value in the buffer (exclusive) */
private[this] var end = 0
diff --git a/javalib/src/main/scala/java/math/BigDecimal.scala b/javalib/src/main/scala/java/math/BigDecimal.scala
index c3ffdba757..6ee2f36636 100644
--- a/javalib/src/main/scala/java/math/BigDecimal.scala
+++ b/javalib/src/main/scala/java/math/BigDecimal.scala
@@ -58,8 +58,13 @@ object BigDecimal {
private final val LongFivePows = newArrayOfPows(28, 5)
- private final val LongFivePowsBitLength =
- Array.tabulate[Int](LongFivePows.length)(i => bitLength(LongFivePows(i)))
+ private final val LongFivePowsBitLength = {
+ val len = LongFivePows.length
+ val result = new Array[Int](len)
+ for (i <- 0 until len)
+ result(i) = bitLength(LongFivePows(i))
+ result
+ }
/** An array of longs with powers of ten.
*
@@ -68,8 +73,13 @@ object BigDecimal {
*/
private[math] final val LongTenPows = newArrayOfPows(19, 10)
- private final val LongTenPowsBitLength =
- Array.tabulate[Int](LongTenPows.length)(i => bitLength(LongTenPows(i)))
+ private final val LongTenPowsBitLength = {
+ val len = LongTenPows.length
+ val result = new Array[Int](len)
+ for (i <- 0 until len)
+ result(i) = bitLength(LongTenPows(i))
+ result
+ }
private final val BigIntScaledByZeroLength = 11
@@ -77,15 +87,23 @@ object BigDecimal {
*
* ([0,0],[1,0],...,[10,0]
).
*/
- private final val BigIntScaledByZero =
- Array.tabulate[BigDecimal](BigIntScaledByZeroLength)(new BigDecimal(_, 0))
+ private final val BigIntScaledByZero = {
+ val result = new Array[BigDecimal](BigIntScaledByZeroLength)
+ for (i <- 0 until BigIntScaledByZeroLength)
+ result(i) = new BigDecimal(i, 0)
+ result
+ }
/** An array with the zero number scaled by the first positive scales.
*
* (0*10^0, 0*10^1, ..., 0*10^10
).
*/
- private final val ZeroScaledBy =
- Array.tabulate[BigDecimal](BigIntScaledByZeroLength)(new BigDecimal(0, _))
+ private final val ZeroScaledBy = {
+ val result = new Array[BigDecimal](BigIntScaledByZeroLength)
+ for (i <- 0 until BigIntScaledByZeroLength)
+ result(i) = new BigDecimal(0, i)
+ result
+ }
/** A string filled with 100 times the character `'0'`.
* It is not a `final` val so that it isn't copied at every call site.
@@ -205,8 +223,13 @@ object BigDecimal {
else 0
}
- private[math] def newArrayOfPows(len: Int, pow: Int): Array[Long] =
- Array.iterate(1L, len)(_ * pow)
+ private[math] def newArrayOfPows(len: Int, pow: Int): Array[Long] = {
+ val result = new Array[Long](len)
+ result(0) = 1L
+ for (i <- 1 until len)
+ result(i) = result(i - 1) * pow
+ result
+ }
/** Return an increment that can be -1,0 or 1, depending on {@code roundingMode}.
*
@@ -276,11 +299,20 @@ object BigDecimal {
32 - java.lang.Integer.numberOfLeadingZeros(smallValue)
}
- @inline
- private def charNotEqualTo(c: Char, cs: Char*): Boolean = !cs.contains(c)
+ private def charNotEqualTo(c: Char, cs: Array[Char]): Boolean = !charEqualTo(c, cs)
- @inline
- private def charEqualTo(c: Char, cs: Char*): Boolean = cs.contains(c)
+ private def charEqualTo(c: Char, cs: Array[Char]): Boolean = {
+ // scalastyle:off return
+ val len = cs.length
+ var i = 0
+ while (i != len) {
+ if (cs(i) == c)
+ return true
+ i += 1
+ }
+ false
+ // scalastyle:on return
+ }
@inline
private def insertString(s: String, pos: Int, s2: String): String =
@@ -374,12 +406,12 @@ class BigDecimal() extends Number with Comparable[BigDecimal] {
if (offset <= last && in(offset) == '+') {
index += 1
// Fail if the next character is another sign.
- if (index < last && charEqualTo(in(index), '+', '-'))
+ if (index < last && charEqualTo(in(index), Array('+', '-')))
throw new NumberFormatException("For input string: " + in.toString)
} else {
// check that '-' is not followed by another sign
val isMinus = index <= last && in(index) == '-'
- val nextIsSign = index + 1 < last && charEqualTo(in(index + 1), '+', '-')
+ val nextIsSign = index + 1 < last && charEqualTo(in(index + 1), Array('+', '-'))
if (isMinus && nextIsSign)
throw new NumberFormatException("For input string: " + in.toString)
}
@@ -388,7 +420,7 @@ class BigDecimal() extends Number with Comparable[BigDecimal] {
var counter = 0
var wasNonZero = false
// Accumulating all digits until a possible decimal point
- while (index <= last && charNotEqualTo(in(index), '.', 'e', 'E')) {
+ while (index <= last && charNotEqualTo(in(index), Array('.', 'e', 'E'))) {
if (!wasNonZero) {
if (in(index) == '0') counter += 1
else wasNonZero = true
@@ -404,7 +436,7 @@ class BigDecimal() extends Number with Comparable[BigDecimal] {
index += 1
// Accumulating all digits until a possible exponent
val begin = index
- while (index <= last && charNotEqualTo(in(index), 'e', 'E')) {
+ while (index <= last && charNotEqualTo(in(index), Array('e', 'E'))) {
if (!wasNonZero) {
if (in(index) == '0') counter += 1
else wasNonZero = true
@@ -420,7 +452,7 @@ class BigDecimal() extends Number with Comparable[BigDecimal] {
}
// An exponent was found
- if ((index <= last) && charEqualTo(in(index), 'e', 'E')) {
+ if ((index <= last) && charEqualTo(in(index), Array('e', 'E'))) {
index += 1
// Checking for a possible sign of scale
val indexIsPlus = index <= last && in(index) == '+'
diff --git a/javalib/src/main/scala/java/math/BigInteger.scala b/javalib/src/main/scala/java/math/BigInteger.scala
index 2de2c425cb..59ce0d1c49 100644
--- a/javalib/src/main/scala/java/math/BigInteger.scala
+++ b/javalib/src/main/scala/java/math/BigInteger.scala
@@ -74,7 +74,12 @@ object BigInteger {
new BigInteger(1, 4), new BigInteger(1, 5), new BigInteger(1, 6),
new BigInteger(1, 7), new BigInteger(1, 8), new BigInteger(1, 9), TEN)
- private final val TWO_POWS = Array.tabulate[BigInteger](32)(i => BigInteger.valueOf(1L << i))
+ private final val TWO_POWS = {
+ val result = new Array[BigInteger](32)
+ for (i <- 0 until 32)
+ result(i) = BigInteger.valueOf(1L << i)
+ result
+ }
/** The first non zero digit is either -1 if sign is zero, otherwise it is >= 0.
*
diff --git a/javalib/src/main/scala/java/math/Division.scala b/javalib/src/main/scala/java/math/Division.scala
index f895fc5fe1..a69382135b 100644
--- a/javalib/src/main/scala/java/math/Division.scala
+++ b/javalib/src/main/scala/java/math/Division.scala
@@ -884,11 +884,14 @@ private[math] object Division {
for (i <- 0 until modulusLen) {
var innnerCarry: Int = 0 // unsigned
val m = Multiplication.unsignedMultAddAdd(res(i), n2, 0, 0).toInt
- for (j <- 0 until modulusLen) {
+ // Work around Scala 2.11 limitation with the IR cleaner ; should be for (j <- 0 until modulusLen)
+ var j = 0
+ while (j < modulusLen) {
val nextInnnerCarry =
unsignedMultAddAdd(m, modulusDigits(j), res(i + j), innnerCarry)
res(i + j) = nextInnnerCarry.toInt
innnerCarry = (nextInnnerCarry >> 32).toInt
+ j += 1
}
val nextOuterCarry =
(outerCarry & UINT_MAX) + (res(i + modulusLen) & UINT_MAX) + (innnerCarry & UINT_MAX)
diff --git a/javalib/src/main/scala/java/math/Multiplication.scala b/javalib/src/main/scala/java/math/Multiplication.scala
index 9f0ca4188e..10ecb738cc 100644
--- a/javalib/src/main/scala/java/math/Multiplication.scala
+++ b/javalib/src/main/scala/java/math/Multiplication.scala
@@ -124,10 +124,13 @@ private[math] object Multiplication {
for (i <- 0 until aLen) {
carry = 0
- for (j <- i + 1 until aLen) {
+ // Work around Scala 2.11 limitation with the IR cleaner ; should be for (j <- i + 1 until aLen)
+ var j = i + 1
+ while (j < aLen) {
val t = unsignedMultAddAdd(a(i), a(j), res(i + j), carry)
res(i + j) = t.toInt
carry = (t >>> 32).toInt
+ j += 1
}
res(i + aLen) = carry
}
@@ -439,16 +442,24 @@ private[math] object Multiplication {
for (i <- 0 until aLen) {
var carry = 0
val aI = a(i)
- for (j <- 0 until bLen) {
+ // Work around Scala 2.11 limitation with the IR cleaner ; should be for (j <- 0 until bLen)
+ var j = 0
+ while (j < bLen) {
val added = unsignedMultAddAdd(aI, b(j), t(i + j), carry)
t(i + j) = added.toInt
carry = (added >>> 32).toInt
+ j += 1
}
t(i + bLen) = carry
}
}
}
- private def newArrayOfPows(len: Int, pow: Int): Array[Int] =
- Array.iterate(1, len)(_ * pow)
+ private def newArrayOfPows(len: Int, pow: Int): Array[Int] = {
+ val result = new Array[Int](len)
+ result(0) = 1
+ for (i <- 1 until len)
+ result(i) = result(i - 1) * pow
+ result
+ }
}
diff --git a/javalib/src/main/scala/java/math/Primality.scala b/javalib/src/main/scala/java/math/Primality.scala
index 354b6607c9..b7fd19101b 100644
--- a/javalib/src/main/scala/java/math/Primality.scala
+++ b/javalib/src/main/scala/java/math/Primality.scala
@@ -79,8 +79,13 @@ private[math] object Primality {
(18, 13), (31, 23), (54, 43), (97, 75))
/** All {@code BigInteger} prime numbers with bit length lesser than 8 bits. */
- private val BiPrimes =
- Array.tabulate[BigInteger](Primes.length)(i => BigInteger.valueOf(Primes(i)))
+ private val BiPrimes = {
+ val len = Primes.length
+ val result = new Array[BigInteger](len)
+ for (i <- 0 until len)
+ result(i) = BigInteger.valueOf(Primes(i))
+ result
+ }
/** A random number is generated until a probable prime number is found.
*
diff --git a/javalib/src/main/scala/java/net/URI.scala b/javalib/src/main/scala/java/net/URI.scala
index 58dda9f946..e114fe6ac7 100644
--- a/javalib/src/main/scala/java/net/URI.scala
+++ b/javalib/src/main/scala/java/net/URI.scala
@@ -19,6 +19,7 @@ import scala.annotation.tailrec
import java.nio._
import java.nio.charset.{CodingErrorAction, StandardCharsets}
+import java.util.JSUtils._
final class URI(origStr: String) extends Serializable with Comparable[URI] {
@@ -35,10 +36,10 @@ final class URI(origStr: String) extends Serializable with Comparable[URI] {
if (_fld == null)
throw new URISyntaxException(origStr, "Malformed URI")
- private val _isAbsolute = _fld(AbsScheme).isDefined
- private val _isOpaque = _fld(AbsOpaquePart).isDefined
+ private val _isAbsolute = undefOrIsDefined(_fld(AbsScheme))
+ private val _isOpaque = undefOrIsDefined(_fld(AbsOpaquePart))
- @inline private def fld(idx: Int): String = _fld(idx).orNull
+ @inline private def fld(idx: Int): String = undefOrGetOrNull(_fld(idx))
@inline private def fld(absIdx: Int, relIdx: Int): String =
if (_isAbsolute) fld(absIdx) else fld(relIdx)
@@ -187,7 +188,7 @@ final class URI(origStr: String) extends Serializable with Comparable[URI] {
def getUserInfo(): String = decodeComponent(_userInfo)
override def hashCode(): Int = {
- import scala.util.hashing.MurmurHash3._
+ import java.util.internal.MurmurHash3._
import URI.normalizeEscapes
def normalizeEscapesHash(str: String): Int =
diff --git a/javalib/src/main/scala/java/nio/ByteBuffer.scala b/javalib/src/main/scala/java/nio/ByteBuffer.scala
index 8b100204f8..92cb2a8ea0 100644
--- a/javalib/src/main/scala/java/nio/ByteBuffer.scala
+++ b/javalib/src/main/scala/java/nio/ByteBuffer.scala
@@ -31,14 +31,14 @@ object ByteBuffer {
// Extended API
- def wrap(array: ArrayBuffer): ByteBuffer =
- TypedArrayByteBuffer.wrap(array)
+ def wrapArrayBuffer(array: ArrayBuffer): ByteBuffer =
+ TypedArrayByteBuffer.wrapArrayBuffer(array)
- def wrap(array: ArrayBuffer, byteOffset: Int, length: Int): ByteBuffer =
- TypedArrayByteBuffer.wrap(array, byteOffset, length)
+ def wrapArrayBuffer(array: ArrayBuffer, byteOffset: Int, length: Int): ByteBuffer =
+ TypedArrayByteBuffer.wrapArrayBuffer(array, byteOffset, length)
- def wrap(array: Int8Array): ByteBuffer =
- TypedArrayByteBuffer.wrap(array)
+ def wrapInt8Array(array: Int8Array): ByteBuffer =
+ TypedArrayByteBuffer.wrapInt8Array(array)
}
abstract class ByteBuffer private[nio] (
diff --git a/javalib/src/main/scala/java/nio/CharBuffer.scala b/javalib/src/main/scala/java/nio/CharBuffer.scala
index 79443286ec..8501f7a01c 100644
--- a/javalib/src/main/scala/java/nio/CharBuffer.scala
+++ b/javalib/src/main/scala/java/nio/CharBuffer.scala
@@ -34,8 +34,8 @@ object CharBuffer {
// Extended API
- def wrap(array: Uint16Array): CharBuffer =
- TypedArrayCharBuffer.wrap(array)
+ def wrapUint16Array(array: Uint16Array): CharBuffer =
+ TypedArrayCharBuffer.wrapUint16Array(array)
}
abstract class CharBuffer private[nio] (
diff --git a/javalib/src/main/scala/java/nio/DataViewExt.scala b/javalib/src/main/scala/java/nio/DataViewExt.scala
new file mode 100644
index 0000000000..f034f2f915
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/DataViewExt.scala
@@ -0,0 +1,46 @@
+/*
+ * Scala.js (https://www.scala-js.org/)
+ *
+ * Copyright EPFL.
+ *
+ * Licensed under Apache License 2.0
+ * (https://www.apache.org/licenses/LICENSE-2.0).
+ *
+ * See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ */
+
+package java.nio
+
+import scala.scalajs.js.typedarray.DataView
+
+/** Copy of features in `scala.scalajs.js.typedarray.DateViewExt`.
+ *
+ * Defined as functions instead of extension methods, because the AnyVal over
+ * a JS type generates an `equals` method that references `BoxesRunTime`.
+ */
+private[nio] object DataViewExt {
+ /** Reads a 2's complement signed 64-bit integers from the data view.
+ * @param index Starting index
+ * @param littleEndian Whether the number is stored in little endian
+ */
+ @inline
+ def dataViewGetInt64(dataView: DataView, index: Int, littleEndian: Boolean): Long = {
+ val high = dataView.getInt32(index + (if (littleEndian) 4 else 0), littleEndian)
+ val low = dataView.getInt32(index + (if (littleEndian) 0 else 4), littleEndian)
+ (high.toLong << 32) | (low.toLong & 0xffffffffL)
+ }
+
+ /** Writes a 2's complement signed 64-bit integers to the data view.
+ * @param index Starting index
+ * @param value Value to be written
+ * @param littleEndian Whether to store the number in little endian
+ */
+ @inline
+ def dataViewSetInt64(dataView: DataView, index: Int, value: Long, littleEndian: Boolean): Unit = {
+ val high = (value >>> 32).toInt
+ val low = value.toInt
+ dataView.setInt32(index + (if (littleEndian) 4 else 0), high, littleEndian)
+ dataView.setInt32(index + (if (littleEndian) 0 else 4), low, littleEndian)
+ }
+}
diff --git a/javalib/src/main/scala/java/nio/DataViewLongBuffer.scala b/javalib/src/main/scala/java/nio/DataViewLongBuffer.scala
index 3ee08fee13..3d083001cb 100644
--- a/javalib/src/main/scala/java/nio/DataViewLongBuffer.scala
+++ b/javalib/src/main/scala/java/nio/DataViewLongBuffer.scala
@@ -12,8 +12,9 @@
package java.nio
+import java.nio.DataViewExt._
+
import scala.scalajs.js.typedarray._
-import DataViewExt._
private[nio] final class DataViewLongBuffer private (
override private[nio] val _dataView: DataView,
@@ -86,11 +87,11 @@ private[nio] final class DataViewLongBuffer private (
@inline
private[nio] def load(index: Int): Long =
- _dataView.getInt64(8 * index, !isBigEndian)
+ dataViewGetInt64(_dataView, 8 * index, !isBigEndian)
@inline
private[nio] def store(index: Int, elem: Long): Unit =
- _dataView.setInt64(8 * index, elem, !isBigEndian)
+ dataViewSetInt64(_dataView, 8 * index, elem, !isBigEndian)
@inline
override private[nio] def load(startIndex: Int,
diff --git a/javalib/src/main/scala/java/nio/DoubleBuffer.scala b/javalib/src/main/scala/java/nio/DoubleBuffer.scala
index 34c77ba0c5..4a4fcda944 100644
--- a/javalib/src/main/scala/java/nio/DoubleBuffer.scala
+++ b/javalib/src/main/scala/java/nio/DoubleBuffer.scala
@@ -28,8 +28,8 @@ object DoubleBuffer {
// Extended API
- def wrap(array: Float64Array): DoubleBuffer =
- TypedArrayDoubleBuffer.wrap(array)
+ def wrapFloat64Array(array: Float64Array): DoubleBuffer =
+ TypedArrayDoubleBuffer.wrapFloat64Array(array)
}
abstract class DoubleBuffer private[nio] (
diff --git a/javalib/src/main/scala/java/nio/FloatBuffer.scala b/javalib/src/main/scala/java/nio/FloatBuffer.scala
index dc816242c6..9f9a8021de 100644
--- a/javalib/src/main/scala/java/nio/FloatBuffer.scala
+++ b/javalib/src/main/scala/java/nio/FloatBuffer.scala
@@ -28,8 +28,8 @@ object FloatBuffer {
// Extended API
- def wrap(array: Float32Array): FloatBuffer =
- TypedArrayFloatBuffer.wrap(array)
+ def wrapFloat32Array(array: Float32Array): FloatBuffer =
+ TypedArrayFloatBuffer.wrapFloat32Array(array)
}
abstract class FloatBuffer private[nio] (
diff --git a/javalib/src/main/scala/java/nio/GenBuffer.scala b/javalib/src/main/scala/java/nio/GenBuffer.scala
index 9489f579a3..514a0daec9 100644
--- a/javalib/src/main/scala/java/nio/GenBuffer.scala
+++ b/javalib/src/main/scala/java/nio/GenBuffer.scala
@@ -118,7 +118,7 @@ private[nio] final class GenBuffer[B <: Buffer] private (val self: B)
@inline
def generic_hashCode(hashSeed: Int): Int = {
- import scala.util.hashing.MurmurHash3._
+ import java.util.internal.MurmurHash3._
val start = position()
val end = limit()
var h = hashSeed
diff --git a/javalib/src/main/scala/java/nio/IntBuffer.scala b/javalib/src/main/scala/java/nio/IntBuffer.scala
index 09cfa88515..5e31304b4f 100644
--- a/javalib/src/main/scala/java/nio/IntBuffer.scala
+++ b/javalib/src/main/scala/java/nio/IntBuffer.scala
@@ -28,8 +28,8 @@ object IntBuffer {
// Extended API
- def wrap(array: Int32Array): IntBuffer =
- TypedArrayIntBuffer.wrap(array)
+ def wrapInt32Array(array: Int32Array): IntBuffer =
+ TypedArrayIntBuffer.wrapInt32Array(array)
}
abstract class IntBuffer private[nio] (
diff --git a/javalib/src/main/scala/java/nio/ShortBuffer.scala b/javalib/src/main/scala/java/nio/ShortBuffer.scala
index d31b13fec8..b3d3b9b0b5 100644
--- a/javalib/src/main/scala/java/nio/ShortBuffer.scala
+++ b/javalib/src/main/scala/java/nio/ShortBuffer.scala
@@ -28,8 +28,8 @@ object ShortBuffer {
// Extended API
- def wrap(array: Int16Array): ShortBuffer =
- TypedArrayShortBuffer.wrap(array)
+ def wrapInt16Array(array: Int16Array): ShortBuffer =
+ TypedArrayShortBuffer.wrapInt16Array(array)
}
abstract class ShortBuffer private[nio] (
diff --git a/javalib/src/main/scala/java/nio/TypedArrayByteBuffer.scala b/javalib/src/main/scala/java/nio/TypedArrayByteBuffer.scala
index 26f93b0012..2371a887e5 100644
--- a/javalib/src/main/scala/java/nio/TypedArrayByteBuffer.scala
+++ b/javalib/src/main/scala/java/nio/TypedArrayByteBuffer.scala
@@ -12,8 +12,9 @@
package java.nio
+import java.nio.DataViewExt._
+
import scala.scalajs.js.typedarray._
-import DataViewExt._
private[nio] final class TypedArrayByteBuffer private (
override private[nio] val _typedArray: Int8Array,
@@ -128,13 +129,13 @@ private[nio] final class TypedArrayByteBuffer private (
}
@noinline def getLong(): Long =
- _dataView.getInt64(getPosAndAdvanceRead(8), !isBigEndian)
+ dataViewGetInt64(_dataView, getPosAndAdvanceRead(8), !isBigEndian)
@noinline def putLong(value: Long): ByteBuffer =
- { ensureNotReadOnly(); _dataView.setInt64(getPosAndAdvanceWrite(8), value, !isBigEndian); this }
+ { ensureNotReadOnly(); dataViewSetInt64(_dataView, getPosAndAdvanceWrite(8), value, !isBigEndian); this }
@noinline def getLong(index: Int): Long =
- _dataView.getInt64(validateIndex(index, 8), !isBigEndian)
+ dataViewGetInt64(_dataView, validateIndex(index, 8), !isBigEndian)
@noinline def putLong(index: Int, value: Long): ByteBuffer =
- { ensureNotReadOnly(); _dataView.setInt64(validateIndex(index, 8), value, !isBigEndian); this }
+ { ensureNotReadOnly(); dataViewSetInt64(_dataView, validateIndex(index, 8), value, !isBigEndian); this }
def asLongBuffer(): LongBuffer =
DataViewLongBuffer.fromTypedArrayByteBuffer(this)
@@ -225,13 +226,13 @@ private[nio] object TypedArrayByteBuffer {
new TypedArrayByteBuffer(new Int8Array(capacity), 0, capacity, false)
}
- def wrap(array: ArrayBuffer): ByteBuffer =
- wrap(new Int8Array(array))
+ def wrapArrayBuffer(array: ArrayBuffer): ByteBuffer =
+ wrapInt8Array(new Int8Array(array))
- def wrap(array: ArrayBuffer, byteOffset: Int, length: Int): ByteBuffer =
- wrap(new Int8Array(array, byteOffset, length))
+ def wrapArrayBuffer(array: ArrayBuffer, byteOffset: Int, length: Int): ByteBuffer =
+ wrapInt8Array(new Int8Array(array, byteOffset, length))
- def wrap(typedArray: Int8Array): ByteBuffer = {
+ def wrapInt8Array(typedArray: Int8Array): ByteBuffer = {
val buf = new TypedArrayByteBuffer(typedArray, 0, typedArray.length, false)
buf._isBigEndian = ByteOrder.areTypedArraysBigEndian
buf
diff --git a/javalib/src/main/scala/java/nio/TypedArrayCharBuffer.scala b/javalib/src/main/scala/java/nio/TypedArrayCharBuffer.scala
index 96a8d82056..71a51057d2 100644
--- a/javalib/src/main/scala/java/nio/TypedArrayCharBuffer.scala
+++ b/javalib/src/main/scala/java/nio/TypedArrayCharBuffer.scala
@@ -135,6 +135,6 @@ private[nio] object TypedArrayCharBuffer {
def fromTypedArrayByteBuffer(byteBuffer: TypedArrayByteBuffer): CharBuffer =
GenTypedArrayBuffer.generic_fromTypedArrayByteBuffer(byteBuffer)
- def wrap(array: Uint16Array): CharBuffer =
+ def wrapUint16Array(array: Uint16Array): CharBuffer =
new TypedArrayCharBuffer(array, 0, array.length, false)
}
diff --git a/javalib/src/main/scala/java/nio/TypedArrayDoubleBuffer.scala b/javalib/src/main/scala/java/nio/TypedArrayDoubleBuffer.scala
index 5cb48beace..4211fb143b 100644
--- a/javalib/src/main/scala/java/nio/TypedArrayDoubleBuffer.scala
+++ b/javalib/src/main/scala/java/nio/TypedArrayDoubleBuffer.scala
@@ -128,6 +128,6 @@ private[nio] object TypedArrayDoubleBuffer {
def fromTypedArrayByteBuffer(byteBuffer: TypedArrayByteBuffer): DoubleBuffer =
GenTypedArrayBuffer.generic_fromTypedArrayByteBuffer(byteBuffer)
- def wrap(array: Float64Array): DoubleBuffer =
+ def wrapFloat64Array(array: Float64Array): DoubleBuffer =
new TypedArrayDoubleBuffer(array, 0, array.length, false)
}
diff --git a/javalib/src/main/scala/java/nio/TypedArrayFloatBuffer.scala b/javalib/src/main/scala/java/nio/TypedArrayFloatBuffer.scala
index d485e87054..cab3cbc756 100644
--- a/javalib/src/main/scala/java/nio/TypedArrayFloatBuffer.scala
+++ b/javalib/src/main/scala/java/nio/TypedArrayFloatBuffer.scala
@@ -128,6 +128,6 @@ private[nio] object TypedArrayFloatBuffer {
def fromTypedArrayByteBuffer(byteBuffer: TypedArrayByteBuffer): FloatBuffer =
GenTypedArrayBuffer.generic_fromTypedArrayByteBuffer(byteBuffer)
- def wrap(array: Float32Array): FloatBuffer =
+ def wrapFloat32Array(array: Float32Array): FloatBuffer =
new TypedArrayFloatBuffer(array, 0, array.length, false)
}
diff --git a/javalib/src/main/scala/java/nio/TypedArrayIntBuffer.scala b/javalib/src/main/scala/java/nio/TypedArrayIntBuffer.scala
index 2d73e5025e..8beab4ac58 100644
--- a/javalib/src/main/scala/java/nio/TypedArrayIntBuffer.scala
+++ b/javalib/src/main/scala/java/nio/TypedArrayIntBuffer.scala
@@ -128,6 +128,6 @@ private[nio] object TypedArrayIntBuffer {
def fromTypedArrayByteBuffer(byteBuffer: TypedArrayByteBuffer): IntBuffer =
GenTypedArrayBuffer.generic_fromTypedArrayByteBuffer(byteBuffer)
- def wrap(array: Int32Array): IntBuffer =
+ def wrapInt32Array(array: Int32Array): IntBuffer =
new TypedArrayIntBuffer(array, 0, array.length, false)
}
diff --git a/javalib/src/main/scala/java/nio/TypedArrayShortBuffer.scala b/javalib/src/main/scala/java/nio/TypedArrayShortBuffer.scala
index 0c77246b34..09a9ca38dc 100644
--- a/javalib/src/main/scala/java/nio/TypedArrayShortBuffer.scala
+++ b/javalib/src/main/scala/java/nio/TypedArrayShortBuffer.scala
@@ -128,6 +128,6 @@ private[nio] object TypedArrayShortBuffer {
def fromTypedArrayByteBuffer(byteBuffer: TypedArrayByteBuffer): ShortBuffer =
GenTypedArrayBuffer.generic_fromTypedArrayByteBuffer(byteBuffer)
- def wrap(array: Int16Array): ShortBuffer =
+ def wrapInt16Array(array: Int16Array): ShortBuffer =
new TypedArrayShortBuffer(array, 0, array.length, false)
}
diff --git a/javalib/src/main/scala/java/nio/charset/Charset.scala b/javalib/src/main/scala/java/nio/charset/Charset.scala
index 2edce1ae00..c053e242ba 100644
--- a/javalib/src/main/scala/java/nio/charset/Charset.scala
+++ b/javalib/src/main/scala/java/nio/charset/Charset.scala
@@ -15,6 +15,7 @@ package java.nio.charset
import java.nio.{ByteBuffer, CharBuffer}
import java.util.{Collections, HashSet, Arrays}
import java.util.ScalaOps._
+import java.util.JSUtils._
import scala.scalajs.js
@@ -78,20 +79,22 @@ object Charset {
def defaultCharset(): Charset =
UTF_8
- def forName(charsetName: String): Charset =
- CharsetMap.getOrElse(charsetName.toLowerCase,
- throw new UnsupportedCharsetException(charsetName))
+ def forName(charsetName: String): Charset = {
+ dictGetOrElse(CharsetMap, charsetName.toLowerCase()) {
+ throw new UnsupportedCharsetException(charsetName)
+ }
+ }
def isSupported(charsetName: String): Boolean =
- CharsetMap.contains(charsetName.toLowerCase)
+ dictContains(CharsetMap, charsetName.toLowerCase())
private lazy val CharsetMap = {
- val m = js.Dictionary.empty[Charset]
- for (c <- js.Array(US_ASCII, ISO_8859_1, UTF_8, UTF_16BE, UTF_16LE, UTF_16)) {
- m(c.name().toLowerCase) = c
+ val m = dictEmpty[Charset]()
+ forArrayElems(js.Array(US_ASCII, ISO_8859_1, UTF_8, UTF_16BE, UTF_16LE, UTF_16)) { c =>
+ dictSet(m, c.name().toLowerCase(), c)
val aliases = c._aliases
for (i <- 0 until aliases.length)
- m(aliases(i).toLowerCase) = c
+ dictSet(m, aliases(i).toLowerCase(), c)
}
m
}
diff --git a/javalib/src/main/scala/java/nio/charset/CoderResult.scala b/javalib/src/main/scala/java/nio/charset/CoderResult.scala
index f7f73967f3..257dd0904b 100644
--- a/javalib/src/main/scala/java/nio/charset/CoderResult.scala
+++ b/javalib/src/main/scala/java/nio/charset/CoderResult.scala
@@ -15,6 +15,7 @@ package java.nio.charset
import scala.annotation.switch
import java.nio._
+import java.util.JSUtils._
import scala.scalajs.js
@@ -77,7 +78,7 @@ object CoderResult {
}
private def malformedForLengthImpl(length: Int): CoderResult = {
- uniqueMalformed(length).fold {
+ undefOrFold(uniqueMalformed(length)) {
val result = new CoderResult(Malformed, length)
uniqueMalformed(length) = result
result
@@ -95,7 +96,7 @@ object CoderResult {
}
private def unmappableForLengthImpl(length: Int): CoderResult = {
- uniqueUnmappable(length).fold {
+ undefOrFold(uniqueUnmappable(length)) {
val result = new CoderResult(Unmappable, length)
uniqueUnmappable(length) = result
result
diff --git a/javalib/src/main/scala/java/util/AbstractMap.scala b/javalib/src/main/scala/java/util/AbstractMap.scala
index d2ea01b065..df75f5ba67 100644
--- a/javalib/src/main/scala/java/util/AbstractMap.scala
+++ b/javalib/src/main/scala/java/util/AbstractMap.scala
@@ -94,7 +94,7 @@ abstract class AbstractMap[K, V] protected () extends java.util.Map[K, V] {
entrySet().scalaOps.exists(entry => Objects.equals(key, entry.getKey()))
def get(key: Any): V = {
- entrySet().scalaOps.find(entry => Objects.equals(key, entry.getKey())).fold[V] {
+ entrySet().scalaOps.findFold(entry => Objects.equals(key, entry.getKey())) {
null.asInstanceOf[V]
} { entry =>
entry.getValue()
diff --git a/javalib/src/main/scala/java/util/ArrayDeque.scala b/javalib/src/main/scala/java/util/ArrayDeque.scala
index 9abd87a7b9..46ef2388a8 100644
--- a/javalib/src/main/scala/java/util/ArrayDeque.scala
+++ b/javalib/src/main/scala/java/util/ArrayDeque.scala
@@ -13,6 +13,7 @@
package java.util
import java.lang.Cloneable
+import java.util.JSUtils._
import scala.scalajs.js
@@ -37,6 +38,9 @@ class ArrayDeque[E] private (private val inner: js.Array[E])
addAll(c)
}
+ @inline
+ override def isEmpty(): Boolean = inner.length == 0
+
def addFirst(e: E): Unit =
offerFirst(e)
@@ -64,21 +68,21 @@ class ArrayDeque[E] private (private val inner: js.Array[E])
}
def removeFirst(): E = {
- if (inner.isEmpty)
+ if (isEmpty())
throw new NoSuchElementException()
else
pollFirst()
}
def removeLast(): E = {
- if (inner.isEmpty)
+ if (isEmpty())
throw new NoSuchElementException()
else
pollLast()
}
def pollFirst(): E = {
- if (inner.isEmpty) null.asInstanceOf[E]
+ if (isEmpty()) null.asInstanceOf[E]
else {
val res = inner.shift()
status += 1
@@ -87,52 +91,65 @@ class ArrayDeque[E] private (private val inner: js.Array[E])
}
def pollLast(): E = {
- if (inner.isEmpty) null.asInstanceOf[E]
+ if (isEmpty()) null.asInstanceOf[E]
else inner.pop()
}
def getFirst(): E = {
- if (inner.isEmpty)
+ if (isEmpty())
throw new NoSuchElementException()
else
peekFirst()
}
def getLast(): E = {
- if (inner.isEmpty)
+ if (isEmpty())
throw new NoSuchElementException()
else
peekLast()
}
def peekFirst(): E = {
- if (inner.isEmpty) null.asInstanceOf[E]
- else inner.head
+ if (isEmpty()) null.asInstanceOf[E]
+ else inner(0)
}
def peekLast(): E = {
- if (inner.isEmpty) null.asInstanceOf[E]
- else inner.last
+ if (isEmpty()) null.asInstanceOf[E]
+ else inner(inner.length - 1)
}
def removeFirstOccurrence(o: Any): Boolean = {
- val index = inner.indexWhere(Objects.equals(_, o))
- if (index >= 0) {
- inner.remove(index)
- status += 1
- true
- } else
- false
+ // scalastyle:off return
+ val inner = this.inner // local copy
+ val len = inner.length
+ var i = 0
+ while (i != len) {
+ if (Objects.equals(inner(i), o)) {
+ arrayRemove(inner, i)
+ status += 1
+ return true
+ }
+ i += 1
+ }
+ false
+ // scalastyle:on return
}
def removeLastOccurrence(o: Any): Boolean = {
- val index = inner.lastIndexWhere(Objects.equals(_, o))
- if (index >= 0) {
- inner.remove(index)
- status += 1
- true
- } else
- false
+ // scalastyle:off return
+ val inner = this.inner // local copy
+ var i = inner.length - 1
+ while (i >= 0) {
+ if (Objects.equals(inner(i), o)) {
+ arrayRemove(inner, i)
+ status += 1
+ return true
+ }
+ i -= 1
+ }
+ false
+ // scalastyle:on return
}
override def add(e: E): Boolean = {
@@ -154,7 +171,7 @@ class ArrayDeque[E] private (private val inner: js.Array[E])
def pop(): E = removeFirst()
- def size(): Int = inner.size
+ def size(): Int = inner.length
private def failFastIterator(startIndex: Int, nex: (Int) => Int) = {
new Iterator[E] {
@@ -169,7 +186,7 @@ class ArrayDeque[E] private (private val inner: js.Array[E])
def hasNext(): Boolean = {
checkStatus()
val n = nex(index)
- (n >= 0) && (n < inner.size)
+ (n >= 0) && (n < inner.length)
}
def next(): E = {
@@ -180,10 +197,10 @@ class ArrayDeque[E] private (private val inner: js.Array[E])
override def remove(): Unit = {
checkStatus()
- if (index < 0 || index >= inner.size) {
+ if (index < 0 || index >= inner.length) {
throw new IllegalStateException()
} else {
- inner.remove(index)
+ arrayRemove(inner, index)
}
}
}
@@ -193,14 +210,16 @@ class ArrayDeque[E] private (private val inner: js.Array[E])
failFastIterator(-1, x => (x + 1))
def descendingIterator(): Iterator[E] =
- failFastIterator(inner.size, x => (x - 1))
+ failFastIterator(inner.length, x => (x - 1))
- override def contains(o: Any): Boolean = inner.exists(Objects.equals(_, o))
+ override def contains(o: Any): Boolean =
+ arrayExists(inner)(Objects.equals(_, o))
override def remove(o: Any): Boolean = removeFirstOccurrence(o)
override def clear(): Unit = {
- if (!inner.isEmpty) status += 1
- inner.clear()
+ if (!isEmpty())
+ status += 1
+ inner.length = 0
}
}
diff --git a/javalib/src/main/scala/java/util/ArrayList.scala b/javalib/src/main/scala/java/util/ArrayList.scala
index 3f573bc526..ad0e9b2d19 100644
--- a/javalib/src/main/scala/java/util/ArrayList.scala
+++ b/javalib/src/main/scala/java/util/ArrayList.scala
@@ -13,6 +13,7 @@
package java.util
import java.lang.Cloneable
+import java.util.JSUtils._
import scala.scalajs._
@@ -60,22 +61,22 @@ class ArrayList[E] private (private[ArrayList] val inner: js.Array[E])
}
override def add(e: E): Boolean = {
- inner += e
+ inner.push(e)
true
}
override def add(index: Int, element: E): Unit = {
checkIndexOnBounds(index)
- inner.insert(index, element)
+ inner.splice(index, 0, element)
}
override def remove(index: Int): E = {
checkIndexInBounds(index)
- inner.remove(index)
+ arrayRemoveAndGet(inner, index)
}
override def clear(): Unit =
- inner.clear()
+ inner.length = 0
override def addAll(index: Int, c: Collection[_ <: E]): Boolean = {
c match {
diff --git a/javalib/src/main/scala/java/util/BitSet.scala b/javalib/src/main/scala/java/util/BitSet.scala
index 5e2c4bd61f..171ed1a629 100644
--- a/javalib/src/main/scala/java/util/BitSet.scala
+++ b/javalib/src/main/scala/java/util/BitSet.scala
@@ -637,16 +637,20 @@ class BitSet private (private var bits: Array[Int]) extends Serializable with Cl
var result: String = "{"
var comma: Boolean = false
+ // Work around Scala 2.11 limitation with the IR cleaner ; should be double-for over i and j
for {
i <- 0 until getActualArrayLength()
- j <- 0 until ElementSize
} {
- if ((bits(i) & (1 << j)) != 0) {
- if (comma)
- result += ", "
- else
- comma = true
- result += (i << AddressBitsPerWord) + j
+ var j = 0
+ while (j < ElementSize) {
+ if ((bits(i) & (1 << j)) != 0) {
+ if (comma)
+ result += ", "
+ else
+ comma = true
+ result += (i << AddressBitsPerWord) + j
+ }
+ j += 1
}
}
diff --git a/javalib/src/main/scala/java/util/Formatter.scala b/javalib/src/main/scala/java/util/Formatter.scala
index 5535fda2cc..5807a2ddcf 100644
--- a/javalib/src/main/scala/java/util/Formatter.scala
+++ b/javalib/src/main/scala/java/util/Formatter.scala
@@ -18,6 +18,7 @@ import scala.scalajs.js
import java.lang.{Double => JDouble}
import java.io._
import java.math.{BigDecimal, BigInteger}
+import java.util.JSUtils._
final class Formatter private (private[this] var dest: Appendable,
formatterLocaleInfo: Formatter.LocaleInfo)
@@ -82,8 +83,12 @@ final class Formatter private (private[this] var dest: Appendable,
@noinline
private def sendToDestSlowPath(ss: js.Array[String]): Unit = {
+ // Workaround Scala 2.11 limitation: cannot nest anonymous functions for the IR cleaner
+ @inline def body(): Unit =
+ forArrayElems(ss)(dest.append(_))
+
trapIOExceptions {
- ss.foreach(dest.append(_))
+ body()
}
}
@@ -334,7 +339,7 @@ final class Formatter private (private[this] var dest: Appendable,
* Int range.
*/
private def parsePositiveInt(capture: js.UndefOr[String]): Int = {
- capture.fold {
+ undefOrFold(capture) {
-1
} { s =>
val x = js.Dynamic.global.parseInt(s, 10).asInstanceOf[Double]
diff --git a/javalib/src/main/scala/java/util/JSUtils.scala b/javalib/src/main/scala/java/util/JSUtils.scala
new file mode 100644
index 0000000000..0f7d3ab22f
--- /dev/null
+++ b/javalib/src/main/scala/java/util/JSUtils.scala
@@ -0,0 +1,182 @@
+/*
+ * Scala.js (https://www.scala-js.org/)
+ *
+ * Copyright EPFL.
+ *
+ * Licensed under Apache License 2.0
+ * (https://www.apache.org/licenses/LICENSE-2.0).
+ *
+ * See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ */
+
+package java.util
+
+import scala.language.implicitConversions
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSBracketAccess
+
+private[java] object JSUtils {
+ @inline
+ def undefined: js.UndefOr[Nothing] = ().asInstanceOf[js.UndefOr[Nothing]]
+
+ @inline
+ def isUndefined(x: Any): scala.Boolean =
+ x.asInstanceOf[AnyRef] eq ().asInstanceOf[AnyRef]
+
+ @inline
+ def undefOrIsDefined[A](x: js.UndefOr[A]): scala.Boolean =
+ x ne ().asInstanceOf[AnyRef]
+
+ @inline
+ def undefOrForceGet[A](x: js.UndefOr[A]): A =
+ x.asInstanceOf[A]
+
+ @inline
+ def undefOrGetOrElse[A](x: js.UndefOr[A])(default: => A): A =
+ if (undefOrIsDefined(x)) x.asInstanceOf[A]
+ else default
+
+ @inline
+ def undefOrGetOrNull[A >: Null](x: js.UndefOr[A]): A =
+ if (undefOrIsDefined(x)) x.asInstanceOf[A]
+ else null
+
+ @inline
+ def undefOrForeach[A](x: js.UndefOr[A])(f: A => Any): Unit = {
+ if (undefOrIsDefined(x))
+ f(undefOrForceGet(x))
+ }
+
+ @inline
+ def undefOrFold[A, B](x: js.UndefOr[A])(default: => B)(f: A => B): B =
+ if (undefOrIsDefined(x)) f(undefOrForceGet(x))
+ else default
+
+ private object Cache {
+ val safeHasOwnProperty =
+ js.Dynamic.global.Object.prototype.hasOwnProperty
+ .asInstanceOf[js.ThisFunction1[js.Dictionary[_], String, scala.Boolean]]
+ }
+
+ @inline
+ private def safeHasOwnProperty(dict: js.Dictionary[_], key: String): scala.Boolean =
+ Cache.safeHasOwnProperty(dict, key)
+
+ @js.native
+ private trait DictionaryRawApply[A] extends js.Object {
+ /** Reads a field of this object by its name.
+ *
+ * This must not be called if the dictionary does not contain the key.
+ */
+ @JSBracketAccess
+ def rawApply(key: String): A = js.native
+
+ /** Writes a field of this object. */
+ @JSBracketAccess
+ def rawUpdate(key: String, value: A): Unit = js.native
+ }
+
+ @inline
+ def dictEmpty[A](): js.Dictionary[A] =
+ new js.Object().asInstanceOf[js.Dictionary[A]]
+
+ @inline
+ def dictGetOrElse[A](dict: js.Dictionary[_ <: A], key: String)(
+ default: => A): A = {
+ if (dictContains(dict, key))
+ dictRawApply(dict, key)
+ else
+ default
+ }
+
+ def dictGetOrElseAndRemove[A](dict: js.Dictionary[_ <: A], key: String,
+ default: A): A = {
+ if (dictContains(dict, key)) {
+ val result = dictRawApply(dict, key)
+ js.special.delete(dict, key)
+ result
+ } else {
+ default
+ }
+ }
+
+ @inline
+ def dictRawApply[A](dict: js.Dictionary[A], key: String): A =
+ dict.asInstanceOf[DictionaryRawApply[A]].rawApply(key)
+
+ def dictContains[A](dict: js.Dictionary[A], key: String): scala.Boolean = {
+ /* We have to use a safe version of hasOwnProperty, because
+ * "hasOwnProperty" could be a key of this dictionary.
+ */
+ safeHasOwnProperty(dict, key)
+ }
+
+ @inline
+ def dictSet[A](dict: js.Dictionary[A], key: String, value: A): Unit =
+ dict.asInstanceOf[DictionaryRawApply[A]].rawUpdate(key, value)
+
+ @inline
+ def forArrayElems[A](array: js.Array[A])(f: A => Any): Unit = {
+ val len = array.length
+ var i = 0
+ while (i != len) {
+ f(array(i))
+ i += 1
+ }
+ }
+
+ @inline
+ def arrayRemove[A](array: js.Array[A], index: Int): Unit =
+ array.splice(index, 1)
+
+ @inline
+ def arrayRemoveAndGet[A](array: js.Array[A], index: Int): A =
+ array.splice(index, 1)(0)
+
+ @inline
+ def arrayExists[A](array: js.Array[A])(f: A => Boolean): Boolean = {
+ // scalastyle:off return
+ val len = array.length
+ var i = 0
+ while (i != len) {
+ if (f(array(i)))
+ return true
+ i += 1
+ }
+ false
+ // scalastyle:on return
+ }
+
+ @js.native
+ private trait RawMap[K, V] extends js.Object {
+ def has(key: K): Boolean = js.native
+ def keys(): js.Iterator[K] = js.native
+ def set(key: K, value: V): js.Map[K, V] = js.native
+ def get(key: K): V = js.native
+ }
+
+ @inline def mapHas[K, V](m: js.Map[K, V], key: K): Boolean =
+ m.asInstanceOf[RawMap[K, V]].has(key)
+
+ @inline def mapGet[K, V](m: js.Map[K, V], key: K): V =
+ m.asInstanceOf[RawMap[K, V]].get(key)
+
+ @inline def mapSet[K, V](m: js.Map[K, V], key: K, value: V): Unit =
+ m.asInstanceOf[RawMap[K, V]].set(key, value)
+
+ @inline def mapGetOrElse[K, V](m: js.Map[K, V], key: K)(default: => V): V =
+ if (mapHas(m, key)) mapGet(m, key)
+ else default
+
+ @inline def mapGetOrElseUpdate[K, V](m: js.Map[K, V], key: K)(default: => V): V = {
+ if (mapHas(m, key)) {
+ mapGet(m, key)
+ } else {
+ val value = default
+ mapSet(m, key, value)
+ value
+ }
+ }
+}
diff --git a/javalib/src/main/scala/java/util/ScalaOps.scala b/javalib/src/main/scala/java/util/ScalaOps.scala
index 4362f77fa8..a3c64920b3 100644
--- a/javalib/src/main/scala/java/util/ScalaOps.scala
+++ b/javalib/src/main/scala/java/util/ScalaOps.scala
@@ -57,8 +57,8 @@ private[java] object ScalaOps {
@inline def indexWhere(f: A => Boolean): Int =
__self.iterator().scalaOps.indexWhere(f)
- @inline def find(f: A => Boolean): Option[A] =
- __self.iterator().scalaOps.find(f)
+ @inline def findFold[B](f: A => Boolean)(default: => B)(g: A => B): B =
+ __self.iterator().scalaOps.findFold(f)(default)(g)
@inline def foldLeft[B](z: B)(f: (B, A) => B): B =
__self.iterator().scalaOps.foldLeft(z)(f)
@@ -112,14 +112,14 @@ private[java] object ScalaOps {
// scalastyle:on return
}
- @inline def find(f: A => Boolean): Option[A] = {
+ @inline def findFold[B](f: A => Boolean)(default: => B)(g: A => B): B = {
// scalastyle:off return
while (__self.hasNext()) {
val x = __self.next()
if (f(x))
- return Some(x)
+ return g(x)
}
- None
+ default
// scalastyle:on return
}
diff --git a/javalib/src/main/scala/java/util/Timer.scala b/javalib/src/main/scala/java/util/Timer.scala
index ac75a1e61d..4be9d67d43 100644
--- a/javalib/src/main/scala/java/util/Timer.scala
+++ b/javalib/src/main/scala/java/util/Timer.scala
@@ -70,16 +70,18 @@ class Timer() {
private def schedulePeriodically(
task: TimerTask, delay: Long, period: Long): Unit = {
acquire(task)
- task.timeout(delay) {
- def loop(): Unit = {
- val startTime = System.nanoTime()
- task.doRun()
- val endTime = System.nanoTime()
- val duration = (endTime - startTime) / 1000000
- task.timeout(period - duration) {
- loop()
- }
+
+ def loop(): Unit = {
+ val startTime = System.nanoTime()
+ task.doRun()
+ val endTime = System.nanoTime()
+ val duration = (endTime - startTime) / 1000000
+ task.timeout(period - duration) {
+ loop()
}
+ }
+
+ task.timeout(delay) {
loop()
}
}
@@ -100,21 +102,23 @@ class Timer() {
private def scheduleFixed(
task: TimerTask, delay: Long, period: Long): Unit = {
acquire(task)
- task.timeout(delay) {
- def loop(scheduledTime: Long): Unit = {
- task.doRun()
- val nextScheduledTime = scheduledTime + period
- val nowTime = System.nanoTime / 1000000L
- if (nowTime >= nextScheduledTime) {
- // Re-run immediately.
+
+ def loop(scheduledTime: Long): Unit = {
+ task.doRun()
+ val nextScheduledTime = scheduledTime + period
+ val nowTime = System.nanoTime / 1000000L
+ if (nowTime >= nextScheduledTime) {
+ // Re-run immediately.
+ loop(nextScheduledTime)
+ } else {
+ // Re-run after a timeout.
+ task.timeout(nextScheduledTime - nowTime) {
loop(nextScheduledTime)
- } else {
- // Re-run after a timeout.
- task.timeout(nextScheduledTime - nowTime) {
- loop(nextScheduledTime)
- }
}
}
+ }
+
+ task.timeout(delay) {
loop(System.nanoTime / 1000000L + period)
}
}
diff --git a/javalib/src/main/scala/java/util/TimerTask.scala b/javalib/src/main/scala/java/util/TimerTask.scala
index 959d206f53..4299157e89 100644
--- a/javalib/src/main/scala/java/util/TimerTask.scala
+++ b/javalib/src/main/scala/java/util/TimerTask.scala
@@ -12,7 +12,8 @@
package java.util
-import scala.scalajs.js.timers._
+import scala.scalajs.js
+import scala.scalajs.js.timers.RawTimers._
import scala.scalajs.js.timers.SetTimeoutHandle
abstract class TimerTask {
@@ -42,7 +43,7 @@ abstract class TimerTask {
private[util] def timeout(delay: Long)(body: => Unit): Unit = {
if (!canceled) {
- handle = setTimeout(delay.toDouble)(body)
+ handle = setTimeout(() => body, delay.toDouble)
}
}
diff --git a/javalib/src/main/scala/java/util/concurrent/CopyOnWriteArrayList.scala b/javalib/src/main/scala/java/util/concurrent/CopyOnWriteArrayList.scala
index f4fd855cec..16d35937fd 100644
--- a/javalib/src/main/scala/java/util/concurrent/CopyOnWriteArrayList.scala
+++ b/javalib/src/main/scala/java/util/concurrent/CopyOnWriteArrayList.scala
@@ -16,6 +16,7 @@ import java.lang.Cloneable
import java.lang.{reflect => jlr}
import java.util._
import java.util.function.{Predicate, UnaryOperator}
+import java.util.JSUtils._
import scala.annotation.tailrec
@@ -47,7 +48,7 @@ class CopyOnWriteArrayList[E <: AnyRef] private (private var inner: js.Array[E])
}
def size(): Int =
- inner.size
+ inner.length
def isEmpty(): Boolean =
size() == 0
@@ -291,7 +292,7 @@ class CopyOnWriteArrayList[E <: AnyRef] private (private var inner: js.Array[E])
}
protected def innerRemove(index: Int): E =
- inner.splice(index, 1)(0)
+ arrayRemoveAndGet(inner, index)
protected def innerRemoveMany(index: Int, count: Int): Unit =
inner.splice(index, count)
diff --git a/javalib/src/main/scala/java/util/internal/MurmurHash3.scala b/javalib/src/main/scala/java/util/internal/MurmurHash3.scala
new file mode 100644
index 0000000000..bcf438f131
--- /dev/null
+++ b/javalib/src/main/scala/java/util/internal/MurmurHash3.scala
@@ -0,0 +1,61 @@
+/*
+ * Scala.js (https://www.scala-js.org/)
+ *
+ * Copyright EPFL.
+ *
+ * Licensed under Apache License 2.0
+ * (https://www.apache.org/licenses/LICENSE-2.0).
+ *
+ * See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ */
+
+package java.util.internal
+
+import java.lang.Integer.{rotateLeft => rotl}
+
+/** Primitives to implement MurmurHash3 hashes in data structures.
+ *
+ * This is copy of parts of `scala.util.hashing.MurmurHash3`.
+ */
+private[java] object MurmurHash3 {
+ /** Mix in a block of data into an intermediate hash value. */
+ final def mix(hash: Int, data: Int): Int = {
+ var h = mixLast(hash, data)
+ h = rotl(h, 13)
+ h * 5 + 0xe6546b64
+ }
+
+ /** May optionally be used as the last mixing step.
+ *
+ * Is a little bit faster than mix, as it does no further mixing of the
+ * resulting hash. For the last element this is not necessary as the hash is
+ * thoroughly mixed during finalization anyway.
+ */
+ final def mixLast(hash: Int, data: Int): Int = {
+ var k = data
+
+ k *= 0xcc9e2d51
+ k = rotl(k, 15)
+ k *= 0x1b873593
+
+ hash ^ k
+ }
+
+ /** Finalize a hash to incorporate the length and make sure all bits avalanche. */
+ @noinline final def finalizeHash(hash: Int, length: Int): Int =
+ avalanche(hash ^ length)
+
+ /** Force all bits of the hash to avalanche. Used for finalizing the hash. */
+ @inline private final def avalanche(hash: Int): Int = {
+ var h = hash
+
+ h ^= h >>> 16
+ h *= 0x85ebca6b
+ h ^= h >>> 13
+ h *= 0xc2b2ae35
+ h ^= h >>> 16
+
+ h
+ }
+}
diff --git a/javalib/src/main/scala/java/util/internal/RefTypes.scala b/javalib/src/main/scala/java/util/internal/RefTypes.scala
new file mode 100644
index 0000000000..d02cf33d8d
--- /dev/null
+++ b/javalib/src/main/scala/java/util/internal/RefTypes.scala
@@ -0,0 +1,94 @@
+/*
+ * Scala.js (https://www.scala-js.org/)
+ *
+ * Copyright EPFL.
+ *
+ * Licensed under Apache License 2.0
+ * (https://www.apache.org/licenses/LICENSE-2.0).
+ *
+ * See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ */
+
+package java.util.internal
+
+@inline
+private[java] class BooleanRef(var elem: Boolean) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object BooleanRef {
+ def create(elem: Boolean): BooleanRef = new BooleanRef(elem)
+ def zero(): BooleanRef = new BooleanRef(false)
+}
+
+@inline
+private[java] class CharRef(var elem: Char) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object CharRef {
+ def create(elem: Char): CharRef = new CharRef(elem)
+ def zero(): CharRef = new CharRef(0.toChar)
+}
+
+@inline
+private[java] class ByteRef(var elem: Byte) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object ByteRef {
+ def create(elem: Byte): ByteRef = new ByteRef(elem)
+ def zero(): ByteRef = new ByteRef(0)
+}
+
+@inline
+private[java] class ShortRef(var elem: Short) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object ShortRef {
+ def create(elem: Short): ShortRef = new ShortRef(elem)
+ def zero(): ShortRef = new ShortRef(0)
+}
+
+@inline
+private[java] class IntRef(var elem: Int) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object IntRef {
+ def create(elem: Int): IntRef = new IntRef(elem)
+ def zero(): IntRef = new IntRef(0)
+}
+
+@inline
+private[java] class LongRef(var elem: Long) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object LongRef {
+ def create(elem: Long): LongRef = new LongRef(elem)
+ def zero(): LongRef = new LongRef(0)
+}
+
+@inline
+private[java] class FloatRef(var elem: Float) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object FloatRef {
+ def create(elem: Float): FloatRef = new FloatRef(elem)
+ def zero(): FloatRef = new FloatRef(0)
+}
+
+@inline
+private[java] class DoubleRef(var elem: Double) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object DoubleRef {
+ def create(elem: Double): DoubleRef = new DoubleRef(elem)
+ def zero(): DoubleRef = new DoubleRef(0)
+}
+
+@inline
+private[java] class ObjectRef[A](var elem: A) extends Serializable {
+ override def toString(): String = String.valueOf(elem)
+}
+private[java] object ObjectRef {
+ def create[A](elem: A): ObjectRef[A] = new ObjectRef(elem)
+ def zero(): ObjectRef[Object] = new ObjectRef(null)
+}
diff --git a/javalib/src/main/scala/java/util/internal/Tuples.scala b/javalib/src/main/scala/java/util/internal/Tuples.scala
new file mode 100644
index 0000000000..d476cd74a9
--- /dev/null
+++ b/javalib/src/main/scala/java/util/internal/Tuples.scala
@@ -0,0 +1,26 @@
+/*
+ * Scala.js (https://www.scala-js.org/)
+ *
+ * Copyright EPFL.
+ *
+ * Licensed under Apache License 2.0
+ * (https://www.apache.org/licenses/LICENSE-2.0).
+ *
+ * See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ */
+
+package java.util.internal
+
+@inline
+final class Tuple2[+T1, +T2](val _1: T1, val _2: T2)
+
+@inline
+final class Tuple3[+T1, +T2, +T3](val _1: T1, val _2: T2, val _3: T3)
+
+@inline
+final class Tuple4[+T1, +T2, +T3, +T4](val _1: T1, val _2: T2, val _3: T3, val _4: T4)
+
+@inline
+final class Tuple8[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8](
+ val _1: T1, val _2: T2, val _3: T3, val _4: T4, val _5: T5, val _6: T6, val _7: T7, val _8: T8)
diff --git a/javalib/src/main/scala/java/util/regex/IndicesBuilder.scala b/javalib/src/main/scala/java/util/regex/IndicesBuilder.scala
index 4b866920b0..257e399807 100644
--- a/javalib/src/main/scala/java/util/regex/IndicesBuilder.scala
+++ b/javalib/src/main/scala/java/util/regex/IndicesBuilder.scala
@@ -14,6 +14,8 @@ package java.util.regex
import scala.annotation.{tailrec, switch}
+import java.util.JSUtils._
+
import scala.scalajs.js
import Pattern.IndicesArray
@@ -79,7 +81,7 @@ private[regex] class IndicesBuilder private (pattern: String, flags: String,
}
val start = index // by definition
- val end = start + allMatchResult(0).get.length()
+ val end = start + undefOrForceGet(allMatchResult(0)).length()
/* Initialize the `indices` array with:
* - `[start, end]` at index 0, which represents the whole match, and
@@ -91,10 +93,10 @@ private[regex] class IndicesBuilder private (pattern: String, flags: String,
*/
val len = groupCount + 1
val indices = new IndicesArray(len)
- indices(0) = js.Tuple2(start, end)
+ indices(0) = js.Array(start, end).asInstanceOf[js.Tuple2[Int, Int]]
var i = 1
while (i != len) {
- indices(i) = js.undefined
+ indices(i) = undefined
i += 1
}
@@ -179,7 +181,7 @@ private[regex] object IndicesBuilder {
final def propagateFromEnd(matchResult: js.RegExp.ExecResult,
indices: IndicesArray, end: Int): Unit = {
- val start = matchResult(newGroup).fold(-1)(matched => end - matched.length)
+ val start = undefOrFold(matchResult(newGroup))(-1)(matched => end - matched.length)
propagate(matchResult, indices, start, end)
}
@@ -191,7 +193,7 @@ private[regex] object IndicesBuilder {
final def propagateFromStart(matchResult: js.RegExp.ExecResult,
indices: IndicesArray, start: Int): Int = {
- val end = matchResult(newGroup).fold(-1)(matched => start + matched.length)
+ val end = undefOrFold(matchResult(newGroup))(-1)(matched => start + matched.length)
propagate(matchResult, indices, start, end)
end
}
@@ -212,8 +214,8 @@ private[regex] object IndicesBuilder {
* always keep the default `-1` if this group node does not match
* anything.
*/
- if (matchResult(newGroup).isDefined)
- indices(number) = js.Tuple2(start, end)
+ if (undefOrIsDefined(matchResult(newGroup)))
+ indices(number) = js.Array(start, end).asInstanceOf[js.Tuple2[Int, Int]]
inner.propagate(matchResult, indices, start, end)
}
}
diff --git a/javalib/src/main/scala/java/util/regex/Matcher.scala b/javalib/src/main/scala/java/util/regex/Matcher.scala
index 4effe7de81..6385dbd96a 100644
--- a/javalib/src/main/scala/java/util/regex/Matcher.scala
+++ b/javalib/src/main/scala/java/util/regex/Matcher.scala
@@ -12,6 +12,8 @@
package java.util.regex
+import java.util.JSUtils._
+
import scala.annotation.switch
import scala.scalajs.js
@@ -182,13 +184,13 @@ final class Matcher private[regex] (
def start(): Int = ensureLastMatch.index + regionStart()
def end(): Int = start() + group().length
- def group(): String = ensureLastMatch(0).get
+ def group(): String = undefOrForceGet(ensureLastMatch(0))
private def indices: IndicesArray =
pattern().getIndices(ensureLastMatch, lastMatchIsForMatches)
private def startInternal(compiledGroup: Int): Int =
- indices(compiledGroup).fold(-1)(_._1 + regionStart())
+ undefOrFold(indices(compiledGroup))(-1)(_._1 + regionStart())
def start(group: Int): Int =
startInternal(pattern().numberedGroup(group))
@@ -197,7 +199,7 @@ final class Matcher private[regex] (
startInternal(pattern().namedGroup(name))
private def endInternal(compiledGroup: Int): Int =
- indices(compiledGroup).fold(-1)(_._2 + regionStart())
+ undefOrFold(indices(compiledGroup))(-1)(_._2 + regionStart())
def end(group: Int): Int =
endInternal(pattern().numberedGroup(group))
@@ -206,10 +208,10 @@ final class Matcher private[regex] (
endInternal(pattern().namedGroup(name))
def group(group: Int): String =
- ensureLastMatch(pattern().numberedGroup(group)).orNull
+ undefOrGetOrNull(ensureLastMatch(pattern().numberedGroup(group)))
def group(name: String): String =
- ensureLastMatch(pattern().namedGroup(name)).orNull
+ undefOrGetOrNull(ensureLastMatch(pattern().namedGroup(name)))
// Seal the state
@@ -266,7 +268,7 @@ object Matcher {
def start(): Int = ensureLastMatch.index + regionStart
def end(): Int = start() + group().length
- def group(): String = ensureLastMatch(0).get
+ def group(): String = undefOrForceGet(ensureLastMatch(0))
private def indices: IndicesArray =
pattern.getIndices(ensureLastMatch, lastMatchIsForMatches)
@@ -276,13 +278,13 @@ object Matcher {
*/
def start(group: Int): Int =
- indices(pattern.numberedGroup(group)).fold(-1)(_._1 + regionStart)
+ undefOrFold(indices(pattern.numberedGroup(group)))(-1)(_._1 + regionStart)
def end(group: Int): Int =
- indices(pattern.numberedGroup(group)).fold(-1)(_._2 + regionStart)
+ undefOrFold(indices(pattern.numberedGroup(group)))(-1)(_._2 + regionStart)
def group(group: Int): String =
- ensureLastMatch(pattern.numberedGroup(group)).orNull
+ undefOrGetOrNull(ensureLastMatch(pattern.numberedGroup(group)))
private def ensureLastMatch: js.RegExp.ExecResult = {
if (lastMatch == null)
diff --git a/javalib/src/main/scala/java/util/regex/Pattern.scala b/javalib/src/main/scala/java/util/regex/Pattern.scala
index fc747f0eba..a26bff33d0 100644
--- a/javalib/src/main/scala/java/util/regex/Pattern.scala
+++ b/javalib/src/main/scala/java/util/regex/Pattern.scala
@@ -14,6 +14,7 @@ package java.util.regex
import scala.annotation.tailrec
+import java.util.JSUtils._
import java.util.ScalaOps._
import scala.scalajs.js
@@ -132,14 +133,14 @@ final class Pattern private[regex] (
}
private[regex] def namedGroup(name: String): Int = {
- groupNumberMap(namedGroups.getOrElse(name, {
+ groupNumberMap(dictGetOrElse(namedGroups, name) {
throw new IllegalArgumentException(s"No group with name <$name>")
- }))
+ })
}
private[regex] def getIndices(lastMatch: js.RegExp.ExecResult, forMatches: Boolean): IndicesArray = {
val lastMatchDyn = lastMatch.asInstanceOf[js.Dynamic]
- if (js.isUndefined(lastMatchDyn.indices)) {
+ if (isUndefined(lastMatchDyn.indices)) {
if (supportsIndices) {
if (!enabledNativeIndices) {
jsRegExpForFind = new js.RegExp(jsPattern, jsFlagsForFind + "d")
diff --git a/javalib/src/main/scala/java/util/regex/PatternCompiler.scala b/javalib/src/main/scala/java/util/regex/PatternCompiler.scala
index bdc9593238..5011bab65a 100644
--- a/javalib/src/main/scala/java/util/regex/PatternCompiler.scala
+++ b/javalib/src/main/scala/java/util/regex/PatternCompiler.scala
@@ -25,10 +25,12 @@ import java.lang.Character.{
MAX_LOW_SURROGATE
}
+import java.util.JSUtils._
import java.util.ScalaOps._
import scala.scalajs.js
-import scala.scalajs.LinkingInfo.{ESVersion, esVersion}
+import scala.scalajs.runtime.linkingInfo
+import scala.scalajs.LinkingInfo.ESVersion
/** Compiler from Java regular expressions to JavaScript regular expressions.
*
@@ -80,15 +82,15 @@ private[regex] object PatternCompiler {
/** Cache for `Support.supportsUnicode`. */
private val _supportsUnicode =
- (esVersion >= ESVersion.ES2015) || featureTest("u")
+ (linkingInfo.esVersion >= ESVersion.ES2015) || featureTest("u")
/** Cache for `Support.supportsSticky`. */
private val _supportsSticky =
- (esVersion >= ESVersion.ES2015) || featureTest("y")
+ (linkingInfo.esVersion >= ESVersion.ES2015) || featureTest("y")
/** Cache for `Support.supportsDotAll`. */
private val _supportsDotAll =
- (esVersion >= ESVersion.ES2018) || featureTest("us")
+ (linkingInfo.esVersion >= ESVersion.ES2018) || featureTest("us")
/** Cache for `Support.supportsIndices`. */
private val _supportsIndices =
@@ -104,17 +106,17 @@ private[regex] object PatternCompiler {
/** Tests whether the underlying JS RegExp supports the 'u' flag. */
@inline
def supportsUnicode: Boolean =
- (esVersion >= ESVersion.ES2015) || _supportsUnicode
+ (linkingInfo.esVersion >= ESVersion.ES2015) || _supportsUnicode
/** Tests whether the underlying JS RegExp supports the 'y' flag. */
@inline
def supportsSticky: Boolean =
- (esVersion >= ESVersion.ES2015) || _supportsSticky
+ (linkingInfo.esVersion >= ESVersion.ES2015) || _supportsSticky
/** Tests whether the underlying JS RegExp supports the 's' flag. */
@inline
def supportsDotAll: Boolean =
- (esVersion >= ESVersion.ES2018) || _supportsDotAll
+ (linkingInfo.esVersion >= ESVersion.ES2018) || _supportsDotAll
/** Tests whether the underlying JS RegExp supports the 'd' flag. */
@inline
@@ -128,7 +130,7 @@ private[regex] object PatternCompiler {
*/
@inline
def enableUnicodeCaseInsensitive: Boolean =
- esVersion >= ESVersion.ES2015
+ linkingInfo.esVersion >= ESVersion.ES2015
/** Tests whether features requiring \p{} and/or look-behind assertions are enabled.
*
@@ -137,7 +139,7 @@ private[regex] object PatternCompiler {
*/
@inline
def enableUnicodeCharacterClassesAndLookBehinds: Boolean =
- esVersion >= ESVersion.ES2018
+ linkingInfo.esVersion >= ESVersion.ES2018
}
import Support._
@@ -212,7 +214,7 @@ private[regex] object PatternCompiler {
import InlinedHelpers._
private def codePointToString(codePoint: Int): String = {
- if (esVersion >= ESVersion.ES2015) {
+ if (linkingInfo.esVersion >= ESVersion.ES2015) {
js.Dynamic.global.String.fromCodePoint(codePoint).asInstanceOf[String]
} else {
if (isBmpCodePoint(codePoint)) {
@@ -286,24 +288,24 @@ private[regex] object PatternCompiler {
* This is a `js.Dictionary` because it can be used even when compiling to
* ECMAScript 5.1.
*/
- private val asciiPOSIXCharacterClasses = {
+ private val asciiPOSIXCharacterClasses: js.Dictionary[CompiledCharClass] = {
import CompiledCharClass._
- js.Dictionary(
- ("Lower", posClass("a-z")),
- ("Upper", posClass("A-Z")),
- ("ASCII", posClass("\u0000-\u007f")),
- ("Alpha", posClass("A-Za-z")), // [\p{Lower}\p{Upper}]
- ("Digit", posClass("0-9")),
- ("Alnum", posClass("0-9A-Za-z")), // [\p{Alpha}\p{Digit}]
- ("Punct", posClass("!-/:-@[-`{-~")), // One of !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
- ("Graph", posClass("!-~")), // [\p{Alnum}\p{Punct}]
- ("Print", posClass(" -~")), // [\p{Graph}\x20]
- ("Blank", posClass("\t ")),
- ("Cntrl", posClass("\u0000-\u001f\u007f")),
- ("XDigit", posClass("0-9A-Fa-f")),
- ("Space", posClass("\t-\r ")) // [ \t\n\x0B\f\r]
- )
+ val r = dictEmpty[CompiledCharClass]()
+ dictSet(r, "Lower", posClass("a-z"))
+ dictSet(r, "Upper", posClass("A-Z"))
+ dictSet(r, "ASCII", posClass("\u0000-\u007f"))
+ dictSet(r, "Alpha", posClass("A-Za-z")) // [\p{Lower}\p{Upper}]
+ dictSet(r, "Digit", posClass("0-9"))
+ dictSet(r, "Alnum", posClass("0-9A-Za-z")) // [\p{Alpha}\p{Digit}]
+ dictSet(r, "Punct", posClass("!-/:-@[-`{-~")) // One of !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
+ dictSet(r, "Graph", posClass("!-~")) // [\p{Alnum}\p{Punct}]
+ dictSet(r, "Print", posClass(" -~")) // [\p{Graph}\x20]
+ dictSet(r, "Blank", posClass("\t "))
+ dictSet(r, "Cntrl", posClass("\u0000-\u001f\u007f"))
+ dictSet(r, "XDigit", posClass("0-9A-Fa-f"))
+ dictSet(r, "Space", posClass("\t-\r ")) // [ \t\n\x0B\f\r]
+ r
}
/** Mapping of predefined character classes to the corresponding character
@@ -333,70 +335,70 @@ private[regex] object PatternCompiler {
"Cc", "Cf", "Cs", "Co", "Cn", "C"
)
- for (gc <- generalCategories) {
+ forArrayElems(generalCategories) { gc =>
val compiled = posP(gc)
- result(gc) = compiled
- result("Is" + gc) = compiled
- result("general_category=" + gc) = compiled
- result("gc=" + gc) = compiled
+ mapSet(result, gc, compiled)
+ mapSet(result, "Is" + gc, compiled)
+ mapSet(result, "general_category=" + gc, compiled)
+ mapSet(result, "gc=" + gc, compiled)
}
// Binary properties
- result("IsAlphabetic") = posP("Alphabetic")
- result("IsIdeographic") = posP("Ideographic")
- result("IsLetter") = posP("Letter")
- result("IsLowercase") = posP("Lowercase")
- result("IsUppercase") = posP("Uppercase")
- result("IsTitlecase") = posP("Lt")
- result("IsPunctuation") = posP("Punctuation")
- result("IsControl") = posP("Control")
- result("IsWhite_Space") = posP("White_Space")
- result("IsDigit") = posP("Nd")
- result("IsHex_Digit") = posP("Hex_Digit")
- result("IsJoin_Control") = posP("Join_Control")
- result("IsNoncharacter_Code_Point") = posP("Noncharacter_Code_Point")
- result("IsAssigned") = posP("Assigned")
+ mapSet(result, "IsAlphabetic", posP("Alphabetic"))
+ mapSet(result, "IsIdeographic", posP("Ideographic"))
+ mapSet(result, "IsLetter", posP("Letter"))
+ mapSet(result, "IsLowercase", posP("Lowercase"))
+ mapSet(result, "IsUppercase", posP("Uppercase"))
+ mapSet(result, "IsTitlecase", posP("Lt"))
+ mapSet(result, "IsPunctuation", posP("Punctuation"))
+ mapSet(result, "IsControl", posP("Control"))
+ mapSet(result, "IsWhite_Space", posP("White_Space"))
+ mapSet(result, "IsDigit", posP("Nd"))
+ mapSet(result, "IsHex_Digit", posP("Hex_Digit"))
+ mapSet(result, "IsJoin_Control", posP("Join_Control"))
+ mapSet(result, "IsNoncharacter_Code_Point", posP("Noncharacter_Code_Point"))
+ mapSet(result, "IsAssigned", posP("Assigned"))
// java.lang.Character classes
- result("javaAlphabetic") = posP("Alphabetic")
- result("javaDefined") = negP("Cn")
- result("javaDigit") = posP("Nd")
- result("javaIdentifierIgnorable") = posClass("\u0000-\u0008\u000E-\u001B\u007F-\u009F\\p{Cf}")
- result("javaIdeographic") = posP("Ideographic")
- result("javaISOControl") = posClass("\u0000-\u001F\u007F-\u009F")
- result("javaJavaIdentifierPart") =
- posClass("\\p{L}\\p{Sc}\\p{Pc}\\p{Nd}\\p{Nl}\\p{Mn}\\p{Mc}\u0000-\u0008\u000E-\u001B\u007F-\u009F\\p{Cf}")
- result("javaJavaIdentifierStart") = posClass("\\p{L}\\p{Sc}\\p{Pc}\\p{Nl}")
- result("javaLetterOrDigit") = posClass("\\p{L}\\p{Nd}")
- result("javaLowerCase") = posP("Lowercase")
- result("javaMirrored") = posP("Bidi_Mirrored")
- result("javaSpaceChar") = posP("Z")
- result("javaTitleCase") = posP("Lt")
- result("javaUnicodeIdentifierPart") =
- posClass("\\p{ID_Continue}\u2E2F\u0000-\u0008\u000E-\u001B\u007F-\u009F\\p{Cf}")
- result("javaUnicodeIdentifierStart") = posClass("\\p{ID_Start}\u2E2F")
- result("javaUpperCase") = posP("Uppercase")
+ mapSet(result, "javaAlphabetic", posP("Alphabetic"))
+ mapSet(result, "javaDefined", negP("Cn"))
+ mapSet(result, "javaDigit", posP("Nd"))
+ mapSet(result, "javaIdentifierIgnorable", posClass("\u0000-\u0008\u000E-\u001B\u007F-\u009F\\p{Cf}"))
+ mapSet(result, "javaIdeographic", posP("Ideographic"))
+ mapSet(result, "javaISOControl", posClass("\u0000-\u001F\u007F-\u009F"))
+ mapSet(result, "javaJavaIdentifierPart",
+ posClass("\\p{L}\\p{Sc}\\p{Pc}\\p{Nd}\\p{Nl}\\p{Mn}\\p{Mc}\u0000-\u0008\u000E-\u001B\u007F-\u009F\\p{Cf}"))
+ mapSet(result, "javaJavaIdentifierStart", posClass("\\p{L}\\p{Sc}\\p{Pc}\\p{Nl}"))
+ mapSet(result, "javaLetterOrDigit", posClass("\\p{L}\\p{Nd}"))
+ mapSet(result, "javaLowerCase", posP("Lowercase"))
+ mapSet(result, "javaMirrored", posP("Bidi_Mirrored"))
+ mapSet(result, "javaSpaceChar", posP("Z"))
+ mapSet(result, "javaTitleCase", posP("Lt"))
+ mapSet(result, "javaUnicodeIdentifierPart",
+ posClass("\\p{ID_Continue}\u2E2F\u0000-\u0008\u000E-\u001B\u007F-\u009F\\p{Cf}"))
+ mapSet(result, "javaUnicodeIdentifierStart", posClass("\\p{ID_Start}\u2E2F"))
+ mapSet(result, "javaUpperCase", posP("Uppercase"))
// [\t-\r\u001C-\u001F\\p{Z}&&[^\u00A0\u2007\u202F]]
- result("javaWhitespace") =
- posClass("\t-\r\u001C-\u001F \u1680\u2000-\u2006\u2008-\u200A\u205F\u3000\\p{Zl}\\p{Zp}")
+ mapSet(result, "javaWhitespace",
+ posClass("\t-\r\u001C-\u001F \u1680\u2000-\u2006\u2008-\u200A\u205F\u3000\\p{Zl}\\p{Zp}"))
/* POSIX character classes with Unicode compatibility
* (resolved from the original definitions, which are in comments)
*/
- result("Lower") = posP("Lower") // \p{IsLowercase}
- result("Upper") = posP("Upper") // \p{IsUppercase}
- result("ASCII") = posClass("\u0000-\u007f")
- result("Alpha") = posP("Alpha") // \p{IsAlphabetic}
- result("Digit") = posP("Nd") // \p{IsDigit}
- result("Alnum") = posClass("\\p{Alpha}\\p{Nd}") // [\p{IsAlphabetic}\p{IsDigit}]
- result("Punct") = posP("P") // \p{IsPunctuation}
+ mapSet(result, "Lower", posP("Lower")) // \p{IsLowercase}
+ mapSet(result, "Upper", posP("Upper")) // \p{IsUppercase}
+ mapSet(result, "ASCII", posClass("\u0000-\u007f"))
+ mapSet(result, "Alpha", posP("Alpha")) // \p{IsAlphabetic}
+ mapSet(result, "Digit", posP("Nd")) // \p{IsDigit}
+ mapSet(result, "Alnum", posClass("\\p{Alpha}\\p{Nd}")) // [\p{IsAlphabetic}\p{IsDigit}]
+ mapSet(result, "Punct", posP("P")) // \p{IsPunctuation}
// [^\p{IsWhite_Space}\p{gc=Cc}\p{gc=Cs}\p{gc=Cn}]
- result("Graph") = negClass("\\p{White_Space}\\p{Cc}\\p{Cs}\\p{Cn}")
+ mapSet(result, "Graph", negClass("\\p{White_Space}\\p{Cc}\\p{Cs}\\p{Cn}"))
/* [\p{Graph}\p{Blank}&&[^\p{Cntrl}]]
* === (by definition of Cntrl)
@@ -416,7 +418,7 @@ private[regex] object PatternCompiler {
* === (because \x09-\x0d and \x85 are all in the Cc category)
* [^\p{Zl}\p{Zp}\p{Cc}\p{Cs}\p{Cn}]
*/
- result("Print") = negClass("\\p{Zl}\\p{Zp}\\p{Cc}\\p{Cs}\\p{Cn}")
+ mapSet(result, "Print", negClass("\\p{Zl}\\p{Zp}\\p{Cc}\\p{Cs}\\p{Cn}"))
/* [\p{IsWhite_Space}&&[^\p{gc=Zl}\p{gc=Zp}\x0a\x0b\x0c\x0d\x85]]
* === (see the excerpt from PropList.txt below)
@@ -424,11 +426,11 @@ private[regex] object PatternCompiler {
* === (by simplification)
* [\x09\p{gc=Zs}]
*/
- result("Blank") = posClass("\t\\p{Zs}")
+ mapSet(result, "Blank", posClass("\t\\p{Zs}"))
- result("Cntrl") = posP("Cc") // \p{gc=Cc}
- result("XDigit") = posClass("\\p{Nd}\\p{Hex}") // [\p{gc=Nd}\p{IsHex_Digit}]
- result("Space") = posP("White_Space") // \p{IsWhite_Space}
+ mapSet(result, "Cntrl", posP("Cc")) // \p{gc=Cc}
+ mapSet(result, "XDigit", posClass("\\p{Nd}\\p{Hex}")) // [\p{gc=Nd}\p{IsHex_Digit}]
+ mapSet(result, "Space", posP("White_Space")) // \p{IsWhite_Space}
result
}
@@ -473,7 +475,7 @@ private[regex] object PatternCompiler {
/* SignWriting is an exception. It has an uppercase 'W' even though it is
* not after '_'. We add the exception to the map immediately.
*/
- result("signwriting") = "SignWriting"
+ mapSet(result, "signwriting", "SignWriting")
result
}
@@ -741,7 +743,7 @@ private final class PatternCompiler(private val pattern: String, private var fla
* We store *original* group numbers, rather than compiled group numbers,
* in order to make the renumbering caused by possessive quantifiers easier.
*/
- private val namedGroups = js.Dictionary.empty[Int]
+ private val namedGroups = dictEmpty[Int]()
@inline private def hasFlag(flag: Int): Boolean = (flags & flag) != 0
@@ -850,7 +852,7 @@ private final class PatternCompiler(private val pattern: String, private var fla
private def processLeadingEmbeddedFlags(): Unit = {
val m = leadingEmbeddedFlagSpecifierRegExp.exec(pattern)
if (m != null) {
- for (chars <- m(1)) {
+ undefOrForeach(m(1)) { chars =>
for (i <- 0 until chars.length())
flags |= charToFlag(chars.charAt(i))
}
@@ -859,7 +861,7 @@ private final class PatternCompiler(private val pattern: String, private var fla
if (hasFlag(UNICODE_CHARACTER_CLASS))
flags |= UNICODE_CASE
- for (chars <- m(2)) {
+ undefOrForeach(m(2)) { chars =>
for (i <- 0 until chars.length())
flags &= ~charToFlag(chars.charAt(i))
}
@@ -872,7 +874,7 @@ private final class PatternCompiler(private val pattern: String, private var fla
*/
// Advance past the embedded flags
- pIndex += m(0).get.length()
+ pIndex += undefOrForceGet(m(0)).length()
}
}
@@ -1362,9 +1364,9 @@ private final class PatternCompiler(private val pattern: String, private var fla
parseError("\\k is not followed by '<' for named capturing group")
pIndex += 1
val groupName = parseGroupName()
- val groupNumber = namedGroups.getOrElse(groupName, {
+ val groupNumber = dictGetOrElse(namedGroups, groupName) {
parseError(s"named capturing group <$groupName> does not exit")
- })
+ }
val compiledGroupNumber = groupNumberMap(groupNumber)
pIndex += 1
// Wrap in a non-capturing group in case it's followed by a (de-escaped) digit
@@ -1607,16 +1609,16 @@ private final class PatternCompiler(private val pattern: String, private var fla
pattern.substring(start, start + 1)
}
- val result = if (!unicodeCharacterClass && asciiPOSIXCharacterClasses.contains(property)) {
+ val result = if (!unicodeCharacterClass && dictContains(asciiPOSIXCharacterClasses, property)) {
val property2 =
if (asciiCaseInsensitive && (property == "Lower" || property == "Upper")) "Alpha"
else property
- asciiPOSIXCharacterClasses(property2)
+ dictRawApply(asciiPOSIXCharacterClasses, property2)
} else {
// For anything else, we need built-in support for \p
requireES2018Features("Unicode character family")
- predefinedPCharacterClasses.getOrElse(property, {
+ mapGetOrElse(predefinedPCharacterClasses, property) {
val scriptPrefixLen = if (property.startsWith("Is")) {
2
} else if (property.startsWith("sc=")) {
@@ -1630,7 +1632,7 @@ private final class PatternCompiler(private val pattern: String, private var fla
parseError(s"Unknown Unicode character class '$property'")
}
CompiledCharClass.posP("sc=" + canonicalizeScriptName(property.substring(scriptPrefixLen)))
- })
+ }
}
pIndex += 1
@@ -1651,7 +1653,7 @@ private final class PatternCompiler(private val pattern: String, private var fla
val lowercase = scriptName.toLowerCase()
- canonicalizedScriptNameCache.getOrElseUpdate(lowercase, {
+ mapGetOrElseUpdate(canonicalizedScriptNameCache, lowercase) {
val canonical = lowercase.jsReplace(scriptCanonicalizeRegExp,
((s: String) => s.toUpperCase()): js.Function1[String, String])
@@ -1663,7 +1665,7 @@ private final class PatternCompiler(private val pattern: String, private var fla
}
canonical
- })
+ }
}
private def compileCharacterClass(): String = {
@@ -1805,11 +1807,11 @@ private final class PatternCompiler(private val pattern: String, private var fla
// Named capturing group
pIndex = start + 3
val name = parseGroupName()
- if (namedGroups.contains(name))
+ if (dictContains(namedGroups, name))
parseError(s"named capturing group <$name> is already defined")
compiledGroupCount += 1
groupNumberMap.push(compiledGroupCount) // this changes originalGroupCount
- namedGroups(name) = originalGroupCount
+ dictSet(namedGroups, name, originalGroupCount)
pIndex += 1
"(" + compileInsideGroup() + ")"
} else {
diff --git a/javalib/src/main/scala/java/util/regex/PatternSyntaxException.scala b/javalib/src/main/scala/java/util/regex/PatternSyntaxException.scala
index 0faf78fafc..99937b1550 100644
--- a/javalib/src/main/scala/java/util/regex/PatternSyntaxException.scala
+++ b/javalib/src/main/scala/java/util/regex/PatternSyntaxException.scala
@@ -13,6 +13,7 @@
package java.util.regex
import scala.scalajs.js
+import scala.scalajs.runtime.linkingInfo
import scala.scalajs.LinkingInfo
class PatternSyntaxException(desc: String, regex: String, index: Int)
@@ -41,7 +42,7 @@ class PatternSyntaxException(desc: String, regex: String, index: Int)
@inline
private def repeat(s: String, count: Int): String = {
// TODO Use java.lang.String.repeat() once we can (JDK 11+ method)
- if (LinkingInfo.esVersion >= LinkingInfo.ESVersion.ES2015) {
+ if (linkingInfo.esVersion >= LinkingInfo.ESVersion.ES2015) {
s.asInstanceOf[js.Dynamic].repeat(count).asInstanceOf[String]
} else {
var result = ""
diff --git a/javalib/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferBridge.scala b/javalib/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferBridge.scala
index c692870f9a..3468a9f7e9 100644
--- a/javalib/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferBridge.scala
+++ b/javalib/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferBridge.scala
@@ -21,12 +21,17 @@
* members are public.
*
* In library/, this file has only the signatures, with stub implementations.
- * In javalib/, it has the proper the proper implementations.
+ * In javalib/, it has the proper implementations.
* The build keeps the .class coming from library/ and the .sjsir file from
* javalib/. This way, we bridge the library and javalib. But that means the
* binary interface of TypedArrayBufferBridge must be strictly equivalent in
* the two copies.
*
+ * Because of these copies, we must also explicitly use `Any` instead of all
+ * JS types in the method signatures. The IR cleaner would replace any JS type
+ * by `Any` in the javalib, so if we don't write them like that in the library
+ * as well, there will be mismatches.
+ *
* (Yes, this is a hack.)
* !!!!!
*/
@@ -36,45 +41,45 @@ package scala.scalajs.js.typedarray
import java.nio._
private[typedarray] object TypedArrayBufferBridge {
- def wrap(array: ArrayBuffer): ByteBuffer =
- ByteBuffer.wrap(array)
+ def wrapArrayBuffer(array: Any): ByteBuffer =
+ ByteBuffer.wrapArrayBuffer(array.asInstanceOf[ArrayBuffer])
- def wrap(array: ArrayBuffer, byteOffset: Int, length: Int): ByteBuffer =
- ByteBuffer.wrap(array, byteOffset, length)
+ def wrapArrayBuffer(array: Any, byteOffset: Int, length: Int): ByteBuffer =
+ ByteBuffer.wrapArrayBuffer(array.asInstanceOf[ArrayBuffer], byteOffset, length)
- def wrap(array: Int8Array): ByteBuffer =
- ByteBuffer.wrap(array)
+ def wrapInt8Array(array: Any): ByteBuffer =
+ ByteBuffer.wrapInt8Array(array.asInstanceOf[Int8Array])
- def wrap(array: Uint16Array): CharBuffer =
- CharBuffer.wrap(array)
+ def wrapUint16Array(array: Any): CharBuffer =
+ CharBuffer.wrapUint16Array(array.asInstanceOf[Uint16Array])
- def wrap(array: Int16Array): ShortBuffer =
- ShortBuffer.wrap(array)
+ def wrapInt16Array(array: Any): ShortBuffer =
+ ShortBuffer.wrapInt16Array(array.asInstanceOf[Int16Array])
- def wrap(array: Int32Array): IntBuffer =
- IntBuffer.wrap(array)
+ def wrapInt32Array(array: Any): IntBuffer =
+ IntBuffer.wrapInt32Array(array.asInstanceOf[Int32Array])
- def wrap(array: Float32Array): FloatBuffer =
- FloatBuffer.wrap(array)
+ def wrapFloat32Array(array: Any): FloatBuffer =
+ FloatBuffer.wrapFloat32Array(array.asInstanceOf[Float32Array])
- def wrap(array: Float64Array): DoubleBuffer =
- DoubleBuffer.wrap(array)
+ def wrapFloat64Array(array: Any): DoubleBuffer =
+ DoubleBuffer.wrapFloat64Array(array.asInstanceOf[Float64Array])
def Buffer_hasArrayBuffer(buffer: Buffer): Boolean =
buffer.hasArrayBuffer()
- def Buffer_arrayBuffer(buffer: Buffer): ArrayBuffer =
+ def Buffer_arrayBuffer(buffer: Buffer): Any =
buffer.arrayBuffer()
def Buffer_arrayBufferOffset(buffer: Buffer): Int =
buffer.arrayBufferOffset()
- def Buffer_dataView(buffer: Buffer): DataView =
+ def Buffer_dataView(buffer: Buffer): Any =
buffer.dataView()
def Buffer_hasTypedArray(buffer: Buffer): Boolean =
buffer.hasTypedArray()
- def Buffer_typedArray(buffer: Buffer): TypedArray[_, _] =
+ def Buffer_typedArray(buffer: Buffer): Any =
buffer.typedArray()
}
diff --git a/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBuffer.scala b/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBuffer.scala
index 8d5319bc20..42e561d39f 100644
--- a/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBuffer.scala
+++ b/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBuffer.scala
@@ -24,33 +24,33 @@ import java.nio._
object TypedArrayBuffer {
/** Wraps an [[ArrayBuffer]] in a direct [[java.nio.ByteBuffer ByteBuffer]]. */
def wrap(array: ArrayBuffer): ByteBuffer =
- TypedArrayBufferBridge.wrap(array)
+ TypedArrayBufferBridge.wrapArrayBuffer(array)
/** Wraps an [[ArrayBuffer]] in a direct [[java.nio.ByteBuffer ByteBuffer]]. */
def wrap(array: ArrayBuffer, byteOffset: Int, length: Int): ByteBuffer =
- TypedArrayBufferBridge.wrap(array, byteOffset, length)
+ TypedArrayBufferBridge.wrapArrayBuffer(array, byteOffset, length)
/** Wraps an [[Int8Array]] in a direct [[java.nio.ByteBuffer ByteBuffer]]. */
def wrap(array: Int8Array): ByteBuffer =
- TypedArrayBufferBridge.wrap(array)
+ TypedArrayBufferBridge.wrapInt8Array(array)
/** Wraps a [[Uint16Array]] in a direct [[java.nio.CharBuffer CharBuffer]]. */
def wrap(array: Uint16Array): CharBuffer =
- TypedArrayBufferBridge.wrap(array)
+ TypedArrayBufferBridge.wrapUint16Array(array)
/** Wraps an [[Int16Array]] in a direct [[java.nio.ShortBuffer ShortBuffer]]. */
def wrap(array: Int16Array): ShortBuffer =
- TypedArrayBufferBridge.wrap(array)
+ TypedArrayBufferBridge.wrapInt16Array(array)
/** Wraps an [[Int32Array]] in a direct [[java.nio.IntBuffer IntBuffer]]. */
def wrap(array: Int32Array): IntBuffer =
- TypedArrayBufferBridge.wrap(array)
+ TypedArrayBufferBridge.wrapInt32Array(array)
/** Wraps a [[Float32Array]] in a direct [[java.nio.FloatBuffer FloatBuffer]]. */
def wrap(array: Float32Array): FloatBuffer =
- TypedArrayBufferBridge.wrap(array)
+ TypedArrayBufferBridge.wrapFloat32Array(array)
/** Wraps a [[Float64Array]] in a direct [[java.nio.DoubleBuffer DoubleBuffer]]. */
def wrap(array: Float64Array): DoubleBuffer =
- TypedArrayBufferBridge.wrap(array)
+ TypedArrayBufferBridge.wrapFloat64Array(array)
}
diff --git a/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferBridge.scala b/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferBridge.scala
index 0f436fda7e..857abcadc4 100644
--- a/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferBridge.scala
+++ b/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferBridge.scala
@@ -21,12 +21,17 @@
* members are public.
*
* In library/, this file has only the signatures, with stub implementations.
- * In javalib/, it has the proper the proper implementations.
+ * In javalib/, it has the proper implementations.
* The build keeps the .class coming from library/ and the .sjsir file from
* javalib/. This way, we bridge the library and javalib. But that means the
* binary interface of TypedArrayBufferBridge must be strictly equivalent in
* the two copies.
*
+ * Because of these copies, we must also explicitly use `Any` instead of all
+ * JS types in the method signatures. The IR cleaner would replace any JS type
+ * by `Any` in the javalib, so if we don't write them like that in the library
+ * as well, there will be mismatches.
+ *
* (Yes, this is a hack.)
* !!!!!
*/
@@ -36,33 +41,33 @@ package scala.scalajs.js.typedarray
import java.nio._
private[typedarray] object TypedArrayBufferBridge {
- def wrap(array: ArrayBuffer): ByteBuffer = stub()
+ def wrapArrayBuffer(array: Any): ByteBuffer = stub()
- def wrap(array: ArrayBuffer, byteOffset: Int, length: Int): ByteBuffer = stub()
+ def wrapArrayBuffer(array: Any, byteOffset: Int, length: Int): ByteBuffer = stub()
- def wrap(array: Int8Array): ByteBuffer = stub()
+ def wrapInt8Array(array: Any): ByteBuffer = stub()
- def wrap(array: Uint16Array): CharBuffer = stub()
+ def wrapUint16Array(array: Any): CharBuffer = stub()
- def wrap(array: Int16Array): ShortBuffer = stub()
+ def wrapInt16Array(array: Any): ShortBuffer = stub()
- def wrap(array: Int32Array): IntBuffer = stub()
+ def wrapInt32Array(array: Any): IntBuffer = stub()
- def wrap(array: Float32Array): FloatBuffer = stub()
+ def wrapFloat32Array(array: Any): FloatBuffer = stub()
- def wrap(array: Float64Array): DoubleBuffer = stub()
+ def wrapFloat64Array(array: Any): DoubleBuffer = stub()
def Buffer_hasArrayBuffer(buffer: Buffer): Boolean = stub()
- def Buffer_arrayBuffer(buffer: Buffer): ArrayBuffer = stub()
+ def Buffer_arrayBuffer(buffer: Buffer): Any = stub()
def Buffer_arrayBufferOffset(buffer: Buffer): Int = stub()
- def Buffer_dataView(buffer: Buffer): DataView = stub()
+ def Buffer_dataView(buffer: Buffer): Any = stub()
def Buffer_hasTypedArray(buffer: Buffer): Boolean = stub()
- def Buffer_typedArray(buffer: Buffer): TypedArray[_, _] = stub()
+ def Buffer_typedArray(buffer: Buffer): Any = stub()
private def stub(): Nothing =
throw new Error("stub")
diff --git a/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferOps.scala b/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferOps.scala
index 9572727d9e..b0c1911a0c 100644
--- a/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferOps.scala
+++ b/library/src/main/scala/scala/scalajs/js/typedarray/TypedArrayBufferOps.scala
@@ -41,7 +41,7 @@ final class TypedArrayBufferOps[ // scalastyle:ignore
* If this buffer has no backing [[ArrayBuffer]], i.e., !hasArrayBuffer()
*/
def arrayBuffer(): ArrayBuffer =
- TypedArrayBufferBridge.Buffer_arrayBuffer(buffer)
+ TypedArrayBufferBridge.Buffer_arrayBuffer(buffer).asInstanceOf[ArrayBuffer]
/** Byte offset in the associated [[ArrayBuffer]] _(optional operation)_.
*
@@ -60,7 +60,7 @@ final class TypedArrayBufferOps[ // scalastyle:ignore
* If this buffer has no backing [[ArrayBuffer]], i.e., !hasArrayBuffer()
*/
def dataView(): DataView =
- TypedArrayBufferBridge.Buffer_dataView(buffer)
+ TypedArrayBufferBridge.Buffer_dataView(buffer).asInstanceOf[DataView]
/** Tests whether this direct buffer has a valid associated [[TypedArray]].
*
diff --git a/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala
index 8e955e5b8d..571e1f0656 100644
--- a/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala
+++ b/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala
@@ -70,9 +70,9 @@ class LibrarySizeTest {
)
testLinkedSizes(
- expectedFastLinkSize = 146336,
- expectedFullLinkSizeWithoutClosure = 135798,
- expectedFullLinkSizeWithClosure = 22074,
+ expectedFastLinkSize = 144675,
+ expectedFullLinkSizeWithoutClosure = 133818,
+ expectedFullLinkSizeWithClosure = 21620,
classDefs,
moduleInitializers = MainTestModuleInitializers
)
diff --git a/project/Build.scala b/project/Build.scala
index d31c011828..2a61950768 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -572,7 +572,7 @@ object Build {
)
val cleanIRSettings = Def.settings(
- // In order to rewrite anonymous functions, the code must not be specialized
+ // In order to rewrite anonymous functions and tuples, the code must not be specialized
scalacOptions += "-no-specialization",
products in Compile := {
@@ -1271,6 +1271,8 @@ object Build {
Nil
},
+ cleanIRSettings,
+
headerSources in Compile ~= { srcs =>
srcs.filter { src =>
val path = src.getPath.replace('\\', '/')
diff --git a/project/JavalibIRCleaner.scala b/project/JavalibIRCleaner.scala
index 8b784e8f0e..8214b6ee71 100644
--- a/project/JavalibIRCleaner.scala
+++ b/project/JavalibIRCleaner.scala
@@ -168,7 +168,7 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
validateClassName(interface.name)
val transformedClassDef =
- Hashers.hashClassDef(this.transformClassDef(preprocessedTree))
+ Hashers.hashClassDef(eliminateRedundantBridges(this.transformClassDef(preprocessedTree)))
postTransformChecks(transformedClassDef)
transformedClassDef
@@ -210,6 +210,89 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
}
}
+ /** Eliminate bridges that have become redundant because of our additional erasure. */
+ private def eliminateRedundantBridges(classDef: ClassDef): ClassDef = {
+ import MemberNamespace._
+
+ def argsCorrespond(args: List[Tree], paramDefs: List[ParamDef]): Boolean = {
+ (args.size == paramDefs.size) && args.zip(paramDefs).forall {
+ case (VarRef(LocalIdent(argName)), ParamDef(LocalIdent(paramName), _, _, _)) =>
+ argName == paramName
+ case _ =>
+ false
+ }
+ }
+
+ val memberDefs = classDef.memberDefs
+
+ // Instance bridges, which call "themselves" (another version of themselves with the same name)
+
+ def isRedundantBridge(memberDef: MemberDef): Boolean = memberDef match {
+ case MethodDef(flags, MethodIdent(name), _, paramDefs, _, Some(body)) if flags.namespace == Public =>
+ body match {
+ case Apply(ApplyFlags.empty, This(), MethodIdent(`name`), args) =>
+ argsCorrespond(args, paramDefs)
+ case _ =>
+ false
+ }
+ case _ =>
+ false
+ }
+
+ val newMemberDefs1 = memberDefs.filterNot(isRedundantBridge(_))
+
+ // Make sure that we did not remove *all* overloads for any method name
+
+ def publicMethodNames(memberDefs: List[MemberDef]): Set[MethodName] = {
+ memberDefs.collect {
+ case MethodDef(flags, name, _, _, _, _) if flags.namespace == Public => name.name
+ }.toSet
+ }
+
+ val lostMethodNames = publicMethodNames(memberDefs) -- publicMethodNames(newMemberDefs1)
+ if (lostMethodNames.nonEmpty) {
+ for (lostMethodName <- lostMethodNames)
+ reportError(s"eliminateRedundantBridges removed all overloads of ${lostMethodName.nameString}")(classDef.pos)
+ }
+
+ // Static forwarders to redundant bridges -- these are duplicate public static methods
+
+ def isStaticForwarder(memberDef: MethodDef): Boolean = memberDef match {
+ case MethodDef(flags, MethodIdent(name), _, paramDefs, _, Some(body)) if flags.namespace == PublicStatic =>
+ body match {
+ case Apply(ApplyFlags.empty, LoadModule(_), MethodIdent(`name`), args) =>
+ argsCorrespond(args, paramDefs)
+ case _ =>
+ false
+ }
+ case _ =>
+ false
+ }
+
+ val seenStaticForwarderNames = mutable.Set.empty[MethodName]
+ val newMemberDefs2 = newMemberDefs1.filter { memberDef =>
+ memberDef match {
+ case m: MethodDef if isStaticForwarder(m) =>
+ seenStaticForwarderNames.add(m.name.name) // keep if it is the first one
+ case _ =>
+ true // always keep
+ }
+ }
+
+ new ClassDef(
+ classDef.name,
+ classDef.originalName,
+ classDef.kind,
+ classDef.jsClassCaptures,
+ classDef.superClass,
+ classDef.interfaces,
+ classDef.jsSuperClass,
+ classDef.jsNativeLoadSpec,
+ newMemberDefs2,
+ classDef.topLevelExportDefs
+ )(classDef.optimizerHints)(classDef.pos)
+ }
+
private def transformParamDefs(paramDefs: List[ParamDef]): List[ParamDef] = {
for (paramDef <- paramDefs) yield {
implicit val pos = paramDef.pos
@@ -244,10 +327,27 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
if isFunctionNType(n, fun.tpe) =>
JSFunctionApply(fun, args)
+ // <= 2.12 : toJSVarArgs(jsArrayOps(jsArray).toSeq) -> jsArray
+ case IntrinsicCall(ScalaJSRuntimeMod, `toJSVarArgsReadOnlyMethodName`,
+ List(Apply(
+ ApplyFlags.empty,
+ IntrinsicCall(JSAnyMod, `jsArrayOpsToArrayOpsMethodName`, List(jsArray)),
+ MethodIdent(`toReadOnlySeqMethodName`),
+ Nil)
+ )) =>
+ jsArray
+
+ // >= 2.13 : toJSVarArgs(toSeq$extension(jsArray)) -> jsArray
+ case IntrinsicCall(ScalaJSRuntimeMod, `toJSVarArgsImmutableMethodName`,
+ List(IntrinsicCall(JSArrayOpsMod, `toImmutableSeqExtensionMethodName`, List(jsArray)))) =>
+ jsArray
+
case IntrinsicCall(JSAnyMod, `jsAnyFromIntMethodName`, List(arg)) =>
arg
case IntrinsicCall(JSAnyMod, `jsAnyFromStringMethodName`, List(arg)) =>
arg
+ case IntrinsicCall(JSAnyMod, `jsArrayOpsToArrayMethodName`, List(arg)) =>
+ arg
case IntrinsicCall(JSDynamicImplicitsMod, `number2dynamicMethodName`, List(arg)) =>
arg
case IntrinsicCall(JSNumberOpsMod, `enableJSNumberOpsDoubleMethodName`, List(arg)) =>
@@ -256,6 +356,8 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
arg
case IntrinsicCall(JSStringOpsMod, `enableJSStringOpsMethodName`, List(arg)) =>
arg
+ case IntrinsicCall(UnionTypeMod, `unionTypeFromMethodName`, List(arg, _)) =>
+ arg
case IntrinsicCall(JSDynamicImplicitsMod, `truthValueMethodName`, List(arg)) =>
AsInstanceOf(
@@ -491,7 +593,15 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) {
def isJavaScriptExceptionWithinItself =
cls == JavaScriptExceptionClass && enclosingClassName == JavaScriptExceptionClass
- if (cls.nameString.startsWith("scala.") && !isJavaScriptExceptionWithinItself)
+ def isTypedArrayBufferBridgeWithinItself = {
+ (cls == TypedArrayBufferBridge || cls == TypedArrayBufferBridgeMod) &&
+ (enclosingClassName == TypedArrayBufferBridge || enclosingClassName == TypedArrayBufferBridgeMod)
+ }
+
+ def isAnException: Boolean =
+ isJavaScriptExceptionWithinItself || isTypedArrayBufferBridgeWithinItself
+
+ if (cls.nameString.startsWith("scala.") && !isAnException)
reportError(s"Illegal reference to Scala class ${cls.nameString}")
}
@@ -521,10 +631,17 @@ object JavalibIRCleaner {
// Within js.JavaScriptException, which is part of the linker private lib, we can refer to itself
private val JavaScriptExceptionClass = ClassName("scala.scalajs.js.JavaScriptException")
+ // Within TypedArrayBufferBridge, which is actually part of the library, we can refer to itself
+ private val TypedArrayBufferBridge = ClassName("scala.scalajs.js.typedarray.TypedArrayBufferBridge")
+ private val TypedArrayBufferBridgeMod = ClassName("scala.scalajs.js.typedarray.TypedArrayBufferBridge$")
+
+ private val ImmutableSeq = ClassName("scala.collection.immutable.Seq")
private val JavaIOSerializable = ClassName("java.io.Serializable")
private val JSAny = ClassName("scala.scalajs.js.Any")
private val JSAnyMod = ClassName("scala.scalajs.js.Any$")
private val JSArray = ClassName("scala.scalajs.js.Array")
+ private val JSArrayOps = ClassName("scala.scalajs.js.ArrayOps")
+ private val JSArrayOpsMod = ClassName("scala.scalajs.js.ArrayOps$")
private val JSDynamic = ClassName("scala.scalajs.js.Dynamic")
private val JSDynamicImplicitsMod = ClassName("scala.scalajs.js.DynamicImplicits$")
private val JSNumberOps = ClassName("scala.scalajs.js.JSNumberOps")
@@ -535,6 +652,9 @@ object JavalibIRCleaner {
private val ScalaSerializable = ClassName("scala.Serializable")
private val ScalaJSRuntimeMod = ClassName("scala.scalajs.runtime.package$")
private val StringContextClass = ClassName("scala.StringContext")
+ private val UnionType = ClassName("scala.scalajs.js.$bar")
+ private val UnionTypeMod = ClassName("scala.scalajs.js.$bar$")
+ private val UnionTypeEvidence = ClassName("scala.scalajs.js.$bar$Evidence")
private val FunctionNClasses: IndexedSeq[ClassName] =
(0 to MaxFunctionArity).map(n => ClassName(s"scala.Function$n"))
@@ -552,16 +672,30 @@ object JavalibIRCleaner {
MethodName("fromInt", List(IntRef), ClassRef(JSAny))
private val jsAnyFromStringMethodName =
MethodName("fromString", List(ClassRef(BoxedStringClass)), ClassRef(JSAny))
+ private val jsArrayOpsToArrayMethodName =
+ MethodName("jsArrayOps", List(ClassRef(JSArray)), ClassRef(JSArray))
+ private val jsArrayOpsToArrayOpsMethodName =
+ MethodName("jsArrayOps", List(ClassRef(JSArray)), ClassRef(JSArrayOps))
private val number2dynamicMethodName =
MethodName("number2dynamic", List(DoubleRef), ClassRef(JSDynamic))
private val sMethodName =
MethodName("s", List(ClassRef(ReadOnlySeq)), ClassRef(BoxedStringClass))
private val stringContextCtorMethodName =
MethodName.constructor(List(ClassRef(ReadOnlySeq)))
+ private val toImmutableSeqExtensionMethodName =
+ MethodName("toSeq$extension", List(ClassRef(JSArray)), ClassRef(ImmutableSeq))
+ private val toJSVarArgsImmutableMethodName =
+ MethodName("toJSVarArgs", List(ClassRef(ImmutableSeq)), ClassRef(JSArray))
+ private val toJSVarArgsReadOnlyMethodName =
+ MethodName("toJSVarArgs", List(ClassRef(ReadOnlySeq)), ClassRef(JSArray))
private val toScalaVarArgsReadOnlyMethodName =
MethodName("toScalaVarArgs", List(ClassRef(JSArray)), ClassRef(ReadOnlySeq))
+ private val toReadOnlySeqMethodName =
+ MethodName("toSeq", Nil, ClassRef(ReadOnlySeq))
private val truthValueMethodName =
MethodName("truthValue", List(ClassRef(JSDynamic)), BooleanRef)
+ private val unionTypeFromMethodName =
+ MethodName("from", List(ClassRef(ObjectClass), ClassRef(UnionTypeEvidence)), ClassRef(UnionType))
private val writeReplaceMethodName =
MethodName("writeReplace", Nil, ClassRef(ObjectClass))
@@ -603,6 +737,30 @@ object JavalibIRCleaner {
funClass -> ObjectClass
}
- functionTypePairs.toMap
+ val refBaseNames =
+ List("Boolean", "Char", "Byte", "Short", "Int", "Long", "Float", "Double", "Object")
+ val refPairs = for {
+ refBaseName <- refBaseNames
+ } yield {
+ val simpleName = refBaseName + "Ref"
+ ClassName("scala.runtime." + simpleName) -> ClassName("java.util.internal." + simpleName)
+ }
+
+ val tuplePairs = for {
+ n <- (2 to 22).toList
+ } yield {
+ ClassName("scala.Tuple" + n) -> ClassName("java.util.internal.Tuple" + n)
+ }
+
+ val otherPairs = List(
+ /* AssertionError conveniently features a constructor taking an Object.
+ * Since any MatchError in the javalib would be a bug, it is fine to
+ * rewrite them to AssertionErrors.
+ */
+ ClassName("scala.MatchError") -> ClassName("java.lang.AssertionError"),
+ )
+
+ val allPairs = functionTypePairs ++ refPairs ++ tuplePairs ++ otherPairs
+ allPairs.toMap
}
}