diff --git a/library-js/src/scala/App.scala b/library-js/src/scala/App.scala new file mode 100644 index 000000000000..bbba62bbf350 --- /dev/null +++ b/library-js/src/scala/App.scala @@ -0,0 +1,85 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import java.lang.System.{currentTimeMillis => currentTime} +import scala.collection.mutable.ListBuffer + +/** The `App` trait can be used to quickly turn objects + * into executable programs. Here is an example: + * {{{ + * object Main extends App { + * Console.println("Hello World: " + (args mkString ", ")) + * } + * }}} + * + * No explicit `main` method is needed. Instead, + * the whole class body becomes the “main method”. + * + * `args` returns the current command line arguments as an array. + * + * ==Caveats== + * + * '''''It should be noted that this trait is implemented using the [[DelayedInit]] + * functionality, which means that fields of the object will not have been initialized + * before the main method has been executed.''''' + * + * Future versions of this trait will no longer extend `DelayedInit`. + * + * @author Martin Odersky + * @since 2.1 + */ +trait App extends DelayedInit { + + /** The time when the execution of this program started, in milliseconds since 1 + * January 1970 UTC. */ + final val executionStart: Long = currentTime + + /** The command line arguments passed to the application's `main` method. + */ + protected final def args: Array[String] = _args + + private[this] var _args: Array[String] = _ + + private[this] val initCode = new ListBuffer[() => Unit] + + /** The init hook. This saves all initialization code for execution within `main`. + * This method is normally never called directly from user code. + * Instead it is called as compiler-generated code for those classes and objects + * (but not traits) that inherit from the `DelayedInit` trait and that do not + * themselves define a `delayedInit` method. + * @param body the initialization code to be stored for later execution + */ + @deprecated("the delayedInit mechanism will disappear", "2.11.0") + override def delayedInit(body: => Unit): Unit = { + initCode += (() => body) + } + + /** The main method. + * This stores all arguments so that they can be retrieved with `args` + * and then executes all initialization code segments in the order in which + * they were passed to `delayedInit`. + * @param args the arguments passed to the main method + */ + final def main(args: Array[String]) = { + this._args = args + for (proc <- initCode) proc() + + /* DELETED for Scala.js + if (util.Properties.propIsSet("scala.time")) { + val total = currentTime - executionStart + Console.println("[total " + total + "ms]") + } + */ + } +} diff --git a/library-js/src/scala/Array.scala b/library-js/src/scala/Array.scala new file mode 100644 index 000000000000..5559b6650c25 --- /dev/null +++ b/library-js/src/scala/Array.scala @@ -0,0 +1,691 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +//import scala.collection.generic._ +import scala.collection.{Factory, immutable, mutable} +import mutable.ArrayBuilder +import immutable.ArraySeq +import scala.language.implicitConversions +import scala.reflect.ClassTag +import scala.runtime.{BoxedUnit, ScalaRunTime} +import scala.runtime.ScalaRunTime.{array_apply, array_update} + +/** Utility methods for operating on arrays. + * For example: + * {{{ + * val a = Array(1, 2) + * val b = Array.ofDim[Int](2) + * val c = Array.concat(a, b) + * }}} + * where the array objects `a`, `b` and `c` have respectively the values + * `Array(1, 2)`, `Array(0, 0)` and `Array(1, 2, 0, 0)`. + * + * @author Martin Odersky + * @since 1.0 + */ +object Array { + def emptyBooleanArray = EmptyArrays.emptyBooleanArray + def emptyByteArray = EmptyArrays.emptyByteArray + def emptyCharArray = EmptyArrays.emptyCharArray + def emptyDoubleArray = EmptyArrays.emptyDoubleArray + def emptyFloatArray = EmptyArrays.emptyFloatArray + def emptyIntArray = EmptyArrays.emptyIntArray + def emptyLongArray = EmptyArrays.emptyLongArray + def emptyShortArray = EmptyArrays.emptyShortArray + def emptyObjectArray = EmptyArrays.emptyObjectArray + + private object EmptyArrays { + val emptyBooleanArray = new Array[Boolean](0) + val emptyByteArray = new Array[Byte](0) + val emptyCharArray = new Array[Char](0) + val emptyDoubleArray = new Array[Double](0) + val emptyFloatArray = new Array[Float](0) + val emptyIntArray = new Array[Int](0) + val emptyLongArray = new Array[Long](0) + val emptyShortArray = new Array[Short](0) + val emptyObjectArray = new Array[Object](0) + } + + /** Provides an implicit conversion from the Array object to a collection Factory */ + implicit def toFactory[A : ClassTag](dummy: Array.type): Factory[A, Array[A]] = new ArrayFactory(dummy) + @SerialVersionUID(3L) + private class ArrayFactory[A : ClassTag](dummy: Array.type) extends Factory[A, Array[A]] with Serializable { + def fromSpecific(it: IterableOnce[A]): Array[A] = Array.from[A](it) + def newBuilder: mutable.Builder[A, Array[A]] = Array.newBuilder[A] + } + + /** + * Returns a new [[scala.collection.mutable.ArrayBuilder]]. + */ + def newBuilder[T](implicit t: ClassTag[T]): ArrayBuilder[T] = ArrayBuilder.make[T](t) + + def from[A : ClassTag](it: IterableOnce[A]): Array[A] = { + val n = it.knownSize + if (n > -1) { + val elements = new Array[A](n) + val iterator = it.iterator + var i = 0 + while (i < n) { + ScalaRunTime.array_update(elements, i, iterator.next()) + i = i + 1 + } + elements + } else { + val b = ArrayBuilder.make[A] + val iterator = it.iterator + while (iterator.hasNext) + b += iterator.next() + b.result() + } + } + + private def slowcopy(src : AnyRef, + srcPos : Int, + dest : AnyRef, + destPos : Int, + length : Int): Unit = { + var i = srcPos + var j = destPos + val srcUntil = srcPos + length + while (i < srcUntil) { + array_update(dest, j, array_apply(src, i)) + i += 1 + j += 1 + } + } + + /** Copy one array to another. + * Equivalent to Java's + * `System.arraycopy(src, srcPos, dest, destPos, length)`, + * except that this also works for polymorphic and boxed arrays. + * + * Note that the passed-in `dest` array will be modified by this call. + * + * @param src the source array. + * @param srcPos starting position in the source array. + * @param dest destination array. + * @param destPos starting position in the destination array. + * @param length the number of array elements to be copied. + * + * @see `java.lang.System#arraycopy` + */ + def copy(src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int): Unit = { + val srcClass = src.getClass + if (srcClass.isArray && dest.getClass.isAssignableFrom(srcClass)) + java.lang.System.arraycopy(src, srcPos, dest, destPos, length) + else + slowcopy(src, srcPos, dest, destPos, length) + } + + /** Copy one array to another, truncating or padding with default values (if + * necessary) so the copy has the specified length. + * + * Equivalent to Java's + * `java.util.Arrays.copyOf(original, newLength)`, + * except that this works for primitive and object arrays in a single method. + * + * @see `java.util.Arrays#copyOf` + */ + def copyOf[A](original: Array[A], newLength: Int): Array[A] = (original match { + case x: Array[BoxedUnit] => newUnitArray(newLength).asInstanceOf[Array[A]] + case x: Array[AnyRef] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Int] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Double] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Long] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Float] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Char] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Byte] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Short] => java.util.Arrays.copyOf(x, newLength) + case x: Array[Boolean] => java.util.Arrays.copyOf(x, newLength) + }).asInstanceOf[Array[A]] + + /** Copy one array to another, truncating or padding with default values (if + * necessary) so the copy has the specified length. The new array can have + * a different type than the original one as long as the values are + * assignment-compatible. When copying between primitive and object arrays, + * boxing and unboxing are supported. + * + * Equivalent to Java's + * `java.util.Arrays.copyOf(original, newLength, newType)`, + * except that this works for all combinations of primitive and object arrays + * in a single method. + * + * @see `java.util.Arrays#copyOf` + */ + def copyAs[A](original: Array[_], newLength: Int)(implicit ct: ClassTag[A]): Array[A] = { + val runtimeClass = ct.runtimeClass + if (runtimeClass == Void.TYPE) newUnitArray(newLength).asInstanceOf[Array[A]] + else { + val destClass = runtimeClass.asInstanceOf[Class[A]] + if (destClass.isAssignableFrom(original.getClass.getComponentType)) { + if (destClass.isPrimitive) copyOf[A](original.asInstanceOf[Array[A]], newLength) + else { + val destArrayClass = java.lang.reflect.Array.newInstance(destClass, 0).getClass.asInstanceOf[Class[Array[AnyRef]]] + java.util.Arrays.copyOf(original.asInstanceOf[Array[AnyRef]], newLength, destArrayClass).asInstanceOf[Array[A]] + } + } else { + val dest = new Array[A](newLength) + Array.copy(original, 0, dest, 0, original.length) + dest + } + } + } + + private def newUnitArray(len: Int): Array[Unit] = { + val result = new Array[Unit](len) + java.util.Arrays.fill(result.asInstanceOf[Array[AnyRef]], ()) + result + } + + /** Returns an array of length 0 */ + def empty[T: ClassTag]: Array[T] = new Array[T](0) + + /** Creates an array with given elements. + * + * @param xs the elements to put in the array + * @return an array containing all elements from xs. + */ + // Subject to a compiler optimization in Cleanup. + // Array(e0, ..., en) is translated to { val a = new Array(3); a(i) = ei; a } + def apply[T: ClassTag](xs: T*): Array[T] = { + val array = new Array[T](xs.length) + val iterator = xs.iterator + var i = 0 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Boolean` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Boolean, xs: Boolean*): Array[Boolean] = { + val array = new Array[Boolean](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Byte` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Byte, xs: Byte*): Array[Byte] = { + val array = new Array[Byte](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Short` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Short, xs: Short*): Array[Short] = { + val array = new Array[Short](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Char` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Char, xs: Char*): Array[Char] = { + val array = new Array[Char](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Int` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Int, xs: Int*): Array[Int] = { + val array = new Array[Int](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Long` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Long, xs: Long*): Array[Long] = { + val array = new Array[Long](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Float` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Float, xs: Float*): Array[Float] = { + val array = new Array[Float](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Double` objects */ + // Subject to a compiler optimization in Cleanup, see above. + def apply(x: Double, xs: Double*): Array[Double] = { + val array = new Array[Double](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates an array of `Unit` objects */ + def apply(x: Unit, xs: Unit*): Array[Unit] = { + val array = new Array[Unit](xs.length + 1) + array(0) = x + val iterator = xs.iterator + var i = 1 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array + } + + /** Creates array with given dimensions */ + def ofDim[T: ClassTag](n1: Int): Array[T] = + new Array[T](n1) + /** Creates a 2-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int): Array[Array[T]] = { + val arr: Array[Array[T]] = (new Array[Array[T]](n1): Array[Array[T]]) + for (i <- 0 until n1) arr(i) = new Array[T](n2) + arr + // tabulate(n1)(_ => ofDim[T](n2)) + } + /** Creates a 3-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int, n3: Int): Array[Array[Array[T]]] = + tabulate(n1)(_ => ofDim[T](n2, n3)) + /** Creates a 4-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int): Array[Array[Array[Array[T]]]] = + tabulate(n1)(_ => ofDim[T](n2, n3, n4)) + /** Creates a 5-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int): Array[Array[Array[Array[Array[T]]]]] = + tabulate(n1)(_ => ofDim[T](n2, n3, n4, n5)) + + /** Concatenates all arrays into a single array. + * + * @param xss the given arrays + * @return the array created from concatenating `xss` + */ + def concat[T: ClassTag](xss: Array[T]*): Array[T] = { + val b = newBuilder[T] + b.sizeHint(xss.map(_.length).sum) + for (xs <- xss) b ++= xs + b.result() + } + + /** Returns an array that contains the results of some element computation a number + * of times. + * + * Note that this means that `elem` is computed a total of n times: + * {{{ + * scala> Array.fill(3){ math.random } + * res3: Array[Double] = Array(0.365461167592537, 1.550395944913685E-4, 0.7907242137333306) + * }}} + * + * @param n the number of elements desired + * @param elem the element computation + * @return an Array of size n, where each element contains the result of computing + * `elem`. + */ + def fill[T: ClassTag](n: Int)(elem: => T): Array[T] = { + if (n <= 0) { + empty[T] + } else { + val array = new Array[T](n) + var i = 0 + while (i < n) { + array(i) = elem + i += 1 + } + array + } + } + + /** Returns a two-dimensional array that contains the results of some element + * computation a number of times. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): Array[Array[T]] = + tabulate(n1)(_ => fill(n2)(elem)) + + /** Returns a three-dimensional array that contains the results of some element + * computation a number of times. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int)(elem: => T): Array[Array[Array[T]]] = + tabulate(n1)(_ => fill(n2, n3)(elem)) + + /** Returns a four-dimensional array that contains the results of some element + * computation a number of times. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => T): Array[Array[Array[Array[T]]]] = + tabulate(n1)(_ => fill(n2, n3, n4)(elem)) + + /** Returns a five-dimensional array that contains the results of some element + * computation a number of times. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => T): Array[Array[Array[Array[Array[T]]]]] = + tabulate(n1)(_ => fill(n2, n3, n4, n5)(elem)) + + /** Returns an array containing values of a given function over a range of integer + * values starting from 0. + * + * @param n The number of elements in the array + * @param f The function computing element values + * @return A traversable consisting of elements `f(0),f(1), ..., f(n - 1)` + */ + def tabulate[T: ClassTag](n: Int)(f: Int => T): Array[T] = { + if (n <= 0) { + empty[T] + } else { + val array = new Array[T](n) + var i = 0 + while (i < n) { + array(i) = f(i) + i += 1 + } + array + } + } + + /** Returns a two-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): Array[Array[T]] = + tabulate(n1)(i1 => tabulate(n2)(f(i1, _))) + + /** Returns a three-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => T): Array[Array[Array[T]]] = + tabulate(n1)(i1 => tabulate(n2, n3)(f(i1, _, _))) + + /** Returns a four-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => T): Array[Array[Array[Array[T]]]] = + tabulate(n1)(i1 => tabulate(n2, n3, n4)(f(i1, _, _, _))) + + /** Returns a five-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => T): Array[Array[Array[Array[Array[T]]]]] = + tabulate(n1)(i1 => tabulate(n2, n3, n4, n5)(f(i1, _, _, _, _))) + + /** Returns an array containing a sequence of increasing integers in a range. + * + * @param start the start value of the array + * @param end the end value of the array, exclusive (in other words, this is the first value '''not''' returned) + * @return the array with values in range `start, start + 1, ..., end - 1` + * up to, but excluding, `end`. + */ + def range(start: Int, end: Int): Array[Int] = range(start, end, 1) + + /** Returns an array containing equally spaced values in some integer interval. + * + * @param start the start value of the array + * @param end the end value of the array, exclusive (in other words, this is the first value '''not''' returned) + * @param step the increment value of the array (may not be zero) + * @return the array with values in `start, start + step, ...` up to, but excluding `end` + */ + def range(start: Int, end: Int, step: Int): Array[Int] = { + if (step == 0) throw new IllegalArgumentException("zero step") + val array = new Array[Int](immutable.Range.count(start, end, step, isInclusive = false)) + + var n = 0 + var i = start + while (if (step < 0) end < i else i < end) { + array(n) = i + i += step + n += 1 + } + array + } + + /** Returns an array containing repeated applications of a function to a start value. + * + * @param start the start value of the array + * @param len the number of elements returned by the array + * @param f the function that is repeatedly applied + * @return the array returning `len` values in the sequence `start, f(start), f(f(start)), ...` + */ + def iterate[T: ClassTag](start: T, len: Int)(f: T => T): Array[T] = { + if (len > 0) { + val array = new Array[T](len) + var acc = start + var i = 1 + array(0) = acc + + while (i < len) { + acc = f(acc) + array(i) = acc + i += 1 + } + array + } else { + empty[T] + } + } + + def equals(xs: Array[AnyRef], ys: Array[AnyRef]): Boolean = { + if (xs eq ys) + return true + if (xs.length != ys.length) + return false + + val len = xs.length + var i = 0 + while (i < len) { + if (xs(i) != ys(i)) + return false + i += 1 + } + true + } + + /** Called in a pattern match like `{ case Array(x,y,z) => println('3 elements')}`. + * + * @param x the selector value + * @return sequence wrapped in a [[scala.Some]], if `x` is an Array, otherwise `None` + */ + def unapplySeq[T](x: Array[T]): UnapplySeqWrapper[T] = new UnapplySeqWrapper(x) + + final class UnapplySeqWrapper[T](private val a: Array[T]) extends AnyVal { + def isEmpty: Boolean = false + def get: UnapplySeqWrapper[T] = this + def lengthCompare(len: Int): Int = a.lengthCompare(len) + def apply(i: Int): T = a(i) + def drop(n: Int): scala.Seq[T] = ArraySeq.unsafeWrapArray(a.drop(n)) // clones the array, also if n == 0 + def toSeq: scala.Seq[T] = a.toSeq // clones the array + } +} + +/** Arrays are mutable, indexed collections of values. `Array[T]` is Scala's representation + * for Java's `T[]`. + * + * {{{ + * val numbers = Array(1, 2, 3, 4) + * val first = numbers(0) // read the first element + * numbers(3) = 100 // replace the 4th array element with 100 + * val biggerNumbers = numbers.map(_ * 2) // multiply all numbers by two + * }}} + * + * Arrays make use of two common pieces of Scala syntactic sugar, shown on lines 2 and 3 of the above + * example code. + * Line 2 is translated into a call to `apply(Int)`, while line 3 is translated into a call to + * `update(Int, T)`. + * + * Two implicit conversions exist in [[scala.Predef]] that are frequently applied to arrays: a conversion + * to [[scala.collection.ArrayOps]] (shown on line 4 of the example above) and a conversion + * to [[scala.collection.mutable.ArraySeq]] (a subtype of [[scala.collection.Seq]]). + * Both types make available many of the standard operations found in the Scala collections API. + * The conversion to `ArrayOps` is temporary, as all operations defined on `ArrayOps` return an `Array`, + * while the conversion to `ArraySeq` is permanent as all operations return a `ArraySeq`. + * + * The conversion to `ArrayOps` takes priority over the conversion to `ArraySeq`. For instance, + * consider the following code: + * + * {{{ + * val arr = Array(1, 2, 3) + * val arrReversed = arr.reverse + * val seqReversed : collection.Seq[Int] = arr.reverse + * }}} + * + * Value `arrReversed` will be of type `Array[Int]`, with an implicit conversion to `ArrayOps` occurring + * to perform the `reverse` operation. The value of `seqReversed`, on the other hand, will be computed + * by converting to `ArraySeq` first and invoking the variant of `reverse` that returns another + * `ArraySeq`. + * + * @author Martin Odersky + * @since 1.0 + * @see [[http://www.scala-lang.org/files/archive/spec/2.13/ Scala Language Specification]], for in-depth information on the transformations the Scala compiler makes on Arrays (Sections 6.6 and 6.15 respectively.) + * @see [[http://docs.scala-lang.org/sips/completed/scala-2-8-arrays.html "Scala 2.8 Arrays"]] the Scala Improvement Document detailing arrays since Scala 2.8. + * @see [[http://docs.scala-lang.org/overviews/collections/arrays.html "The Scala 2.8 Collections' API"]] section on `Array` by Martin Odersky for more information. + * @hideImplicitConversion scala.Predef.booleanArrayOps + * @hideImplicitConversion scala.Predef.byteArrayOps + * @hideImplicitConversion scala.Predef.charArrayOps + * @hideImplicitConversion scala.Predef.doubleArrayOps + * @hideImplicitConversion scala.Predef.floatArrayOps + * @hideImplicitConversion scala.Predef.intArrayOps + * @hideImplicitConversion scala.Predef.longArrayOps + * @hideImplicitConversion scala.Predef.refArrayOps + * @hideImplicitConversion scala.Predef.shortArrayOps + * @hideImplicitConversion scala.Predef.unitArrayOps + * @hideImplicitConversion scala.LowPriorityImplicits.wrapRefArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapIntArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapDoubleArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapLongArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapFloatArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapCharArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapByteArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapShortArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapBooleanArray + * @hideImplicitConversion scala.LowPriorityImplicits.wrapUnitArray + * @hideImplicitConversion scala.LowPriorityImplicits.genericWrapArray + * @define coll array + * @define Coll `Array` + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define collectExample + * @define undefinedorder + */ +final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable { + + /** The length of the array */ + def length: Int = throw new Error() + + /** The element at given index. + * + * Indices start at `0`; `xs.apply(0)` is the first element of array `xs`. + * Note the indexing syntax `xs(i)` is a shorthand for `xs.apply(i)`. + * + * @param i the index + * @return the element at the given index + * @throws ArrayIndexOutOfBoundsException if `i < 0` or `length <= i` + */ + def apply(i: Int): T = throw new Error() + + /** Update the element at given index. + * + * Indices start at `0`; `xs.update(i, x)` replaces the i^th^ element in the array. + * Note the syntax `xs(i) = x` is a shorthand for `xs.update(i, x)`. + * + * @param i the index + * @param x the value to be written at index `i` + * @throws ArrayIndexOutOfBoundsException if `i < 0` or `length <= i` + */ + def update(i: Int, x: T): Unit = { throw new Error() } + + /** Clone the Array. + * + * @return A clone of the Array. + */ + override def clone(): Array[T] = throw new Error() +} diff --git a/library-js/src/scala/Console.scala b/library-js/src/scala/Console.scala new file mode 100644 index 000000000000..443c838462a6 --- /dev/null +++ b/library-js/src/scala/Console.scala @@ -0,0 +1,284 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import java.io.{ BufferedReader, InputStream, InputStreamReader, OutputStream, PrintStream, Reader } +import scala.io.AnsiColor +import scala.util.DynamicVariable + +/** Implements functionality for printing Scala values on the terminal. For reading values + * use [[scala.io.StdIn$ StdIn]]. + * Also defines constants for marking up text on ANSI terminals. + * + * == Console Output == + * + * Use the print methods to output text. + * {{{ + * scala> Console.printf( + * "Today the outside temperature is a balmy %.1f°C. %<.1f°C beats the previous record of %.1f°C.\n", + * -137.0, + * -135.05) + * Today the outside temperature is a balmy -137.0°C. -137.0°C beats the previous record of -135.1°C. + * }}} + * + * == ANSI escape codes == + * Use the ANSI escape codes for colorizing console output either to STDOUT or STDERR. + * {{{ + * import Console.{GREEN, RED, RESET, YELLOW_B, UNDERLINED} + * + * object PrimeTest { + * + * def isPrime(): Unit = { + * + * val candidate = io.StdIn.readInt().ensuring(_ > 1) + * + * val prime = (2 to candidate - 1).forall(candidate % _ != 0) + * + * if (prime) + * Console.println(s"${RESET}${GREEN}yes${RESET}") + * else + * Console.err.println(s"${RESET}${YELLOW_B}${RED}${UNDERLINED}NO!${RESET}") + * } + * + * def main(args: Array[String]): Unit = isPrime() + * + * } + * }}} + * + * + * + * + * + * + * + * + *
$ scala PrimeTest
1234567891
yes
$ scala PrimeTest
56474
NO!
+ * + * == IO redefinition == + * + * Use IO redefinition to temporarily swap in a different set of input and/or output streams. In this example the stream based + * method above is wrapped into a function. + * + * {{{ + * import java.io.{ByteArrayOutputStream, StringReader} + * + * object FunctionalPrimeTest { + * + * def isPrime(candidate: Int): Boolean = { + * + * val input = new StringReader(s"$candidate\n") + * val outCapture = new ByteArrayOutputStream + * val errCapture = new ByteArrayOutputStream + * + * Console.withIn(input) { + * Console.withOut(outCapture) { + * Console.withErr(errCapture) { + * PrimeTest.isPrime() + * } + * } + * } + * + * if (outCapture.toByteArray.nonEmpty) // "yes" + * true + * else if (errCapture.toByteArray.nonEmpty) // "NO!" + * false + * else throw new IllegalArgumentException(candidate.toString) + * } + * + * def main(args: Array[String]): Unit = { + * val primes = (2 to 50) filter (isPrime) + * println(s"First primes: $primes") + * } + * + * } + * }}} + * + * + * + * + * + *
$ scala FunctionalPrimeTest
First primes: Vector(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47)
+ * + * @author Matthias Zenger + * @since 1.0 + * + * @groupname console-output Console Output + * @groupprio console-output 30 + * @groupdesc console-output These methods provide output via the console. + * + * @groupname io-default IO Defaults + * @groupprio io-default 50 + * @groupdesc io-default These values provide direct access to the standard IO channels + * + * @groupname io-redefinition IO Redefinition + * @groupprio io-redefinition 60 + * @groupdesc io-redefinition These methods allow substituting alternative streams for the duration of + * a body of code. Threadsafe by virtue of [[scala.util.DynamicVariable]]. + * + */ +object Console extends AnsiColor { + private[this] val outVar = new DynamicVariable[PrintStream](java.lang.System.out) + private[this] val errVar = new DynamicVariable[PrintStream](java.lang.System.err) + private[this] val inVar = new DynamicVariable[BufferedReader](null) + //new BufferedReader(new InputStreamReader(java.lang.System.in))) + + protected def setOutDirect(out: PrintStream): Unit = outVar.value = out + protected def setErrDirect(err: PrintStream): Unit = errVar.value = err + protected def setInDirect(in: BufferedReader): Unit = inVar.value = in + + /** The default output, can be overridden by `withOut` + * @group io-default + */ + def out = outVar.value + /** The default error, can be overridden by `withErr` + * @group io-default + */ + def err = errVar.value + /** The default input, can be overridden by `withIn` + * @group io-default + */ + def in = inVar.value + + /** Sets the default output stream for the duration + * of execution of one thunk. + * + * @example {{{ + * withOut(Console.err) { println("This goes to default _error_") } + * }}} + * + * @param out the new output stream. + * @param thunk the code to execute with + * the new output stream active + * @return the results of `thunk` + * @see `withOut[T](out:OutputStream)(thunk: => T)` + * @group io-redefinition + */ + def withOut[T](out: PrintStream)(thunk: =>T): T = + outVar.withValue(out)(thunk) + + /** Sets the default output stream for the duration + * of execution of one thunk. + * + * @param out the new output stream. + * @param thunk the code to execute with + * the new output stream active + * @return the results of `thunk` + * @see `withOut[T](out:PrintStream)(thunk: => T)` + * @group io-redefinition + */ + def withOut[T](out: OutputStream)(thunk: =>T): T = + withOut(new PrintStream(out))(thunk) + + /** Set the default error stream for the duration + * of execution of one thunk. + * @example {{{ + * withErr(Console.out) { err.println("This goes to default _out_") } + * }}} + * + * @param err the new error stream. + * @param thunk the code to execute with + * the new error stream active + * @return the results of `thunk` + * @see `withErr[T](err:OutputStream)(thunk: =>T)` + * @group io-redefinition + */ + def withErr[T](err: PrintStream)(thunk: =>T): T = + errVar.withValue(err)(thunk) + + /** Sets the default error stream for the duration + * of execution of one thunk. + * + * @param err the new error stream. + * @param thunk the code to execute with + * the new error stream active + * @return the results of `thunk` + * @see `withErr[T](err:PrintStream)(thunk: =>T)` + * @group io-redefinition + */ + def withErr[T](err: OutputStream)(thunk: =>T): T = + withErr(new PrintStream(err))(thunk) + + /** Sets the default input stream for the duration + * of execution of one thunk. + * + * @example {{{ + * val someFile:Reader = openFile("file.txt") + * withIn(someFile) { + * // Reads a line from file.txt instead of default input + * println(readLine) + * } + * }}} + * + * @param thunk the code to execute with + * the new input stream active + * + * @return the results of `thunk` + * @see `withIn[T](in:InputStream)(thunk: =>T)` + * @group io-redefinition + */ + def withIn[T](reader: Reader)(thunk: =>T): T = + inVar.withValue(new BufferedReader(reader))(thunk) + + /** Sets the default input stream for the duration + * of execution of one thunk. + * + * @param in the new input stream. + * @param thunk the code to execute with + * the new input stream active + * @return the results of `thunk` + * @see `withIn[T](reader:Reader)(thunk: =>T)` + * @group io-redefinition + */ + def withIn[T](in: InputStream)(thunk: =>T): T = + withIn(new InputStreamReader(in))(thunk) + + /** Prints an object to `out` using its `toString` method. + * + * @param obj the object to print; may be null. + * @group console-output + */ + def print(obj: Any): Unit = { + out.print(if (null == obj) "null" else obj.toString()) + } + + /** Flushes the output stream. This function is required when partial + * output (i.e. output not terminated by a newline character) has + * to be made visible on the terminal. + * @group console-output + */ + def flush(): Unit = { out.flush() } + + /** Prints a newline character on the default output. + * @group console-output + */ + def println(): Unit = { out.println() } + + /** Prints out an object to the default output, followed by a newline character. + * + * @param x the object to print. + * @group console-output + */ + def println(x: Any): Unit = { out.println(x) } + + /** Prints its arguments as a formatted string to the default output, + * based on a string pattern (in a fashion similar to printf in C). + * + * The interpretation of the formatting patterns is described in [[java.util.Formatter]]. + * + * @param text the pattern for formatting the arguments. + * @param args the arguments used to instantiating the pattern. + * @throws java.lang.IllegalArgumentException if there was a problem with the format string or arguments + * @group console-output + */ + def printf(text: String, args: Any*): Unit = { out.print(text format (args : _*)) } +} diff --git a/library-js/src/scala/Enumeration.scala b/library-js/src/scala/Enumeration.scala new file mode 100644 index 000000000000..d2d379245d96 --- /dev/null +++ b/library-js/src/scala/Enumeration.scala @@ -0,0 +1,330 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.collection.{SpecificIterableFactory, StrictOptimizedIterableOps, View, immutable, mutable} +import java.lang.reflect.{Field => JField, Method => JMethod} + +import scala.annotation.implicitNotFound +import scala.reflect.NameTransformer._ +import scala.util.matching.Regex + +/** Defines a finite set of values specific to the enumeration. Typically + * these values enumerate all possible forms something can take and provide + * a lightweight alternative to case classes. + * + * Each call to a `Value` method adds a new unique value to the enumeration. + * To be accessible, these values are usually defined as `val` members of + * the enumeration. + * + * All values in an enumeration share a common, unique type defined as the + * `Value` type member of the enumeration (`Value` selected on the stable + * identifier path of the enumeration instance). + * + * Values SHOULD NOT be added to an enumeration after its construction; + * doing so makes the enumeration thread-unsafe. If values are added to an + * enumeration from multiple threads (in a non-synchronized fashion) after + * construction, the behavior of the enumeration is undefined. + * + * @example {{{ + * // Define a new enumeration with a type alias and work with the full set of enumerated values + * object WeekDay extends Enumeration { + * type WeekDay = Value + * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * import WeekDay._ + * + * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) + * + * WeekDay.values filter isWorkingDay foreach println + * // output: + * // Mon + * // Tue + * // Wed + * // Thu + * // Fri + * }}} + * + * @example {{{ + * // Example of adding attributes to an enumeration by extending the Enumeration.Val class + * object Planet extends Enumeration { + * protected case class Val(mass: Double, radius: Double) extends super.Val { + * def surfaceGravity: Double = Planet.G * mass / (radius * radius) + * def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity + * } + * import scala.language.implicitConversions + * implicit def valueToPlanetVal(x: Value): Val = x.asInstanceOf[Val] + * + * val G: Double = 6.67300E-11 + * val Mercury = Val(3.303e+23, 2.4397e6) + * val Venus = Val(4.869e+24, 6.0518e6) + * val Earth = Val(5.976e+24, 6.37814e6) + * val Mars = Val(6.421e+23, 3.3972e6) + * val Jupiter = Val(1.9e+27, 7.1492e7) + * val Saturn = Val(5.688e+26, 6.0268e7) + * val Uranus = Val(8.686e+25, 2.5559e7) + * val Neptune = Val(1.024e+26, 2.4746e7) + * } + * + * println(Planet.values.filter(_.radius > 7.0e6)) + * // output: + * // Planet.ValueSet(Jupiter, Saturn, Uranus, Neptune) + * }}} + * + * @param initial The initial value from which to count the integers that + * identifies values at run-time. + * @author Matthias Zenger + */ +@SerialVersionUID(8476000850333817230L) +abstract class Enumeration (initial: Int) extends Serializable { + thisenum => + + def this() = this(0) + + /* Note that `readResolve` cannot be private, since otherwise + the JVM does not invoke it when deserializing subclasses. */ + protected def readResolve(): AnyRef = ??? + + /** The name of this enumeration. + */ + override def toString = + (getClass.getName.stripSuffix("$").split('.')).last.split('$').last + + /** The mapping from the integer used to identify values to the actual + * values. */ + private val vmap: mutable.Map[Int, Value] = new mutable.HashMap + + /** The cache listing all values of this enumeration. */ + @transient private var vset: ValueSet = null + @transient @volatile private var vsetDefined = false + + /** The mapping from the integer used to identify values to their + * names. */ + private[this] val nmap: mutable.Map[Int, String] = new mutable.HashMap + + /** The values of this enumeration as a set. + */ + def values: ValueSet = { + if (!vsetDefined) { + vset = (ValueSet.newBuilder ++= vmap.values).result() + vsetDefined = true + } + vset + } + + /** The integer to use to identify the next created value. */ + protected var nextId: Int = initial + + /** The string to use to name the next created value. */ + protected var nextName: Iterator[String] = _ + + private def nextNameOrNull = + if (nextName != null && nextName.hasNext) nextName.next() else null + + /** The highest integer amongst those used to identify values in this + * enumeration. */ + private[this] var topId = initial + + /** The lowest integer amongst those used to identify values in this + * enumeration, but no higher than 0. */ + private[this] var bottomId = if(initial < 0) initial else 0 + + /** The one higher than the highest integer amongst those used to identify + * values in this enumeration. */ + final def maxId = topId + + /** The value of this enumeration with given id `x` + */ + final def apply(x: Int): Value = vmap(x) + + /** Return a `Value` from this `Enumeration` whose name matches + * the argument `s`. The names are determined automatically via reflection. + * + * @param s an `Enumeration` name + * @return the `Value` of this `Enumeration` if its name matches `s` + * @throws NoSuchElementException if no `Value` with a matching + * name is in this `Enumeration` + */ + final def withName(s: String): Value = { + val (unnamed, named) = values partition { + _.toString().startsWith(" v + // If we have unnamed values, we issue a detailed error message + case None if unnamed.nonEmpty => + throw new NoSuchElementException( + s"""Couldn't find enum field with name $s. + |However, there were the following unnamed fields: + |${unnamed.mkString(" ","\n ","")}""".stripMargin) + // Normal case (no unnamed Values) + case _ => None.get + } + } + + /** Creates a fresh value, part of this enumeration. */ + protected final def Value: Value = Value(nextId) + + /** Creates a fresh value, part of this enumeration, identified by the + * integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @return Fresh value identified by `i`. + */ + protected final def Value(i: Int): Value = Value(i, nextNameOrNull) + + /** Creates a fresh value, part of this enumeration, called `name`. + * + * @param name A human-readable name for that value. + * @return Fresh value called `name`. + */ + protected final def Value(name: String): Value = Value(nextId, name) + + /** Creates a fresh value, part of this enumeration, called `name` + * and identified by the integer `i`. + * + * @param i An integer that identifies this value at run-time. It must be + * unique amongst all values of the enumeration. + * @param name A human-readable name for that value. + * @return Fresh value with the provided identifier `i` and name `name`. + */ + protected final def Value(i: Int, name: String): Value = new Val(i, name) + + /** The type of the enumerated values. */ + @SerialVersionUID(7091335633555234129L) + abstract class Value extends Ordered[Value] with Serializable { + /** the id and bit location of this enumeration value */ + def id: Int + /** a marker so we can tell whose values belong to whom come reflective-naming time */ + private[Enumeration] val outerEnum = thisenum + + override def compare(that: Value): Int = + if (this.id < that.id) -1 + else if (this.id == that.id) 0 + else 1 + override def equals(other: Any) = other match { + case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) + case _ => false + } + override def hashCode: Int = id.## + + /** Create a ValueSet which contains this value and another one */ + def + (v: Value) = ValueSet(this, v) + } + + /** A class implementing the [[scala.Enumeration.Value]] type. This class + * can be overridden to change the enumeration's naming and integer + * identification behaviour. + */ + @SerialVersionUID(0 - 3501153230598116017L) + protected class Val(i: Int, name: String) extends Value with Serializable { + def this(i: Int) = this(i, nextNameOrNull) + def this(name: String) = this(nextId, name) + def this() = this(nextId) + + assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) + vmap(i) = this + vsetDefined = false + nextId = i + 1 + if (nextId > topId) topId = nextId + if (i < bottomId) bottomId = i + def id = i + override def toString() = + if (name != null) name + // Scala.js specific + else s"" + + protected def readResolve(): AnyRef = { + val enumeration = thisenum.readResolve().asInstanceOf[Enumeration] + if (enumeration.vmap == null) this + else enumeration.vmap(i) + } + } + + /** An ordering by id for values of this set */ + implicit object ValueOrdering extends Ordering[Value] { + def compare(x: Value, y: Value): Int = x compare y + } + + /** A class for sets of values. + * Iterating through this set will yield values in increasing order of their ids. + * + * @param nnIds The set of ids of values (adjusted so that the lowest value does + * not fall below zero), organized as a `BitSet`. + * @define Coll `collection.immutable.SortedSet` + */ + class ValueSet private[ValueSet] (private[this] var nnIds: immutable.BitSet) + extends immutable.AbstractSet[Value] + with immutable.SortedSet[Value] + with immutable.SortedSetOps[Value, immutable.SortedSet, ValueSet] + with StrictOptimizedIterableOps[Value, immutable.Set, ValueSet] + with Serializable { + + implicit def ordering: Ordering[Value] = ValueOrdering + def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet = + new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId))) + + override def empty = ValueSet.empty + override def knownSize: Int = nnIds.size + override def isEmpty: Boolean = nnIds.isEmpty + def contains(v: Value) = nnIds contains (v.id - bottomId) + def incl (value: Value) = new ValueSet(nnIds + (value.id - bottomId)) + def excl (value: Value) = new ValueSet(nnIds - (value.id - bottomId)) + def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id)) + override def iteratorFrom(start: Value) = nnIds iteratorFrom start.id map (id => thisenum.apply(bottomId + id)) + override def className = s"$thisenum.ValueSet" + /** Creates a bit mask for the zero-adjusted ids in this set as a + * new array of longs */ + def toBitMask: Array[Long] = nnIds.toBitMask + + override protected def fromSpecific(coll: IterableOnce[Value]) = ValueSet.fromSpecific(coll) + override protected def newSpecificBuilder = ValueSet.newBuilder + + def map(f: Value => Value): ValueSet = fromSpecific(new View.Map(this, f)) + def flatMap(f: Value => IterableOnce[Value]): ValueSet = fromSpecific(new View.FlatMap(this, f)) + + // necessary for disambiguation: + override def map[B](f: Value => B)(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] = + super[SortedSet].map[B](f) + override def flatMap[B](f: Value => IterableOnce[B])(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] = + super[SortedSet].flatMap[B](f) + override def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(ValueSet.zipOrdMsg) ev: Ordering[(Value, B)]): immutable.SortedSet[(Value, B)] = + super[SortedSet].zip[B](that) + override def collect[B](pf: PartialFunction[Value, B])(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] = + super[SortedSet].collect[B](pf) + } + + /** A factory object for value sets */ + @SerialVersionUID(3L) + object ValueSet extends SpecificIterableFactory[Value, ValueSet] { + private final val ordMsg = "No implicit Ordering[${B}] found to build a SortedSet[${B}]. You may want to upcast to a Set[Value] first by calling `unsorted`." + private final val zipOrdMsg = "No implicit Ordering[${B}] found to build a SortedSet[(Value, ${B})]. You may want to upcast to a Set[Value] first by calling `unsorted`." + + /** The empty value set */ + val empty = new ValueSet(immutable.BitSet.empty) + /** A value set containing all the values for the zero-adjusted ids + * corresponding to the bits in an array */ + def fromBitMask(elems: Array[Long]): ValueSet = new ValueSet(immutable.BitSet.fromBitMask(elems)) + /** A builder object for value sets */ + def newBuilder: mutable.Builder[Value, ValueSet] = new mutable.Builder[Value, ValueSet] { + private[this] val b = new mutable.BitSet + def addOne (x: Value) = { b += (x.id - bottomId); this } + def clear() = b.clear() + def result() = new ValueSet(b.toImmutable) + } + def fromSpecific(it: IterableOnce[Value]): ValueSet = + newBuilder.addAll(it).result() + } +} diff --git a/library-js/src/scala/Symbol.scala b/library-js/src/scala/Symbol.scala new file mode 100644 index 000000000000..d656129ae2b3 --- /dev/null +++ b/library-js/src/scala/Symbol.scala @@ -0,0 +1,113 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala + +import scala.scalajs.js + +/** This class provides a simple way to get unique objects for equal strings. + * Since symbols are interned, they can be compared using reference equality. + * Instances of `Symbol` can be created easily with Scala's built-in quote + * mechanism. + * + * For instance, the Scala term `'mysym` will + * invoke the constructor of the `Symbol` class in the following way: + * `Symbol("mysym")`. + * + * @author Martin Odersky, Iulian Dragos + * @since 1.7 + */ +final class Symbol private (val name: String) extends Serializable { + /** Converts this symbol to a string. + */ + override def toString(): String = "Symbol(" + name + ")" + + @throws(classOf[java.io.ObjectStreamException]) + private def readResolve(): Any = Symbol.apply(name) + override def hashCode = name.hashCode() + override def equals(other: Any) = this eq other.asInstanceOf[AnyRef] +} + +// Modified to use Scala.js specific cache +object Symbol extends JSUniquenessCache[Symbol] { + override def apply(name: String): Symbol = super.apply(name) + protected def valueFromKey(name: String): Symbol = new Symbol(name) + protected def keyFromValue(sym: Symbol): Option[String] = Some(sym.name) +} + +private[scala] abstract class JSUniquenessCache[V] +{ + private val cache = js.Dictionary.empty[V] + + protected def valueFromKey(k: String): V + protected def keyFromValue(v: V): Option[String] + + def apply(name: String): V = + cache.getOrElseUpdate(name, valueFromKey(name)) + + def unapply(other: V): Option[String] = keyFromValue(other) +} + +/** This is private so it won't appear in the library API, but + * abstracted to offer some hope of reusability. */ +/* DELETED for Scala.js +private[scala] abstract class UniquenessCache[K >: js.String, V >: Null] +{ + + import java.lang.ref.WeakReference + import java.util.WeakHashMap + import java.util.concurrent.locks.ReentrantReadWriteLock + + private[this] val rwl = new ReentrantReadWriteLock() + private[this] val rlock = rwl.readLock + private[this] val wlock = rwl.writeLock + private[this] val map = new WeakHashMap[K, WeakReference[V]] + + protected def valueFromKey(k: K): V + protected def keyFromValue(v: V): Option[K] + + def apply(name: K): V = { + def cached(): V = { + rlock.lock + try { + val reference = map get name + if (reference == null) null + else reference.get // will be null if we were gc-ed + } + finally rlock.unlock + } + def updateCache(): V = { + wlock.lock + try { + val res = cached() + if (res != null) res + else { + // If we don't remove the old String key from the map, we can + // wind up with one String as the key and a different String as + // the name field in the Symbol, which can lead to surprising GC + // behavior and duplicate Symbols. See scala/bug#6706. + map remove name + val sym = valueFromKey(name) + map.put(name, new WeakReference(sym)) + sym + } + } + finally wlock.unlock + } + + val res = cached() + if (res == null) updateCache() + else res + } + def unapply(other: V): Option[K] = keyFromValue(other) +} +*/ diff --git a/library-js/src/scala/collection/immutable/NumericRange.scala b/library-js/src/scala/collection/immutable/NumericRange.scala new file mode 100644 index 000000000000..c12e85e5e349 --- /dev/null +++ b/library-js/src/scala/collection/immutable/NumericRange.scala @@ -0,0 +1,497 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection.immutable + +import scala.collection.Stepper.EfficientSplit +import scala.collection.{AbstractIterator, AnyStepper, IterableFactoryDefaults, Iterator, Stepper, StepperShape} + +/** `NumericRange` is a more generic version of the + * `Range` class which works with arbitrary types. + * It must be supplied with an `Integral` implementation of the + * range type. + * + * Factories for likely types include `Range.BigInt`, `Range.Long`, + * and `Range.BigDecimal`. `Range.Int` exists for completeness, but + * the `Int`-based `scala.Range` should be more performant. + * + * {{{ + * val r1 = Range(0, 100, 1) + * val veryBig = Int.MaxValue.toLong + 1 + * val r2 = Range.Long(veryBig, veryBig + 100, 1) + * assert(r1 sameElements r2.map(_ - veryBig)) + * }}} + * + * @define Coll `NumericRange` + * @define coll numeric range + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@SerialVersionUID(3L) +sealed class NumericRange[T]( + val start: T, + val end: T, + val step: T, + val isInclusive: Boolean +)(implicit + num: Integral[T] +) + extends AbstractSeq[T] + with IndexedSeq[T] + with IndexedSeqOps[T, IndexedSeq, IndexedSeq[T]] + with StrictOptimizedSeqOps[T, IndexedSeq, IndexedSeq[T]] + with IterableFactoryDefaults[T, IndexedSeq] + with Serializable { self => + + override def iterator: Iterator[T] = new NumericRange.NumericRangeIterator(this, num) + + override def stepper[S <: Stepper[_]](implicit shape: StepperShape[T, S]): S with EfficientSplit = { + import scala.collection.convert._ + import impl._ + val s = shape.shape match { + case StepperShape.IntShape => new IntNumericRangeStepper (this.asInstanceOf[NumericRange[Int]], 0, length) + case StepperShape.LongShape => new LongNumericRangeStepper (this.asInstanceOf[NumericRange[Long]], 0, length) + case _ => shape.parUnbox(new AnyNumericRangeStepper[T](this, 0, length).asInstanceOf[AnyStepper[T] with EfficientSplit]) + } + s.asInstanceOf[S with EfficientSplit] + } + + + /** Note that NumericRange must be invariant so that constructs + * such as "1L to 10 by 5" do not infer the range type as AnyVal. + */ + import num._ + + // See comment in Range for why this must be lazy. + override lazy val length: Int = NumericRange.count(start, end, step, isInclusive) + override lazy val isEmpty: Boolean = ( + (num.gt(start, end) && num.gt(step, num.zero)) + || (num.lt(start, end) && num.lt(step, num.zero)) + || (num.equiv(start, end) && !isInclusive) + ) + override def last: T = + if (isEmpty) Nil.head + else locationAfterN(length - 1) + override def init: NumericRange[T] = + if (isEmpty) Nil.init + else new NumericRange(start, end - step, step, isInclusive) + + override def head: T = if (isEmpty) Nil.head else start + override def tail: NumericRange[T] = + if (isEmpty) Nil.tail + else if(isInclusive) new NumericRange.Inclusive(start + step, end, step) + else new NumericRange.Exclusive(start + step, end, step) + + /** Create a new range with the start and end values of this range and + * a new `step`. + */ + def by(newStep: T): NumericRange[T] = copy(start, end, newStep) + + + /** Create a copy of this range. + */ + def copy(start: T, end: T, step: T): NumericRange[T] = + new NumericRange(start, end, step, isInclusive) + + @throws[IndexOutOfBoundsException] + def apply(idx: Int): T = { + if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException(s"$idx is out of bounds (min 0, max ${length - 1})") + else locationAfterN(idx) + } + + override def foreach[@specialized(Specializable.Unit) U](f: T => U): Unit = { + var count = 0 + var current = start + while (count < length) { + f(current) + current += step + count += 1 + } + } + + // TODO: these private methods are straight copies from Range, duplicated + // to guard against any (most likely illusory) performance drop. They should + // be eliminated one way or another. + + // Tests whether a number is within the endpoints, without testing + // whether it is a member of the sequence (i.e. when step > 1.) + private def isWithinBoundaries(elem: T) = !isEmpty && ( + (step > zero && start <= elem && elem <= last ) || + (step < zero && last <= elem && elem <= start) + ) + // Methods like apply throw exceptions on invalid n, but methods like take/drop + // are forgiving: therefore the checks are with the methods. + private def locationAfterN(n: Int): T = start + (step * fromInt(n)) + + private def crossesTheEndAfterN(n: Int): Boolean = { + // if we're sure that subtraction in the context of T won't overflow, we use this function + // to calculate the length of the range + def unsafeRangeLength(r: NumericRange[T]): T = { + val diff = num.minus(r.end, r.start) + val quotient = num.quot(diff, r.step) + val remainder = num.rem(diff, r.step) + if (!r.isInclusive && num.equiv(remainder, num.zero)) + num.max(quotient, num.zero) + else + num.max(num.plus(quotient, num.one), num.zero) + } + + // detects whether value can survive a bidirectional trip to -and then from- Int. + def fitsInInteger(value: T): Boolean = num.equiv(num.fromInt(num.toInt(value)), value) + + val stepIsInTheSameDirectionAsStartToEndVector = + (num.gt(end, start) && num.gt(step, num.zero)) || (num.lt(end, start) && num.sign(step) == -num.one) + + if (num.equiv(start, end) || n <= 0 || !stepIsInTheSameDirectionAsStartToEndVector) return n >= 1 + + val sameSign = num.equiv(num.sign(start), num.sign(end)) + + if (sameSign) { // subtraction is safe + val len = unsafeRangeLength(this) + if (fitsInInteger(len)) n >= num.toInt(len) else num.gteq(num.fromInt(n), len) + } else { + // split to two ranges, which subtraction is safe in both of them (around zero) + val stepsRemainderToZero = num.rem(start, step) + val walksOnZero = num.equiv(stepsRemainderToZero, num.zero) + val closestToZero = if (walksOnZero) -step else stepsRemainderToZero + + /* + When splitting into two ranges, we should be super-careful about one of the sides hitting MinValue of T, + so we take two steps smaller than zero to ensure unsafeRangeLength won't overflow (taking one step may overflow depending on the step). + Same thing happens for MaxValue from zero, so we take one step further to ensure the safety of unsafeRangeLength. + After performing such operation, there are some elements remaining in between and around zero, + which their length is represented by carry. + */ + val (l: NumericRange[T], r: NumericRange[T], carry: Int) = + if (num.lt(start, num.zero)) { + if (walksOnZero) { + val twoStepsAfterLargestNegativeNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (NumericRange(start, closestToZero, step), copy(twoStepsAfterLargestNegativeNumber, end, step), 2) + } else { + (NumericRange(start, closestToZero, step), copy(num.plus(closestToZero, step), end, step), 1) + } + } else { + if (walksOnZero) { + val twoStepsAfterZero = num.times(step, num.fromInt(2)) + (copy(twoStepsAfterZero, end, step), NumericRange.inclusive(start, -step, step), 2) + } else { + val twoStepsAfterSmallestPositiveNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (copy(twoStepsAfterSmallestPositiveNumber, end, step), NumericRange.inclusive(start, closestToZero, step), 2) + } + } + + val leftLength = unsafeRangeLength(l) + val rightLength = unsafeRangeLength(r) + + // instead of `n >= rightLength + leftLength + curry` which may cause addition overflow, + // this can be used `(n - leftLength - curry) >= rightLength` (Both in Int and T, depends on whether the lengths fit in Int) + if (fitsInInteger(leftLength) && fitsInInteger(rightLength)) + n - num.toInt(leftLength) - carry >= num.toInt(rightLength) + else + num.gteq(num.minus(num.minus(num.fromInt(n), leftLength), num.fromInt(carry)), rightLength) + } + } + + // When one drops everything. Can't ever have unchecked operations + // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } + // will overflow. This creates an exclusive range where start == end + // based on the given value. + private def newEmptyRange(value: T) = NumericRange(value, value, step) + + override def take(n: Int): NumericRange[T] = { + if (n <= 0 || isEmpty) newEmptyRange(start) + else if (crossesTheEndAfterN(n)) this + else new NumericRange.Inclusive(start, locationAfterN(n - 1), step) + } + + override def drop(n: Int): NumericRange[T] = { + if (n <= 0 || isEmpty) this + else if (crossesTheEndAfterN(n)) newEmptyRange(end) + else copy(locationAfterN(n), end, step) + } + + override def splitAt(n: Int): (NumericRange[T], NumericRange[T]) = (take(n), drop(n)) + + override def reverse: NumericRange[T] = + if (isEmpty) this + else { + val newStep = -step + if (num.sign(newStep) == num.sign(step)) { + throw new ArithmeticException("number type is unsigned, and .reverse requires a negative step") + } else new NumericRange.Inclusive(last, start, newStep) + } + + import NumericRange.defaultOrdering + + override def min[T1 >: T](implicit ord: Ordering[T1]): T = + // We can take the fast path: + // - If the Integral of this NumericRange is also the requested Ordering + // (Integral <: Ordering). This can happen for custom Integral types. + // - The Ordering is the default Ordering of a well-known Integral type. + if ((ord eq num) || defaultOrdering.get(num).exists(ord eq _)) { + if (num.sign(step) > zero) head + else last + } else super.min(ord) + + override def max[T1 >: T](implicit ord: Ordering[T1]): T = + // See comment for fast path in min(). + if ((ord eq num) || defaultOrdering.get(num).exists(ord eq _)) { + if (num.sign(step) > zero) last + else head + } else super.max(ord) + + // a well-typed contains method. + def containsTyped(x: T): Boolean = + isWithinBoundaries(x) && (((x - start) % step) == zero) + + override def contains[A1 >: T](x: A1): Boolean = + try containsTyped(x.asInstanceOf[T]) + catch { case _: ClassCastException => false } + + override def sum[B >: T](implicit num: Numeric[B]): B = { + if (isEmpty) num.zero + else if (size == 1) head + else { + // If there is no overflow, use arithmetic series formula + // a + ... (n terms total) ... + b = n*(a+b)/2 + if ((num eq scala.math.Numeric.IntIsIntegral)|| + (num eq scala.math.Numeric.ShortIsIntegral)|| + (num eq scala.math.Numeric.ByteIsIntegral)|| + (num eq scala.math.Numeric.CharIsIntegral)) { + // We can do math with no overflow in a Long--easy + val exact = (size * ((num toLong head) + (num toInt last))) / 2 + num fromInt exact.toInt + } + else if (num eq scala.math.Numeric.LongIsIntegral) { + // Uh-oh, might be overflow, so we have to divide before we overflow. + // Either numRangeElements or (head + last) must be even, so divide the even one before multiplying + val a = head.toLong + val b = last.toLong + val ans = + if ((size & 1) == 0) (size / 2) * (a + b) + else size * { + // Sum is even, but we might overflow it, so divide in pieces and add back remainder + val ha = a/2 + val hb = b/2 + ha + hb + ((a - 2*ha) + (b - 2*hb)) / 2 + } + ans.asInstanceOf[B] + } + else { + // User provided custom Numeric, so we cannot rely on arithmetic series formula (e.g. won't work on something like Z_6) + if (isEmpty) num.zero + else { + var acc = num.zero + var i = head + var idx = 0 + while(idx < length) { + acc = num.plus(acc, i) + i = i + step + idx = idx + 1 + } + acc + } + } + } + } + + override lazy val hashCode: Int = super.hashCode() + override protected final def applyPreferredMaxLength: Int = Int.MaxValue + + override def equals(other: Any): Boolean = other match { + case x: NumericRange[_] => + (x canEqual this) && (length == x.length) && ( + (isEmpty) || // all empty sequences are equal + (start == x.start && last == x.last) // same length and same endpoints implies equality + ) + case _ => + super.equals(other) + } + + override def toString: String = { + val empty = if (isEmpty) "empty " else "" + val preposition = if (isInclusive) "to" else "until" + val stepped = if (step == 1) "" else s" by $step" + s"${empty}NumericRange $start $preposition $end$stepped" + } + + override protected[this] def className = "NumericRange" +} + +/** A companion object for numeric ranges. + * @define Coll `NumericRange` + * @define coll numeric range + */ +object NumericRange { + private def bigDecimalCheckUnderflow[T](start: T, end: T, step: T)(implicit num: Integral[T]): Unit = { + def FAIL(boundary: T, step: T): Unit = { + val msg = boundary match { + case bd: BigDecimal => s"Precision ${bd.mc.getPrecision}" + case _ => "Precision" + } + throw new IllegalArgumentException( + s"$msg inadequate to represent steps of size $step near $boundary" + ) + } + if (num.minus(num.plus(start, step), start) != step) FAIL(start, step) + if (num.minus(end, num.minus(end, step)) != step) FAIL(end, step) + } + + /** Calculates the number of elements in a range given start, end, step, and + * whether or not it is inclusive. Throws an exception if step == 0 or + * the number of elements exceeds the maximum Int. + */ + def count[T](start: T, end: T, step: T, isInclusive: Boolean)(implicit num: Integral[T]): Int = { + val zero = num.zero + val upward = num.lt(start, end) + val posStep = num.gt(step, zero) + + if (step == zero) throw new IllegalArgumentException("step cannot be 0.") + else if (start == end) if (isInclusive) 1 else 0 + else if (upward != posStep) 0 + else { + /* We have to be frightfully paranoid about running out of range. + * We also can't assume that the numbers will fit in a Long. + * We will assume that if a > 0, -a can be represented, and if + * a < 0, -a+1 can be represented. We also assume that if we + * can't fit in Int, we can represent 2*Int.MaxValue+3 (at least). + * And we assume that numbers wrap rather than cap when they overflow. + */ + // Check whether we can short-circuit by deferring to Int range. + val startint = num.toInt(start) + if (start == num.fromInt(startint)) { + val endint = num.toInt(end) + if (end == num.fromInt(endint)) { + val stepint = num.toInt(step) + if (step == num.fromInt(stepint)) { + return { + if (isInclusive) Range.inclusive(startint, endint, stepint).length + else Range (startint, endint, stepint).length + } + } + } + } + // If we reach this point, deferring to Int failed. + // Numbers may be big. + if (num.isInstanceOf[Numeric.BigDecimalAsIfIntegral]) { + bigDecimalCheckUnderflow(start, end, step) // Throw exception if math is inaccurate (including no progress at all) + } + val one = num.one + val limit = num.fromInt(Int.MaxValue) + def check(t: T): T = + if (num.gt(t, limit)) throw new IllegalArgumentException("More than Int.MaxValue elements.") + else t + // If the range crosses zero, it might overflow when subtracted + val startside = num.sign(start) + val endside = num.sign(end) + num.toInt{ + if (num.gteq(num.times(startside, endside), zero)) { + // We're sure we can subtract these numbers. + // Note that we do not use .rem because of different conventions for Long and BigInt + val diff = num.minus(end, start) + val quotient = check(num.quot(diff, step)) + val remainder = num.minus(diff, num.times(quotient, step)) + if (!isInclusive && zero == remainder) quotient else check(num.plus(quotient, one)) + } + else { + // We might not even be able to subtract these numbers. + // Jump in three pieces: + // * start to -1 or 1, whichever is closer (waypointA) + // * one step, which will take us at least to 0 (ends at waypointB) + // * (except with really small numbers) + // * there to the end + val negone = num.fromInt(-1) + val startlim = if (posStep) negone else one + //Use start value if the start value is closer to zero than startlim + // * e.g. .5 is closer to zero than 1 and -.5 is closer to zero than -1 + val startdiff = { + if ((posStep && num.lt(startlim, start)) || (!posStep && num.gt(startlim, start))) + start + else + num.minus(startlim, start) + } + val startq = check(num.quot(startdiff, step)) + val waypointA = if (startq == zero) start else num.plus(start, num.times(startq, step)) + val waypointB = num.plus(waypointA, step) + check { + if (num.lt(waypointB, end) != upward) { + // No last piece + if (isInclusive && waypointB == end) num.plus(startq, num.fromInt(2)) + else num.plus(startq, one) + } + else { + // There is a last piece + val enddiff = num.minus(end,waypointB) + val endq = check(num.quot(enddiff, step)) + val last = if (endq == zero) waypointB else num.plus(waypointB, num.times(endq, step)) + // Now we have to tally up all the pieces + // 1 for the initial value + // startq steps to waypointA + // 1 step to waypointB + // endq steps to the end (one less if !isInclusive and last==end) + num.plus(startq, num.plus(endq, if (!isInclusive && last==end) one else num.fromInt(2))) + } + } + } + } + } + } + + @SerialVersionUID(3L) + class Inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) + extends NumericRange(start, end, step, true) { + override def copy(start: T, end: T, step: T): Inclusive[T] = + NumericRange.inclusive(start, end, step) + + def exclusive: Exclusive[T] = NumericRange(start, end, step) + } + + @SerialVersionUID(3L) + class Exclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) + extends NumericRange(start, end, step, false) { + override def copy(start: T, end: T, step: T): Exclusive[T] = + NumericRange(start, end, step) + + def inclusive: Inclusive[T] = NumericRange.inclusive(start, end, step) + } + + def apply[T](start: T, end: T, step: T)(implicit num: Integral[T]): Exclusive[T] = + new Exclusive(start, end, step) + def inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]): Inclusive[T] = + new Inclusive(start, end, step) + + private[collection] val defaultOrdering = Map[Numeric[_], Ordering[_]]( + Numeric.IntIsIntegral -> Ordering.Int, + Numeric.ShortIsIntegral -> Ordering.Short, + Numeric.ByteIsIntegral -> Ordering.Byte, + Numeric.CharIsIntegral -> Ordering.Char, + Numeric.LongIsIntegral -> Ordering.Long + ) + + @SerialVersionUID(3L) + private final class NumericRangeIterator[T](self: NumericRange[T], num: Integral[T]) extends AbstractIterator[T] with Serializable { + import num.mkNumericOps + + private[this] var _hasNext = !self.isEmpty + private[this] var _next: T = self.start + private[this] val lastElement: T = if (_hasNext) self.last else self.start + override def knownSize: Int = if (_hasNext) num.toInt((lastElement - _next) / self.step) + 1 else 0 + def hasNext: Boolean = _hasNext + def next(): T = { + if (!_hasNext) Iterator.empty.next() + val value = _next + _hasNext = value != lastElement + _next = num.plus(value, self.step) + value + } + } +} diff --git a/library-js/src/scala/collection/immutable/Range.scala b/library-js/src/scala/collection/immutable/Range.scala new file mode 100644 index 000000000000..310c45c07918 --- /dev/null +++ b/library-js/src/scala/collection/immutable/Range.scala @@ -0,0 +1,680 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package collection.immutable + +import scala.collection.Stepper.EfficientSplit +import scala.collection.convert.impl.RangeStepper +import scala.collection.{AbstractIterator, AnyStepper, IterableFactoryDefaults, Iterator, Stepper, StepperShape} +import scala.util.hashing.MurmurHash3 + +/** The `Range` class represents integer values in range + * ''[start;end)'' with non-zero step value `step`. + * It's a special case of an indexed sequence. + * For example: + * + * {{{ + * val r1 = 0 until 10 + * val r2 = r1.start until r1.end by r1.step + 1 + * println(r2.length) // = 5 + * }}} + * + * Ranges that contain more than `Int.MaxValue` elements can be created, but + * these overfull ranges have only limited capabilities. Any method that + * could require a collection of over `Int.MaxValue` length to be created, or + * could be asked to index beyond `Int.MaxValue` elements will throw an + * exception. Overfull ranges can safely be reduced in size by changing + * the step size (e.g. `by 3`) or taking/dropping elements. `contains`, + * `equals`, and access to the ends of the range (`head`, `last`, `tail`, + * `init`) are also permitted on overfull ranges. + * + * @param start the start of this range. + * @param end the end of the range. For exclusive ranges, e.g. + * `Range(0,3)` or `(0 until 3)`, this is one + * step past the last one in the range. For inclusive + * ranges, e.g. `Range.inclusive(0,3)` or `(0 to 3)`, + * it may be in the range if it is not skipped by the step size. + * To find the last element inside a non-empty range, + * use `last` instead. + * @param step the step for the range. + * + * @define coll range + * @define mayNotTerminateInf + * @define willNotTerminateInf + * @define doesNotUseBuilders + * '''Note:''' this method does not use builders to construct a new range, + * and its complexity is O(1). + */ +@SerialVersionUID(3L) +sealed abstract class Range( + val start: Int, + val end: Int, + val step: Int +) + extends AbstractSeq[Int] + with IndexedSeq[Int] + with IndexedSeqOps[Int, IndexedSeq, IndexedSeq[Int]] + with StrictOptimizedSeqOps[Int, IndexedSeq, IndexedSeq[Int]] + with IterableFactoryDefaults[Int, IndexedSeq] + with Serializable { range => + + final override def iterator: Iterator[Int] = new RangeIterator(start, step, lastElement, isEmpty) + + override final def stepper[S <: Stepper[_]](implicit shape: StepperShape[Int, S]): S with EfficientSplit = { + val st = new RangeStepper(start, step, 0, length) + val r = + if (shape.shape == StepperShape.IntShape) st + else { + assert(shape.shape == StepperShape.ReferenceShape, s"unexpected StepperShape: $shape") + AnyStepper.ofParIntStepper(st) + } + r.asInstanceOf[S with EfficientSplit] + } + + private[this] def gap = end.toLong - start.toLong + private[this] def isExact = gap % step == 0 + private[this] def hasStub = isInclusive || !isExact + private[this] def longLength = gap / step + ( if (hasStub) 1 else 0 ) + + def isInclusive: Boolean + + final override val isEmpty: Boolean = ( + (start > end && step > 0) + || (start < end && step < 0) + || (start == end && !isInclusive) + ) + + private[this] val numRangeElements: Int = { + if (step == 0) throw new IllegalArgumentException("step cannot be 0.") + else if (isEmpty) 0 + else { + val len = longLength + if (len > scala.Int.MaxValue) -1 + else len.toInt + } + } + + final def length = if (numRangeElements < 0) fail() else numRangeElements + + // This field has a sensible value only for non-empty ranges + private[this] val lastElement = step match { + case 1 => if (isInclusive) end else end-1 + case -1 => if (isInclusive) end else end+1 + case _ => + val remainder = (gap % step).toInt + if (remainder != 0) end - remainder + else if (isInclusive) end + else end - step + } + + /** The last element of this range. This method will return the correct value + * even if there are too many elements to iterate over. + */ + final override def last: Int = + if (isEmpty) throw Range.emptyRangeError("last") else lastElement + final override def head: Int = + if (isEmpty) throw Range.emptyRangeError("head") else start + + /** Creates a new range containing all the elements of this range except the last one. + * + * $doesNotUseBuilders + * + * @return a new range consisting of all the elements of this range except the last one. + */ + final override def init: Range = + if (isEmpty) throw Range.emptyRangeError("init") else dropRight(1) + + /** Creates a new range containing all the elements of this range except the first one. + * + * $doesNotUseBuilders + * + * @return a new range consisting of all the elements of this range except the first one. + */ + final override def tail: Range = { + if (isEmpty) throw Range.emptyRangeError("tail") + if (numRangeElements == 1) newEmptyRange(end) + else if(isInclusive) new Range.Inclusive(start + step, end, step) + else new Range.Exclusive(start + step, end, step) + } + + override def map[B](f: Int => B): IndexedSeq[B] = { + validateMaxLength() + super.map(f) + } + + final protected def copy(start: Int = start, end: Int = end, step: Int = step, isInclusive: Boolean = isInclusive): Range = + if(isInclusive) new Range.Inclusive(start, end, step) else new Range.Exclusive(start, end, step) + + /** Create a new range with the `start` and `end` values of this range and + * a new `step`. + * + * @return a new range with a different step + */ + final def by(step: Int): Range = copy(start, end, step) + + // Check cannot be evaluated eagerly because we have a pattern where + // ranges are constructed like: "x to y by z" The "x to y" piece + // should not trigger an exception. So the calculation is delayed, + // which means it will not fail fast for those cases where failing was + // correct. + private[this] def validateMaxLength(): Unit = { + if (numRangeElements < 0) + fail() + } + private[this] def fail() = Range.fail(start, end, step, isInclusive) + + @throws[IndexOutOfBoundsException] + final def apply(idx: Int): Int = { + validateMaxLength() + if (idx < 0 || idx >= numRangeElements) throw new IndexOutOfBoundsException(s"$idx is out of bounds (min 0, max ${numRangeElements-1})") + else start + (step * idx) + } + + /*@`inline`*/ final override def foreach[@specialized(Unit) U](f: Int => U): Unit = { + // Implementation chosen on the basis of favorable microbenchmarks + // Note--initialization catches step == 0 so we don't need to here + if (!isEmpty) { + var i = start + while (true) { + f(i) + if (i == lastElement) return + i += step + } + } + } + + override final def indexOf[@specialized(Int) B >: Int](elem: B, from: Int = 0): Int = + elem match { + case i: Int => + val pos = posOf(i) + if (pos >= from) pos else -1 + case _ => super.indexOf(elem, from) + } + + override final def lastIndexOf[@specialized(Int) B >: Int](elem: B, end: Int = length - 1): Int = + elem match { + case i: Int => + val pos = posOf(i) + if (pos <= end) pos else -1 + case _ => super.lastIndexOf(elem, end) + } + + private[this] def posOf(i: Int): Int = + if (contains(i)) (i - start) / step else -1 + + override def sameElements[B >: Int](that: IterableOnce[B]): Boolean = that match { + case other: Range => + (this.length : @annotation.switch) match { + case 0 => other.isEmpty + case 1 => other.length == 1 && this.start == other.start + case n => other.length == n && ( + (this.start == other.start) + && (this.step == other.step) + ) + } + case _ => super.sameElements(that) + } + + /** Creates a new range containing the first `n` elements of this range. + * + * @param n the number of elements to take. + * @return a new range consisting of `n` first elements. + */ + final override def take(n: Int): Range = + if (n <= 0 || isEmpty) newEmptyRange(start) + else if (n >= numRangeElements && numRangeElements >= 0) this + else { + // May have more than Int.MaxValue elements in range (numRangeElements < 0) + // but the logic is the same either way: take the first n + new Range.Inclusive(start, locationAfterN(n - 1), step) + } + + /** Creates a new range containing all the elements of this range except the first `n` elements. + * + * @param n the number of elements to drop. + * @return a new range consisting of all the elements of this range except `n` first elements. + */ + final override def drop(n: Int): Range = + if (n <= 0 || isEmpty) this + else if (n >= numRangeElements && numRangeElements >= 0) newEmptyRange(end) + else { + // May have more than Int.MaxValue elements (numRangeElements < 0) + // but the logic is the same either way: go forwards n steps, keep the rest + copy(locationAfterN(n), end, step) + } + + /** Creates a new range consisting of the last `n` elements of the range. + * + * $doesNotUseBuilders + */ + final override def takeRight(n: Int): Range = { + if (n <= 0) newEmptyRange(start) + else if (numRangeElements >= 0) drop(numRangeElements - n) + else { + // Need to handle over-full range separately + val y = last + val x = y - step.toLong*(n-1) + if ((step > 0 && x < start) || (step < 0 && x > start)) this + else Range.inclusive(x.toInt, y, step) + } + } + + /** Creates a new range consisting of the initial `length - n` elements of the range. + * + * $doesNotUseBuilders + */ + final override def dropRight(n: Int): Range = { + if (n <= 0) this + else if (numRangeElements >= 0) take(numRangeElements - n) + else { + // Need to handle over-full range separately + val y = last - step.toInt*n + if ((step > 0 && y < start) || (step < 0 && y > start)) newEmptyRange(start) + else Range.inclusive(start, y.toInt, step) + } + } + + // Advance from the start while we meet the given test + private[this] def argTakeWhile(p: Int => Boolean): Long = { + if (isEmpty) start + else { + var current = start + val stop = last + while (current != stop && p(current)) current += step + if (current != stop || !p(current)) current + else current.toLong + step + } + } + + final override def takeWhile(p: Int => Boolean): Range = { + val stop = argTakeWhile(p) + if (stop==start) newEmptyRange(start) + else { + val x = (stop - step).toInt + if (x == last) this + else Range.inclusive(start, x, step) + } + } + + final override def dropWhile(p: Int => Boolean): Range = { + val stop = argTakeWhile(p) + if (stop == start) this + else { + val x = (stop - step).toInt + if (x == last) newEmptyRange(last) + else Range.inclusive(x + step, last, step) + } + } + + final override def span(p: Int => Boolean): (Range, Range) = { + val border = argTakeWhile(p) + if (border == start) (newEmptyRange(start), this) + else { + val x = (border - step).toInt + if (x == last) (this, newEmptyRange(last)) + else (Range.inclusive(start, x, step), Range.inclusive(x+step, last, step)) + } + } + + /** Creates a new range containing the elements starting at `from` up to but not including `until`. + * + * $doesNotUseBuilders + * + * @param from the element at which to start + * @param until the element at which to end (not included in the range) + * @return a new range consisting of a contiguous interval of values in the old range + */ + final override def slice(from: Int, until: Int): Range = + if (from <= 0) take(until) + else if (until >= numRangeElements && numRangeElements >= 0) drop(from) + else { + val fromValue = locationAfterN(from) + if (from >= until) newEmptyRange(fromValue) + else Range.inclusive(fromValue, locationAfterN(until-1), step) + } + + // Overridden only to refine the return type + final override def splitAt(n: Int): (Range, Range) = (take(n), drop(n)) + + // Methods like apply throw exceptions on invalid n, but methods like take/drop + // are forgiving: therefore the checks are with the methods. + private[this] def locationAfterN(n: Int) = start + (step * n) + + // When one drops everything. Can't ever have unchecked operations + // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } + // will overflow. This creates an exclusive range where start == end + // based on the given value. + private[this] def newEmptyRange(value: Int) = new Range.Exclusive(value, value, step) + + /** Returns the reverse of this range. + */ + final override def reverse: Range = + if (isEmpty) this + else new Range.Inclusive(last, start, -step) + + /** Make range inclusive. + */ + final def inclusive: Range = + if (isInclusive) this + else new Range.Inclusive(start, end, step) + + final def contains(x: Int) = { + if (x == end && !isInclusive) false + else if (step > 0) { + if (x < start || x > end) false + else (step == 1) || (Integer.remainderUnsigned(x - start, step) == 0) + } + else { + if (x < end || x > start) false + else (step == -1) || (Integer.remainderUnsigned(start - x, -step) == 0) + } + } + /* Seq#contains has a type parameter so the optimised contains above doesn't override it */ + override final def contains[B >: Int](elem: B): Boolean = elem match { + case i: Int => this.contains(i) + case _ => super.contains(elem) + } + + final override def sum[B >: Int](implicit num: Numeric[B]): Int = { + if (num eq scala.math.Numeric.IntIsIntegral) { + // this is normal integer range with usual addition. arithmetic series formula can be used + if (isEmpty) 0 + else if (size == 1) head + else ((size * (head.toLong + last)) / 2).toInt + } else { + // user provided custom Numeric, we cannot rely on arithmetic series formula + if (isEmpty) num.toInt(num.zero) + else { + var acc = num.zero + var i = head + while (true) { + acc = num.plus(acc, i) + if (i == lastElement) return num.toInt(acc) + i = i + step + } + 0 // Never hit this--just to satisfy compiler since it doesn't know while(true) has type Nothing + } + } + } + + final override def min[A1 >: Int](implicit ord: Ordering[A1]): Int = + if (ord eq Ordering.Int) { + if (step > 0) head + else last + } else if (Ordering.Int isReverseOf ord) { + if (step > 0) last + else head + } else super.min(ord) + + final override def max[A1 >: Int](implicit ord: Ordering[A1]): Int = + if (ord eq Ordering.Int) { + if (step > 0) last + else head + } else if (Ordering.Int isReverseOf ord) { + if (step > 0) head + else last + } else super.max(ord) + + override def tails: Iterator[Range] = + new AbstractIterator[Range] { + private[this] var i = 0 + override def hasNext = i <= Range.this.length + override def next() = { + if (hasNext) { + val res = Range.this.drop(i) + i += 1 + res + } else { + Iterator.empty.next() + } + } + } + + override def inits: Iterator[Range] = + new AbstractIterator[Range] { + private[this] var i = 0 + override def hasNext = i <= Range.this.length + override def next() = { + if (hasNext) { + val res = Range.this.dropRight(i) + i += 1 + res + } else { + Iterator.empty.next() + } + } + } + override protected final def applyPreferredMaxLength: Int = Int.MaxValue + + final override def equals(other: Any) = other match { + case x: Range => + // Note: this must succeed for overfull ranges (length > Int.MaxValue) + if (isEmpty) x.isEmpty // empty sequences are equal + else // this is non-empty... + x.nonEmpty && start == x.start && { // ...so other must contain something and have same start + val l0 = last + (l0 == x.last && ( // And same end + start == l0 || step == x.step // And either the same step, or not take any steps + )) + } + case _ => + super.equals(other) + } + + final override def hashCode: Int = + if(length >= 2) MurmurHash3.rangeHash(start, step, lastElement) + else super.hashCode + + final override def toString: String = { + val preposition = if (isInclusive) "to" else "until" + val stepped = if (step == 1) "" else s" by $step" + val prefix = if (isEmpty) "empty " else if (!isExact) "inexact " else "" + s"${prefix}Range $start $preposition $end$stepped" + } + + override protected[this] def className = "Range" + + override def distinct: Range = this + + override def grouped(size: Int): Iterator[Range] = { + require(size >= 1, f"size=$size%d, but size must be positive") + if (isEmpty) { + Iterator.empty + } else { + val s = size + new AbstractIterator[Range] { + private[this] var i = 0 + override def hasNext = Range.this.length > i + override def next() = + if (hasNext) { + val x = Range.this.slice(i, i + s) + i += s + x + } else { + Iterator.empty.next() + } + } + } + } + + override def sorted[B >: Int](implicit ord: Ordering[B]): IndexedSeq[Int] = + if (ord eq Ordering.Int) { + if (step > 0) { + this + } else { + reverse + } + } else { + super.sorted(ord) + } +} + +/** + * Companion object for ranges. + * @define Coll `Range` + * @define coll range + */ +object Range { + + private def description(start: Int, end: Int, step: Int, isInclusive: Boolean) = + start + (if (isInclusive) " to " else " until ") + end + " by " + step + + private def fail(start: Int, end: Int, step: Int, isInclusive: Boolean) = + throw new IllegalArgumentException(description(start, end, step, isInclusive) + + ": seqs cannot contain more than Int.MaxValue elements.") + + /** Counts the number of range elements. + * precondition: step != 0 + * If the size of the range exceeds Int.MaxValue, the + * result will be negative. + */ + def count(start: Int, end: Int, step: Int, isInclusive: Boolean): Int = { + if (step == 0) + throw new IllegalArgumentException("step cannot be 0.") + + val isEmpty = + if (start == end) !isInclusive + else if (start < end) step < 0 + else step > 0 + + if (isEmpty) 0 + else { + // Counts with Longs so we can recognize too-large ranges. + val gap: Long = end.toLong - start.toLong + val jumps: Long = gap / step + // Whether the size of this range is one larger than the + // number of full-sized jumps. + val hasStub = isInclusive || (gap % step != 0) + val result: Long = jumps + ( if (hasStub) 1 else 0 ) + + if (result > scala.Int.MaxValue) -1 + else result.toInt + } + } + def count(start: Int, end: Int, step: Int): Int = + count(start, end, step, isInclusive = false) + + /** Make a range from `start` until `end` (exclusive) with given step value. + * @note step != 0 + */ + def apply(start: Int, end: Int, step: Int): Range.Exclusive = new Range.Exclusive(start, end, step) + + /** Make a range from `start` until `end` (exclusive) with step value 1. + */ + def apply(start: Int, end: Int): Range.Exclusive = new Range.Exclusive(start, end, 1) + + /** Make an inclusive range from `start` to `end` with given step value. + * @note step != 0 + */ + def inclusive(start: Int, end: Int, step: Int): Range.Inclusive = new Range.Inclusive(start, end, step) + + /** Make an inclusive range from `start` to `end` with step value 1. + */ + def inclusive(start: Int, end: Int): Range.Inclusive = new Range.Inclusive(start, end, 1) + + @SerialVersionUID(3L) + @inline + final class Inclusive(start: Int, end: Int, step: Int) extends Range(start, end, step) { + def isInclusive = true + } + + @SerialVersionUID(3L) + @inline + final class Exclusive(start: Int, end: Int, step: Int) extends Range(start, end, step) { + def isInclusive = false + } + + // BigInt and Long are straightforward generic ranges. + object BigInt { + def apply(start: BigInt, end: BigInt, step: BigInt) = NumericRange(start, end, step) + def inclusive(start: BigInt, end: BigInt, step: BigInt) = NumericRange.inclusive(start, end, step) + } + + object Long { + def apply(start: Long, end: Long, step: Long) = NumericRange(start, end, step) + def inclusive(start: Long, end: Long, step: Long) = NumericRange.inclusive(start, end, step) + } + + // BigDecimal uses an alternative implementation of Numeric in which + // it pretends to be Integral[T] instead of Fractional[T]. See Numeric for + // details. The intention is for it to throw an exception anytime + // imprecision or surprises might result from anything, although this may + // not yet be fully implemented. + object BigDecimal { + implicit val bigDecAsIntegral: Numeric.BigDecimalAsIfIntegral = Numeric.BigDecimalAsIfIntegral + + def apply(start: BigDecimal, end: BigDecimal, step: BigDecimal) = + NumericRange(start, end, step) + def inclusive(start: BigDecimal, end: BigDecimal, step: BigDecimal) = + NumericRange.inclusive(start, end, step) + } + + // As there is no appealing default step size for not-really-integral ranges, + // we offer a partially constructed object. + class Partial[T, U](private val f: T => U) extends AnyVal { + def by(x: T): U = f(x) + override def toString = "Range requires step" + } + + // Illustrating genericity with Int Range, which should have the same behavior + // as the original Range class. However we leave the original Range + // indefinitely, for performance and because the compiler seems to bootstrap + // off it and won't do so with our parameterized version without modifications. + object Int { + def apply(start: Int, end: Int, step: Int) = NumericRange(start, end, step) + def inclusive(start: Int, end: Int, step: Int) = NumericRange.inclusive(start, end, step) + } + + private def emptyRangeError(what: String): Throwable = + new NoSuchElementException(what + " on empty Range") +} + +/** + * @param lastElement The last element included in the Range + * @param initiallyEmpty Whether the Range was initially empty or not + */ +@SerialVersionUID(3L) +private class RangeIterator( + start: Int, + step: Int, + lastElement: Int, + initiallyEmpty: Boolean +) extends AbstractIterator[Int] with Serializable { + private[this] var _hasNext: Boolean = !initiallyEmpty + private[this] var _next: Int = start + override def knownSize: Int = if (_hasNext) (lastElement - _next) / step + 1 else 0 + def hasNext: Boolean = _hasNext + @throws[NoSuchElementException] + def next(): Int = { + if (!_hasNext) Iterator.empty.next() + val value = _next + _hasNext = value != lastElement + _next = value + step + value + } + + override def drop(n: Int): Iterator[Int] = { + if (n > 0) { + val longPos = _next.toLong + step * n + if (step > 0) { + _next = Math.min(lastElement, longPos).toInt + _hasNext = longPos <= lastElement + } + else if (step < 0) { + _next = Math.max(lastElement, longPos).toInt + _hasNext = longPos >= lastElement + } + } + this + } +} diff --git a/library-js/src/scala/collection/mutable/ArrayBuilder.scala b/library-js/src/scala/collection/mutable/ArrayBuilder.scala new file mode 100644 index 000000000000..531a6438a2f1 --- /dev/null +++ b/library-js/src/scala/collection/mutable/ArrayBuilder.scala @@ -0,0 +1,646 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.reflect.ClassTag +import scala.runtime.BoxedUnit + +import scala.scalajs.js +import scala.scalajs.LinkingInfo + +/** A builder class for arrays. + * + * @since 2.8 + * + * @tparam T the type of the elements for the builder. + */ +@SerialVersionUID(3L) +sealed abstract class ArrayBuilder[T] + extends ReusableBuilder[T, Array[T]] + with Serializable { + protected[this] var capacity: Int = 0 + protected[this] def elems: Array[T] + protected var size: Int = 0 + + def length: Int = size + + override def knownSize: Int = size + + protected[this] final def ensureSize(size: Int): Unit = { + if (capacity < size || capacity == 0) { + var newsize = if (capacity == 0) 16 else capacity * 2 + while (newsize < size) newsize *= 2 + resize(newsize) + } + } + + override final def sizeHint(size: Int): Unit = + if (capacity < size) resize(size) + + def clear(): Unit = size = 0 + + protected[this] def resize(size: Int): Unit + + /** Add all elements of an array */ + def addAll(xs: Array[_ <: T]): this.type = addAll(xs, 0, xs.length) + + /** Add a slice of an array */ + def addAll(xs: Array[_ <: T], offset: Int, length: Int): this.type = { + ensureSize(this.size + length) + Array.copy(xs, offset, elems, this.size, length) + size += length + this + } + + override def addAll(xs: IterableOnce[T]): this.type = { + val k = xs.knownSize + if(k > 0) { + ensureSize(this.size + k) + xs match { + case xs: Iterable[T] => xs.copyToArray(elems, this.size) + case _ => xs.iterator.copyToArray(elems, this.size) + } + size += k + } else if(k < 0) super.addAll(xs) + this + } +} + +/** A companion object for array builders. + * + * @since 2.8 + */ +object ArrayBuilder { + + /** Creates a new arraybuilder of type `T`. + * + * @tparam T type of the elements for the array builder, with a `ClassTag` context bound. + * @return a new empty array builder. + */ + @inline + def make[T: ClassTag]: ArrayBuilder[T] = + if (LinkingInfo.isWebAssembly) makeForWasm + else makeForJS + + /** Implementation of `make` for JS. */ + @inline + private def makeForJS[T: ClassTag]: ArrayBuilder[T] = + new ArrayBuilder.generic[T](implicitly[ClassTag[T]].runtimeClass) + + /** Implementation of `make` for Wasm. + * + * This is the original upstream implementation. + */ + @inline + private def makeForWasm[T: ClassTag]: ArrayBuilder[T] = { + val tag = implicitly[ClassTag[T]] + tag.runtimeClass match { + case java.lang.Byte.TYPE => new ArrayBuilder.ofByte().asInstanceOf[ArrayBuilder[T]] + case java.lang.Short.TYPE => new ArrayBuilder.ofShort().asInstanceOf[ArrayBuilder[T]] + case java.lang.Character.TYPE => new ArrayBuilder.ofChar().asInstanceOf[ArrayBuilder[T]] + case java.lang.Integer.TYPE => new ArrayBuilder.ofInt().asInstanceOf[ArrayBuilder[T]] + case java.lang.Long.TYPE => new ArrayBuilder.ofLong().asInstanceOf[ArrayBuilder[T]] + case java.lang.Float.TYPE => new ArrayBuilder.ofFloat().asInstanceOf[ArrayBuilder[T]] + case java.lang.Double.TYPE => new ArrayBuilder.ofDouble().asInstanceOf[ArrayBuilder[T]] + case java.lang.Boolean.TYPE => new ArrayBuilder.ofBoolean().asInstanceOf[ArrayBuilder[T]] + case java.lang.Void.TYPE => new ArrayBuilder.ofUnit().asInstanceOf[ArrayBuilder[T]] + case _ => new ArrayBuilder.ofRef[T with AnyRef]()(tag.asInstanceOf[ClassTag[T with AnyRef]]).asInstanceOf[ArrayBuilder[T]] + } + } + + /** A generic ArrayBuilder optimized for Scala.js. + * + * @tparam T type of elements for the array builder. + * @param elementClass runtime class of the elements in the array. + */ + @inline + private final class generic[T](elementClass: Class[_]) extends ArrayBuilder[T] { + + private val isCharArrayBuilder = classOf[Char] == elementClass + protected[this] def elems: Array[T] = throw new Error("unreachable") + private var jsElems: js.Array[Any] = js.Array() + + override def length: Int = jsElems.length + + override def knownSize: Int = jsElems.length + + def addOne(elem: T): this.type = { + val unboxedElem = + if (isCharArrayBuilder) elem.asInstanceOf[Char].toInt + else if (elem == null) zeroOf(elementClass) + else elem + jsElems.push(unboxedElem) + this + } + + /** Add a slice of an array */ + override def addAll(xs: Array[_ <: T], offset: Int, length: Int): this.type = { + val end = offset + length + var i = offset + while (i < end) { + this += xs(i) + i += 1 + } + this + } + + override def addAll(xs: IterableOnce[T]): this.type = { + val it = xs.iterator + while (it.hasNext) { + this += it.next() + } + this + } + + override def clear(): Unit = + jsElems = js.Array() + + protected[this] def resize(size: Int): Unit = () + + def result(): Array[T] = { + val elemRuntimeClass = + if (classOf[Unit] == elementClass) classOf[BoxedUnit] + else if (classOf[Null] == elementClass || classOf[Nothing] == elementClass) classOf[Object] + else elementClass + genericArrayBuilderResult(elemRuntimeClass, jsElems) + } + + override def toString(): String = "ArrayBuilder.generic" + } + + // Intrinsic + private def zeroOf(runtimeClass: Class[_]): Any = runtimeClass match { + case java.lang.Byte.TYPE => 0.toByte + case java.lang.Short.TYPE => 0.toShort + case java.lang.Character.TYPE => 0 // yes, as an Int + case java.lang.Integer.TYPE => 0 + case java.lang.Long.TYPE => 0L + case java.lang.Float.TYPE => 0.0f + case java.lang.Double.TYPE => 0.0 + case java.lang.Boolean.TYPE => false + case java.lang.Void.TYPE => () + case _ => null + } + + // Intrinsic + private def genericArrayBuilderResult[T](runtimeClass: Class[_], + a: js.Array[Any]): Array[T] = { + val len = a.length + + if (classOf[Char] == runtimeClass) { + val result = new Array[Char](len) + var i = 0 + while (i != len) { + result(i) = a(i).asInstanceOf[Int].toChar + i += 1 + } + result.asInstanceOf[Array[T]] + } else { + val result: Array[T] = java.lang.reflect.Array.newInstance( + runtimeClass, len).asInstanceOf[Array[T]] + var i = 0 + while (i != len) { + result(i) = a(i).asInstanceOf[T] + i += 1 + } + result + } + } + + /** A class for array builders for arrays of reference types. + * + * This builder can be reused. + * + * @tparam T type of elements for the array builder, subtype of `AnyRef` with a `ClassTag` context bound. + */ + @SerialVersionUID(3L) + final class ofRef[T <: AnyRef](implicit ct: ClassTag[T]) extends ArrayBuilder[T] { + + protected var elems: Array[T] = _ + + private def mkArray(size: Int): Array[T] = { + if (capacity == size && capacity > 0) elems + else if (elems eq null) new Array[T](size) + else java.util.Arrays.copyOf[T](elems, size) + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: T): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def clear(): Unit = { + super.clear() + if(elems ne null) java.util.Arrays.fill(elems.asInstanceOf[Array[AnyRef]], null) + } + + override def equals(other: Any): Boolean = other match { + case x: ofRef[_] => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofRef" + } + + /** A class for array builders for arrays of `byte`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofByte extends ArrayBuilder[Byte] { + + protected var elems: Array[Byte] = _ + + private def mkArray(size: Int): Array[Byte] = { + val newelems = new Array[Byte](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Byte): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofByte => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofByte" + } + + /** A class for array builders for arrays of `short`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofShort extends ArrayBuilder[Short] { + + protected var elems: Array[Short] = _ + + private def mkArray(size: Int): Array[Short] = { + val newelems = new Array[Short](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Short): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofShort => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofShort" + } + + /** A class for array builders for arrays of `char`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofChar extends ArrayBuilder[Char] { + + protected var elems: Array[Char] = _ + + private def mkArray(size: Int): Array[Char] = { + val newelems = new Array[Char](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Char): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofChar => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofChar" + } + + /** A class for array builders for arrays of `int`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofInt extends ArrayBuilder[Int] { + + protected var elems: Array[Int] = _ + + private def mkArray(size: Int): Array[Int] = { + val newelems = new Array[Int](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Int): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofInt => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofInt" + } + + /** A class for array builders for arrays of `long`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofLong extends ArrayBuilder[Long] { + + protected var elems: Array[Long] = _ + + private def mkArray(size: Int): Array[Long] = { + val newelems = new Array[Long](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Long): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofLong => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofLong" + } + + /** A class for array builders for arrays of `float`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofFloat extends ArrayBuilder[Float] { + + protected var elems: Array[Float] = _ + + private def mkArray(size: Int): Array[Float] = { + val newelems = new Array[Float](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Float): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofFloat => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofFloat" + } + + /** A class for array builders for arrays of `double`s. It can be reused. */ + @SerialVersionUID(3L) + final class ofDouble extends ArrayBuilder[Double] { + + protected var elems: Array[Double] = _ + + private def mkArray(size: Int): Array[Double] = { + val newelems = new Array[Double](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Double): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofDouble => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofDouble" + } + + /** A class for array builders for arrays of `boolean`s. It can be reused. */ + @SerialVersionUID(3L) + class ofBoolean extends ArrayBuilder[Boolean] { + + protected var elems: Array[Boolean] = _ + + private def mkArray(size: Int): Array[Boolean] = { + val newelems = new Array[Boolean](size) + if (this.size > 0) Array.copy(elems, 0, newelems, 0, this.size) + newelems + } + + protected[this] def resize(size: Int): Unit = { + elems = mkArray(size) + capacity = size + } + + def addOne(elem: Boolean): this.type = { + ensureSize(size + 1) + elems(size) = elem + size += 1 + this + } + + def result() = { + if (capacity != 0 && capacity == size) { + capacity = 0 + val res = elems + elems = null + res + } + else mkArray(size) + } + + override def equals(other: Any): Boolean = other match { + case x: ofBoolean => (size == x.size) && (elems == x.elems) + case _ => false + } + + override def toString = "ArrayBuilder.ofBoolean" + } + + /** A class for array builders for arrays of `Unit` type. It can be reused. */ + @SerialVersionUID(3L) + final class ofUnit extends ArrayBuilder[Unit] { + + protected def elems: Array[Unit] = throw new UnsupportedOperationException() + + def addOne(elem: Unit): this.type = { + size += 1 + this + } + + override def addAll(xs: IterableOnce[Unit]): this.type = { + size += xs.iterator.size + this + } + + override def addAll(xs: Array[_ <: Unit], offset: Int, length: Int): this.type = { + size += length + this + } + + def result() = { + val ans = new Array[Unit](size) + var i = 0 + while (i < size) { ans(i) = (); i += 1 } + ans + } + + override def equals(other: Any): Boolean = other match { + case x: ofUnit => (size == x.size) + case _ => false + } + + protected[this] def resize(size: Int): Unit = () + + override def toString = "ArrayBuilder.ofUnit" + } +} diff --git a/library-js/src/scala/collection/mutable/Buffer.scala b/library-js/src/scala/collection/mutable/Buffer.scala new file mode 100644 index 000000000000..b08e6d6506a6 --- /dev/null +++ b/library-js/src/scala/collection/mutable/Buffer.scala @@ -0,0 +1,233 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.collection +package mutable + +import scala.scalajs.js + +/** A `Buffer` is a growable and shrinkable `Seq`. */ +trait Buffer[A] + extends Seq[A] + with SeqOps[A, Buffer, Buffer[A]] + with Growable[A] + with Shrinkable[A] + with IterableFactoryDefaults[A, Buffer] { + + override def iterableFactory: SeqFactory[Buffer] = Buffer + + override def knownSize: Int = super[Seq].knownSize + + //TODO Prepend is a logical choice for a readable name of `+=:` but it conflicts with the renaming of `append` to `add` + /** Prepends a single element at the front of this $coll. + * + * @param elem the element to $add. + * @return the $coll itself + */ + def prepend(elem: A): this.type + + /** Appends the given elements to this buffer. + * + * @param elem the element to append. + */ + @`inline` final def append(elem: A): this.type = addOne(elem) + + @deprecated("Use appendAll instead", "2.13.0") + @`inline` final def append(elems: A*): this.type = addAll(elems) + + /** Appends the elements contained in a iterable object to this buffer. + * @param xs the iterable object containing the elements to append. + */ + @`inline` final def appendAll(xs: IterableOnce[A]): this.type = addAll(xs) + + + /** Alias for `prepend` */ + @`inline` final def +=: (elem: A): this.type = prepend(elem) + + def prependAll(elems: IterableOnce[A]): this.type = { insertAll(0, elems); this } + + @deprecated("Use prependAll instead", "2.13.0") + @`inline` final def prepend(elems: A*): this.type = prependAll(elems) + + /** Alias for `prependAll` */ + @inline final def ++=:(elems: IterableOnce[A]): this.type = prependAll(elems) + + /** Inserts a new element at a given index into this buffer. + * + * @param idx the index where the new elements is inserted. + * @param elem the element to insert. + * @throws IndexOutOfBoundsException if the index `idx` is not in the valid range + * `0 <= idx <= length`. + */ + @throws[IndexOutOfBoundsException] + def insert(idx: Int, elem: A): Unit + + /** Inserts new elements at the index `idx`. Opposed to method + * `update`, this method will not replace an element with a new + * one. Instead, it will insert a new element at index `idx`. + * + * @param idx the index where a new element will be inserted. + * @param elems the iterable object providing all elements to insert. + * @throws IndexOutOfBoundsException if `idx` is out of bounds. + */ + @throws[IndexOutOfBoundsException] + def insertAll(idx: Int, elems: IterableOnce[A]): Unit + + /** Removes the element at a given index position. + * + * @param idx the index which refers to the element to delete. + * @return the element that was formerly at index `idx`. + */ + @throws[IndexOutOfBoundsException] + def remove(idx: Int): A + + /** Removes the element on a given index position. It takes time linear in + * the buffer size. + * + * @param idx the index which refers to the first element to remove. + * @param count the number of elements to remove. + * @throws IndexOutOfBoundsException if the index `idx` is not in the valid range + * `0 <= idx <= length - count` (with `count > 0`). + * @throws IllegalArgumentException if `count < 0`. + */ + @throws[IndexOutOfBoundsException] + @throws[IllegalArgumentException] + def remove(idx: Int, count: Int): Unit + + /** Removes a single element from this buffer, at its first occurrence. + * If the buffer does not contain that element, it is unchanged. + * + * @param x the element to remove. + * @return the buffer itself + */ + def subtractOne (x: A): this.type = { + val i = indexOf(x) + if (i != -1) remove(i) + this + } + + /** Removes the first ''n'' elements of this buffer. + * + * @param n the number of elements to remove from the beginning + * of this buffer. + */ + def trimStart(n: Int): Unit = remove(0, normalized(n)) + + /** Removes the last ''n'' elements of this buffer. + * + * @param n the number of elements to remove from the end + * of this buffer. + */ + def trimEnd(n: Int): Unit = { + val norm = normalized(n) + remove(length - norm, norm) + } + + def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A], replaced: Int): this.type + + // +=, ++=, clear inherited from Growable + // Per remark of @ichoran, we should preferably not have these: + // + // def +=:(elem: A): this.type = { insert(0, elem); this } + // def +=:(elem1: A, elem2: A, elems: A*): this.type = elem1 +=: elem2 +=: elems ++=: this + // def ++=:(elems: IterableOnce[A]): this.type = { insertAll(0, elems); this } + + def dropInPlace(n: Int): this.type = { remove(0, normalized(n)); this } + def dropRightInPlace(n: Int): this.type = { + val norm = normalized(n) + remove(length - norm, norm) + this + } + def takeInPlace(n: Int): this.type = { + val norm = normalized(n) + remove(norm, length - norm) + this + } + def takeRightInPlace(n: Int): this.type = { remove(0, length - normalized(n)); this } + def sliceInPlace(start: Int, end: Int): this.type = takeInPlace(end).dropInPlace(start) + private def normalized(n: Int): Int = math.min(math.max(n, 0), length) + + def dropWhileInPlace(p: A => Boolean): this.type = { + val idx = indexWhere(!p(_)) + if (idx < 0) { clear(); this } else dropInPlace(idx) + } + def takeWhileInPlace(p: A => Boolean): this.type = { + val idx = indexWhere(!p(_)) + if (idx < 0) this else takeInPlace(idx) + } + def padToInPlace(len: Int, elem: A): this.type = { + while (length < len) +=(elem) + this + } + + @deprecatedOverriding("Compatibility override", since="2.13.0") + override protected[this] def stringPrefix = "Buffer" +} + +trait IndexedBuffer[A] extends IndexedSeq[A] + with IndexedSeqOps[A, IndexedBuffer, IndexedBuffer[A]] + with Buffer[A] + with IterableFactoryDefaults[A, IndexedBuffer] { + + override def iterableFactory: SeqFactory[IndexedBuffer] = IndexedBuffer + + def flatMapInPlace(f: A => IterableOnce[A]): this.type = { + // There's scope for a better implementation which copies elements in place. + var i = 0 + val s = size + val newElems = new Array[IterableOnce[A]](s) + while (i < s) { newElems(i) = f(this(i)); i += 1 } + clear() + i = 0 + while (i < s) { ++=(newElems(i)); i += 1 } + this + } + + def filterInPlace(p: A => Boolean): this.type = { + var i, j = 0 + while (i < size) { + if (p(apply(i))) { + if (i != j) { + this(j) = this(i) + } + j += 1 + } + i += 1 + } + + if (i == j) this else takeInPlace(j) + } + + def patchInPlace(from: Int, patch: scala.collection.IterableOnce[A], replaced: Int): this.type = { + val replaced0 = math.min(math.max(replaced, 0), length) + val i = math.min(math.max(from, 0), length) + var j = 0 + val iter = patch.iterator + while (iter.hasNext && j < replaced0 && i + j < length) { + update(i + j, iter.next()) + j += 1 + } + if (iter.hasNext) insertAll(i + j, iter) + else if (j < replaced0) remove(i + j, math.min(replaced0 - j, length - i - j)) + this + } +} + +@SerialVersionUID(3L) +object Buffer extends SeqFactory.Delegate[Buffer](js.WrappedArray) + +@SerialVersionUID(3L) +object IndexedBuffer extends SeqFactory.Delegate[IndexedBuffer](js.WrappedArray) + +/** Explicit instantiation of the `Buffer` trait to reduce class file size in subclasses. */ +@SerialVersionUID(3L) +abstract class AbstractBuffer[A] extends AbstractSeq[A] with Buffer[A] diff --git a/library-js/src/scala/concurrent/ExecutionContext.scala b/library-js/src/scala/concurrent/ExecutionContext.scala new file mode 100644 index 000000000000..38582321cccd --- /dev/null +++ b/library-js/src/scala/concurrent/ExecutionContext.scala @@ -0,0 +1,227 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.concurrent + + +import java.util.concurrent.{ ExecutorService, Executor } +import scala.annotation.implicitNotFound + +/** + * An `ExecutionContext` can execute program logic asynchronously, + * typically but not necessarily on a thread pool. + * + * A general purpose `ExecutionContext` must be asynchronous in executing + * any `Runnable` that is passed into its `execute`-method. A special purpose + * `ExecutionContext` may be synchronous but must only be passed to code that + * is explicitly safe to be run using a synchronously executing `ExecutionContext`. + * + * APIs such as `Future.onComplete` require you to provide a callback + * and an implicit `ExecutionContext`. The implicit `ExecutionContext` + * will be used to execute the callback. + * + * While it is possible to simply import + * `scala.concurrent.ExecutionContext.Implicits.global` to obtain an + * implicit `ExecutionContext`, application developers should carefully + * consider where they want to set execution policy; + * ideally, one place per application—or per logically related section of code— + * will make a decision about which `ExecutionContext` to use. + * That is, you will mostly want to avoid hardcoding, especially via an import, + * `scala.concurrent.ExecutionContext.Implicits.global`. + * The recommended approach is to add `(implicit ec: ExecutionContext)` to methods, + * or class constructor parameters, which need an `ExecutionContext`. + * + * Then locally import a specific `ExecutionContext` in one place for the entire + * application or module, passing it implicitly to individual methods. + * Alternatively define a local implicit val with the required `ExecutionContext`. + * + * A custom `ExecutionContext` may be appropriate to execute code + * which blocks on IO or performs long-running computations. + * `ExecutionContext.fromExecutorService` and `ExecutionContext.fromExecutor` + * are good ways to create a custom `ExecutionContext`. + * + * The intent of `ExecutionContext` is to lexically scope code execution. + * That is, each method, class, file, package, or application determines + * how to run its own code. This avoids issues such as running + * application callbacks on a thread pool belonging to a networking library. + * The size of a networking library's thread pool can be safely configured, + * knowing that only that library's network operations will be affected. + * Application callback execution can be configured separately. + */ +@implicitNotFound("""Cannot find an implicit ExecutionContext. You might pass +an (implicit ec: ExecutionContext) parameter to your method. + +The ExecutionContext is used to configure how and on which +thread pools Futures will run, so the specific ExecutionContext +that is selected is important. + +If your application does not define an ExecutionContext elsewhere, +consider using Scala's global ExecutionContext by defining +the following: + +implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global""") +trait ExecutionContext { + + /** Runs a block of code on this execution context. + * + * @param runnable the task to execute + */ + def execute(runnable: Runnable): Unit + + /** Reports that an asynchronous computation failed. + * + * @param cause the cause of the failure + */ + def reportFailure(@deprecatedName("t") cause: Throwable): Unit + + /** Prepares for the execution of a task. Returns the prepared + * execution context. The recommended implementation of + * `prepare` is to return `this`. + * + * This method should no longer be overridden or called. It was + * originally expected that `prepare` would be called by + * all libraries that consume ExecutionContexts, in order to + * capture thread local context. However, this usage has proven + * difficult to implement in practice and instead it is + * now better to avoid using `prepare` entirely. + * + * Instead, if an `ExecutionContext` needs to capture thread + * local context, it should capture that context when it is + * constructed, so that it doesn't need any additional + * preparation later. + */ + @deprecated("preparation of ExecutionContexts will be removed", "2.12.0") + // This cannot be removed until there is a suitable replacement + def prepare(): ExecutionContext = this +} + +/** + * An [[ExecutionContext]] that is also a + * Java [[http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html Executor]]. + */ +trait ExecutionContextExecutor extends ExecutionContext with Executor + +/** + * An [[ExecutionContext]] that is also a + * Java [[http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html ExecutorService]]. + */ +trait ExecutionContextExecutorService extends ExecutionContextExecutor with ExecutorService + + +/** Contains factory methods for creating execution contexts. + */ +object ExecutionContext { + /** + * The explicit global `ExecutionContext`. Invoke `global` when you want to provide the global + * `ExecutionContext` explicitly. + * + * The default `ExecutionContext` implementation is backed by a work-stealing thread pool. + * It can be configured via the following [[scala.sys.SystemProperties]]: + * + * `scala.concurrent.context.minThreads` = defaults to "1" + * `scala.concurrent.context.numThreads` = defaults to "x1" (i.e. the current number of available processors * 1) + * `scala.concurrent.context.maxThreads` = defaults to "x1" (i.e. the current number of available processors * 1) + * `scala.concurrent.context.maxExtraThreads` = defaults to "256" + * + * The pool size of threads is then `numThreads` bounded by `minThreads` on the lower end and `maxThreads` on the high end. + * + * The `maxExtraThreads` is the maximum number of extra threads to have at any given time to evade deadlock, + * see [[scala.concurrent.BlockContext]]. + * + * @return the global `ExecutionContext` + */ + final lazy val global: ExecutionContextExecutor = + scala.scalajs.concurrent.JSExecutionContext.queue + + /** + * WARNING: Only ever execute logic which will quickly return control to the caller. + * + * This `ExecutionContext` steals execution time from other threads by having its + * `Runnable`s run on the `Thread` which calls `execute` and then yielding back control + * to the caller after *all* its `Runnable`s have been executed. + * Nested invocations of `execute` will be trampolined to prevent uncontrolled stack space growth. + * + * When using `parasitic` with abstractions such as `Future` it will in many cases be non-deterministic + * as to which `Thread` will be executing the logic, as it depends on when/if that `Future` is completed. + * + * Do *not* call any blocking code in the `Runnable`s submitted to this `ExecutionContext` + * as it will prevent progress by other enqueued `Runnable`s and the calling `Thread`. + * + * Symptoms of misuse of this `ExecutionContext` include, but are not limited to, deadlocks + * and severe performance problems. + * + * Any `NonFatal` or `InterruptedException`s will be reported to the `defaultReporter`. + */ + object parasitic extends ExecutionContextExecutor with BatchingExecutor { + override final def submitForExecution(runnable: Runnable): Unit = runnable.run() + override final def execute(runnable: Runnable): Unit = submitSyncBatched(runnable) + override final def reportFailure(t: Throwable): Unit = defaultReporter(t) + } + + object Implicits { + /** + * The implicit global `ExecutionContext`. Import `global` when you want to provide the global + * `ExecutionContext` implicitly. + * + * The default `ExecutionContext` implementation is backed by a work-stealing thread pool. By default, + * the thread pool uses a target number of worker threads equal to the number of + * [[https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors-- available processors]]. + */ + implicit final def global: ExecutionContext = ExecutionContext.global + } + + /** Creates an `ExecutionContext` from the given `ExecutorService`. + * + * @param e the `ExecutorService` to use. If `null`, a new `ExecutorService` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. + * @param reporter a function for error reporting + * @return the `ExecutionContext` using the given `ExecutorService` + */ + def fromExecutorService(e: ExecutorService, reporter: Throwable => Unit): ExecutionContextExecutorService = + impl.ExecutionContextImpl.fromExecutorService(e, reporter) + + /** Creates an `ExecutionContext` from the given `ExecutorService` with the [[scala.concurrent.ExecutionContext$.defaultReporter default reporter]]. + * + * If it is guaranteed that none of the executed tasks are blocking, a single-threaded `ExecutorService` + * can be used to create an `ExecutionContext` as follows: + * + * {{{ + * import java.util.concurrent.Executors + * val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor()) + * }}} + * + * @param e the `ExecutorService` to use. If `null`, a new `ExecutorService` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. + * @return the `ExecutionContext` using the given `ExecutorService` + */ + def fromExecutorService(e: ExecutorService): ExecutionContextExecutorService = fromExecutorService(e, defaultReporter) + + /** Creates an `ExecutionContext` from the given `Executor`. + * + * @param e the `Executor` to use. If `null`, a new `Executor` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. + * @param reporter a function for error reporting + * @return the `ExecutionContext` using the given `Executor` + */ + def fromExecutor(e: Executor, reporter: Throwable => Unit): ExecutionContextExecutor = + impl.ExecutionContextImpl.fromExecutor(e, reporter) + + /** Creates an `ExecutionContext` from the given `Executor` with the [[scala.concurrent.ExecutionContext$.defaultReporter default reporter]]. + * + * @param e the `Executor` to use. If `null`, a new `Executor` is created with [[scala.concurrent.ExecutionContext$.global default configuration]]. + * @return the `ExecutionContext` using the given `Executor` + */ + def fromExecutor(e: Executor): ExecutionContextExecutor = fromExecutor(e, defaultReporter) + + /** The default reporter simply prints the stack trace of the `Throwable` to [[http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#err System.err]]. + * + * @return the function for error reporting + */ + final val defaultReporter: Throwable => Unit = _.printStackTrace() +} diff --git a/library-js/src/scala/math/ScalaNumber.scala b/library-js/src/scala/math/ScalaNumber.scala new file mode 100644 index 000000000000..811346d2f869 --- /dev/null +++ b/library-js/src/scala/math/ScalaNumber.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + + +package scala.math + +/** A marker class for Number types introduced by Scala + * @author Martin Odersky, Paul Phillips + * @version 2.8 + * @since 2.8 + */ +abstract class ScalaNumber extends java.lang.Number { + protected def isWhole(): Boolean + def underlying(): Object +} diff --git a/library-js/src/scala/package.scala b/library-js/src/scala/package.scala new file mode 100644 index 000000000000..c9f1fdf8314e --- /dev/null +++ b/library-js/src/scala/package.scala @@ -0,0 +1,146 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +import scala.annotation.migration + +/** + * Core Scala types. They are always available without an explicit import. + * @contentDiagram hideNodes "scala.Serializable" + */ +package object scala { + type Cloneable = java.lang.Cloneable + type Serializable = java.io.Serializable + + type Throwable = java.lang.Throwable + type Exception = java.lang.Exception + type Error = java.lang.Error + + type RuntimeException = java.lang.RuntimeException + type NullPointerException = java.lang.NullPointerException + type ClassCastException = java.lang.ClassCastException + type IndexOutOfBoundsException = java.lang.IndexOutOfBoundsException + type ArrayIndexOutOfBoundsException = java.lang.ArrayIndexOutOfBoundsException + type StringIndexOutOfBoundsException = java.lang.StringIndexOutOfBoundsException + type UnsupportedOperationException = java.lang.UnsupportedOperationException + type IllegalArgumentException = java.lang.IllegalArgumentException + type NoSuchElementException = java.util.NoSuchElementException + type NumberFormatException = java.lang.NumberFormatException + type AbstractMethodError = java.lang.AbstractMethodError + type InterruptedException = java.lang.InterruptedException + + // A dummy used by the specialization annotation. + val AnyRef = new Specializable { + override def toString = "object AnyRef" + } + + @deprecated("Use IterableOnce instead of TraversableOnce", "2.13.0") + type TraversableOnce[+A] = scala.collection.IterableOnce[A] + + type IterableOnce[+A] = scala.collection.IterableOnce[A] + + @deprecated("Use Iterable instead of Traversable", "2.13.0") + type Traversable[+A] = scala.collection.Iterable[A] + @deprecated("Use Iterable instead of Traversable", "2.13.0") + val Traversable = scala.collection.Iterable + + type Iterable[+A] = scala.collection.Iterable[A] + val Iterable = scala.collection.Iterable + + @migration("scala.Seq is now scala.collection.immutable.Seq instead of scala.collection.Seq", "2.13.0") + type Seq[+A] = scala.collection.immutable.Seq[A] + val Seq = scala.collection.immutable.Seq + + @migration("scala.IndexedSeq is now scala.collection.immutable.IndexedSeq instead of scala.collection.IndexedSeq", "2.13.0") + type IndexedSeq[+A] = scala.collection.immutable.IndexedSeq[A] + val IndexedSeq = scala.collection.immutable.IndexedSeq + + type Iterator[+A] = scala.collection.Iterator[A] + val Iterator = scala.collection.Iterator + + @deprecated("Use scala.collection.BufferedIterator instead of scala.BufferedIterator", "2.13.0") + type BufferedIterator[+A] = scala.collection.BufferedIterator[A] + + type List[+A] = scala.collection.immutable.List[A] + val List = scala.collection.immutable.List + + val Nil = scala.collection.immutable.Nil + + type ::[A] = scala.collection.immutable.::[A] + val :: = scala.collection.immutable.:: + + val +: = scala.collection.+: + val :+ = scala.collection.:+ + + @deprecated("Use LazyList instead of Stream", "2.13.0") + type Stream[+A] = scala.collection.immutable.Stream[A] + @deprecated("Use LazyList instead of Stream", "2.13.0") + val Stream = scala.collection.immutable.Stream + + type LazyList[+A] = scala.collection.immutable.LazyList[A] + val LazyList = scala.collection.immutable.LazyList + // This should be an alias to LazyList.#:: but we need to support Stream, too + //val #:: = scala.collection.immutable.LazyList.#:: + object #:: { + def unapply[A](s: LazyList[A]): Option[(A, LazyList[A])] = + if (s.nonEmpty) Some((s.head, s.tail)) else None + def unapply[A](s: Stream[A]): Option[(A, Stream[A])] = + if (s.nonEmpty) Some((s.head, s.tail)) else None + } + + type Vector[+A] = scala.collection.immutable.Vector[A] + val Vector = scala.collection.immutable.Vector + + type StringBuilder = scala.collection.mutable.StringBuilder + val StringBuilder = scala.collection.mutable.StringBuilder + + type Range = scala.collection.immutable.Range + val Range = scala.collection.immutable.Range + + // Numeric types which were moved into scala.math.* + + type BigDecimal = scala.math.BigDecimal + lazy val BigDecimal = scala.math.BigDecimal + + type BigInt = scala.math.BigInt + lazy val BigInt = scala.math.BigInt + + type Equiv[T] = scala.math.Equiv[T] + val Equiv = scala.math.Equiv + + type Fractional[T] = scala.math.Fractional[T] + val Fractional = scala.math.Fractional + + type Integral[T] = scala.math.Integral[T] + val Integral = scala.math.Integral + + type Numeric[T] = scala.math.Numeric[T] + val Numeric = scala.math.Numeric + + type Ordered[T] = scala.math.Ordered[T] + val Ordered = scala.math.Ordered + + type Ordering[T] = scala.math.Ordering[T] + val Ordering = scala.math.Ordering + + type PartialOrdering[T] = scala.math.PartialOrdering[T] + type PartiallyOrdered[T] = scala.math.PartiallyOrdered[T] + + type Either[+A, +B] = scala.util.Either[A, B] + val Either = scala.util.Either + + type Left[+A, +B] = scala.util.Left[A, B] + val Left = scala.util.Left + + type Right[+A, +B] = scala.util.Right[A, B] + val Right = scala.util.Right + +} diff --git a/library-js/src/scala/reflect/ClassTag.scala b/library-js/src/scala/reflect/ClassTag.scala new file mode 100644 index 000000000000..a66a1a6a8e34 --- /dev/null +++ b/library-js/src/scala/reflect/ClassTag.scala @@ -0,0 +1,146 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package reflect + +import scala.annotation.unchecked.uncheckedStable + +import java.lang.{ Class => jClass } + +import scala.collection.mutable +import scala.runtime.BoxedUnit + +/** + * + * A `ClassTag[T]` stores the erased class of a given type `T`, accessible via the `runtimeClass` + * field. This is particularly useful for instantiating `Array`s whose element types are unknown + * at compile time. + * + * `ClassTag`s are a weaker special case of [[scala.reflect.api.TypeTags#TypeTag]]s, in that they + * wrap only the runtime class of a given type, whereas a `TypeTag` contains all static type + * information. That is, `ClassTag`s are constructed from knowing only the top-level class of a + * type, without necessarily knowing all of its argument types. This runtime information is enough + * for runtime `Array` creation. + * + * For example: + * {{{ + * scala> def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*) + * mkArray: [T](elems: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T] + * + * scala> mkArray(42, 13) + * res0: Array[Int] = Array(42, 13) + * + * scala> mkArray("Japan","Brazil","Germany") + * res1: Array[String] = Array(Japan, Brazil, Germany) + * }}} + * + * See [[scala.reflect.api.TypeTags]] for more examples, or the + * [[http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html Reflection Guide: TypeTags]] + * for more details. + * + */ +@scala.annotation.implicitNotFound(msg = "No ClassTag available for ${T}") +trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serializable { + // please, don't add any APIs here, like it was with `newWrappedArray` and `newArrayBuilder` + // class tags, and all tags in general, should be as minimalistic as possible + + /** A class representing the type `U` to which `T` would be erased. + * Note that there is no subtyping relationship between `T` and `U`. + */ + def runtimeClass: jClass[_] + + /** Produces a `ClassTag` that knows how to instantiate an `Array[Array[T]]` */ + def wrap: ClassTag[Array[T]] = ClassTag[Array[T]](arrayClass(runtimeClass)) + + /** Produces a new array with element type `T` and length `len` */ + def newArray(len: Int): Array[T] = + java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]] + + /** A ClassTag[T] can serve as an extractor that matches only objects of type T. + * + * The compiler tries to turn unchecked type tests in pattern matches into checked ones + * by wrapping a `(_: T)` type pattern as `ct(_: T)`, where `ct` is the `ClassTag[T]` instance. + * Type tests necessary before calling other extractors are treated similarly. + * `SomeExtractor(...)` is turned into `ct(SomeExtractor(...))` if `T` in `SomeExtractor.unapply(x: T)` + * is uncheckable, but we have an instance of `ClassTag[T]`. + */ + def unapply(x: Any): Option[T] = + if (runtimeClass.isInstance(x)) Some(x.asInstanceOf[T]) + else None + + // case class accessories + override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]] + override def equals(x: Any) = x.isInstanceOf[ClassTag[_]] && this.runtimeClass == x.asInstanceOf[ClassTag[_]].runtimeClass + override def hashCode = runtimeClass.## + override def toString = { + def prettyprint(clazz: jClass[_]): String = + if (clazz.isArray) s"Array[${prettyprint(clazz.getComponentType)}]" else + clazz.getName + prettyprint(runtimeClass) + } +} + +/** + * Class tags corresponding to primitive types and constructor/extractor for ClassTags. + */ +object ClassTag { + import ManifestFactory._ + + val Byte : ByteManifest = ManifestFactory.Byte + val Short : ShortManifest = ManifestFactory.Short + val Char : CharManifest = ManifestFactory.Char + val Int : IntManifest = ManifestFactory.Int + val Long : LongManifest = ManifestFactory.Long + val Float : FloatManifest = ManifestFactory.Float + val Double : DoubleManifest = ManifestFactory.Double + val Boolean : BooleanManifest = ManifestFactory.Boolean + val Unit : UnitManifest = ManifestFactory.Unit + val Any : ClassTag[scala.Any] = ManifestFactory.Any + val Object : ClassTag[java.lang.Object] = ManifestFactory.Object + val AnyVal : ClassTag[scala.AnyVal] = ManifestFactory.AnyVal + val AnyRef : ClassTag[scala.AnyRef] = ManifestFactory.AnyRef + val Nothing : ClassTag[scala.Nothing] = ManifestFactory.Nothing + val Null : ClassTag[scala.Null] = ManifestFactory.Null + + @inline + @SerialVersionUID(1L) + private class GenericClassTag[T](val runtimeClass: jClass[_]) extends ClassTag[T] { + override def newArray(len: Int): Array[T] = { + java.lang.reflect.Array.newInstance(runtimeClass, len).asInstanceOf[Array[T]] + } + } + + def apply[T](runtimeClass1: jClass[_]): ClassTag[T] = + runtimeClass1 match { + case java.lang.Byte.TYPE => ClassTag.Byte.asInstanceOf[ClassTag[T]] + case java.lang.Short.TYPE => ClassTag.Short.asInstanceOf[ClassTag[T]] + case java.lang.Character.TYPE => ClassTag.Char.asInstanceOf[ClassTag[T]] + case java.lang.Integer.TYPE => ClassTag.Int.asInstanceOf[ClassTag[T]] + case java.lang.Long.TYPE => ClassTag.Long.asInstanceOf[ClassTag[T]] + case java.lang.Float.TYPE => ClassTag.Float.asInstanceOf[ClassTag[T]] + case java.lang.Double.TYPE => ClassTag.Double.asInstanceOf[ClassTag[T]] + case java.lang.Boolean.TYPE => ClassTag.Boolean.asInstanceOf[ClassTag[T]] + case java.lang.Void.TYPE => ClassTag.Unit.asInstanceOf[ClassTag[T]] + case _ => + if (classOf[java.lang.Object] == runtimeClass1) + ClassTag.Object.asInstanceOf[ClassTag[T]] + else if (classOf[scala.runtime.Nothing$] == runtimeClass1) + ClassTag.Nothing.asInstanceOf[ClassTag[T]] + else if (classOf[scala.runtime.Null$] == runtimeClass1) + ClassTag.Null.asInstanceOf[ClassTag[T]] + else + new GenericClassTag[T](runtimeClass1) + } + + def unapply[T](ctag: ClassTag[T]): Option[Class[_]] = Some(ctag.runtimeClass) +} diff --git a/library-js/src/scala/reflect/Manifest.scala b/library-js/src/scala/reflect/Manifest.scala new file mode 100644 index 000000000000..faa2fe6f6e5e --- /dev/null +++ b/library-js/src/scala/reflect/Manifest.scala @@ -0,0 +1,461 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package reflect + +import scala.collection.mutable.{ArrayBuilder, ArraySeq} + +/** A `Manifest[T]` is an opaque descriptor for type T. Its supported use + * is to give access to the erasure of the type as a `Class` instance, as + * is necessary for the creation of native `Arrays` if the class is not + * known at compile time. + * + * The type-relation operators `<:<` and `=:=` should be considered + * approximations only, as there are numerous aspects of type conformance + * which are not yet adequately represented in manifests. + * + * Example usages: + * {{{ + * def arr[T] = new Array[T](0) // does not compile + * def arr[T](implicit m: Manifest[T]) = new Array[T](0) // compiles + * def arr[T: Manifest] = new Array[T](0) // shorthand for the preceding + * + * // Methods manifest and optManifest are in [[scala.Predef]]. + * def isApproxSubType[T: Manifest, U: Manifest] = manifest[T] <:< manifest[U] + * isApproxSubType[List[String], List[AnyRef]] // true + * isApproxSubType[List[String], List[Int]] // false + * + * def methods[T: Manifest] = manifest[T].runtimeClass.getMethods + * def retType[T: Manifest](name: String) = + * methods[T] find (_.getName == name) map (_.getGenericReturnType) + * + * retType[Map[_, _]]("values") // Some(scala.collection.Iterable) + * }}} + */ +@scala.annotation.implicitNotFound(msg = "No Manifest available for ${T}.") +// TODO undeprecated until Scala reflection becomes non-experimental +// @deprecated("use scala.reflect.ClassTag (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") +trait Manifest[T] extends ClassManifest[T] with Equals { + override def typeArguments: List[Manifest[_]] = Nil + + override def arrayManifest: Manifest[Array[T]] = + Manifest.classType[Array[T]](arrayClass[T](runtimeClass), this) + + override def canEqual(that: Any): Boolean = that match { + case _: Manifest[_] => true + case _ => false + } + /** Note: testing for erasure here is important, as it is many times + * faster than <:< and rules out most comparisons. + */ + override def equals(that: Any): Boolean = that match { + case m: Manifest[_] => (m canEqual this) && (this.runtimeClass == m.runtimeClass) && (this <:< m) && (m <:< this) + case _ => false + } + override def hashCode = this.runtimeClass.## +} + +/** The object `Manifest` defines factory methods for manifests. + * It is intended for use by the compiler and should not be used in client code. + */ +// TODO undeprecated until Scala reflection becomes non-experimental +// @deprecated("use scala.reflect.ClassTag (to capture erasures), scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") +object Manifest { + /* Forward all the public members of ManifestFactory, since this object used + * to be a `private val Manifest = ManifestFactory` in the package object. It + * was moved here because it needs to be in the same file as `trait Manifest` + * defined above. + */ + + def valueManifests: List[AnyValManifest[_]] = + ManifestFactory.valueManifests + + val Byte: ManifestFactory.ByteManifest = ManifestFactory.Byte + val Short: ManifestFactory.ShortManifest = ManifestFactory.Short + val Char: ManifestFactory.CharManifest = ManifestFactory.Char + val Int: ManifestFactory.IntManifest = ManifestFactory.Int + val Long: ManifestFactory.LongManifest = ManifestFactory.Long + val Float: ManifestFactory.FloatManifest = ManifestFactory.Float + val Double: ManifestFactory.DoubleManifest = ManifestFactory.Double + val Boolean: ManifestFactory.BooleanManifest = ManifestFactory.Boolean + val Unit: ManifestFactory.UnitManifest = ManifestFactory.Unit + + val Any: Manifest[scala.Any] = ManifestFactory.Any + val Object: Manifest[java.lang.Object] = ManifestFactory.Object + val AnyRef: Manifest[scala.AnyRef] = ManifestFactory.AnyRef + val AnyVal: Manifest[scala.AnyVal] = ManifestFactory.AnyVal + val Null: Manifest[scala.Null] = ManifestFactory.Null + val Nothing: Manifest[scala.Nothing] = ManifestFactory.Nothing + + /** Manifest for the singleton type `value.type`. */ + def singleType[T <: AnyRef](value: AnyRef): Manifest[T] = + ManifestFactory.singleType[T](value) + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a top-level or static class. + * @note This no-prefix, no-arguments case is separate because we + * it's called from ScalaRunTime.boxArray itself. If we + * pass varargs as arrays into this, we get an infinitely recursive call + * to boxArray. (Besides, having a separate case is more efficient) + */ + def classType[T](clazz: Predef.Class[_]): Manifest[T] = + ManifestFactory.classType[T](clazz) + + /** Manifest for the class type `clazz`, where `clazz` is + * a top-level or static class and args are its type arguments. */ + def classType[T](clazz: Predef.Class[T], arg1: Manifest[_], args: Manifest[_]*): Manifest[T] = + ManifestFactory.classType[T](clazz, arg1, args: _*) + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a class with non-package prefix type `prefix` and type arguments `args`. + */ + def classType[T](prefix: Manifest[_], clazz: Predef.Class[_], args: Manifest[_]*): Manifest[T] = + ManifestFactory.classType[T](prefix, clazz, args: _*) + + def arrayType[T](arg: Manifest[_]): Manifest[Array[T]] = + ManifestFactory.arrayType[T](arg) + + /** Manifest for the abstract type `prefix # name`. `upperBound` is not + * strictly necessary as it could be obtained by reflection. It was + * added so that erasure can be calculated without reflection. */ + def abstractType[T](prefix: Manifest[_], name: String, upperBound: Predef.Class[_], args: Manifest[_]*): Manifest[T] = + ManifestFactory.abstractType[T](prefix, name, upperBound, args: _*) + + /** Manifest for the unknown type `_ >: L <: U` in an existential. */ + def wildcardType[T](lowerBound: Manifest[_], upperBound: Manifest[_]): Manifest[T] = + ManifestFactory.wildcardType[T](lowerBound, upperBound) + + /** Manifest for the intersection type `parents_0 with ... with parents_n`. */ + def intersectionType[T](parents: Manifest[_]*): Manifest[T] = + ManifestFactory.intersectionType[T](parents: _*) + +} + +// TODO undeprecated until Scala reflection becomes non-experimental +// @deprecated("use type tags and manually check the corresponding class or type instead", "2.10.0") +@SerialVersionUID(1L) +abstract class AnyValManifest[T <: AnyVal](override val toString: String) extends Manifest[T] with Equals { + override def <:<(that: ClassManifest[_]): Boolean = + (that eq this) || (that eq Manifest.Any) || (that eq Manifest.AnyVal) + override def canEqual(other: Any) = other match { + case _: AnyValManifest[_] => true + case _ => false + } + override def equals(that: Any): Boolean = this eq that.asInstanceOf[AnyRef] + override def hashCode = System.identityHashCode(this) +} + +/** `ManifestFactory` defines factory methods for manifests. + * It is intended for use by the compiler and should not be used in client code. + * + * Unlike `Manifest`, this factory isn't annotated with a deprecation warning. + * This is done to prevent avalanches of deprecation warnings in the code that calls methods with manifests. + * Why so complicated? Read up the comments for `ClassManifestFactory`. + */ +object ManifestFactory { + def valueManifests: List[AnyValManifest[_]] = + List(Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit) + + @SerialVersionUID(1L) + private[reflect] class ByteManifest extends AnyValManifest[scala.Byte]("Byte") { + def runtimeClass = java.lang.Byte.TYPE + @inline override def newArray(len: Int): Array[Byte] = new Array[Byte](len) + override def newWrappedArray(len: Int): ArraySeq[Byte] = new ArraySeq.ofByte(new Array[Byte](len)) + override def newArrayBuilder(): ArrayBuilder[Byte] = new ArrayBuilder.ofByte() + override def unapply(x: Any): Option[Byte] = { + x match { + case d: Byte => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Byte + } + private object ByteManifest extends ByteManifest + val Byte: ByteManifest = ByteManifest + + @SerialVersionUID(1L) + private[reflect] class ShortManifest extends AnyValManifest[scala.Short]("Short") { + def runtimeClass = java.lang.Short.TYPE + @inline override def newArray(len: Int): Array[Short] = new Array[Short](len) + override def newWrappedArray(len: Int): ArraySeq[Short] = new ArraySeq.ofShort(new Array[Short](len)) + override def newArrayBuilder(): ArrayBuilder[Short] = new ArrayBuilder.ofShort() + override def unapply(x: Any): Option[Short] = { + x match { + case d: Short => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Short + } + private object ShortManifest extends ShortManifest + val Short: ShortManifest = ShortManifest + + @SerialVersionUID(1L) + private[reflect] class CharManifest extends AnyValManifest[scala.Char]("Char") { + def runtimeClass = java.lang.Character.TYPE + @inline override def newArray(len: Int): Array[Char] = new Array[Char](len) + override def newWrappedArray(len: Int): ArraySeq[Char] = new ArraySeq.ofChar(new Array[Char](len)) + override def newArrayBuilder(): ArrayBuilder[Char] = new ArrayBuilder.ofChar() + override def unapply(x: Any): Option[Char] = { + x match { + case d: Char => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Char + } + private object CharManifest extends CharManifest + val Char: CharManifest = CharManifest + + @SerialVersionUID(1L) + private[reflect] class IntManifest extends AnyValManifest[scala.Int]("Int") { + def runtimeClass = java.lang.Integer.TYPE + @inline override def newArray(len: Int): Array[Int] = new Array[Int](len) + override def newWrappedArray(len: Int): ArraySeq[Int] = new ArraySeq.ofInt(new Array[Int](len)) + override def newArrayBuilder(): ArrayBuilder[Int] = new ArrayBuilder.ofInt() + override def unapply(x: Any): Option[Int] = { + x match { + case d: Int => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Int + } + private object IntManifest extends IntManifest + val Int: IntManifest = IntManifest + + @SerialVersionUID(1L) + private[reflect] class LongManifest extends AnyValManifest[scala.Long]("Long") { + def runtimeClass = java.lang.Long.TYPE + @inline override def newArray(len: Int): Array[Long] = new Array[Long](len) + override def newWrappedArray(len: Int): ArraySeq[Long] = new ArraySeq.ofLong(new Array[Long](len)) + override def newArrayBuilder(): ArrayBuilder[Long] = new ArrayBuilder.ofLong() + override def unapply(x: Any): Option[Long] = { + x match { + case d: Long => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Long + } + private object LongManifest extends LongManifest + val Long: LongManifest = LongManifest + + @SerialVersionUID(1L) + private[reflect] class FloatManifest extends AnyValManifest[scala.Float]("Float") { + def runtimeClass = java.lang.Float.TYPE + @inline override def newArray(len: Int): Array[Float] = new Array[Float](len) + override def newWrappedArray(len: Int): ArraySeq[Float] = new ArraySeq.ofFloat(new Array[Float](len)) + override def newArrayBuilder(): ArrayBuilder[Float] = new ArrayBuilder.ofFloat() + override def unapply(x: Any): Option[Float] = { + x match { + case d: Float => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Float + } + private object FloatManifest extends FloatManifest + val Float: FloatManifest = FloatManifest + + @SerialVersionUID(1L) + private[reflect] class DoubleManifest extends AnyValManifest[scala.Double]("Double") { + def runtimeClass = java.lang.Double.TYPE + @inline override def newArray(len: Int): Array[Double] = new Array[Double](len) + override def newWrappedArray(len: Int): ArraySeq[Double] = new ArraySeq.ofDouble(new Array[Double](len)) + override def newArrayBuilder(): ArrayBuilder[Double] = new ArrayBuilder.ofDouble() + + override def unapply(x: Any): Option[Double] = { + x match { + case d: Double => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Double + } + private object DoubleManifest extends DoubleManifest + val Double: DoubleManifest = DoubleManifest + + @SerialVersionUID(1L) + private[reflect] class BooleanManifest extends AnyValManifest[scala.Boolean]("Boolean") { + def runtimeClass = java.lang.Boolean.TYPE + @inline override def newArray(len: Int): Array[Boolean] = new Array[Boolean](len) + override def newWrappedArray(len: Int): ArraySeq[Boolean] = new ArraySeq.ofBoolean(new Array[Boolean](len)) + override def newArrayBuilder(): ArrayBuilder[Boolean] = new ArrayBuilder.ofBoolean() + override def unapply(x: Any): Option[Boolean] = { + x match { + case d: Boolean => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Boolean + } + private object BooleanManifest extends BooleanManifest + val Boolean: BooleanManifest = BooleanManifest + + @SerialVersionUID(1L) + private[reflect] class UnitManifest extends AnyValManifest[scala.Unit]("Unit") { + def runtimeClass = java.lang.Void.TYPE + @inline override def newArray(len: Int): Array[Unit] = new Array[Unit](len) + override def newWrappedArray(len: Int): ArraySeq[Unit] = new ArraySeq.ofUnit(new Array[Unit](len)) + override def newArrayBuilder(): ArrayBuilder[Unit] = new ArrayBuilder.ofUnit() + override protected def arrayClass[T](tp: Class[_]): Class[Array[T]] = + if (tp eq runtimeClass) classOf[Array[scala.runtime.BoxedUnit]].asInstanceOf[Class[Array[T]]] + else super.arrayClass(tp) + override def unapply(x: Any): Option[Unit] = { + x match { + case d: Unit => Some(d) + case _ => None + } + } + private def readResolve(): Any = Manifest.Unit + } + private object UnitManifest extends UnitManifest + val Unit: UnitManifest = UnitManifest + + private object AnyManifest extends PhantomManifest[scala.Any](classOf[java.lang.Object], "Any") { + override def runtimeClass = classOf[java.lang.Object] + override def newArray(len: Int) = new Array[scala.Any](len) + override def <:<(that: ClassManifest[_]): Boolean = (that eq this) + private def readResolve(): Any = Manifest.Any + } + val Any: Manifest[scala.Any] = AnyManifest + + private object ObjectManifest extends PhantomManifest[java.lang.Object](classOf[java.lang.Object], "Object") { + override def runtimeClass = classOf[java.lang.Object] + override def newArray(len: Int) = new Array[java.lang.Object](len) + override def <:<(that: ClassManifest[_]): Boolean = (that eq this) || (that eq Any) + private def readResolve(): Any = Manifest.Object + } + val Object: Manifest[java.lang.Object] = ObjectManifest + + val AnyRef: Manifest[scala.AnyRef] = Object + + private object AnyValManifest extends PhantomManifest[scala.AnyVal](classOf[java.lang.Object], "AnyVal") { + override def runtimeClass = classOf[java.lang.Object] + override def newArray(len: Int) = new Array[scala.AnyVal](len) + override def <:<(that: ClassManifest[_]): Boolean = (that eq this) || (that eq Any) + private def readResolve(): Any = Manifest.AnyVal + } + val AnyVal: Manifest[scala.AnyVal] = AnyValManifest + + private object NullManifest extends PhantomManifest[scala.Null](classOf[scala.runtime.Null$], "Null") { + override def runtimeClass = classOf[scala.runtime.Null$] + override def newArray(len: Int) = new Array[scala.Null](len) + override def <:<(that: ClassManifest[_]): Boolean = + (that ne null) && (that ne Nothing) && !(that <:< AnyVal) + private def readResolve(): Any = Manifest.Null + } + val Null: Manifest[scala.Null] = NullManifest + + private object NothingManifest extends PhantomManifest[scala.Nothing](classOf[scala.runtime.Nothing$], "Nothing") { + override def runtimeClass = classOf[scala.runtime.Nothing$] + override def newArray(len: Int) = new Array[scala.Nothing](len) + override def <:<(that: ClassManifest[_]): Boolean = (that ne null) + private def readResolve(): Any = Manifest.Nothing + } + val Nothing: Manifest[scala.Nothing] = NothingManifest + + @SerialVersionUID(1L) + private class SingletonTypeManifest[T <: AnyRef](value: AnyRef) extends Manifest[T] { + lazy val runtimeClass = value.getClass + override lazy val toString = value.toString + ".type" + } + + /** Manifest for the singleton type `value.type`. */ + def singleType[T <: AnyRef](value: AnyRef): Manifest[T] = + new SingletonTypeManifest[T](value) + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a top-level or static class. + * @note This no-prefix, no-arguments case is separate because we + * it's called from ScalaRunTime.boxArray itself. If we + * pass varargs as arrays into this, we get an infinitely recursive call + * to boxArray. (Besides, having a separate case is more efficient) + */ + def classType[T](clazz: Predef.Class[_]): Manifest[T] = + new ClassTypeManifest[T](None, clazz, Nil) + + /** Manifest for the class type `clazz`, where `clazz` is + * a top-level or static class and args are its type arguments. */ + def classType[T](clazz: Predef.Class[T], arg1: Manifest[_], args: Manifest[_]*): Manifest[T] = + new ClassTypeManifest[T](None, clazz, arg1 :: args.toList) + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a class with non-package prefix type `prefix` and type arguments `args`. + */ + def classType[T](prefix: Manifest[_], clazz: Predef.Class[_], args: Manifest[_]*): Manifest[T] = + new ClassTypeManifest[T](Some(prefix), clazz, args.toList) + + @SerialVersionUID(1L) + private abstract class PhantomManifest[T](_runtimeClass: Predef.Class[_], + override val toString: String) extends ClassTypeManifest[T](None, _runtimeClass, Nil) { + override def equals(that: Any): Boolean = this eq that.asInstanceOf[AnyRef] + override def hashCode = System.identityHashCode(this) + } + + /** Manifest for the class type `clazz[args]`, where `clazz` is + * a top-level or static class. */ + @SerialVersionUID(1L) + private class ClassTypeManifest[T](prefix: Option[Manifest[_]], + runtimeClass1: Predef.Class[_], + override val typeArguments: List[Manifest[_]]) extends Manifest[T] { + def runtimeClass: Predef.Class[_] = runtimeClass1 + override def toString = + (if (prefix.isEmpty) "" else prefix.get.toString+"#") + + (if (runtimeClass.isArray) "Array" else runtimeClass.getName) + + argString + } + + def arrayType[T](arg: Manifest[_]): Manifest[Array[T]] = + arg.asInstanceOf[Manifest[T]].arrayManifest + + @SerialVersionUID(1L) + private class AbstractTypeManifest[T](prefix: Manifest[_], name: String, upperBound: Predef.Class[_], args: scala.collection.Seq[Manifest[_]]) extends Manifest[T] { + def runtimeClass = upperBound + override val typeArguments = args.toList + override def toString = prefix.toString+"#"+name+argString + } + + /** Manifest for the abstract type `prefix # name`. `upperBound` is not + * strictly necessary as it could be obtained by reflection. It was + * added so that erasure can be calculated without reflection. */ + def abstractType[T](prefix: Manifest[_], name: String, upperBound: Predef.Class[_], args: Manifest[_]*): Manifest[T] = + new AbstractTypeManifest[T](prefix, name, upperBound, args) + + @SerialVersionUID(1L) + private class WildcardManifest[T](lowerBound: Manifest[_], upperBound: Manifest[_]) extends Manifest[T] { + def runtimeClass = upperBound.runtimeClass + override def toString = + "_" + + (if (lowerBound eq Nothing) "" else " >: "+lowerBound) + + (if (upperBound eq Nothing) "" else " <: "+upperBound) + } + + /** Manifest for the unknown type `_ >: L <: U` in an existential. + */ + def wildcardType[T](lowerBound: Manifest[_], upperBound: Manifest[_]): Manifest[T] = + new WildcardManifest[T](lowerBound, upperBound) + + @SerialVersionUID(1L) + private class IntersectionTypeManifest[T](parents: Array[Manifest[_]]) extends Manifest[T] { + // We use an `Array` instead of a `Seq` for `parents` to avoid cyclic dependencies during deserialization + // which can cause serialization proxies to leak and cause a ClassCastException. + def runtimeClass = parents(0).runtimeClass + override def toString = parents.mkString(" with ") + } + + /** Manifest for the intersection type `parents_0 with ... with parents_n`. */ + def intersectionType[T](parents: Manifest[_]*): Manifest[T] = + new IntersectionTypeManifest[T](parents.toArray) +} diff --git a/library-js/src/scala/runtime/BoxesRunTime.scala b/library-js/src/scala/runtime/BoxesRunTime.scala new file mode 100644 index 000000000000..48412eb77846 --- /dev/null +++ b/library-js/src/scala/runtime/BoxesRunTime.scala @@ -0,0 +1,145 @@ +package scala.runtime + +import scala.math.ScalaNumber + +/* The declaration of the class is only to make the JVM back-end happy when + * compiling the scalalib. + */ +final class BoxesRunTime + +object BoxesRunTime { + def boxToBoolean(b: Boolean): java.lang.Boolean = + b.asInstanceOf[java.lang.Boolean] + + def boxToCharacter(c: Char): java.lang.Character = + c.asInstanceOf[java.lang.Character] + + def boxToByte(b: Byte): java.lang.Boolean = + b.asInstanceOf[java.lang.Boolean] + + def boxToShort(s: Short): java.lang.Short = + s.asInstanceOf[java.lang.Short] + + def boxToInteger(i: Int): java.lang.Integer = + i.asInstanceOf[java.lang.Integer] + + def boxToLong(l: Long): java.lang.Long = + l.asInstanceOf[java.lang.Long] + + def boxToFloat(f: Float): java.lang.Float = + f.asInstanceOf[java.lang.Float] + + def boxToDouble(d: Double): java.lang.Double = + d.asInstanceOf[java.lang.Double] + + def unboxToBoolean(b: Any): Boolean = b.asInstanceOf[Boolean] + + def unboxToChar(c: Any): Char = c.asInstanceOf[Char] + + def unboxToByte(b: Any): Byte = b.asInstanceOf[Byte] + + def unboxToShort(s: Any): Short = s.asInstanceOf[Short] + + def unboxToInt(i: Any): Int = i.asInstanceOf[Int] + + def unboxToLong(l: Any): Long = l.asInstanceOf[Long] + + def unboxToFloat(f: Any): Float = f.asInstanceOf[Float] + + def unboxToDouble(d: Any): Double = d.asInstanceOf[Double] + + def equals(x: Object, y: Object): Boolean = + if (scala.scalajs.js.special.strictEquals(x, y)) true + else equals2(x, y) + + @inline // only called by equals(), not by codegen + def equals2(x: Object, y: Object): Boolean = { + x match { + case xn: java.lang.Number => equalsNumObject(xn, y) + case xc: java.lang.Character => equalsCharObject(xc, y) + case null => y eq null + case _ => x.equals(y) + } + } + + def equalsNumObject(xn: java.lang.Number, y: Object): Boolean = { + y match { + case yn: java.lang.Number => equalsNumNum(xn, yn) + case yc: java.lang.Character => equalsNumChar(xn, yc) + case _ => + if (xn eq null) + y eq null + else + xn.equals(y) + } + } + + def equalsNumNum(xn: java.lang.Number, yn: java.lang.Number): Boolean = { + (xn: Any) match { + case xn: Double => + (yn: Any) match { + case yn: Double => xn == yn + case yn: Long => xn == yn + case yn: ScalaNumber => yn.equals(xn) // xn is not a ScalaNumber + case _ => false // xn.equals(yn) must be false here + } + case xn: Long => + (yn: Any) match { + case yn: Long => xn == yn + case yn: Double => xn == yn + case yn: ScalaNumber => yn.equals(xn) // xn is not a ScalaNumber + case _ => false // xn.equals(yn) must be false here + } + case null => yn eq null + case _ => xn.equals(yn) + } + } + + def equalsCharObject(xc: java.lang.Character, y: Object): Boolean = { + y match { + case yc: java.lang.Character => xc.charValue() == yc.charValue() + case yn: java.lang.Number => equalsNumChar(yn, xc) + case _ => + if (xc eq null) + y eq null + else + false // xc.equals(y) must be false here, because y is not a Char + } + } + + @inline + private def equalsNumChar(xn: java.lang.Number, yc: java.lang.Character): Boolean = { + (xn: Any) match { + case xn: Double => xn == yc.charValue() + case xn: Long => xn == yc.charValue() + case _ => + if (xn eq null) yc eq null + else xn.equals(yc) + } + } + + @inline + def hashFromLong(n: java.lang.Long): Int = + Statics.longHash(n.asInstanceOf[Long]) + + @inline + def hashFromDouble(n: java.lang.Double): Int = + Statics.doubleHash(n.asInstanceOf[Double]) + + @inline + def hashFromFloat(n: java.lang.Float): Int = + Statics.floatHash(n.asInstanceOf[Float]) + + @inline // called only by ScalaRunTime.hash() + def hashFromNumber(n: java.lang.Number): Int = { + (n: Any) match { + case n: Double => Statics.doubleHash(n) + case n: Long => Statics.longHash(n) + case n => n.hashCode() + } + } + + @inline + def hashFromObject(a: Object): Int = + Statics.anyHash(a) +} diff --git a/library-js/src/scala/runtime/ScalaRunTime.scala b/library-js/src/scala/runtime/ScalaRunTime.scala new file mode 100644 index 000000000000..6e3803675608 --- /dev/null +++ b/library-js/src/scala/runtime/ScalaRunTime.scala @@ -0,0 +1,273 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package runtime + +import scala.collection.{AbstractIterator, AnyConstr, SortedOps, StrictOptimizedIterableOps, StringOps, StringView, View} +import scala.collection.immutable.{ArraySeq, NumericRange} +import scala.collection.mutable.StringBuilder +import scala.reflect.{ClassTag, classTag} +import java.lang.{Class => jClass} +import java.lang.reflect.{Method => JMethod} + +import scala.collection.generic.IsIterable + +/** The object ScalaRunTime provides support methods required by + * the scala runtime. All these methods should be considered + * outside the API and subject to change or removal without notice. + */ +object ScalaRunTime { + def isArray(x: Any, atLevel: Int = 1): Boolean = + x != null && isArrayClass(x.getClass, atLevel) + + private def isArrayClass(clazz: jClass[_], atLevel: Int): Boolean = + clazz != null && clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) + + // A helper method to make my life in the pattern matcher a lot easier. + def drop[Repr](coll: Repr, num: Int)(implicit iterable: IsIterable[Repr] { type C <: Repr }): Repr = + iterable(coll) drop num + + /** Return the class object representing an array with element class `clazz`. + */ + def arrayClass(clazz: jClass[_]): jClass[_] = { + // newInstance throws an exception if the erasure is Void.TYPE. see scala/bug#5680 + if (clazz == java.lang.Void.TYPE) classOf[Array[Unit]] + else java.lang.reflect.Array.newInstance(clazz, 0).getClass + } + + /** Return the class object representing an unboxed value type, + * e.g., classOf[int], not classOf[java.lang.Integer]. The compiler + * rewrites expressions like 5.getClass to come here. + */ + def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] = + classTag[T].runtimeClass.asInstanceOf[jClass[T]] + + /** Retrieve generic array element */ + def array_apply(xs: AnyRef, idx: Int): Any = { + xs match { + case x: Array[AnyRef] => x(idx).asInstanceOf[Any] + case x: Array[Int] => x(idx).asInstanceOf[Any] + case x: Array[Double] => x(idx).asInstanceOf[Any] + case x: Array[Long] => x(idx).asInstanceOf[Any] + case x: Array[Float] => x(idx).asInstanceOf[Any] + case x: Array[Char] => x(idx).asInstanceOf[Any] + case x: Array[Byte] => x(idx).asInstanceOf[Any] + case x: Array[Short] => x(idx).asInstanceOf[Any] + case x: Array[Boolean] => x(idx).asInstanceOf[Any] + case null => throw new NullPointerException + } + } + + /** update generic array element */ + def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { + xs match { + case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] + case x: Array[Int] => x(idx) = value.asInstanceOf[Int] + case x: Array[Double] => x(idx) = value.asInstanceOf[Double] + case x: Array[Long] => x(idx) = value.asInstanceOf[Long] + case x: Array[Float] => x(idx) = value.asInstanceOf[Float] + case x: Array[Char] => x(idx) = value.asInstanceOf[Char] + case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] + case x: Array[Short] => x(idx) = value.asInstanceOf[Short] + case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] + case null => throw new NullPointerException + } + } + + /** Get generic array length */ + @inline def array_length(xs: AnyRef): Int = java.lang.reflect.Array.getLength(xs) + + // TODO: bytecode Object.clone() will in fact work here and avoids + // the type switch. See Array_clone comment in BCodeBodyBuilder. + def array_clone(xs: AnyRef): AnyRef = xs match { + case x: Array[AnyRef] => x.clone() + case x: Array[Int] => x.clone() + case x: Array[Double] => x.clone() + case x: Array[Long] => x.clone() + case x: Array[Float] => x.clone() + case x: Array[Char] => x.clone() + case x: Array[Byte] => x.clone() + case x: Array[Short] => x.clone() + case x: Array[Boolean] => x.clone() + case null => throw new NullPointerException + } + + /** Convert an array to an object array. + * Needed to deal with vararg arguments of primitive types that are passed + * to a generic Java vararg parameter T ... + */ + def toObjectArray(src: AnyRef): Array[Object] = src match { + case x: Array[AnyRef] => x + case _ => + val length = array_length(src) + val dest = new Array[Object](length) + for (i <- 0 until length) + array_update(dest, i, array_apply(src, i)) + dest + } + + def toArray[T](xs: scala.collection.Seq[T]) = { + val arr = new Array[AnyRef](xs.length) + var i = 0 + for (x <- xs) { + arr(i) = x.asInstanceOf[AnyRef] + i += 1 + } + arr + } + + // Java bug: https://bugs.java.com/view_bug.do?bug_id=4071957 + // More background at ticket #2318. + def ensureAccessible(m: JMethod): JMethod = scala.reflect.ensureAccessible(m) + + def _toString(x: Product): String = + x.productIterator.mkString(x.productPrefix + "(", ",", ")") + + def _hashCode(x: Product): Int = scala.util.hashing.MurmurHash3.productHash(x) + + /** A helper for case classes. */ + def typedProductIterator[T](x: Product): Iterator[T] = { + new AbstractIterator[T] { + private[this] var c: Int = 0 + private[this] val cmax = x.productArity + def hasNext = c < cmax + def next() = { + val result = x.productElement(c) + c += 1 + result.asInstanceOf[T] + } + } + } + + /** Given any Scala value, convert it to a String. + * + * The primary motivation for this method is to provide a means for + * correctly obtaining a String representation of a value, while + * avoiding the pitfalls of naively calling toString on said value. + * In particular, it addresses the fact that (a) toString cannot be + * called on null and (b) depending on the apparent type of an + * array, toString may or may not print it in a human-readable form. + * + * @param arg the value to stringify + * @return a string representation of arg. + */ + def stringOf(arg: Any): String = stringOf(arg, scala.Int.MaxValue) + def stringOf(arg: Any, maxElements: Int): String = { + def packageOf(x: AnyRef) = x.getClass.getPackage match { + case null => "" + case p => p.getName + } + def isScalaClass(x: AnyRef) = packageOf(x) startsWith "scala." + def isScalaCompilerClass(x: AnyRef) = packageOf(x) startsWith "scala.tools.nsc." + + // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) + def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") + + // We use reflection because the scala.xml package might not be available + def isSubClassOf(potentialSubClass: Class[_], ofClass: String) = + try { + val classLoader = potentialSubClass.getClassLoader + val clazz = Class.forName(ofClass, /*initialize =*/ false, classLoader) + clazz.isAssignableFrom(potentialSubClass) + } catch { + case cnfe: ClassNotFoundException => false + } + def isXmlNode(potentialSubClass: Class[_]) = isSubClassOf(potentialSubClass, "scala.xml.Node") + def isXmlMetaData(potentialSubClass: Class[_]) = isSubClassOf(potentialSubClass, "scala.xml.MetaData") + + // When doing our own iteration is dangerous + def useOwnToString(x: Any) = x match { + // Range/NumericRange have a custom toString to avoid walking a gazillion elements + case _: Range | _: NumericRange[_] => true + // Sorted collections to the wrong thing (for us) on iteration - ticket #3493 + case _: SortedOps[_, _] => true + // StringBuilder(a, b, c) and similar not so attractive + case _: StringView | _: StringOps | _: StringBuilder => true + // Don't want to evaluate any elements in a view + case _: View[_] => true + // Node extends NodeSeq extends Seq[Node] and MetaData extends Iterable[MetaData] + // -> catch those by isXmlNode and isXmlMetaData. + // Don't want to a) traverse infinity or b) be overly helpful with peoples' custom + // collections which may have useful toString methods - ticket #3710 + // or c) print AbstractFiles which are somehow also Iterable[AbstractFile]s. + case x: Iterable[_] => (!x.isInstanceOf[StrictOptimizedIterableOps[_, AnyConstr, _]]) || !isScalaClass(x) || isScalaCompilerClass(x) || isXmlNode(x.getClass) || isXmlMetaData(x.getClass) + // Otherwise, nothing could possibly go wrong + case _ => false + } + + // A variation on inner for maps so they print -> instead of bare tuples + def mapInner(arg: Any): String = arg match { + case (k, v) => inner(k) + " -> " + inner(v) + case _ => inner(arg) + } + + // Special casing Unit arrays, the value class which uses a reference array type. + def arrayToString(x: AnyRef) = { + if (x.getClass.getComponentType == classOf[BoxedUnit]) + 0 until (array_length(x) min maxElements) map (_ => "()") mkString ("Array(", ", ", ")") + else + x.asInstanceOf[Array[_]].iterator.take(maxElements).map(inner).mkString("Array(", ", ", ")") + } + + // The recursively applied attempt to prettify Array printing. + // Note that iterator is used if possible and foreach is used as a + // last resort, because the parallel collections "foreach" in a + // random order even on sequences. + def inner(arg: Any): String = arg match { + case null => "null" + case "" => "\"\"" + case x: String => if (x.head.isWhitespace || x.last.isWhitespace) "\"" + x + "\"" else x + case x if useOwnToString(x) => x.toString + case x: AnyRef if isArray(x) => arrayToString(x) + case x: scala.collection.Map[_, _] => x.iterator take maxElements map mapInner mkString (x.collectionClassName + "(", ", ", ")") + case x: Iterable[_] => x.iterator take maxElements map inner mkString (x.collectionClassName + "(", ", ", ")") + case x: Product1[_] if isTuple(x) => "(" + inner(x._1) + ",)" // that special trailing comma + case x: Product if isTuple(x) => x.productIterator map inner mkString ("(", ",", ")") + case x => x.toString + } + + // The try/catch is defense against iterables which aren't actually designed + // to be iterated, such as some scala.tools.nsc.io.AbstractFile derived classes. + try inner(arg) + catch { + case _: UnsupportedOperationException | _: AssertionError => "" + arg + } + } + + /** stringOf formatted for use in a repl result. */ + def replStringOf(arg: Any, maxElements: Int): String = + stringOf(arg, maxElements) match { + case null => "null toString" + case s if s.indexOf('\n') >= 0 => "\n" + s + "\n" + case s => s + "\n" + } + + // Convert arrays to immutable.ArraySeq for use with Java varargs: + def genericWrapArray[T](xs: Array[T]): ArraySeq[T] = + if (xs eq null) null + else ArraySeq.unsafeWrapArray(xs) + def wrapRefArray[T <: AnyRef](xs: Array[T]): ArraySeq[T] = { + if (xs eq null) null + else if (xs.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq[T]] + else new ArraySeq.ofRef[T](xs) + } + def wrapIntArray(xs: Array[Int]): ArraySeq[Int] = if (xs ne null) new ArraySeq.ofInt(xs) else null + def wrapDoubleArray(xs: Array[Double]): ArraySeq[Double] = if (xs ne null) new ArraySeq.ofDouble(xs) else null + def wrapLongArray(xs: Array[Long]): ArraySeq[Long] = if (xs ne null) new ArraySeq.ofLong(xs) else null + def wrapFloatArray(xs: Array[Float]): ArraySeq[Float] = if (xs ne null) new ArraySeq.ofFloat(xs) else null + def wrapCharArray(xs: Array[Char]): ArraySeq[Char] = if (xs ne null) new ArraySeq.ofChar(xs) else null + def wrapByteArray(xs: Array[Byte]): ArraySeq[Byte] = if (xs ne null) new ArraySeq.ofByte(xs) else null + def wrapShortArray(xs: Array[Short]): ArraySeq[Short] = if (xs ne null) new ArraySeq.ofShort(xs) else null + def wrapBooleanArray(xs: Array[Boolean]): ArraySeq[Boolean] = if (xs ne null) new ArraySeq.ofBoolean(xs) else null + def wrapUnitArray(xs: Array[Unit]): ArraySeq[Unit] = if (xs ne null) new ArraySeq.ofUnit(xs) else null +} diff --git a/library-js/src/scala/util/DynamicVariable.scala b/library-js/src/scala/util/DynamicVariable.scala new file mode 100644 index 000000000000..b3eb4048499d --- /dev/null +++ b/library-js/src/scala/util/DynamicVariable.scala @@ -0,0 +1,74 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util + +import java.lang.InheritableThreadLocal + +/** `DynamicVariables` provide a binding mechanism where the current + * value is found through dynamic scope, but where access to the + * variable itself is resolved through static scope. + * + * The current value can be retrieved with the value method. New values + * should be pushed using the `withValue` method. Values pushed via + * `withValue` only stay valid while the `withValue`'s second argument, a + * parameterless closure, executes. When the second argument finishes, + * the variable reverts to the previous value. + * + * {{{ + * someDynamicVariable.withValue(newValue) { + * // ... code called in here that calls value ... + * // ... will be given back the newValue ... + * } + * }}} + * + * Each thread gets its own stack of bindings. When a + * new thread is created, the `DynamicVariable` gets a copy + * of the stack of bindings from the parent thread, and + * from then on the bindings for the new thread + * are independent of those for the original thread. + * + * @author Lex Spoon + * @since 2.6 + */ +class DynamicVariable[T](init: T) { + /* Scala.js: replaced InheritableThreadLocal by a simple var. + * This removes the dependency on ThreadLocal and InheritableThreadLocal from the + * Hello World. + */ + private[this] var v = init + + /** Retrieve the current value */ + def value: T = v + + /** Set the value of the variable while executing the specified + * thunk. + * + * @param newval The value to which to set the variable + * @param thunk The code to evaluate under the new setting + */ + def withValue[S](newval: T)(thunk: => S): S = { + val oldval = v + v = newval + + try thunk + finally v = oldval + } + + /** Change the currently bound value, discarding the old value. + * Usually withValue() gives better semantics. + */ + def value_=(newval: T) = v = newval + + override def toString: String = "DynamicVariable(" + value + ")" +} diff --git a/library-js/src/scala/util/control/NoStackTrace.scala b/library-js/src/scala/util/control/NoStackTrace.scala new file mode 100644 index 000000000000..046abf2fd830 --- /dev/null +++ b/library-js/src/scala/util/control/NoStackTrace.scala @@ -0,0 +1,39 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala +package util.control + +/** A trait for exceptions which, for efficiency reasons, do not + * fill in the stack trace. Stack trace suppression can be disabled + * on a global basis via a system property wrapper in + * [[scala.sys.SystemProperties]]. + * + * @note Since JDK 1.7, a similar effect can be achieved with `class Ex extends Throwable(..., writableStackTrace = false)` + * + * @author Paul Phillips + * @since 2.8 + */ +trait NoStackTrace extends Throwable { + override def fillInStackTrace(): Throwable = + if (NoStackTrace.noSuppression) super.fillInStackTrace() + else this +} + +object NoStackTrace { + final def noSuppression = _noSuppression + + // two-stage init to make checkinit happy, since sys.SystemProperties.noTraceSupression.value calls back into NoStackTrace.noSuppression + final private var _noSuppression = false + // !!! Disabled in Scala.js because SystemProperties is not supported + //_noSuppression = System.getProperty("scala.control.noTraceSuppression", "").equalsIgnoreCase("true") +}