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
45 changes: 45 additions & 0 deletions library/src/scala/runtime/ArrayCharSequence.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,55 @@ import scala.language.`2.13`

// Still need this one since the implicit class ArrayCharSequence only converts
// a single argument.
/** A `CharSequence` view of a slice of an `Array[Char]`.
*
* The sequence consists of the characters of `xs` from index `start` until
* `end`. Characters are read from the array on demand, so later writes to
* the array are visible through this sequence. The bounds are not validated
* on construction: `end <= start` yields an empty sequence, and out-of-range
* bounds only surface when characters are accessed.
*
* @param xs the underlying character array
* @param start the index in `xs` of the first character of the sequence
* @param end the index in `xs` one past the last character of the sequence
*/
final class ArrayCharSequence(val xs: Array[Char], start: Int, end: Int) extends CharSequence {
// yikes
// java.lang.VerifyError: (class: scala/runtime/ArrayCharSequence, method: <init> signature: ([C)V)
// Constructor must call super() or this()
//
// def this(xs: Array[Char]) = this(xs, 0, xs.length)

/** Returns the number of characters in this sequence: `end - start`, or `0` if `end <= start`. */
def length: Int = math.max(0, end - start)
/** Returns the character at the given index of this sequence, that is, the
* character at index `start + index` of the underlying array.
*
* @param index the index of the character to return, from `0` to `length - 1`
* @throws ArrayIndexOutOfBoundsException if `index` is negative or not less
* than `length`, or if the slice this sequence was constructed with
* falls outside the array, since those bounds are not validated (the
* exception message reports the bounds of the underlying array, not
* of this sequence)
*/
def charAt(index: Int): Char = {
if (0 <= index && index < length)
xs(start + index)
else throw new ArrayIndexOutOfBoundsException(s"$index is out of bounds (min 0, max ${xs.length - 1})")
}
/** Returns a new `ArrayCharSequence` over the characters of this sequence
* from index `start0` until `end0`.
*
* The result is a view over the same underlying array; no characters are
* copied.
*
* @param start0 the index in this sequence of the first character of the subsequence
* @param end0 the index in this sequence one past the last character of the subsequence
* @return the subsequence view; empty if `end0 <= start0` (no exception is
* thrown for an inverted range, unlike the `CharSequence` contract)
* @throws ArrayIndexOutOfBoundsException if `start0` is negative or `end0`
* is greater than `length`
*/
def subSequence(start0: Int, end0: Int): CharSequence = {
if (start0 < 0) throw new ArrayIndexOutOfBoundsException(s"$start0 is out of bounds (min 0, max ${length -1})")
else if (end0 > length) throw new ArrayIndexOutOfBoundsException(s"$end0 is out of bounds (min 0, max ${xs.length -1})")
Expand All @@ -40,6 +76,15 @@ final class ArrayCharSequence(val xs: Array[Char], start: Int, end: Int) extends
new ArrayCharSequence(xs, start1, start1 + newlen)
}
}
/** Returns the characters of this sequence as a `String`.
*
* The bounds are clamped to the underlying array before copying: a
* negative `start` is treated as `0` and the end is capped at the array's
* length, so a sequence constructed with out-of-range bounds yields
* characters rather than throwing. The count is taken from the declared
* bounds, so a negative `start` shifts the window: `start = -2, end = 5`
* copies seven characters from index `0`, not the five in range.
*/
override def toString() = {
val start = math.max(this.start, 0)
val end = math.min(xs.length, start + length)
Expand Down
18 changes: 18 additions & 0 deletions library/src/scala/runtime/EnumValue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,27 @@ package scala.runtime
import language.experimental.captureChecking

transparent trait EnumValue extends Product, Serializable:
/** Returns `true` if `that` is the same object as this one, by reference.
*
* Each simple enum case is a singleton, so it can only compare equal to
* itself.
*
* @param that the value to compare with this enum value
*/
override def canEqual(that: Any) = this eq that.asInstanceOf[AnyRef]
/** Returns `0`: a simple enum case has no case fields. */
override def productArity: Int = 0
/** Always throws: a simple enum case has no elements.
*
* @param n the index of the requested element; no index is valid
* @throws IndexOutOfBoundsException always, with `n` as its message
*/
override def productElement(n: Int): Any =
throw IndexOutOfBoundsException(n.toString)
/** Always throws: a simple enum case has no elements.
*
* @param n the index of the requested element name; no index is valid
* @throws IndexOutOfBoundsException always, with `n` as its message
*/
override def productElementName(n: Int): String =
throw IndexOutOfBoundsException(n.toString)
1 change: 1 addition & 0 deletions library/src/scala/runtime/FunctionXXL.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ trait FunctionXXL {
*/
def apply(xs: IArray[Object]): Object

/** Returns the string `"<functionXXL>"`, mirroring the `"<functionN>"` rendering of the `Function0` to `Function22` traits. */
override def toString() = "<functionXXL>"
}
39 changes: 39 additions & 0 deletions library/src/scala/runtime/LambdaDeserialize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ import scala.collection.immutable

import scala.language.`2.13`

/** The per-class state behind the synthetic `$deserializeLambda$` method of
* a class hosting lambdas: the class's lookup, a map from implementation
* method name-and-descriptor keys to their method handles, and a cache of
* deserialization factories keyed the same way.
*
* Created by `LambdaDeserialize.bootstrap`, which the JVM invokes via
* `invokedynamic`.
*/
final class LambdaDeserialize private (lookup: MethodHandles.Lookup, targetMethods: Array[MethodHandle]) {
private val targetMethodMap: util.HashMap[String, MethodHandle] = new util.HashMap[String, MethodHandle](targetMethods.length)

Expand All @@ -32,16 +40,47 @@ final class LambdaDeserialize private (lookup: MethodHandles.Lookup, targetMetho

private val cache = new util.HashMap[String, MethodHandle]

/** Returns an instance of the functional interface described by
* `serialized`, delegating to [[LambdaDeserializer.deserializeLambda]]
* with this instance's lookup, factory cache, and target method map.
*
* @param serialized the serialized form of the lambda to deserialize
* @throws IllegalArgumentException if the implementation method named by
* `serialized` is not among this instance's target methods
*/
def deserializeLambda(serialized: SerializedLambda): AnyRef = LambdaDeserializer.deserializeLambda(lookup, cache, targetMethodMap, serialized)
}

object LambdaDeserialize {
/** Bootstrap method that the JVM invokes, via the `invokedynamic`
* instruction in the synthetic `$deserializeLambda$` method of a class
* hosting lambdas, to link that method's call site.
*
* @param lookup the lookup of the class hosting the lambdas
* @param invokedName never used
* @param invokedType the type the call site's target is adapted to,
* taking a `SerializedLambda` and returning the
* deserialized object
* @param targetMethods handles for the lambda implementation methods of
* the class, from which deserialization requests
* are resolved by name and descriptor
* @return a `ConstantCallSite` whose target is `deserializeLambda` bound
* to a `LambdaDeserialize` built over `lookup` and
* `targetMethods`
*/
@varargs @throws[Throwable]
def bootstrap(lookup: MethodHandles.Lookup, @unused invokedName: String, invokedType: MethodType, targetMethods: MethodHandle*): CallSite = {
val targetMethodsArray = targetMethods.asInstanceOf[immutable.ArraySeq[?]].unsafeArray.asInstanceOf[Array[MethodHandle]]
val exact = MethodHandleConstants.LAMBDA_DESERIALIZE_DESERIALIZE_LAMBDA.bindTo(new LambdaDeserialize(lookup, targetMethodsArray)).asType(invokedType)
new ConstantCallSite(exact)
}

/** Returns the key under which an implementation method is stored in the
* target method map and factory cache: `name` concatenated with
* `descriptor`.
*
* @param name the name of the implementation method
* @param descriptor the JVM method descriptor of its signature
*/
def nameAndDescriptorKey(name: String, descriptor: String): String = name + descriptor
}
15 changes: 15 additions & 0 deletions library/src/scala/runtime/LambdaDeserializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ object LambdaDeserializer {
else result
}

/** Deserializes a lambda like [[deserializeLambda]], but returns `null`
* instead of throwing when the implementation method named by
* `serialized` has no entry in `targetMethodMap`.
*
* @param lookup The factory for method handles. Must have access to the implementation method, the
* functional interface class, and `java.io.Serializable`.
* @param cache A cache used to avoid spinning up a class for each deserialization of a given lambda. May be `null`
* @param targetMethodMap a mapping from lambda implementation method name and signature keys (as produced by
* `LambdaDeserialize.nameAndDescriptorKey`) to their `MethodHandle`s, used to look up the
* implementation method during deserialization. Must not be `null`
* @param serialized The lambda to deserialize. Note that this is typically created by the `readResolve`
* member of the anonymous class created by `LambdaMetaFactory`.
* @return an instance of the functional interface, or `null` if the implementation
* method is not found in `targetMethodMap`
*/
def deserializeLambdaOrNull(lookup: MethodHandles.Lookup, cache: java.util.Map[String, MethodHandle],
targetMethodMap: java.util.Map[String, MethodHandle], serialized: SerializedLambda): AnyRef | Null = {
assert(targetMethodMap != null)
Expand Down
Loading
Loading