Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions library-js/src/scala/Array.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,23 @@ import scala.runtime.ScalaRunTime.{array_apply, array_update}
* @since 1.0
*/
object Array {
/** An empty `Array[Boolean]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyBooleanArray = EmptyArrays.emptyBooleanArray
/** An empty `Array[Byte]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyByteArray = EmptyArrays.emptyByteArray
/** An empty `Array[Char]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyCharArray = EmptyArrays.emptyCharArray
/** An empty `Array[Double]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyDoubleArray = EmptyArrays.emptyDoubleArray
/** An empty `Array[Float]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyFloatArray = EmptyArrays.emptyFloatArray
/** An empty `Array[Int]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyIntArray = EmptyArrays.emptyIntArray
/** An empty `Array[Long]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyLongArray = EmptyArrays.emptyLongArray
/** An empty `Array[Short]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyShortArray = EmptyArrays.emptyShortArray
/** An empty `Array[Object]`, shared to avoid allocation; a zero-length array cannot be mutated. */
def emptyObjectArray = EmptyArrays.emptyObjectArray

private object EmptyArrays {
Expand Down Expand Up @@ -82,6 +91,20 @@ object Array {
*/
def newBuilder[T](implicit t: ClassTag[T]): ArrayBuilder[T] = ArrayBuilder.make[T](using t)

/** Builds an array from the iterable collection.
*
* ```
* scala> val a = Array.from(Seq(1, 5))
* val a: Array[Int] = Array(1, 5)
*
* scala> val b = Array.from(Range(1, 5))
* val b: Array[Int] = Array(1, 2, 3, 4)
* ```
*
* @tparam A the element type of the array
* @param it the iterable collection
* @return an array consisting of elements of the iterable collection
*/
def from[A: ClassTag](it: IterableOnce[A]^): Array[A] = {
val n = it.knownSize
if (n > -1) {
Expand Down Expand Up @@ -678,6 +701,21 @@ object Array {
}
}

/** Compares two arrays per element.
*
* A more efficient version of `xs.sameElements(ys)`.
*
* Note that arrays are invariant in Scala, but it may
* be sound to cast an array of arbitrary reference type
* to `Array[AnyRef]`. Arrays are covariant in their
* element type at run time.
*
* `Array.equals(xs.asInstanceOf[Array[AnyRef]], ys.asInstanceOf[Array[AnyRef]])`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use triple quotes here.

*
* @param xs an array of AnyRef
* @param ys an array of AnyRef
* @return true if corresponding elements are equal
*/
def equals(xs: Array[AnyRef], ys: Array[AnyRef]): Boolean = {
if (xs eq ys)
return true
Expand All @@ -702,12 +740,47 @@ object Array {
*/
def unapplySeq[T](x: Array[T]): UnapplySeqWrapper[T] = new UnapplySeqWrapper(x)

/** A wrapper that lets an array be destructured by a sequence pattern such as
* `case Array(x, y, z) =>`.
*
* The members below implement the name-based extractor protocol directly against
* the wrapped array, so a fixed-arity pattern matches without building an
* intermediate sequence.
*
* @tparam T the element type of the wrapped array
* @param a the array to be destructured
*/
final class UnapplySeqWrapper[T](private val a: Array[T]) extends AnyVal {
/** Returns `false`, since this extractor never yields an empty result.
*
* The extraction itself cannot fail, so [[get]] is always available. Whether a
* particular sequence pattern matches is decided separately, by [[lengthCompare]]
* and the element accessors.
*/
def isEmpty: Boolean = false
/** Returns this wrapper, whose sequence-like operations supply the elements of the pattern. */
def get: UnapplySeqWrapper[T] = this
/** Compares the length of the wrapped array to a test value.
*
* @param len the test value that gets compared with the length
* @return a value less than, equal to, or greater than `0` as the length of the array is less than, equal to, or greater than `len`
*/
def lengthCompare(len: Int): Int = a.lengthCompare(len)
/** Returns the element of the wrapped array at the given index.
*
* @param i the index, which must be in the range from `0` until the length of the array
* @return the element at index `i`
* @throws ArrayIndexOutOfBoundsException if `i` is negative or not less than the length of the array
*/
def apply(i: Int): T = a(i)
/** Returns all elements of the wrapped array except the first `n`, as needed to bind the
* variable-length part of a pattern such as `case Array(x, rest*) =>`.
*
* @param n the number of leading elements to skip
* @return a sequence backed by a fresh copy of the remaining elements, even when `n` is `0`
*/
def drop(n: Int): scala.Seq[T] = ArraySeq.unsafeWrapArray(a.drop(n)) // clones the array, also if n == 0
/** Returns the elements of the wrapped array as a sequence backed by a copy of the array. */
def toSeq: scala.Seq[T] = a.toSeq // clones the array
}
}
Expand Down
15 changes: 15 additions & 0 deletions library-js/src/scala/Console.scala
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,23 @@ object Console extends AnsiColor {
private[this] val inVar = new DynamicVariable[BufferedReader](null.asInstanceOf[BufferedReader])
//new BufferedReader(new InputStreamReader(java.lang.System.in)))

/** Redefines the default output stream, replacing the current binding directly
* rather than scoping the change to a thunk as `withOut` does.
*
* @param out the new default output stream
*/
protected def setOutDirect(out: PrintStream): Unit = outVar.value = out
/** Redefines the default error stream, replacing the current binding directly
* rather than scoping the change to a thunk as `withErr` does.
*
* @param err the new default error stream
*/
protected def setErrDirect(err: PrintStream): Unit = errVar.value = err
/** Redefines the default input, replacing the current binding directly
* rather than scoping the change to a thunk as `withIn` does.
*
* @param in the new default input reader
*/
protected def setInDirect(in: BufferedReader): Unit = inVar.value = in

/** The default output, can be overridden by `withOut`.
Expand Down
139 changes: 139 additions & 0 deletions library-js/src/scala/Enumeration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,16 @@ import scala.util.matching.Regex
abstract class Enumeration (initial: Int) extends Serializable {
thisenum =>

/** Creates an enumeration whose value ids are counted from `0`. */
def this() = this(0)

/* Note that `readResolve` cannot be private, since otherwise
the JVM does not invoke it when deserializing subclasses. */
/** Serialization hook that on the JVM resolves a deserialized enumeration to
* its singleton module instance; not implemented on Scala.js.
*
* @throws NotImplementedError always
*/
protected def readResolve(): AnyRef = ???

/** The name of this enumeration. */
Expand Down Expand Up @@ -217,14 +223,26 @@ abstract class Enumeration (initial: Int) extends Serializable {
/** A marker so we can tell whose values belong to whom come reflective-naming time. */
private[Enumeration] val outerEnum = thisenum

/** Compares this value with another value of this enumeration by id.
*
* @param that the value to compare with
* @return `-1`, `0`, or `1` as this value's id is less than, equal to, or
* greater than the id of `that`
*/
override def compare(that: Value): Int =
if (this.id < that.id) -1
else if (this.id == that.id) 0
else 1
/** Returns `true` if `other` is a value of the same enumeration instance
* with the same id as this value.
*
* @param other the object to compare with
*/
override def equals(other: Any) = other match {
case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id)
case _ => false
}
/** Returns the hash code of this value, computed from its id. */
override def hashCode: Int = id.##

/** Creates a ValueSet which contains this value and another one.
Expand All @@ -240,8 +258,22 @@ abstract class Enumeration (initial: Int) extends Serializable {
*/
@SerialVersionUID(0 - 3501153230598116017L)
protected class Val(i: Int, name: String | Null) extends Value with Serializable {
/** Creates a fresh value identified by `i`, named by the next name in
* `nextName` if defined, or unnamed otherwise.
*
* @param i the integer that identifies the value at run-time; must be
* unique amongst all values of the enumeration
*/
def this(i: Int) = this(i, nextNameOrNull)
/** Creates a fresh value identified by `nextId`, called `name`.
*
* @param name a human-readable name for the value, or `null` for an
* unnamed value
*/
def this(name: String | Null) = this(nextId, name)
/** Creates a fresh value identified by `nextId`, named by the next name in
* `nextName` if defined, or unnamed otherwise.
*/
def this() = this(nextId)

assert(!vmap.isDefinedAt(i), "Duplicate id: " + i)
Expand All @@ -250,12 +282,23 @@ abstract class Enumeration (initial: Int) extends Serializable {
nextId = i + 1
if (nextId > topId) topId = nextId
if (i < bottomId) bottomId = i
/** The integer that identifies this value within its enumeration. */
def id = i
/** Returns the name of this value if one was provided or drawn from
* `nextName`, otherwise a placeholder of the form
* `<Unknown name for enum field #$i of class $cls>` (on Scala.js, names of
* unnamed values cannot be recovered by reflection).
*/
override def toString() =
if (name != null) name
// Scala.js specific
else s"<Unknown name for enum field #$i of class ${getClass}>"

/** During deserialization, replaces this value with the equivalent value
* registered in the owning enumeration, as resolved by
* `Enumeration.readResolve` (which is unimplemented on Scala.js and throws
* `NotImplementedError` unless overridden).
*/
protected def readResolve(): AnyRef = {
val enumeration = thisenum.readResolve().asInstanceOf[Enumeration]
if (enumeration.vmap == null) this
Expand All @@ -265,6 +308,13 @@ abstract class Enumeration (initial: Int) extends Serializable {

/** An ordering by id for values of this set. */
implicit object ValueOrdering extends Ordering[Value] {
/** Compares two values of this enumeration by id.
*
* @param x the first value to compare
* @param y the second value to compare
* @return `-1`, `0`, or `1` as the id of `x` is less than, equal to, or
* greater than the id of `y`
*/
def compare(x: Value, y: Value): Int = x compare y
}

Expand All @@ -282,37 +332,121 @@ abstract class Enumeration (initial: Int) extends Serializable {
with StrictOptimizedIterableOps[Value, immutable.Set, ValueSet]
with Serializable {

/** Returns [[ValueOrdering]], the ordering of values by their ids. */
implicit def ordering: Ordering[Value] = ValueOrdering
/** Returns the values of this set whose ids lie within given bounds.
*
* @param from the value whose id is the inclusive lower bound, or `None`
* for no lower bound
* @param until the value whose id is the exclusive upper bound, or `None`
* for no upper bound
* @return a `ValueSet` containing the values of this set within the bounds
*/
def rangeImpl(from: Option[Value], until: Option[Value]): ValueSet =
new ValueSet(nnIds.rangeImpl(from.map(_.id - bottomId), until.map(_.id - bottomId)))

/** Returns the empty set of values of this enumeration. */
override def empty = ValueSet.empty
/** Returns the number of values in this set; always known, since the
* underlying bit set is finite.
*/
override def knownSize: Int = nnIds.size
/** Returns `true` if this set contains no values. */
override def isEmpty: Boolean = nnIds.isEmpty
/** Tests whether this set contains a given value.
*
* @param v the value to test for membership
*/
def contains(v: Value) = nnIds contains (v.id - bottomId)
/** Returns a new `ValueSet` containing the values of this set and the given value.
*
* @param value the value to add
*/
def incl (value: Value) = new ValueSet(nnIds + (value.id - bottomId))
/** Returns a new `ValueSet` containing the values of this set except the given value.
*
* @param value the value to remove
*/
def excl (value: Value) = new ValueSet(nnIds - (value.id - bottomId))
/** Returns an iterator over the values in this set, in increasing order of their ids. */
def iterator = nnIds.iterator map (id => thisenum.apply(bottomId + id))
/** Returns an iterator over the values in this set whose ids are greater
* than or equal to that of `start`, in increasing order of their ids.
*
* @param start the inclusive lower bound for the values to return
*/
override def iteratorFrom(start: Value) = nnIds iteratorFrom start.id map (id => thisenum.apply(bottomId + id))
/** Returns the name used to prefix the string representation of this set:
* the enumeration's name followed by `.ValueSet`.
*/
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

/** Builds a `ValueSet` containing the values of the given collection.
*
* @param coll the source of values for the new set
*/
override protected def fromSpecific(coll: IterableOnce[Value]) = ValueSet.fromSpecific(coll)
/** Returns a new builder that accumulates values into a `ValueSet`. */
override protected def newSpecificBuilder = ValueSet.newBuilder

/** Builds a new `ValueSet` by applying a function to each value of this set.
*
* @param f the function to apply to each value
* @return a `ValueSet` of the results, ordered by their ids
*/
def map(f: Value => Value): ValueSet = fromSpecific(new View.Map(this, f))
/** Builds a new `ValueSet` by applying a function to each value of this set
* and collecting all values in the results.
*
* @param f the function to apply to each value
* @return a `ValueSet` of all values produced by `f`, ordered by their ids
*/
def flatMap(f: Value => IterableOnce[Value]): ValueSet = fromSpecific(new View.FlatMap(this, f))

// necessary for disambiguation:
/** Builds a new sorted set by applying a function to each value of this set.
*
* @tparam B the element type of the returned set
* @param f the function to apply to each value
* @param ev the ordering for the elements of the returned set
* @return a sorted set of the results of applying `f` to each value
*/
override def map[B](f: Value => B)(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] =
super[SortedSet].map[B](f)
/** Builds a new sorted set by applying a function to each value of this set
* and collecting all elements in the results.
*
* @tparam B the element type of the returned set
* @param f the function to apply to each value
* @param ev the ordering for the elements of the returned set
* @return a sorted set of all elements produced by `f`
*/
override def flatMap[B](f: Value => IterableOnce[B])(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] =
super[SortedSet].flatMap[B](f)
/** Builds a new sorted set of pairs formed from the values of this set and
* the corresponding elements of another collection, dropping whatever
* remains of the longer of the two.
*
* @tparam B the type of the second element of each pair
* @param that the collection providing the second element of each pair
* @param ev the ordering for the pairs in the returned set
* @return a sorted set of corresponding pairs
*/
override def zip[B](that: IterableOnce[B])(implicit @implicitNotFound(ValueSet.zipOrdMsg) ev: Ordering[(Value, B)]): immutable.SortedSet[(Value, B)] =
super[SortedSet].zip[B](that)
/** Builds a new sorted set by applying a partial function to each value of
* this set on which it is defined.
*
* @tparam B the element type of the returned set
* @param pf the partial function to apply to each value
* @param ev the ordering for the elements of the returned set
* @return a sorted set of the results of applying `pf` to each value on
* which it is defined
*/
override def collect[B](pf: PartialFunction[Value, B])(implicit @implicitNotFound(ValueSet.ordMsg) ev: Ordering[B]): immutable.SortedSet[B] =
super[SortedSet].collect[B](pf)
}
Expand All @@ -338,6 +472,11 @@ abstract class Enumeration (initial: Int) extends Serializable {
def clear() = b.clear()
def result() = new ValueSet(b.toImmutable)
}
/** Builds a `ValueSet` containing the values of the given collection.
*
* @param it the source of values for the new set
* @return a `ValueSet` containing the values of `it`
*/
def fromSpecific(it: IterableOnce[Value]): ValueSet =
newBuilder.addAll(it).result()
}
Expand Down
7 changes: 7 additions & 0 deletions library-js/src/scala/MatchError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,12 @@ final class MatchError(@transient obj: Any) extends RuntimeException {
this
}

/** Returns a message describing the object that failed to match.
*
* The message contains the object's string representation and its class name,
* falling back to the class name alone if its `toString` throws, and naming

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback, when happens, is:

"an instance " + ofClass

Not just the class name alone.

Also, the comment didn't mention that when obj is null it will return "null".

* the class "a JS class" if the object has no Java class. The message is
* computed at most once.
*/
override def getMessage() = objString
}
Loading
Loading