diff --git a/internal/compiler-bridge/src-2.10/main/scala/xsbt/ExtractAPI.scala b/internal/compiler-bridge/src-2.10/main/scala/xsbt/ExtractAPI.scala index 6b7708720e..041ddff2e5 100644 --- a/internal/compiler-bridge/src-2.10/main/scala/xsbt/ExtractAPI.scala +++ b/internal/compiler-bridge/src-2.10/main/scala/xsbt/ExtractAPI.scala @@ -136,18 +136,15 @@ class ExtractAPI[GlobalType <: Global]( def renaming(symbol: Symbol): Option[String] = renameTo.get(symbol) } - // call back to the xsbti.SafeLazy class in main sbt code to construct a SafeLazy instance - // we pass a thunk, whose class is loaded by the interface class loader (this class's loader) - // SafeLazy ensures that once the value is forced, the thunk is nulled out and so - // references to the thunk's classes are not retained. Specifically, it allows the interface classes - // (those in this subproject) to be garbage collected after compilation. - private[this] val safeLazy = Class.forName("xsbti.SafeLazy").getMethod("apply", classOf[xsbti.F0[_]]) - private def lzy[S <: AnyRef](s: => S): xsbti.api.Lazy[S] = - { - val z = safeLazy.invoke(null, Message(s)).asInstanceOf[xsbti.api.Lazy[S]] - pending += z - z - } + /** + * Construct a lazy instance from a by-name parameter that will null out references to once + * the value is forced and therefore references to thunk's classes will be garbage collected. + */ + private def lzy[S <: AnyRef](s: => S): xsbti.api.Lazy[S] = { + val lazyImpl = xsbti.api.SafeLazy.apply(Message(s)) + pending += lazyImpl + lazyImpl + } /** * Force all lazy structures. This is necessary so that we see the symbols/types at this phase and diff --git a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala index ffac1bbc60..a9c34d9c0c 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala @@ -58,7 +58,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with dependencyExtractor.localInheritanceDependencies foreach processDependency(context = LocalDependencyByInheritance) processTopLevelImportDependencies(dependencyExtractor.topLevelImportDependencies) } else { - throw new UnsupportedOperationException("Turning off name hashing is not supported in class-based dependency trackging.") + throw new UnsupportedOperationException(Feedback.NameHashingDisabled) } /* * Registers top level import dependencies as coming from a first top level class/trait/object declared @@ -75,13 +75,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with deps foreach { dep => processDependency(context = DependencyByMemberRef)(ClassDependency(firstClassSymbol, dep)) } - case None => - reporter.warning( - unit.position(0), - """|Found top level imports but no class, trait or object is defined in the compilation unit. - |The incremental compiler cannot record the dependency information in such case. - |Some errors like unused import referring to a non-existent class might not be reported.""".stripMargin - ) + case None => reporter.warning(unit.position(0), Feedback.OrphanTopLevelImports) } } /* @@ -163,10 +157,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with } } private def addClassDependency(deps: HashSet[ClassDependency], fromClass: Symbol, dep: Symbol): Unit = { - assert( - fromClass.isClass, - s"The ${fromClass.fullName} defined at ${fromClass.fullLocationString} is not a class symbol." - ) + assert(fromClass.isClass, Feedback.expectedClassSymbol(fromClass)) val depClass = enclOrModuleClass(dep) if (fromClass.associatedFile != depClass.associatedFile) { deps += ClassDependency(fromClass, depClass) @@ -182,25 +173,18 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with } } - @inline - def ignoreType(tpe: Type) = - tpe == null || - tpe == NoType || - tpe.typeSymbol == EmptyPackageClass - private def addTreeDependency(tree: Tree): Unit = { addDependency(tree.symbol) val tpe = tree.tpe - if (!ignoreType(tpe)) - foreachSymbolInType(tpe)(addDependency) + if (!ignoredType(tpe)) + foreachNotPackageSymbolInType(tpe)(addDependency) () } private def addDependency(dep: Symbol): Unit = { val fromClass = resolveDependencySource().fromClass - if (fromClass == NoSymbol || fromClass.hasPackageFlag) { + if (ignoredSymbol(fromClass) || fromClass.hasPackageFlag) { if (inImportNode) addTopLevelImportDependency(dep) - else - devWarning(s"No enclosing class. Discarding dependency on $dep (currentOwner = $currentOwner).") + else devWarning(Feedback.missingEnclosingClass(dep, currentOwner)) } else { addClassDependency(_memberRefDependencies, fromClass, dep) } @@ -276,12 +260,12 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with traverseTrees(body) // In some cases (eg. macro annotations), `typeTree.tpe` may be null. See sbt/sbt#1593 and sbt/sbt#1655. - case typeTree: TypeTree if !ignoreType(typeTree.tpe) => - symbolsInType(typeTree.tpe) foreach addDependency + case typeTree: TypeTree if !ignoredType(typeTree.tpe) => + foreachNotPackageSymbolInType(typeTree.tpe)(addDependency) case m @ MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => traverse(original) super.traverse(m) - case _: ClassDef | _: ModuleDef if tree.symbol != null && tree.symbol != NoSymbol => + case _: ClassDef | _: ModuleDef if !ignoredSymbol(tree.symbol) => // make sure we cache lookups for all classes declared in the compilation unit; the recorded information // will be used in Analyzer phase val sym = if (tree.symbol.isModule) tree.symbol.moduleClass else tree.symbol @@ -295,7 +279,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with addDependency(symbol) } val addSymbolsFromType: Type => Unit = { tpe => - foreachSymbolInType(tpe)(addDependency) + foreachNotPackageSymbolInType(tpe)(addDependency) } } diff --git a/internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala b/internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala index 0313be3eb1..29def886a0 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala @@ -136,18 +136,15 @@ class ExtractAPI[GlobalType <: Global]( def renaming(symbol: Symbol): Option[String] = renameTo.get(symbol) } - // call back to the xsbti.SafeLazy class in main sbt code to construct a SafeLazy instance - // we pass a thunk, whose class is loaded by the interface class loader (this class's loader) - // SafeLazy ensures that once the value is forced, the thunk is nulled out and so - // references to the thunk's classes are not retained. Specifically, it allows the interface classes - // (those in this subproject) to be garbage collected after compilation. - private[this] val safeLazy = Class.forName("xsbti.SafeLazy").getMethod("apply", classOf[xsbti.F0[_]]) - private def lzy[S <: AnyRef](s: => S): xsbti.api.Lazy[S] = - { - val z = safeLazy.invoke(null, Message(s)).asInstanceOf[xsbti.api.Lazy[S]] - pending += z - z - } + /** + * Construct a lazy instance from a by-name parameter that will null out references to once + * the value is forced and therefore references to thunk's classes will be garbage collected. + */ + private def lzy[S <: AnyRef](s: => S): xsbti.api.Lazy[S] = { + val lazyImpl = xsbti.api.SafeLazy.apply(Message(s)) + pending += lazyImpl + lazyImpl + } /** * Force all lazy structures. This is necessary so that we see the symbols/types at this phase and @@ -624,4 +621,4 @@ class ExtractAPI[GlobalType <: Global]( implicit def compat(ann: AnnotationInfo): IsStatic = new IsStatic(ann) annotations.filter(_.isStatic) } -} \ No newline at end of file +} diff --git a/internal/compiler-bridge/src/main/scala/xsbt/ExtractUsedNames.scala b/internal/compiler-bridge/src/main/scala/xsbt/ExtractUsedNames.scala index 471660a129..d724476bf5 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/ExtractUsedNames.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/ExtractUsedNames.scala @@ -22,11 +22,9 @@ import scala.collection.mutable * Names mentioned in Import nodes are handled properly but require some special logic for two * reasons: * - * 1. import node itself has a term symbol associated with it with a name `. - * I (gkossakowski) tried to track down what role this symbol serves but I couldn't. - * It doesn't look like there are many places in Scala compiler that refer to - * that kind of symbols explicitly. - * 2. ImportSelector is not subtype of Tree therefore is not processed by `Tree.foreach` + * 1. The `termSymbol` of Import nodes point to the symbol of the prefix it imports from + * (not the actual members that we import, that are represented as names). + * 2. ImportSelector is not subtype of Tree therefore is not processed by `Tree.foreach`. * * Another type of tree nodes that requires special handling is TypeTree. TypeTree nodes * has a little bit odd representation: @@ -65,12 +63,7 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext val firstClassName = className(firstClassSymbol) traverser.usedNamesFromClass(firstClassName) ++= namesUsedAtTopLevel.map(decodeName) case None => - reporter.warning( - unit.position(0), - """|Found names used at the top level but no class, trait or object is defined in the compilation unit. - |The incremental compiler cannot record used names in such case. - |Some errors like unused import referring to a non-existent class might not be reported.""".stripMargin - ) + reporter.warning(unit.position(0), Feedback.OrphanNames) } } @@ -106,7 +99,7 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext } /** Returns mutable set with all names from given class used in current context */ - def usedNamesFromClass(className: String): collection.mutable.Set[String] = + def usedNamesFromClass(className: String): collection.mutable.Set[String] = { usedNamesFromClasses.get(className) match { case None => val emptySet = scala.collection.mutable.Set.empty[String] @@ -114,6 +107,7 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext emptySet case Some(setForClass) => setForClass } + } /* * Some macros appear to contain themselves as original tree. @@ -130,10 +124,6 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext private def handleClassicTreeNode(tree: Tree): Unit = tree match { case _: DefTree | _: Template => () - // turns out that Import node has a TermSymbol associated with it - // I (Grzegorz) tried to understand why it's there and what does it represent but - // that logic was introduced in 2005 without any justification I'll just ignore the - // import node altogether and just process the selectors in the import node case Import(_, selectors: List[ImportSelector]) => val enclosingNonLocalClass = resolveEnclosingNonLocalClass() def usedNameInImportSelector(name: Name): Unit = @@ -154,7 +144,7 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext case t if t.hasSymbolField => addSymbol(t.symbol) if (t.tpe != null) - foreachSymbolInType(t.tpe)(addSymbol) + foreachNotPackageSymbolInType(t.tpe)(addSymbol) case _ => } @@ -205,22 +195,8 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext if (s.isModule) s.moduleClass else s.enclClass } - /** - * Needed for compatibility with Scala 2.8 which doesn't define `tpnme` - */ - private object tpnme { - val EMPTY = nme.EMPTY.toTypeName - val EMPTY_PACKAGE_NAME = nme.EMPTY_PACKAGE_NAME.toTypeName - } - private def eligibleAsUsedName(symbol: Symbol): Boolean = { - def emptyName(name: Name): Boolean = name match { - case nme.EMPTY | nme.EMPTY_PACKAGE_NAME | tpnme.EMPTY | tpnme.EMPTY_PACKAGE_NAME => true - case _ => false - } - // Synthetic names are no longer included. See https://github.com/sbt/sbt/issues/2537 - (symbol != NoSymbol) && - !emptyName(symbol.name) + !ignoredSymbol(symbol) && !isEmptyName(symbol.name) } } diff --git a/internal/compiler-bridge/src/main/scala/xsbt/GlobalHelpers.scala b/internal/compiler-bridge/src/main/scala/xsbt/GlobalHelpers.scala index 44a64eabb9..4888265838 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/GlobalHelpers.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/GlobalHelpers.scala @@ -6,16 +6,31 @@ trait GlobalHelpers { val global: Global import global._ - def symbolsInType(tp: Type): Set[Symbol] = { - val typeSymbolCollector = - new CollectTypeCollector({ - case tpe if (tpe != null) && !tpe.typeSymbolDirect.hasPackageFlag => tpe.typeSymbolDirect - }) + /** Return true if type shall be ignored, false otherwise. */ + @inline def ignoredType(tpe: Type) = { + tpe == null || + tpe == NoType || + tpe.typeSymbol == EmptyPackageClass + } + + /** Return true if symbol shall be ignored, false otherwise. */ + @inline def ignoredSymbol(symbol: Symbol) = { + symbol == null || + symbol == NoSymbol || + symbol == EmptyPackageClass + } - typeSymbolCollector.collect(tp).toSet + /** Return true if name is empty, false otherwise. */ + def isEmptyName(name: Name): Boolean = { + name match { + case nme.EMPTY | nme.EMPTY_PACKAGE_NAME | + tpnme.EMPTY | tpnme.EMPTY_PACKAGE_NAME => true + case _ => false + } } - def foreachSymbolInType(tpe: Type)(op: Symbol => Unit): Unit = { + /** Apply `op` on every type symbol which doesn't represent a package. */ + def foreachNotPackageSymbolInType(tpe: Type)(op: Symbol => Unit): Unit = { new ForEachTypeTraverser(_ match { case null => case tpe => @@ -45,4 +60,21 @@ trait GlobalHelpers { }.headOption } } + + /** Define common error messages for error reporting and assertions. */ + object Feedback { + val NameHashingDisabled = "Turning off name hashing is not supported in class-based dependency trackging." + val OrphanTopLevelImports = noTopLevelMember("top level imports") + val OrphanNames = noTopLevelMember("names") + + def expectedClassSymbol(culprit: Symbol): String = + s"The ${culprit.fullName} defined at ${culprit.fullLocationString} is not a class symbol." + def missingEnclosingClass(culprit: Symbol, owner: Symbol): String = + s"No enclosing class. Discarding dependency on $culprit (currentOwner = $owner)." + def noTopLevelMember(found: String) = s""" + |Found $found but no class, trait or object is defined in the compilation unit. + |The incremental compiler cannot record the dependency information in such case. + |Some errors like unused import referring to a non-existent class might not be reported. + """.stripMargin + } } diff --git a/internal/compiler-interface/src/main/java/xsbti/api/SafeLazy.java b/internal/compiler-interface/src/main/java/xsbti/api/SafeLazy.java new file mode 100644 index 0000000000..1f26e3a3ea --- /dev/null +++ b/internal/compiler-interface/src/main/java/xsbti/api/SafeLazy.java @@ -0,0 +1,58 @@ +package xsbti.api; + +/** + * Implement a Scala `lazy val` in Java for the facing sbt interface. + * + * It holds a reference to a thunk that is lazily evaluated and then + * its reference is clear to avoid memory leaks in memory-intensive code. + * It needs to be defined in [[xsbti]] or a subpackage, see + * [[xsbti.api.Lazy]] or [[xsbti.F0]] for similar definitions. + */ +public final class SafeLazy { + + /* We do not use conversions from and to Scala functions because [[xsbti]] + * cannot hold any reference to Scala code nor the Scala library. */ + + /** Return a sbt [[xsbti.api.Lazy]] from a given Scala parameterless function. */ + public static xsbti.api.Lazy apply(xsbti.F0 sbtThunk) { + return new Impl(sbtThunk); + } + + /** Return a sbt [[xsbti.api.Lazy]] from a strict value. */ + public static xsbti.api.Lazy strict(T value) { + // Convert strict parameter to sbt function returning it + return apply(new xsbti.F0() { + public T apply() { + return value; + } + }); + } + + private static final class Impl extends xsbti.api.AbstractLazy { + private xsbti.F0 thunk; + private T result; + private boolean flag = false; + + Impl(xsbti.F0 thunk) { + this.thunk = thunk; + } + + /** + * Return cached result or force lazy evaluation. + * + * Don't call it in a multi-threaded environment. + */ + public T get() { + if (flag) return result; + else { + result = thunk.apply(); + flag = true; + // Clear reference so that thunk is GC'ed + thunk = null; + return result; + } + } + } +} + + diff --git a/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala b/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala index 8f5cff967e..dffbe7387a 100644 --- a/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala +++ b/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala @@ -7,8 +7,7 @@ import java.lang.annotation.Annotation import annotation.tailrec import inc.classfile.ClassFile import xsbti.api -import xsbti.SafeLazy -import SafeLazy.strict +import xsbti.api.SafeLazyProxy import collection.mutable import sbt.io.IO @@ -78,10 +77,10 @@ object ClassToAPI { val name = classCanonicalName(c) val tpe = if (Modifier.isInterface(c.getModifiers)) Trait else ClassDef lazy val (static, instance) = structure(c, enclPkg, cmap) - val cls = new api.ClassLike(name, acc, mods, annots, tpe, strict(Empty), lzy(instance, cmap), emptyStringArray, children.toArray, + val cls = new api.ClassLike(name, acc, mods, annots, tpe, lzyS(Empty), lzy(instance, cmap), emptyStringArray, children.toArray, topLevel, typeParameters(typeParameterTypes(c))) val clsDef = new api.ClassLikeDef(name, acc, mods, annots, typeParameters(typeParameterTypes(c)), tpe) - val stat = new api.ClassLike(name, acc, mods, annots, Module, strict(Empty), lzy(static, cmap), emptyStringArray, emptyTypeArray, + val stat = new api.ClassLike(name, acc, mods, annots, Module, lzyS(Empty), lzy(static, cmap), emptyStringArray, emptyTypeArray, topLevel, emptyTypeParameterArray) val statDef = new api.ClassLikeDef(name, acc, mods, annots, emptyTypeParameterArray, Module) val defs = cls :: stat :: Nil @@ -112,8 +111,8 @@ object ClassToAPI { private[this] def classFileForClass(c: Class[_]): ClassFile = classfile.Parser.apply(IO.classfileLocation(c)) - private[this] def lzyS[T <: AnyRef](t: T): xsbti.api.Lazy[T] = lzy(t) - def lzy[T <: AnyRef](t: => T): xsbti.api.Lazy[T] = xsbti.SafeLazy(t) + @inline private[this] def lzyS[T <: AnyRef](t: T): xsbti.api.Lazy[T] = SafeLazyProxy.strict(t) + @inline final def lzy[T <: AnyRef](t: => T): xsbti.api.Lazy[T] = SafeLazyProxy(t) private[this] def lzy[T <: AnyRef](t: => T, cmap: ClassMap): xsbti.api.Lazy[T] = { val s = lzy(t) cmap.lz += s @@ -127,7 +126,7 @@ object ClassToAPI { private val emptySimpleTypeArray = new Array[xsbti.api.SimpleType](0) private val lzyEmptyTpeArray = lzyS(emptyTypeArray) private val lzyEmptyDefArray = lzyS(new Array[xsbti.api.ClassDefinition](0)) - private val lzyEmptyStructure = strict(new xsbti.api.Structure(lzyEmptyTpeArray, lzyEmptyDefArray, lzyEmptyDefArray)) + private val lzyEmptyStructure = lzyS(new xsbti.api.Structure(lzyEmptyTpeArray, lzyEmptyDefArray, lzyEmptyDefArray)) private def allSuperTypes(t: Type): Seq[Type] = { diff --git a/internal/zinc-apiinfo/src/main/scala/xsbt/api/APIUtil.scala b/internal/zinc-apiinfo/src/main/scala/xsbt/api/APIUtil.scala index 816eecd3dd..2d22418daf 100644 --- a/internal/zinc-apiinfo/src/main/scala/xsbt/api/APIUtil.scala +++ b/internal/zinc-apiinfo/src/main/scala/xsbt/api/APIUtil.scala @@ -1,6 +1,5 @@ package xsbt.api -import xsbti.SafeLazy import xsbti.api._ import scala.collection.mutable.HashSet @@ -76,7 +75,7 @@ object APIUtil { new xsbti.api.ClassLike(name, new Public, emptyModifiers, Array.empty, definitionType, lzy(emptyType), lzy(emptyStructure), Array.empty, Array.empty, true, Array.empty) - private[this] def lzy[T <: AnyRef](t: T): Lazy[T] = SafeLazy.strict(t) + private[this] def lzy[T <: AnyRef](t: T): Lazy[T] = SafeLazyProxy.strict(t) private[this] val emptyType = new EmptyType } diff --git a/internal/zinc-apiinfo/src/main/scala/xsbt/api/SafeLazyProxy.scala b/internal/zinc-apiinfo/src/main/scala/xsbt/api/SafeLazyProxy.scala new file mode 100644 index 0000000000..827753e2df --- /dev/null +++ b/internal/zinc-apiinfo/src/main/scala/xsbt/api/SafeLazyProxy.scala @@ -0,0 +1,27 @@ +package xsbti.api + +/** + * Proxy `SafeLazy` functionality from the Java implementation + * implementation in [[xsbt.api.SafeLazy]] to Scala helpers. + * + * The implementation of these helpers are not reused between each + * other because they create intermediate anonymous functions and + * the price of a new object in this hot path is not worth it. + */ +object SafeLazyProxy { + /** + * Return a lazy implementation of a Scala by-name parameter. + */ + def apply[T](s: => T): Lazy[T] = { + val sbtThunk = new xsbti.F0[T] { def apply() = s } + SafeLazy.apply(sbtThunk) + } + + /** + * Return a lazy implementation of a strict value. + */ + def strict[T](s: T): Lazy[T] = { + val sbtThunk = new xsbti.F0[T] { def apply() = s } + SafeLazy.apply(sbtThunk) + } +} diff --git a/internal/zinc-apiinfo/src/main/scala/xsbti/SafeLazy.scala b/internal/zinc-apiinfo/src/main/scala/xsbti/SafeLazy.scala deleted file mode 100644 index 9ea3383a72..0000000000 --- a/internal/zinc-apiinfo/src/main/scala/xsbti/SafeLazy.scala +++ /dev/null @@ -1,24 +0,0 @@ -// needs to be in xsbti package (or a subpackage) to pass through the filter in DualLoader -// and be accessible to the compiler-side interface -package xsbti - -object SafeLazy { - def apply[T <: AnyRef](eval: xsbti.F0[T]): xsbti.api.Lazy[T] = - apply(eval()) - def apply[T <: AnyRef](eval: => T): xsbti.api.Lazy[T] = - fromFunction0(() => eval) - def fromFunction0[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] = - new Impl(eval) - - def strict[T <: AnyRef](value: T): xsbti.api.Lazy[T] = apply(value) - - private[this] final class Impl[T <: AnyRef](private[this] var eval: () => T) extends xsbti.api.AbstractLazy[T] { - private[this] lazy val _t = - { - val t = eval() - eval = null // clear the reference, ensuring the only memory we hold onto is the result - t - } - def get: T = _t - } -} diff --git a/internal/zinc-core/src/main/scala/sbt/internal/inc/APIs.scala b/internal/zinc-core/src/main/scala/sbt/internal/inc/APIs.scala index 0ebfb3bdee..1d227af6e4 100644 --- a/internal/zinc-core/src/main/scala/sbt/internal/inc/APIs.scala +++ b/internal/zinc-core/src/main/scala/sbt/internal/inc/APIs.scala @@ -6,7 +6,6 @@ package internal package inc import xsbti.api._ -import xsbti.SafeLazy import APIs.getAPI import xsbt.api.{ APIUtil, SameAPI } @@ -48,7 +47,7 @@ object APIs { val emptyCompilation = new xsbti.api.Compilation(-1, Array()) val emptyNameHashes = new xsbti.api.NameHashes(Array.empty, Array.empty) val emptyCompanions = new xsbti.api.Companions(emptyAPI, emptyAPI) - val emptyAnalyzedClass = new xsbti.api.AnalyzedClass(emptyCompilation, emptyName, SafeLazy(emptyCompanions), emptyAPIHash, + val emptyAnalyzedClass = new xsbti.api.AnalyzedClass(emptyCompilation, emptyName, SafeLazyProxy(emptyCompanions), emptyAPIHash, emptyNameHashes, false) def getAPI[T](map: Map[T, AnalyzedClass], className: T): AnalyzedClass = map.getOrElse(className, emptyAnalyzedClass) } diff --git a/internal/zinc-core/src/main/scala/sbt/internal/inc/Compile.scala b/internal/zinc-core/src/main/scala/sbt/internal/inc/Compile.scala index 9bf757b860..5e3b020cb5 100644 --- a/internal/zinc-core/src/main/scala/sbt/internal/inc/Compile.scala +++ b/internal/zinc-core/src/main/scala/sbt/internal/inc/Compile.scala @@ -9,7 +9,7 @@ import sbt.internal.inc.Analysis.{ LocalProduct, NonLocalProduct } import xsbt.api.{ APIUtil, HashAPI, NameHashing } import xsbti.api._ import xsbti.compile.{ CompileAnalysis, DependencyChanges, IncOptions, MultipleOutput, Output, SingleOutput } -import xsbti.{ Position, Problem, SafeLazy, Severity } +import xsbti.{ Position, Problem, Severity } import sbt.util.Logger import sbt.util.Logger.{ m2o, problem } import java.io.File @@ -274,7 +274,8 @@ private final class AnalysisCallback( val hasMacro: Boolean = macroClasses.contains(name) val (companions, apiHash) = companionsWithHash(name) val nameHashes = nameHashesForCompanions(name) - val ac = new AnalyzedClass(compilation, name, SafeLazy(companions), apiHash, nameHashes, hasMacro) + val safeCompanions = SafeLazyProxy(companions) + val ac = new AnalyzedClass(compilation, name, safeCompanions, apiHash, nameHashes, hasMacro) ac } diff --git a/internal/zinc-core/src/test/scala/sbt/inc/TestAnalysisCallback.scala b/internal/zinc-core/src/test/scala/sbt/inc/TestAnalysisCallback.scala index eaa3de345e..c180eec90a 100644 --- a/internal/zinc-core/src/test/scala/sbt/inc/TestAnalysisCallback.scala +++ b/internal/zinc-core/src/test/scala/sbt/inc/TestAnalysisCallback.scala @@ -5,7 +5,6 @@ package inc import java.io.File import scala.collection.mutable.{ ArrayBuffer, HashMap } -import xsbti.SafeLazy import xsbti.api._ import xsbti.api.DependencyContext._ import xsbt.api.{ HashAPI, NameHashing, APIUtil } @@ -105,7 +104,7 @@ class TestAnalysisCallback( val hasMacro: Boolean = macroClasses.contains(name) val (companions, apiHash) = companionsWithHash(name) val nameHashes = nameHashesForCompanions(name) - val ac = new AnalyzedClass(compilation, name, SafeLazy(companions), apiHash, nameHashes, hasMacro) + val ac = new AnalyzedClass(compilation, name, SafeLazyProxy(companions), apiHash, nameHashes, hasMacro) ac } diff --git a/internal/zinc-core/src/test/scala/sbt/inc/TestCaseGenerators.scala b/internal/zinc-core/src/test/scala/sbt/inc/TestCaseGenerators.scala index c0d6755930..d9a0c7518b 100644 --- a/internal/zinc-core/src/test/scala/sbt/inc/TestCaseGenerators.scala +++ b/internal/zinc-core/src/test/scala/sbt/inc/TestCaseGenerators.scala @@ -10,7 +10,6 @@ import Gen._ import sbt.internal.util.Relation import xsbti.api._ -import xsbti.SafeLazy import xsbti.api.DependencyContext._ /** @@ -82,7 +81,7 @@ object TestCaseGenerators { private[this] def makeCompanions(name: String): Companions = new Companions(makeClassLike(name, DefinitionType.ClassDef), makeClassLike(name, DefinitionType.Module)) - private[this] def lzy[T <: AnyRef](x: T) = SafeLazy.strict(x) + private[this] def lzy[T <: AnyRef](x: T) = SafeLazyProxy.strict(x) def genNameHash(defn: String): Gen[xsbti.api.NameHash] = const(new xsbti.api.NameHash(defn, defn.hashCode())) @@ -113,7 +112,7 @@ object TestCaseGenerators { apiHash <- arbitrary[Int] hasMacro <- arbitrary[Boolean] nameHashes <- genNameHashes(Seq(name)) - } yield new AnalyzedClass(new Compilation(startTime, Array()), name, SafeLazy(makeCompanions(name)), apiHash, nameHashes, hasMacro) + } yield new AnalyzedClass(new Compilation(startTime, Array()), name, SafeLazyProxy(makeCompanions(name)), apiHash, nameHashes, hasMacro) def genClasses(all_defns: Seq[String]): Gen[Seq[AnalyzedClass]] = Gen.sequence[List[AnalyzedClass], AnalyzedClass](all_defns.map(genClass)) diff --git a/internal/zinc-persist/src/main/scala/sbt/internal/inc/FileBasedStore.scala b/internal/zinc-persist/src/main/scala/sbt/internal/inc/FileBasedStore.scala index b1a59e2f3b..54a5663c11 100644 --- a/internal/zinc-persist/src/main/scala/sbt/internal/inc/FileBasedStore.scala +++ b/internal/zinc-persist/src/main/scala/sbt/internal/inc/FileBasedStore.scala @@ -8,7 +8,6 @@ package inc import java.io._ import java.util.zip.{ ZipInputStream, ZipEntry } import sbt.io.{ IO, Using } -import xsbti.SafeLazy import xsbti.compile.{ CompileAnalysis, MiniSetup } import xsbti.api.Companions import scala.util.control.Exception.allCatch diff --git a/internal/zinc-persist/src/main/scala/sbt/internal/inc/TextAnalysisFormat.scala b/internal/zinc-persist/src/main/scala/sbt/internal/inc/TextAnalysisFormat.scala index 4f84b89999..15f7da503e 100644 --- a/internal/zinc-persist/src/main/scala/sbt/internal/inc/TextAnalysisFormat.scala +++ b/internal/zinc-persist/src/main/scala/sbt/internal/inc/TextAnalysisFormat.scala @@ -4,8 +4,8 @@ package inc import java.io._ import sbt.internal.util.Relation -import xsbti.{ T2, SafeLazy } -import xsbti.api.{ AnalyzedClass, Compilation, Companions, NameHashes, Lazy } +import xsbti.T2 +import xsbti.api.{ AnalyzedClass, Compilation, Companions, NameHashes, Lazy, SafeLazyProxy } import xsbti.compile.{ CompileAnalysis, MultipleOutput, SingleOutput, MiniOptions, MiniSetup, FileHash } import javax.xml.bind.DatatypeConverter import java.net.URI @@ -273,6 +273,7 @@ object TextAnalysisFormat { FormatTimer.close("sbinary write") } + @inline final def lzy[T](t: => T) = SafeLazyProxy(t) def read(in: BufferedReader, companionsStore: Option[CompanionsStore]): APIs = { val internal = readMap(in)(Headers.internal, identity[String], stringToAnalyzedClass) val external = readMap(in)(Headers.external, identity[String], stringToAnalyzedClass) @@ -281,11 +282,11 @@ object TextAnalysisFormat { companionsStore match { case Some(companionsStore) => val companions: Lazy[(Map[String, Companions], Map[String, Companions])] = - SafeLazy(companionsStore.getUncaught()) + lzy(companionsStore.getUncaught()) APIs( - internal map { case (k, v) => k -> v.withApi(SafeLazy(companions.get._1(k))) }, - external map { case (k, v) => k -> v.withApi(SafeLazy(companions.get._2(k))) } + internal map { case (k, v) => k -> v.withApi(lzy(companions.get._1(k))) }, + external map { case (k, v) => k -> v.withApi(lzy(companions.get._2(k))) } ) case _ => APIs(internal, external) } diff --git a/internal/zinc-persist/src/main/scala/xsbt/api/AnalyzedClassFormat.scala b/internal/zinc-persist/src/main/scala/xsbt/api/AnalyzedClassFormat.scala index 1f20bca23f..bf43bbe1ef 100644 --- a/internal/zinc-persist/src/main/scala/xsbt/api/AnalyzedClassFormat.scala +++ b/internal/zinc-persist/src/main/scala/xsbt/api/AnalyzedClassFormat.scala @@ -3,7 +3,6 @@ */ package xsbt.api -import xsbti.SafeLazy import xsbti.api._ import sbinary._ import sbinary.DefaultProtocol._ @@ -16,7 +15,7 @@ object AnalyzedClassFormats { a => (a.compilation, a.name, a.apiHash, a.nameHashes, a.hasMacro), (x: (Compilation, String, Int, NameHashes, Boolean)) => x match { case (compilation: Compilation, name: String, apiHash: Int, nameHashes: NameHashes, hasMacro: Boolean) => - new AnalyzedClass(compilation, name, SafeLazy(emptyCompanions), apiHash, nameHashes, hasMacro) + new AnalyzedClass(compilation, name, SafeLazyProxy(emptyCompanions), apiHash, nameHashes, hasMacro) } ) }