Skip to content
This repository
Browse code

Fast PartialFunction # orElse.

  • Loading branch information...
commit 5fb26c6a889cf1609823338df8783bf880769b3f 1 parent 32a7535
odersky odersky authored
2  src/actors/scala/actors/Actor.scala
@@ -247,7 +247,7 @@ object Actor extends Combinators {
247 247
248 248 private class RecursiveProxyHandler(a: ReplyReactor, f: PartialFunction[Any, Unit])
249 249 extends scala.runtime.AbstractPartialFunction[Any, Unit] {
250   - def isDefinedAt(m: Any): Boolean =
  250 + def _isDefinedAt(m: Any): Boolean =
251 251 true // events are immediately removed from the mailbox
252 252 def apply(m: Any) {
253 253 if (f.isDefinedAt(m)) f(m)
2  src/actors/scala/actors/Future.scala
@@ -201,7 +201,7 @@ object Futures {
201 201
202 202 def awaitWith(partFuns: Seq[PartialFunction[Any, Pair[Int, Any]]]) {
203 203 val reaction: PartialFunction[Any, Unit] = new scala.runtime.AbstractPartialFunction[Any, Unit] {
204   - def isDefinedAt(msg: Any) = msg match {
  204 + def _isDefinedAt(msg: Any) = msg match {
205 205 case TIMEOUT => true
206 206 case _ => partFuns exists (_ isDefinedAt msg)
207 207 }
2  src/actors/scala/actors/Reactor.scala
@@ -39,7 +39,7 @@ private[actors] object Reactor {
39 39 }
40 40
41 41 val waitingForNone: PartialFunction[Any, Unit] = new scala.runtime.AbstractPartialFunction[Any, Unit] {
42   - def isDefinedAt(x: Any) = false
  42 + def _isDefinedAt(x: Any) = false
43 43 def apply(x: Any) {}
44 44 }
45 45 }
1  src/compiler/scala/reflect/internal/Flags.scala
@@ -96,6 +96,7 @@ class ModifierFlags {
96 96 final val INTERFACE = 0x00000080 // symbol is an interface (i.e. a trait which defines only abstract methods)
97 97 final val MUTABLE = 0x00001000 // symbol is a mutable variable.
98 98 final val PARAM = 0x00002000 // symbol is a (value or type) parameter to a method
  99 + final val MACRO = 0x00008000 // symbol is a macro definition
99 100
100 101 final val COVARIANT = 0x00010000 // symbol is a covariant type variable
101 102 final val BYNAMEPARAM = 0x00010000 // parameter is by name
10 src/compiler/scala/tools/cmd/FromString.scala
@@ -16,7 +16,7 @@ import scala.reflect.OptManifest
16 16 */
17 17 abstract class FromString[+T](implicit m: OptManifest[T]) extends scala.runtime.AbstractPartialFunction[String, T] {
18 18 def apply(s: String): T
19   - def isDefinedAt(s: String): Boolean = true
  19 + def _isDefinedAt(s: String): Boolean = true
20 20 def zero: T = apply("")
21 21
22 22 def targetString: String = m.toString
@@ -30,20 +30,20 @@ object FromString {
30 30 /** Path related stringifiers.
31 31 */
32 32 val ExistingFile: FromString[File] = new FromString[File] {
33   - override def isDefinedAt(s: String) = toFile(s).isFile
  33 + override def _isDefinedAt(s: String) = toFile(s).isFile
34 34 def apply(s: String): File =
35 35 if (isDefinedAt(s)) toFile(s)
36 36 else cmd.runAndExit(println("'%s' is not an existing file." format s))
37 37 }
38 38 val ExistingDir: FromString[Directory] = new FromString[Directory] {
39   - override def isDefinedAt(s: String) = toDir(s).isDirectory
  39 + override def _isDefinedAt(s: String) = toDir(s).isDirectory
40 40 def apply(s: String): Directory =
41 41 if (isDefinedAt(s)) toDir(s)
42 42 else cmd.runAndExit(println("'%s' is not an existing directory." format s))
43 43 }
44 44 def ExistingDirRelativeTo(root: Directory) = new FromString[Directory] {
45 45 private def resolve(s: String) = toDir(s) toAbsoluteWithRoot root toDirectory
46   - override def isDefinedAt(s: String) = resolve(s).isDirectory
  46 + override def _isDefinedAt(s: String) = resolve(s).isDirectory
47 47 def apply(s: String): Directory =
48 48 if (isDefinedAt(s)) resolve(s)
49 49 else cmd.runAndExit(println("'%s' is not an existing directory." format resolve(s)))
@@ -65,7 +65,7 @@ object FromString {
65 65 /** Implicit as the most likely to be useful as-is.
66 66 */
67 67 implicit val IntFromString: FromString[Int] = new FromString[Int] {
68   - override def isDefinedAt(s: String) = safeToInt(s).isDefined
  68 + override def _isDefinedAt(s: String) = safeToInt(s).isDefined
69 69 def apply(s: String) = safeToInt(s).get
70 70 def safeToInt(s: String): Option[Int] = try Some(java.lang.Integer.parseInt(s)) catch { case _: NumberFormatException => None }
71 71 }
2  src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc
7 7 package interpreter
8 8
9 9 class AbstractOrMissingHandler[T](onError: String => Unit, value: T) extends scala.runtime.AbstractPartialFunction[Throwable, T] {
10   - def isDefinedAt(t: Throwable) = t match {
  10 + def _isDefinedAt(t: Throwable) = t match {
11 11 case _: AbstractMethodError => true
12 12 case _: NoSuchMethodError => true
13 13 case _: MissingRequirementError => true
2  src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
@@ -433,7 +433,7 @@ trait ParallelMatching extends ast.TreeDSL
433 433 case (false, false) => pivotLen == x.nonStarLength
434 434 }
435 435
436   - def isDefinedAt(pat: Pattern) = pat match {
  436 + def _isDefinedAt(pat: Pattern) = pat match {
437 437 case x: SequenceLikePattern => seqIsDefinedAt(x)
438 438 case WildcardPattern() => true
439 439 case _ => false
2  src/library/scala/Function.scala
@@ -41,7 +41,7 @@ object Function {
41 41 */
42 42 def unlift[T, R](f: T => Option[R]): PartialFunction[T, R] = new runtime.AbstractPartialFunction[T, R] {
43 43 def apply(x: T): R = f(x).get
44   - def isDefinedAt(x: T): Boolean = f(x).isDefined
  44 + def _isDefinedAt(x: T): Boolean = f(x).isDefined
45 45 override def lift: T => Option[R] = f
46 46 }
47 47
7 src/library/scala/PartialFunction.scala
@@ -41,7 +41,7 @@ trait PartialFunction[-A, +B] extends (A => B) {
41 41 */
42 42 def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] =
43 43 new runtime.AbstractPartialFunction[A1, B1] {
44   - def isDefinedAt(x: A1): Boolean =
  44 + def _isDefinedAt(x: A1): Boolean =
45 45 PartialFunction.this.isDefinedAt(x) || that.isDefinedAt(x)
46 46 def apply(x: A1): B1 =
47 47 if (PartialFunction.this.isDefinedAt(x)) PartialFunction.this.apply(x)
@@ -59,7 +59,7 @@ trait PartialFunction[-A, +B] extends (A => B) {
59 59 * arguments `x` to `k(this(x))`.
60 60 */
61 61 override def andThen[C](k: B => C) : PartialFunction[A, C] = new runtime.AbstractPartialFunction[A, C] {
62   - def isDefinedAt(x: A): Boolean = PartialFunction.this.isDefinedAt(x)
  62 + def _isDefinedAt(x: A): Boolean = PartialFunction.this.isDefinedAt(x)
63 63 def apply(x: A): C = k(PartialFunction.this.apply(x))
64 64 }
65 65
@@ -90,7 +90,8 @@ trait PartialFunction[-A, +B] extends (A => B) {
90 90 */
91 91 object PartialFunction {
92 92 private[this] final val empty_pf: PartialFunction[Any, Nothing] = new runtime.AbstractPartialFunction[Any, Nothing] {
93   - def isDefinedAt(x: Any) = false
  93 + def _isDefinedAt(x: Any) = false
  94 + override def isDefinedAt(x: Any) = false
94 95 def apply(x: Any): Nothing = throw new MatchError(x)
95 96 override def orElse[A1, B1](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = that
96 97 override def orElseFast[A1, B1](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = that
24 src/library/scala/runtime/AbstractPartialFunction.scala
@@ -23,24 +23,30 @@ abstract class AbstractPartialFunction[-T1, +R]
23 23 with PartialFunction[T1, R]
24 24 with Cloneable {
25 25
26   - private var fallBack: PartialFunction[T1 @uncheckedVariance, R @uncheckedVariance] = PartialFunction.empty
  26 + private var fallBackField: PartialFunction[T1 @uncheckedVariance, R @uncheckedVariance] = _
27 27
28   - override protected def missingCase(x: T1): R = synchronized {
29   - fallBack(x)
  28 + def fallBack: PartialFunction[T1, R] = synchronized {
  29 + if (fallBackField == null) fallBackField = PartialFunction.empty
  30 + fallBackField
30 31 }
31 32
  33 + override protected def missingCase(x: T1): R = fallBack(x)
  34 +
32 35 // Question: Need to ensure that fallBack is overwritten before any access
33 36 // Is the `synchronized` here the right thing to achieve this?
34 37 // Is there a cheaper way?
35   - override def orElseFast[A1 <: T1, B1 >: R](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = {
  38 + override def orElse[A1 <: T1, B1 >: R](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = {
36 39 val result = this.clone.asInstanceOf[AbstractPartialFunction[A1, B1]]
37 40 result.synchronized {
38   - result.fallBack = this.fallBack orElseFast that
  41 + result.fallBackField = this.fallBackField orElse that
39 42 result
40 43 }
41 44 }
42   -/*
43   - def isDefinedAt(x: T1): scala.Boolean = isDefinedAtCurrent(x) || fallBack.isDefinedAt(x)
44   - def isDefinedAtCurrent(x: T1): scala.Boolean = false
45   -*/
  45 +
  46 + def isDefinedAt(x: T1): scala.Boolean = _isDefinedAt(x) || fallBack.isDefinedAt(x)
  47 + def _isDefinedAt(x: T1): scala.Boolean
  48 +
46 49 }
  50 +
  51 +
  52 +
2  src/library/scala/util/control/Exception.scala
@@ -232,6 +232,6 @@ object Exception {
232 232 private def pfFromExceptions(exceptions: Class[_]*): PartialFunction[Throwable, Nothing] =
233 233 new scala.runtime.AbstractPartialFunction[Throwable, Nothing] {
234 234 def apply(x: Throwable) = throw x
235   - def isDefinedAt(x: Throwable) = wouldMatch(x, exceptions)
  235 + def _isDefinedAt(x: Throwable) = wouldMatch(x, exceptions)
236 236 }
237 237 }

2 comments on commit 5fb26c6

Pavel E. Pavlov

Hmm, this implementation seems to be broken for me.

1) All the non-compiler-generated subclasses of AbstractPartialFunction
must call missingCase from apply unless its _isDefnedAt always returns true or its orElse is redefined.
Otherwise PFs returned by orElse on them will be broken.
With quick grep on nightly built scala-libray-src.jar & scala-compiler-src.jar I found 8 such broken classes:

  • Function.unlift
  • PartialFunction.orElse (non-optimized)
  • PartialFunction.andThen
  • actors.Futures.awaitWith
  • actors.Reactor.waitingForNone
  • util.control.Exception.pfFromExceptions
  • tools.nsc.interpreter.AbstractOrMissingHandler
  • tools.nsc.matching.ParalleMatching.scala, lazy val successMatrixFn

A few other comments on the code:

2) // Question: Need to ensure that fallBack is overwritten before any access
// Is the synchronized here the right thing to achieve this?
// Is there a cheaper way?

It is enough to declare fallBackField field as volatile.
As you have not yet exposed just cloned object (in orElse) to anyone,
it is safe to write into volatile fallBackField field then let the object escape.
In that case JMM guarantees that anyone will see right value of the field here.
Moreover, to simplify overall scheme, it is better to remove assignment to the field from the accessor
(moving it back to constructor as in previous commit).
With this you can eliminate both synchronized blocks from the code.

3) It seems that orElseFast is not used anywhere.

nafg

Why does this commit add ModifierFlags.MACRO?

Please sign in to comment.
Something went wrong with that request. Please try again.