diff --git a/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala b/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala index e377de6da22b..abdd5cfdd534 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package sbt import scala.annotation.tailrec @@ -24,7 +25,7 @@ private[sbt] trait ThunkHolder { * It will be forced by the next call to `forceThunks()` */ def lzy[T <: AnyRef](t: => T): api.Lazy[T] = { - val l = SafeLazy(() => t) + val l = SafeLazyWrapper(() => t) thunks += l l } @@ -37,10 +38,69 @@ private[sbt] trait ThunkHolder { * see https://github.com/sbt/zinc/issues/114 */ def strict2lzy[T <: AnyRef](t: T): api.Lazy[T] = - SafeLazy.strict(t) + SafeLazyWrapper.strict(t) } -// TODO: Use xsbti.SafeLazy once https://github.com/sbt/zinc/issues/113 is fixed +/** Wrapper around SafeLazy implementations. + * + * `xsbti.SafeLazy` is part of sbt but it is not part of the `interface` jar + * that dotty depends on, therefore we can only access it by reflection, + * and this will only succeed when dotty is run by sbt (otherwise + * `xsbti.SafeLazy` won't be on the classpath at all). + * + * For testing purposes, we still want to be able to run the sbt phases outside + * of sbt, using `-Yforce-sbt-phases` and `-Ydump-sbt-inc`, therefore we + * provide a copy of SafeLazy in `dotty.tools.dotc.sbt.SafeLazy` that we use + * when `xsbti.SafeLazy` is unavailable. + * + * This raises a question: why bother with `xsbti.SafeLazy` if we have our own + * version anyway? Because sbt uses Java serialization to persist the output of + * the incremental compilation analysis when sbt is stopped and restarted. If + * we used `dotty.tools.dotc.sbt.SafeLazy` with sbt, deserialization would fail + * and every restart of sbt would require a full recompilation. + * + * Note: this won't be needed once we switch to zinc 1.0 where `SafeLazy` becomes + * part of the `interface` jar, see https://github.com/sbt/zinc/issues/113 + */ +private object SafeLazyWrapper { + + @sharable private[this] val safeLazy = + try { + Class.forName("xsbti.SafeLazy") + } catch { + case e: ClassNotFoundException => + null + } + + @sharable private[this] val safeLazyApply = + if (safeLazy != null) + safeLazy.getMethod("apply", classOf[xsbti.F0[_]]) + else + null + @sharable private[this] val safeLazyStrict = + if (safeLazy != null) + safeLazy.getMethod("strict", classOf[Object]) + else + null + + def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] = + if (safeLazyApply != null) + safeLazyApply + .invoke(null, new xsbti.F0[T] { def apply() = eval() }) + .asInstanceOf[xsbti.api.Lazy[T]] + else + SafeLazy(eval) + + def strict[T <: AnyRef](value: T): xsbti.api.Lazy[T] = + if (safeLazyStrict != null) + safeLazyStrict + .invoke(null, value) + .asInstanceOf[xsbti.api.Lazy[T]] + else + SafeLazy.strict(value) +} + +// Adapted from https://github.com/sbt/sbt/blob/0.13/compile/api/src/main/scala/xsbti/SafeLazy.scala private object SafeLazy { def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] = new Impl(eval)