diff --git a/build.sbt b/build.sbt index 3fc75650a..c291e9643 100644 --- a/build.sbt +++ b/build.sbt @@ -189,6 +189,7 @@ lazy val sbtRoot: Project = (project in file(".")) completeProj, logicProj, utilCache, + taskProj, ) .settings( minimalSettings, diff --git a/tasks/src/main/scala/sbt/Execute.scala b/tasks/src/main/scala/sbt/Execute.scala index 8474b8778..ec3650e17 100644 --- a/tasks/src/main/scala/sbt/Execute.scala +++ b/tasks/src/main/scala/sbt/Execute.scala @@ -19,6 +19,7 @@ import scala.annotation.tailrec import scala.collection.mutable import scala.collection.JavaConverters._ import mutable.Map +import sbt.internal.util.AList private[sbt] object Execute { def idMap[A1, A2]: Map[A1, A2] = (new java.util.IdentityHashMap[A1, A2]).asScala @@ -43,12 +44,12 @@ sealed trait Completed { } private[sbt] trait NodeView[F[_]] { def apply[A](a: F[A]): Node[F, A] - def inline[A](a: F[A]): Option[() => A] + def inline1[A](a: F[A]): Option[() => A] } final class Triggers[F[_]]( - val runBefore: collection.Map[F[_], Seq[F[_]]], - val injectFor: collection.Map[F[_], Seq[F[_]]], - val onComplete: RMap[F, Result] => RMap[F, Result] + val runBefore: collection.Map[F[Any], Seq[F[Any]]], + val injectFor: collection.Map[F[Any], Seq[F[Any]]], + val onComplete: RMap[F, Result] => RMap[F, Result], ) private[sbt] final class Execute[F[_] <: AnyRef]( @@ -56,21 +57,20 @@ private[sbt] final class Execute[F[_] <: AnyRef]( triggers: Triggers[F], progress: ExecuteProgress[F] )(implicit view: NodeView[F]) { - type Strategy = CompletionService[F[_], Completed] + type Strategy = CompletionService[F[Any], Completed] - private[this] val forward = idMap[F[_], IDSet[F[_]]] - private[this] val reverse = idMap[F[_], Iterable[F[_]]] + private[this] val forward = idMap[F[Any], IDSet[F[Any]]] + private[this] val reverse = idMap[F[Any], Iterable[F[Any]]] private[this] val callers = pMap[F, Compose[IDSet, F]#Apply] - private[this] val state = idMap[F[_], State] + private[this] val state = idMap[F[Any], State] private[this] val viewCache = pMap[F, Node[F, *]] private[this] val results = pMap[F, Result] - private[this] val getResult: F ~> Result = λ[F ~> Result](a => - view.inline(a) match { - case Some(v) => Value(v()) - case None => results(a) - } - ) + private[this] val getResult: [A] => F[A] => Result[A] = [A] => + (a: F[A]) => + view.inline1(a) match + case Some(v) => Result.Value(v()) + case None => results(a) private[this] type State = State.Value private[this] object State extends Enumeration { val Pending, Running, Calling, Done = Value @@ -85,7 +85,7 @@ private[sbt] final class Execute[F[_] <: AnyRef]( def run[A](root: F[A])(implicit strategy: Strategy): Result[A] = try { runKeep(root)(strategy)(root) - } catch { case i: Incomplete => Inc(i) } + } catch { case i: Incomplete => Result.Inc(i) } def runKeep[A](root: F[A])(implicit strategy: Strategy): RMap[F, Result] = { assert(state.isEmpty, "Execute already running/ran.") @@ -142,7 +142,7 @@ private[sbt] final class Execute[F[_] <: AnyRef]( results.get(target) match { case Some(result) => retire(node, result) case None => - state(node) = Calling + state(node.asInstanceOf) = Calling addChecked(target) addCaller(node, target) } @@ -164,10 +164,10 @@ private[sbt] final class Execute[F[_] <: AnyRef]( } results(node) = result - state(node) = Done + state(node.asInstanceOf) = Done progress.afterCompleted(node, result) - remove(reverse.asInstanceOf[Map[F[A], Iterable[F[_]]]], node) foreach { dep => - notifyDone(node, dep) + remove(reverse.asInstanceOf[Map[F[A], Iterable[F[Any]]]], node) foreach { dep => + notifyDone(node, dep.asInstanceOf) } callers.remove(node).toList.flatten.foreach { c => retire(c, callerResult(c, result)) @@ -180,23 +180,23 @@ private[sbt] final class Execute[F[_] <: AnyRef]( assert(done(node)) assert(results(node) == result) readyInv(node) - assert(!(reverse contains node)) - assert(!(callers contains node)) + assert(!(reverse.contains(node.asInstanceOf))) + assert(!(callers.contains(node))) assert(triggeredBy(node) forall added) } } def callerResult[A](node: F[A], result: Result[A]): Result[A] = result match { - case _: Value[A] => result - case Inc(i) => Inc(Incomplete(Some(node), tpe = i.tpe, causes = i :: Nil)) + case _: Result.Value[A] => result + case Result.Inc(i) => Result.Inc(Incomplete(Some(node), tpe = i.tpe, causes = i :: Nil)) } - def notifyDone(node: F[_], dependent: F[_])(implicit strategy: Strategy): Unit = { + def notifyDone[A](node: F[A], dependent: F[Any])(implicit strategy: Strategy): Unit = { val f = forward(dependent) - f -= node + f -= node.asInstanceOf if (f.isEmpty) { - remove[F[_], IDSet[F[_]]](forward, dependent) - ready(dependent) + remove[F[Any], IDSet[F[Any]]](forward.asInstanceOf, dependent) + ready[Any](dependent) } } @@ -220,10 +220,10 @@ private[sbt] final class Execute[F[_] <: AnyRef]( pre { newPre(node) } val v = register(node) - val deps = dependencies(v) ++ runBefore(node) - val active = IDSet[F[_]](deps filter notDone) + val deps: Iterable[F[Any]] = dependencies(v) ++ runBefore(node.asInstanceOf) + val active = IDSet[F[Any]](deps filter notDone.asInstanceOf) progress.afterRegistered( - node, + node.asInstanceOf, deps, active.toList /* active is mutable, so take a snapshot */ @@ -231,10 +231,10 @@ private[sbt] final class Execute[F[_] <: AnyRef]( if (active.isEmpty) ready(node) else { - forward(node) = active + forward(node.asInstanceOf) = active.asInstanceOf for (a <- active) { - addChecked(a) - addReverse(a, node) + addChecked[Any](a.asInstanceOf) + addReverse[Any](a.asInstanceOf, node.asInstanceOf) } } @@ -254,32 +254,33 @@ private[sbt] final class Execute[F[_] <: AnyRef]( pre { assert(pending(node)) readyInv(node) - assert(reverse contains node) + assert(reverse.contains(node.asInstanceOf)) } - state(node) = Running - progress.afterReady(node) + state(node.asInstanceOf) = Running + progress.afterReady(node.asInstanceOf) submit(node) post { readyInv(node) - assert(reverse contains node) + assert(reverse.contains(node.asInstanceOf)) assert(running(node)) } } /** Enters the given node into the system. */ def register[A](node: F[A]): Node[F, A] = { - state(node) = Pending - reverse(node) = Seq() + state(node.asInstanceOf) = Pending + reverse(node.asInstanceOf) = Seq() viewCache.getOrUpdate(node, view(node)) } /** Send the work for this node to the provided Strategy. */ def submit[A](node: F[A])(implicit strategy: Strategy): Unit = { val v = viewCache(node) - val rs = v.alist.transform(v.in, getResult) - strategy.submit(node, () => work(node, v.work(rs))) + val rs = AList.tuple.transform[F, Result, v.Tup](v.in)(getResult) + // v.alist.transform(v.in)(getResult) + strategy.submit(node.asInstanceOf, () => work(node, v.work(rs))) } /** @@ -287,7 +288,7 @@ private[sbt] final class Execute[F[_] <: AnyRef]( * post-processing to perform after the result is retrieved from the Strategy. */ def work[A](node: F[A], f: => Either[F[A], A])(implicit strategy: Strategy): Completed = { - progress.beforeWork(node) + progress.beforeWork(node.asInstanceOf) val rawResult = wideConvert(f).left.map { case i: Incomplete => if (config.overwriteNode(i)) i.copy(node = Some(node)) else i case e => Incomplete(Some(node), Incomplete.Error, directCause = Some(e)) @@ -305,86 +306,87 @@ private[sbt] final class Execute[F[_] <: AnyRef]( rawResult: Either[Incomplete, Either[F[A], A]] ): Either[F[A], Result[A]] = rawResult match { - case Left(i) => Right(Inc(i)) - case Right(Right(v)) => Right(Value(v)) + case Left(i) => Right(Result.Inc(i)) + case Right(Right(v)) => Right(Result.Value(v)) case Right(Left(target)) => Left(target) } def remove[K, V](map: Map[K, V], k: K): V = map.remove(k).getOrElse(sys.error("Key '" + k + "' not in map :\n" + map)) - def addReverse(node: F[_], dependent: F[_]): Unit = reverse(node) ++= Seq(dependent) + def addReverse[A](node: F[A], dependent: F[Any]): Unit = + reverse(node.asInstanceOf) ++= Seq(dependent) def addCaller[A](caller: F[A], target: F[A]): Unit = callers.getOrUpdate(target, IDSet.create[F[A]]) += caller - def dependencies(node: F[_]): Iterable[F[_]] = dependencies(viewCache(node)) - def dependencies(v: Node[F, _]): Iterable[F[_]] = - v.alist.toList(v.in).filter(dep => view.inline(dep).isEmpty) + def dependencies[A](node: F[A]): Iterable[F[Any]] = dependencies(viewCache(node.asInstanceOf)) + def dependencies[A](v: Node[F, A]): Iterable[F[Any]] = + AList.tuple.toList[F, v.Tup](v.in).filter(dep => view.inline1(dep).isEmpty) + // v.alist.toList(v.in).filter(dep => view.inline(dep).isEmpty) - def runBefore(node: F[_]): Seq[F[_]] = getSeq(triggers.runBefore, node) - def triggeredBy(node: F[_]): Seq[F[_]] = getSeq(triggers.injectFor, node) - def getSeq(map: collection.Map[F[_], Seq[F[_]]], node: F[_]): Seq[F[_]] = - map.getOrElse(node, nilSeq[F[_]]) + def runBefore[A](node: F[A]): Seq[F[A]] = + getSeq[A](triggers.runBefore, node) + def triggeredBy[A](node: F[A]): Seq[F[A]] = getSeq(triggers.injectFor, node) + def getSeq[A](map: collection.Map[F[Any], Seq[F[Any]]], node: F[A]): Seq[F[A]] = + map.getOrElse(node.asInstanceOf, nilSeq[F[Any]]).asInstanceOf // Contracts - def addedInv(node: F[_]): Unit = topologicalSort(node) foreach addedCheck - def addedCheck(node: F[_]): Unit = { + def addedInv[A](node: F[A]): Unit = topologicalSort(node) foreach addedCheck + def addedCheck[A](node: F[A]): Unit = { assert(added(node), "Not added: " + node) - assert(viewCache contains node, "Not in view cache: " + node) - dependencyCheck(node) + assert(viewCache.contains[Any](node.asInstanceOf), "Not in view cache: " + node) + dependencyCheck(node.asInstanceOf) } - def dependencyCheck(node: F[_]): Unit = { + def dependencyCheck(node: F[Any]): Unit = { dependencies(node) foreach { dep => def onOpt[A](o: Option[A])(f: A => Boolean) = o match { case None => false; case Some(x) => f(x) } - def checkForward = onOpt(forward.get(node)) { _ contains dep } - def checkReverse = onOpt(reverse.get(dep)) { _.exists(_ == node) } - assert(done(dep) ^ (checkForward && checkReverse)) + def checkForward = onOpt(forward.get(node.asInstanceOf)) { _ contains dep.asInstanceOf } + def checkReverse = onOpt(reverse.get(dep.asInstanceOf)) { _.exists(_ == node) } + assert(done(dep.asInstanceOf) ^ (checkForward && checkReverse)) } } - def pendingInv(node: F[_]): Unit = { + def pendingInv[A](node: F[A]): Unit = { assert(atState(node, Pending)) - assert((dependencies(node) ++ runBefore(node)) exists notDone) + assert((dependencies(node) ++ runBefore(node)) exists notDone.asInstanceOf) } - def runningInv(node: F[_]): Unit = { - assert(dependencies(node) forall done) - assert(!(forward contains node)) + def runningInv[A](node: F[A]): Unit = { + assert(dependencies(node) forall done.asInstanceOf) + assert(!(forward.contains(node.asInstanceOf))) } - def newPre(node: F[_]): Unit = { + def newPre[A](node: F[A]): Unit = { isNew(node) - assert(!(reverse contains node)) - assert(!(forward contains node)) - assert(!(callers contains node)) - assert(!(viewCache contains node)) - assert(!(results contains node)) + assert(!(reverse.contains(node.asInstanceOf))) + assert(!(forward.contains(node.asInstanceOf))) + assert(!(callers.contains[Any](node.asInstanceOf))) + assert(!(viewCache.contains[Any](node.asInstanceOf))) + assert(!(results.contains[Any](node.asInstanceOf))) } - def topologicalSort(node: F[_]): Seq[F[_]] = { - val seen = IDSet.create[F[_]] - def visit(n: F[_]): List[F[_]] = - (seen process n)(List[F[_]]()) { - node :: dependencies(n).foldLeft(List[F[_]]()) { (ss, dep) => - visit(dep) ::: ss + def topologicalSort[A](node: F[A]): Seq[F[Any]] = { + val seen = IDSet.create[F[Any]] + def visit(n: F[Any]): List[F[Any]] = + (seen process n)(List[F[Any]]()) { + node.asInstanceOf :: dependencies(n).foldLeft(List[F[Any]]()) { (ss, dep) => + visit(dep.asInstanceOf) ::: ss } } - visit(node).reverse + visit(node.asInstanceOf).reverse } - def readyInv(node: F[_]): Unit = { - assert(dependencies(node) forall done) - assert(!(forward contains node)) + def readyInv[A](node: F[A]): Unit = { + assert(dependencies(node) forall done.asInstanceOf) + assert(!(forward.contains(node.asInstanceOf))) } // cyclic reference checking def snapshotCycleCheck(): Unit = - callers.toSeq foreach { - case (called: F[c], callers) => - for (caller <- callers) cycleCheck(caller.asInstanceOf[F[c]], called) - case _ => () + callers.toSeq foreach { case (called: F[c], callers) => + for (caller <- callers) cycleCheck(caller.asInstanceOf[F[c]], called) } def cycleCheck[A](node: F[A], target: F[A]): Unit = { @@ -407,14 +409,14 @@ private[sbt] final class Execute[F[_] <: AnyRef]( // state testing - def pending(d: F[_]) = atState(d, Pending) - def running(d: F[_]) = atState(d, Running) - def calling(d: F[_]) = atState(d, Calling) - def done(d: F[_]) = atState(d, Done) - def notDone(d: F[_]) = !done(d) - def atState(d: F[_], s: State) = state.get(d) == Some(s) - def isNew(d: F[_]) = !added(d) - def added(d: F[_]) = state contains d + def pending[A](d: F[A]) = atState(d, Pending) + def running[A](d: F[A]) = atState(d, Running) + def calling[A](d: F[A]) = atState(d, Calling) + def done[A](d: F[A]) = atState(d, Done) + def notDone[A](d: F[A]) = !done(d) + private def atState[A](d: F[A], s: State) = state.get(d.asInstanceOf) == Some(s) + def isNew[A](d: F[A]) = !added(d) + def added[A](d: F[A]) = state.contains(d.asInstanceOf) def complete = state.values.forall(_ == Done) def pre(f: => Unit) = if (checkPreAndPostConditions) f diff --git a/tasks/src/main/scala/sbt/ExecuteProgress.scala b/tasks/src/main/scala/sbt/ExecuteProgress.scala index c05b3d113..e84603e5d 100644 --- a/tasks/src/main/scala/sbt/ExecuteProgress.scala +++ b/tasks/src/main/scala/sbt/ExecuteProgress.scala @@ -22,20 +22,20 @@ trait ExecuteProgress[F[_]] { * `task` are `allDeps` and the subset of those dependencies that have not completed are * `pendingDeps`. */ - def afterRegistered(task: F[_], allDeps: Iterable[F[_]], pendingDeps: Iterable[F[_]]): Unit + def afterRegistered(task: F[Any], allDeps: Iterable[F[Any]], pendingDeps: Iterable[F[Any]]): Unit /** * Notifies that all of the dependencies of `task` have completed and `task` is therefore ready to * run. The task has not been scheduled on a thread yet. */ - def afterReady(task: F[_]): Unit + def afterReady(task: F[Any]): Unit /** * Notifies that the work for `task` is starting after this call returns. This is called from the * thread the task executes on, unlike most other methods in this callback. It is called * immediately before the task's work starts with minimal intervening executor overhead. */ - def beforeWork(task: F[_]): Unit + def beforeWork(task: F[Any]): Unit /** * Notifies that the work for `task` work has finished. The task may have computed the next task @@ -67,13 +67,13 @@ object ExecuteProgress { def empty[F[_]]: ExecuteProgress[F] = new ExecuteProgress[F] { override def initial(): Unit = () override def afterRegistered( - task: F[_], - allDeps: Iterable[F[_]], - pendingDeps: Iterable[F[_]] + task: F[Any], + allDeps: Iterable[F[Any]], + pendingDeps: Iterable[F[Any]] ): Unit = () - override def afterReady(task: F[_]): Unit = () - override def beforeWork(task: F[_]): Unit = () + override def afterReady(task: F[Any]): Unit = () + override def beforeWork(task: F[Any]): Unit = () override def afterWork[A](task: F[A], result: Either[F[A], Result[A]]): Unit = () override def afterCompleted[A](task: F[A], result: Result[A]): Unit = () override def afterAllCompleted(results: RMap[F, Result]): Unit = () @@ -85,16 +85,16 @@ object ExecuteProgress { reporters foreach { _.initial() } } override def afterRegistered( - task: F[_], - allDeps: Iterable[F[_]], - pendingDeps: Iterable[F[_]] + task: F[Any], + allDeps: Iterable[F[Any]], + pendingDeps: Iterable[F[Any]] ): Unit = { reporters foreach { _.afterRegistered(task, allDeps, pendingDeps) } } - override def afterReady(task: F[_]): Unit = { + override def afterReady(task: F[Any]): Unit = { reporters foreach { _.afterReady(task) } } - override def beforeWork(task: F[_]): Unit = { + override def beforeWork(task: F[Any]): Unit = { reporters foreach { _.beforeWork(task) } } override def afterWork[A](task: F[A], result: Either[F[A], Result[A]]): Unit = { diff --git a/tasks/src/main/scala/sbt/Node.scala b/tasks/src/main/scala/sbt/Node.scala index 3baba7be1..bc5176631 100644 --- a/tasks/src/main/scala/sbt/Node.scala +++ b/tasks/src/main/scala/sbt/Node.scala @@ -7,21 +7,18 @@ package sbt -import sbt.internal.util.AList - /** * Represents a task node in a format understood by the task evaluation engine Execute. * - * @tparam A + * @tparam Effect * the task type constructor - * @tparam T + * @tparam A * the type computed by this node */ -trait Node[A[_], T] { - type K[L[x]] - val in: K[A] - val alist: AList[K] +trait Node[Effect[_], A] { + type Tup <: Tuple + def in: Tuple.Map[Tup, Effect] /** Computes the result of this task given the results from the inputs. */ - def work(inputs: K[Result]): Either[A[T], T] + def work(inputs: Tuple.Map[Tup, Result]): Either[Effect[A], A] } diff --git a/tasks/src/main/scala/sbt/Result.scala b/tasks/src/main/scala/sbt/Result.scala index e608786a8..b454e3f3c 100644 --- a/tasks/src/main/scala/sbt/Result.scala +++ b/tasks/src/main/scala/sbt/Result.scala @@ -12,26 +12,27 @@ import sbt.internal.util.~> // used instead of Either[Incomplete, T] for type inference /** Result of completely evaluating a task. */ -sealed trait Result[+T] { - def toEither: Either[Incomplete, T] -} +enum Result[+A]: + /** Indicates the task did not complete normally and so it does not have a value. */ + case Inc(cause: Incomplete) extends Result[Nothing] -/** Indicates the task did not complete normally and so it does not have a value. */ -final case class Inc(cause: Incomplete) extends Result[Nothing] { - def toEither: Either[Incomplete, Nothing] = Left(cause) -} + /** Indicates the task completed normally and produced the given `value`. */ + case Value[+A](value: A) extends Result[A] -/** Indicates the task completed normally and produced the given `value`. */ -final case class Value[+T](value: T) extends Result[T] { - def toEither: Either[Incomplete, T] = Right(value) -} + def toEither: Either[Incomplete, A] = this match + case Inc(cause) => Left(cause) + case Value(value) => Right(value) +end Result object Result { type Id[X] = X - val tryValue = λ[Result ~> Id] { - case Value(v) => v - case Inc(i) => throw i - } + val tryValue: [A] => Result[A] => A = + [A] => + (r: Result[A]) => + r match + case Result.Value(v) => v + case Result.Inc(i) => throw i + def tryValues[S](r: Seq[Result[Unit]], v: Result[S]): S = { r foreach tryValue[Unit] tryValue[S](v) diff --git a/util-collection/src/main/scala-2/sbt/internal/util/PMap.scala b/util-collection/src/main/scala-2/sbt/internal/util/PMap.scala deleted file mode 100644 index ed90560ee..000000000 --- a/util-collection/src/main/scala-2/sbt/internal/util/PMap.scala +++ /dev/null @@ -1,129 +0,0 @@ -/* - * sbt - * Copyright 2011 - 2018, Lightbend, Inc. - * Copyright 2008 - 2010, Mark Harrah - * Licensed under Apache License 2.0 (see LICENSE) - */ - -package sbt.internal.util - -import collection.mutable - -trait RMap[K[_], V[_]] { - def apply[T](k: K[T]): V[T] - def get[T](k: K[T]): Option[V[T]] - def contains[T](k: K[T]): Boolean - def toSeq: Seq[(K[_], V[_])] - - def toTypedSeq: Seq[TPair[_]] = toSeq.map { - case (k: K[t], v) => TPair[t](k, v.asInstanceOf[V[t]]) - } - - def keys: Iterable[K[_]] - def values: Iterable[V[_]] - def isEmpty: Boolean - - sealed case class TPair[T](key: K[T], value: V[T]) -} - -trait IMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { - def put[T](k: K[T], v: V[T]): IMap[K, V] - def remove[T](k: K[T]): IMap[K, V] - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): IMap[K, V] - def mapValues[V2[_]](f: V ~> V2): IMap[K, V2] - def mapSeparate[VL[_], VR[_]](f: V ~> λ[T => Either[VL[T], VR[T]]]): (IMap[K, VL], IMap[K, VR]) -} - -trait PMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { - def update[T](k: K[T], v: V[T]): Unit - def remove[T](k: K[T]): Option[V[T]] - def getOrUpdate[T](k: K[T], make: => V[T]): V[T] - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] -} - -object PMap { - implicit def toFunction[K[_], V[_]](map: PMap[K, V]): K[_] => V[_] = k => map(k) - def empty[K[_], V[_]]: PMap[K, V] = new DelegatingPMap[K, V](new mutable.HashMap) -} - -object IMap { - - /** - * Only suitable for K that is invariant in its type parameter. - * Option and List keys are not suitable, for example, - * because None <:< Option[String] and None <: Option[Int]. - */ - def empty[K[_], V[_]]: IMap[K, V] = new IMap0[K, V](Map.empty) - - private[sbt] def fromJMap[K[_], V[_]](map: java.util.Map[K[_], V[_]]): IMap[K, V] = - new IMap0[K, V](new WrappedMap(map)) - - private[sbt] class IMap0[K[_], V[_]](val backing: Map[K[_], V[_]]) - extends AbstractRMap[K, V] - with IMap[K, V] { - def get[T](k: K[T]): Option[V[T]] = (backing get k).asInstanceOf[Option[V[T]]] - def put[T](k: K[T], v: V[T]) = new IMap0[K, V](backing.updated(k, v)) - def remove[T](k: K[T]) = new IMap0[K, V](backing - k) - - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]) = - put(k, f(this get k getOrElse init)) - - def mapValues[V2[_]](f: V ~> V2) = - new IMap0[K, V2](Map(backing.iterator.map { case (k, v) => k -> f(v) }.toArray: _*)) - - def mapSeparate[VL[_], VR[_]](f: V ~> λ[T => Either[VL[T], VR[T]]]) = { - val left = new java.util.concurrent.ConcurrentHashMap[K[_], VL[_]] - val right = new java.util.concurrent.ConcurrentHashMap[K[_], VR[_]] - Par(backing.toVector).foreach { - case (k, v) => - f(v) match { - case Left(l) => left.put(k, l) - case Right(r) => right.put(k, r) - } - } - (new IMap0[K, VL](new WrappedMap(left)), new IMap0[K, VR](new WrappedMap(right))) - } - - def toSeq = backing.toSeq - def keys = backing.keys - def values = backing.values - def isEmpty = backing.isEmpty - - override def toString = backing.toString - } -} - -abstract class AbstractRMap[K[_], V[_]] extends RMap[K, V] { - def apply[T](k: K[T]): V[T] = get(k).get - def contains[T](k: K[T]): Boolean = get(k).isDefined -} - -/** - * Only suitable for K that is invariant in its type parameter. - * Option and List keys are not suitable, for example, - * because None <:< Option[String] and None <: Option[Int]. - */ -class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]]) - extends AbstractRMap[K, V] - with PMap[K, V] { - def get[T](k: K[T]): Option[V[T]] = cast[T](backing.get(k)) - def update[T](k: K[T], v: V[T]): Unit = { backing(k) = v } - def remove[T](k: K[T]) = cast(backing.remove(k)) - def getOrUpdate[T](k: K[T], make: => V[T]) = cast[T](backing.getOrElseUpdate(k, make)) - - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] = { - val v = f(this get k getOrElse init) - update(k, v) - v - } - - def toSeq = backing.toSeq - def keys = backing.keys - def values = backing.values - def isEmpty = backing.isEmpty - - private[this] def cast[T](v: V[_]): V[T] = v.asInstanceOf[V[T]] - private[this] def cast[T](o: Option[V[_]]): Option[V[T]] = o map cast[T] - - override def toString = backing.toString -} diff --git a/util-collection/src/main/scala/sbt/internal/util/PMap.scala b/util-collection/src/main/scala/sbt/internal/util/PMap.scala new file mode 100644 index 000000000..1d94e06d5 --- /dev/null +++ b/util-collection/src/main/scala/sbt/internal/util/PMap.scala @@ -0,0 +1,140 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt.internal.util + +import collection.mutable + +trait RMap[K[_], V[_]] { + def apply[T](k: K[T]): V[T] + def get[T](k: K[T]): Option[V[T]] + def contains[T](k: K[T]): Boolean + def toSeq: Seq[(K[Any], V[Any])] + + def toTypedSeq: Seq[TPair[_]] = toSeq.map { case (k: K[t], v) => + TPair[t](k, v.asInstanceOf[V[t]]) + } + + def keys: Iterable[K[Any]] + def values: Iterable[V[Any]] + def isEmpty: Boolean + + sealed case class TPair[T](key: K[T], value: V[T]) +} + +trait IMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { + def put[T](k: K[T], v: V[T]): IMap[K, V] + def remove[T](k: K[T]): IMap[K, V] + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): IMap[K, V] + def mapValues[V2[_]](f: [A] => V[A] => V2[A]): IMap[K, V2] + def mapSeparate[VL[_], VR[_]](f: V ~> λ[T => Either[VL[T], VR[T]]]): (IMap[K, VL], IMap[K, VR]) +} + +trait PMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { + def update[T](k: K[T], v: V[T]): Unit + def remove[T](k: K[T]): Option[V[T]] + def getOrUpdate[T](k: K[T], make: => V[T]): V[T] + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] +} + +object PMap { + // implicit def toFunction[K[_], V[_]](map: PMap[K, V]): [A] => K[A] => V[A] = + // [A] => (k: K[A]) => map.apply[A](k) + + given [K[_], V[_]]: Conversion[PMap[K, V], [A] => (K[A]) => V[A]] = + new Conversion[PMap[K, V], [A] => K[A] => V[A]]: + def apply(map: PMap[K, V]): [A] => K[A] => V[A] = + [A] => (k: K[A]) => map.apply[A](k) + def empty[K[_], V[_]]: PMap[K, V] = new DelegatingPMap[K, V](new mutable.HashMap) +} + +object IMap { + + /** + * Only suitable for K that is invariant in its type parameter. Option and List keys are not + * suitable, for example, because None <:< Option[String] and None <: Option[Int]. + */ + def empty[K[_], V[_]]: IMap[K, V] = new IMap0[K, V](Map.empty) + + private[sbt] def fromJMap[K[_], V[_]](map: java.util.Map[K[Any], V[Any]]): IMap[K, V] = + new IMap0[K, V](new WrappedMap[K[Any], V[Any]](map)) + + private[sbt] class IMap0[K[_], V[_]](val backing: Map[K[Any], V[Any]]) + extends AbstractRMap[K, V] + with IMap[K, V] { + def get[T](k: K[T]): Option[V[T]] = + (backing get k.asInstanceOf).asInstanceOf[Option[V[T]]] + def put[T](k: K[T], v: V[T]) = + new IMap0[K, V](backing.updated(k.asInstanceOf, v.asInstanceOf)) + def remove[T](k: K[T]) = new IMap0[K, V](backing - k.asInstanceOf) + + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]) = + put(k, f(this get k getOrElse init)) + + def mapValues[V2[_]](f: [A] => V[A] => V2[A]) = + new IMap0[K, V2](Map(backing.iterator.map { case (k, v) => + k -> f(v.asInstanceOf[V[Any]]) + }.toArray: _*)) + + def mapSeparate[VL[_], VR[_]](f: V ~> λ[T => Either[VL[T], VR[T]]]) = { + val left = new java.util.concurrent.ConcurrentHashMap[K[Any], VL[Any]] + val right = new java.util.concurrent.ConcurrentHashMap[K[Any], VR[Any]] + Par(backing.toVector).foreach { case (k, v) => + f(v.asInstanceOf[V[Any]]) match { + case Left(l) => left.put(k, l) + case Right(r) => right.put(k, r) + } + } + ( + new IMap0[K, VL](new WrappedMap(left.asInstanceOf)), + new IMap0[K, VR](new WrappedMap(right.asInstanceOf)) + ) + } + + def toSeq = backing.toSeq.asInstanceOf[Seq[(K[Any], V[Any])]] + def keys = backing.keys.asInstanceOf[Iterable[K[Any]]] + def values = backing.values.asInstanceOf[Iterable[V[Any]]] + def isEmpty = backing.isEmpty + + override def toString = backing.toString + } +} + +abstract class AbstractRMap[K[_], V[_]] extends RMap[K, V] { + def apply[T](k: K[T]): V[T] = get(k).get + def contains[T](k: K[T]): Boolean = get(k).isDefined +} + +/** + * Only suitable for K that is invariant in its type parameter. Option and List keys are not + * suitable, for example, because None <:< Option[String] and None <: Option[Int]. + */ +class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[Any], V[Any]]) + extends AbstractRMap[K, V] + with PMap[K, V] { + def get[T](k: K[T]): Option[V[T]] = cast[T](backing.get(k.asInstanceOf)) + def update[T](k: K[T], v: V[T]): Unit = { backing(k.asInstanceOf) = v.asInstanceOf } + def remove[T](k: K[T]) = cast(backing.remove(k.asInstanceOf)) + def getOrUpdate[T](k: K[T], make: => V[T]) = + cast[T](backing.getOrElseUpdate(k.asInstanceOf, make.asInstanceOf)) + + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] = { + val v = f(this get k getOrElse init) + update(k, v) + v + } + + def toSeq = backing.toSeq.asInstanceOf[Seq[(K[Any], V[Any])]] + def keys = backing.keys.asInstanceOf[Iterable[K[Any]]] + def values = backing.values.asInstanceOf[Iterable[V[Any]]] + def isEmpty = backing.isEmpty + + private[this] def cast[A](v: V[Any]): V[A] = v.asInstanceOf[V[A]] + private[this] def cast[A](o: Option[V[Any]]): Option[V[A]] = o map cast[A] + + override def toString = backing.toString +} diff --git a/util-collection/src/main/scala-2.13/sbt/internal/util/Par.scala b/util-collection/src/main/scala/sbt/internal/util/Par.scala similarity index 100% rename from util-collection/src/main/scala-2.13/sbt/internal/util/Par.scala rename to util-collection/src/main/scala/sbt/internal/util/Par.scala diff --git a/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala b/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala index 8a0663e6d..accf313ff 100644 --- a/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala +++ b/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala @@ -15,10 +15,10 @@ trait TypeFunctions: import TypeFunctions._ sealed trait Const[A] { type Apply[B] = A } sealed trait ConstK[A] { type l[L[x]] = A } - sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] } - sealed trait ∙[A[_], B[_]] { type l[T] = A[B[T]] } - */ + sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] } + + sealed trait ∙[A[_], B[_]] { type l[T] = A[B[T]] } private type AnyLeft[T] = Left[T, Nothing] private type AnyRight[T] = Right[Nothing, T] final val left: [A] => A => Left[A, Nothing] = [A] => (a: A) => Left(a) @@ -71,15 +71,17 @@ object TypeFunctions extends TypeFunctions: end TypeFunctions */ -/* -trait ~>[-A[_], +B[_]] { outer => - def apply[T](a: A[T]): B[T] +trait ~>[-F1[_], +F2[_]] { outer => + def apply[A](f1: F1[A]): F2[A] // directly on ~> because of type inference limitations - final def ∙[C[_]](g: C ~> A): C ~> B = λ[C ~> B](c => outer.apply(g(c))) - final def ∙[C, D](g: C => D)(implicit ev: D <:< A[D]): C => B[D] = i => apply(ev(g(i))) - final def fn[T]: A[T] => B[T] = (t: A[T]) => apply[T](t) + final def ∙[F3[_]](g: F3 ~> F1): F3 ~> F2 = new ~>[F3, F2] { + override def apply[A](f3: F3[A]) = outer.apply(g(f3)) + } + final def ∙[C, D](g: C => D)(implicit ev: D <:< F1[D]): C => F2[D] = i => apply(ev(g(i))) + lazy val fn: [A] => F1[A] => F2[A] = [A] => (f1: F1[A]) => outer.apply[A](f1) } +/* object ~> { import TypeFunctions._ val Id: Id ~> Id = idK[Id] diff --git a/util-collection/src/main/scala-2.13/sbt/internal/util/WrappedMap.scala b/util-collection/src/main/scala/sbt/internal/util/WrappedMap.scala similarity index 99% rename from util-collection/src/main/scala-2.13/sbt/internal/util/WrappedMap.scala rename to util-collection/src/main/scala/sbt/internal/util/WrappedMap.scala index 8d8a29e1d..22cc832e7 100644 --- a/util-collection/src/main/scala-2.13/sbt/internal/util/WrappedMap.scala +++ b/util-collection/src/main/scala/sbt/internal/util/WrappedMap.scala @@ -8,6 +8,7 @@ package sbt.internal.util import scala.collection.JavaConverters._ + private[util] class WrappedMap[K, V](val jmap: java.util.Map[K, V]) extends Map[K, V] { def removed(key: K): scala.collection.immutable.Map[K, V] = jmap.asScala.toMap.removed(key) def updated[V1 >: V](key: K, value: V1): scala.collection.immutable.Map[K, V1] =