mirror of https://github.com/sbt/sbt.git
improving incremental compilation
support lazy arguments in data type generator SafeLazy implementation that explicitly clears the reference to the thunk in API representation, drop synthetic modifier and merge deferred into abstract handle cyclic structures in API generation, display, comparison, persistence gzip compile cache file bump to 2.8.1.RC3, project definition cleanup fix main method detection to check for the right name properly view inherited definitions exclude constructors of ancestors
This commit is contained in:
parent
3c5d8ab29b
commit
5ed8f3c042
|
|
@ -53,7 +53,8 @@ class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: Component
|
|||
private[this] def loader =
|
||||
{
|
||||
val interfaceJar = getInterfaceJar(log)
|
||||
val dual = createDualLoader(scalaInstance.loader, getClass.getClassLoader) // this goes to scalaLoader for scala classes and sbtLoader for xsbti classes
|
||||
// this goes to scalaInstance.loader for scala classes and the loader of this class for xsbti classes
|
||||
val dual = createDualLoader(scalaInstance.loader, getClass.getClassLoader)
|
||||
new URLClassLoader(Array(interfaceJar.toURI.toURL), dual)
|
||||
}
|
||||
private def getInterfaceClass(name: String, log: Logger) = Class.forName(name, true, loader)
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package xsbt.api
|
||||
|
||||
import xsbti.api._
|
||||
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStream, ObjectInputStream, ObjectOutputStream, OutputStream}
|
||||
|
||||
object APIFormat
|
||||
{
|
||||
def write(api: Source): Array[Byte] =
|
||||
{
|
||||
val baos = new ByteArrayOutputStream
|
||||
write(api, baos)
|
||||
baos.toByteArray
|
||||
}
|
||||
def write(api: Source, out: OutputStream)
|
||||
{
|
||||
val objOut = new ObjectOutputStream(out)
|
||||
try { objOut.writeObject(api) }
|
||||
finally { objOut.close() }
|
||||
}
|
||||
def read(bytes: Array[Byte]): Source = read(new ByteArrayInputStream(bytes))
|
||||
def read(in: InputStream): Source =
|
||||
{
|
||||
try
|
||||
{
|
||||
val objIn = new ObjectInputStream(in)
|
||||
try { objIn.readObject().asInstanceOf[Source] }
|
||||
finally { objIn.close() }
|
||||
}
|
||||
catch { case _: java.io.EOFException | _: java.io.InvalidClassException => emptySource}
|
||||
}
|
||||
def emptySource: Source = new Source(Array(), Array())
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package xsbt.api
|
||||
|
||||
import xsbti.api._
|
||||
import scala.collection.mutable.HashSet
|
||||
|
||||
object APIUtil
|
||||
{
|
||||
val modifiersToByte = (m: Modifiers) => {
|
||||
import m._
|
||||
def x(b: Boolean, bit: Int) = if(b) 1 << bit else 0
|
||||
( x(isAbstract, 0) | x(isOverride, 1) | x(isFinal, 2) | x(isSealed, 3) | x(isImplicit, 4) | x(isLazy, 5) ).toByte
|
||||
}
|
||||
val byteToModifiers = (b: Byte) => {
|
||||
def x(bit: Int) = (b & (1 << bit)) != 0
|
||||
new Modifiers( x(0), x(1), x(2), x(3), x(4), x(5) )
|
||||
}
|
||||
|
||||
def verifyTypeParameters(s: Source): Boolean =
|
||||
{
|
||||
val check = new CheckTypeParameters
|
||||
val invalid = check(s)
|
||||
if(!invalid.isEmpty) println("References to undefined type parameters: " + invalid.mkString(", "))
|
||||
invalid.isEmpty
|
||||
}
|
||||
private[this] class CheckTypeParameters extends Visit
|
||||
{
|
||||
private val defined = new HashSet[Int]
|
||||
private val referenced = new HashSet[Int]
|
||||
def apply(s: Source): List[Int] =
|
||||
{
|
||||
super.visit(s)
|
||||
(referenced filterNot defined).toList
|
||||
}
|
||||
override def visitTypeParameter(parameter: TypeParameter)
|
||||
{
|
||||
defined += parameter.id
|
||||
super.visitTypeParameter(parameter)
|
||||
}
|
||||
override def visitParameterRef(ref: ParameterRef)
|
||||
{
|
||||
referenced += ref.id
|
||||
super.visitParameterRef(ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package sbt
|
|||
import java.lang.reflect.{Array => _, _}
|
||||
import java.lang.annotation.Annotation
|
||||
import xsbti.api
|
||||
import xsbti.SafeLazy
|
||||
import SafeLazy.strict
|
||||
|
||||
object ClassToAPI
|
||||
{
|
||||
|
|
@ -27,10 +29,10 @@ object ClassToAPI
|
|||
val annots = annotations(c.getAnnotations)
|
||||
val name = c.getName
|
||||
val tpe = if(Modifier.isInterface(c.getModifiers)) Trait else ClassDef
|
||||
val (static, instance) = structure(c)
|
||||
val cls = new api.ClassLike(tpe, Empty, instance, typeParameters(c.getTypeParameters), name, acc, mods, annots)
|
||||
lazy val (static, instance) = structure(c)
|
||||
val cls = new api.ClassLike(tpe, strict(Empty), lzy(instance), typeParameters(c.getTypeParameters), name, acc, mods, annots)
|
||||
def makeStatic(s: api.Structure) =
|
||||
new api.ClassLike(Module, Empty, s, Array(), name, acc, mods, annots)
|
||||
new api.ClassLike(Module, strict(Empty), strict(s), Array(), name, acc, mods, annots)
|
||||
cls :: static.map(makeStatic).toList
|
||||
}
|
||||
|
||||
|
|
@ -42,17 +44,20 @@ object ClassToAPI
|
|||
val classes = merge[Class[_]](c, c.getClasses, c.getDeclaredClasses, toDefinitions, (_: Seq[Class[_]]).partition(isStatic), _.getEnclosingClass != c)
|
||||
val all = (methods ++ fields ++ constructors ++ classes)
|
||||
val parentTypes = parents(c)
|
||||
val instanceStructure = new api.Structure(parentTypes.toArray, all.declared.toArray, all.inherited.toArray)
|
||||
def static = new api.Structure(Array(), all.staticDeclared.toArray, all.staticInherited.toArray)
|
||||
val instanceStructure = new api.Structure(lzy(parentTypes.toArray), lzy(all.declared.toArray), lzy(all.inherited.toArray))
|
||||
def static = new api.Structure(emptyTpeArray, lzy(all.staticDeclared.toArray), lzy(all.staticInherited.toArray))
|
||||
val staticStructure = if(all.staticDeclared.isEmpty && all.staticInherited.isEmpty) None else Some(static)
|
||||
(staticStructure, instanceStructure)
|
||||
}
|
||||
def lzy[T <: AnyRef](t: => T): xsbti.api.Lazy[T] = xsbti.SafeLazy(t)
|
||||
private val emptyTpeArray = lzy(Array[xsbti.api.Type]())
|
||||
private val emptyDefArray = lzy(Array[xsbti.api.Definition]())
|
||||
|
||||
def parents(c: Class[_]): Seq[api.Type] =
|
||||
types(c.getGenericSuperclass +: c.getGenericInterfaces)
|
||||
def types(ts: Seq[Type]): Array[api.Type] = ts filter (_ ne null) map reference toArray;
|
||||
def upperBounds(ts: Array[Type]): api.Type =
|
||||
new api.Structure(types(ts), Array(), Array())
|
||||
new api.Structure(lzy(types(ts)), emptyDefArray, emptyDefArray)
|
||||
|
||||
def fieldToDef(f: Field): api.FieldLike =
|
||||
{
|
||||
|
|
@ -133,7 +138,7 @@ object ClassToAPI
|
|||
def modifiers(i: Int): api.Modifiers =
|
||||
{
|
||||
import Modifier.{isAbstract, isFinal}
|
||||
new api.Modifiers( isAbstract(i), false, false, isFinal(i), false, false, false, false)
|
||||
new api.Modifiers( isAbstract(i), false, isFinal(i), false, false, false)
|
||||
}
|
||||
def access(i: Int): api.Access =
|
||||
{
|
||||
|
|
@ -213,4 +218,4 @@ object ClassToAPI
|
|||
private[this] def PrimitiveMap = PrimitiveNames.map( j => (j, j.capitalize)) :+ ("void" -> "Unit")
|
||||
private[this] val PrimitiveRefs = PrimitiveMap.map { case (n, sn) => (n, reference("scala." + sn)) }.toMap
|
||||
def primitive(name: String): api.SimpleType = PrimitiveRefs(name)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
// 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.Lazy[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
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
package xsbt.api
|
||||
|
||||
import xsbti.api._
|
||||
import TagTypeVariables.TypeVars
|
||||
|
||||
import Function.tupled
|
||||
import scala.collection.{immutable, mutable}
|
||||
|
|
@ -36,7 +35,7 @@ object TopLevel
|
|||
def definitions(i: Iterable[Source]) = SameAPI.separateDefinitions(i.toSeq.flatMap( _.definitions ))
|
||||
def names(s: Iterable[Definition]): Set[String] = Set() ++ s.map(_.name)
|
||||
}
|
||||
|
||||
import TagTypeVariables.TypeVars
|
||||
/** Checks the API of two source files for equality.*/
|
||||
object SameAPI
|
||||
{
|
||||
|
|
@ -89,10 +88,12 @@ object SameAPI
|
|||
class SameAPI(tagsA: TypeVars, tagsB: TypeVars, includePrivate: Boolean, includeParamNames: Boolean)
|
||||
{
|
||||
import SameAPI._
|
||||
|
||||
|
||||
private val pending = new mutable.HashSet[(Structure, Structure)]
|
||||
private[this] val debugEnabled = java.lang.Boolean.getBoolean("xsbt.api.debug")
|
||||
def debug(flag: Boolean, msg: => String): Boolean =
|
||||
{
|
||||
//if(!flag) println(msg)
|
||||
if(debugEnabled && !flag) println(msg)
|
||||
flag
|
||||
}
|
||||
|
||||
|
|
@ -192,13 +193,11 @@ class SameAPI(tagsA: TypeVars, tagsB: TypeVars, includePrivate: Boolean, include
|
|||
import m._
|
||||
val bs = new mutable.BitSet
|
||||
setIf(bs, isAbstract, 0)
|
||||
setIf(bs, isDeferred, 1)
|
||||
setIf(bs, isOverride, 2)
|
||||
setIf(bs, isFinal, 3)
|
||||
setIf(bs, isSealed, 4)
|
||||
setIf(bs, isImplicit, 5)
|
||||
setIf(bs, isLazy, 6)
|
||||
setIf(bs, isSynthetic, 7)
|
||||
setIf(bs, isOverride, 1)
|
||||
setIf(bs, isFinal, 2)
|
||||
setIf(bs, isSealed, 3)
|
||||
setIf(bs, isImplicit, 4)
|
||||
setIf(bs, isLazy, 5)
|
||||
bs.toImmutable
|
||||
}
|
||||
def setIf(bs: mutable.BitSet, flag: Boolean, i: Int): Unit =
|
||||
|
|
@ -314,6 +313,12 @@ class SameAPI(tagsA: TypeVars, tagsB: TypeVars, includePrivate: Boolean, include
|
|||
sameSimpleType(a.baseType, b.baseType) &&
|
||||
sameAnnotations(a.annotations, b.annotations)
|
||||
def sameStructure(a: Structure, b: Structure): Boolean =
|
||||
if(pending add ((a,b)) )
|
||||
try { sameStructureDirect(a,b) }
|
||||
finally { pending -= ((a,b)) }
|
||||
else
|
||||
true
|
||||
def sameStructureDirect(a: Structure, b: Structure): Boolean =
|
||||
sameSeq(a.parents, b.parents)(sameType) &&
|
||||
sameMembers(a.declared, b.declared) &&
|
||||
sameMembers(a.inherited, b.inherited)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
package xsbt.api
|
||||
|
||||
import xsbti.api._
|
||||
import xsbti.api._
|
||||
|
||||
trait Show[A]
|
||||
{
|
||||
|
|
@ -16,7 +16,7 @@ final class ShowLazy[A](delegate: => Show[A]) extends Show[A]
|
|||
def show(a: A) = s.show(a)
|
||||
}
|
||||
|
||||
import ShowAPI._
|
||||
import ShowAPI._
|
||||
|
||||
object ShowAPI
|
||||
{
|
||||
|
|
@ -26,7 +26,7 @@ object ShowAPI
|
|||
def bounds(lower: Type, upper: Type)(implicit t: Show[Type]): String =
|
||||
">: " + t.show(lower) + " <: " + t.show(upper)
|
||||
|
||||
import ParameterModifier._
|
||||
import ParameterModifier._
|
||||
def parameterModifier(base: String, pm: ParameterModifier): String =
|
||||
pm match
|
||||
{
|
||||
|
|
@ -50,7 +50,7 @@ trait ShowBase
|
|||
implicit def showAnnotationArgument: Show[AnnotationArgument] =
|
||||
new Show[AnnotationArgument] { def show(a: AnnotationArgument) = a.name + " = " + a.value }
|
||||
|
||||
import Variance._
|
||||
import Variance._
|
||||
implicit def showVariance: Show[Variance] =
|
||||
new Show[Variance] { def show(v: Variance) = v match { case Invariant => ""; case Covariant => "+"; case Contravariant => "-" } }
|
||||
|
||||
|
|
@ -102,9 +102,8 @@ trait ShowBase
|
|||
(m.isFinal, "final") ::
|
||||
(m.isSealed, "sealed") ::
|
||||
(m.isImplicit, "implicit") ::
|
||||
(m.isAbstract || m.isDeferred, "abstract") ::
|
||||
(m.isAbstract, "abstract") ::
|
||||
(m.isLazy, "lazy") ::
|
||||
(m.isSynthetic, "synthetic") ::
|
||||
Nil
|
||||
mods.filter(_._1).map(_._2).mkString(" ")
|
||||
}
|
||||
|
|
@ -142,7 +141,9 @@ trait ShowDefinitions
|
|||
|
||||
implicit def showTypeDeclaration(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type]): Show[TypeDeclaration] =
|
||||
new Show[TypeDeclaration] { def show(td: TypeDeclaration) = parameterizedDef(td, "type")(acs, ms, ans, tp) + bounds(td.lowerBound, td.upperBound) }
|
||||
|
||||
def showClassLikeSimple(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], dt: Show[DefinitionType]): Show[ClassLike] =
|
||||
new Show[ClassLike] { def show(cl: ClassLike) = parameterizedDef(cl, dt.show(cl.definitionType))(acs, ms, ans, tp) }
|
||||
|
||||
def parameterizedDef(d: ParameterizedDefinition, label: String)(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]]): String =
|
||||
definitionBase(d, label)(acs, ms, ans) + tp.show(d.typeParameters)
|
||||
def definitionBase(d: Definition, label: String)(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation]): String =
|
||||
|
|
@ -209,7 +210,8 @@ trait ShowTypes
|
|||
implicit def showStructure(implicit t: Show[Type], d: Show[Definition]): Show[Structure] =
|
||||
new Show[Structure] {
|
||||
def show(s: Structure) =
|
||||
concat(s.parents, t, " with ") + "\n{\n Declared:\n" + lines(s.declared, d) + "\n Inherited:\n" + lines(s.inherited, d) + "\n}"
|
||||
// don't show inherited to avoid dealing with cycles
|
||||
concat(s.parents, t, " with ") + "\n{\n" + lines(s.declared, d) + "\n}"
|
||||
}
|
||||
implicit def showAnnotated(implicit as: Show[Annotation], t: Show[SimpleType]): Show[Annotated] =
|
||||
new Show[Annotated] { def show(a: Annotated) = spaced(a.annotations, as) + " " + t.show(a.baseType) }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package xsbt.api
|
||||
|
||||
import xsbti.api._
|
||||
import xsbti.api._
|
||||
import scala.collection.mutable
|
||||
|
||||
object TagTypeVariables
|
||||
{
|
||||
|
|
@ -8,9 +12,12 @@ object TagTypeVariables
|
|||
def apply(s: Source): TypeVars = (new TagTypeVariables).tag(s)
|
||||
}
|
||||
import TagTypeVariables.TypeVars
|
||||
private class TagTypeVariables extends NotNull
|
||||
private class TagTypeVariables
|
||||
{
|
||||
private val tags = new scala.collection.mutable.HashMap[Int, (Int, Int)]
|
||||
private val taggedStructures = new mutable.HashSet[Structure]
|
||||
private val taggedClasses = new mutable.HashSet[ClassLike]
|
||||
|
||||
private val tags = new mutable.HashMap[Int, (Int, Int)]
|
||||
private var level = 0
|
||||
private var index = 0
|
||||
|
||||
|
|
@ -31,7 +38,8 @@ private class TagTypeVariables extends NotNull
|
|||
case t: TypeAlias => tagTypeAlias(t)
|
||||
}
|
||||
}
|
||||
def tagClass(c: ClassLike): Unit =
|
||||
def tagClass(c: ClassLike): Unit = if(taggedClasses add c) tagClass0(c)
|
||||
def tagClass0(c: ClassLike): Unit =
|
||||
tagParameterizedDefinition(c) {
|
||||
tagType(c.selfType)
|
||||
tagStructure(c.structure)
|
||||
|
|
@ -108,7 +116,8 @@ private class TagTypeVariables extends NotNull
|
|||
tagType(a.baseType)
|
||||
tagAnnotations(a.annotations)
|
||||
}
|
||||
def tagStructure(structure: Structure)
|
||||
def tagStructure(structure: Structure): Unit = if(taggedStructures add structure) tagStructure0(structure)
|
||||
def tagStructure0(structure: Structure)
|
||||
{
|
||||
tagTypes(structure.parents)
|
||||
tagDefinitions(structure.declared)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package xsbt.api
|
||||
|
||||
import xsbti.api._
|
||||
import scala.collection.mutable
|
||||
|
||||
class Visit
|
||||
{
|
||||
private[this] val visitedStructures = new mutable.HashSet[Structure]
|
||||
private[this] val visitedClassLike = new mutable.HashSet[ClassLike]
|
||||
|
||||
def visit(s: Source): Unit =
|
||||
s.definitions foreach visitDefinition
|
||||
|
||||
def visitDefinitions(ds: Seq[Definition]) = ds foreach visitDefinition
|
||||
def visitDefinition(d: Definition)
|
||||
{
|
||||
d match
|
||||
{
|
||||
case c: ClassLike => visitClass(c)
|
||||
case f: FieldLike => visitField(f)
|
||||
case d: Def => visitDef(d)
|
||||
case t: TypeDeclaration => visitTypeDeclaration(t)
|
||||
case t: TypeAlias => visitTypeAlias(t)
|
||||
}
|
||||
}
|
||||
final def visitClass(c: ClassLike): Unit = if(visitedClassLike add c) visitClass0(c)
|
||||
def visitClass0(c: ClassLike)
|
||||
{
|
||||
visitParameterizedDefinition(c)
|
||||
visitType(c.selfType)
|
||||
visitStructure(c.structure)
|
||||
}
|
||||
def visitField(f: FieldLike)
|
||||
{
|
||||
visitType(f.tpe)
|
||||
visitAnnotations(f.annotations)
|
||||
}
|
||||
def visitDef(d: Def)
|
||||
{
|
||||
visitParameterizedDefinition(d)
|
||||
visitValueParameters(d.valueParameters)
|
||||
visitType(d.returnType)
|
||||
}
|
||||
def visitValueParameters(valueParameters: Seq[ParameterList]) = valueParameters foreach visitValueParameterList
|
||||
def visitValueParameterList(list: ParameterList) = list.parameters foreach visitValueParameter
|
||||
def visitValueParameter(parameter: MethodParameter) = visitType(parameter.tpe)
|
||||
|
||||
def visitParameterizedDefinition[T <: ParameterizedDefinition](d: T)
|
||||
{
|
||||
visitAnnotations(d.annotations)
|
||||
visitTypeParameters(d.typeParameters)
|
||||
}
|
||||
def visitTypeDeclaration(d: TypeDeclaration)
|
||||
{
|
||||
visitParameterizedDefinition(d)
|
||||
visitType(d.lowerBound)
|
||||
visitType(d.upperBound)
|
||||
}
|
||||
def visitTypeAlias(d: TypeAlias)
|
||||
{
|
||||
visitParameterizedDefinition(d)
|
||||
visitType(d.tpe)
|
||||
}
|
||||
|
||||
def visitTypeParameters(parameters: Seq[TypeParameter]) = parameters foreach visitTypeParameter
|
||||
def visitTypeParameter(parameter: TypeParameter)
|
||||
{
|
||||
visitTypeParameters(parameter.typeParameters)
|
||||
visitType(parameter.lowerBound)
|
||||
visitType(parameter.upperBound)
|
||||
}
|
||||
def visitAnnotations(annotations: Seq[Annotation]) = annotations foreach visitAnnotation
|
||||
def visitAnnotation(annotation: Annotation) = visitType(annotation.base)
|
||||
|
||||
def visitTypes(ts: Seq[Type]) = ts.foreach(visitType)
|
||||
def visitType(t: Type)
|
||||
{
|
||||
t match
|
||||
{
|
||||
case s: Structure => visitStructure(s)
|
||||
case e: Existential => visitExistential(e)
|
||||
case p: Polymorphic => visitPolymorphic(p)
|
||||
case a: Annotated => visitAnnotated(a)
|
||||
case p: Parameterized => visitParameterized(p)
|
||||
case p: Projection => visitProjection(p)
|
||||
case _: EmptyType => visitEmptyType()
|
||||
case s: Singleton => visitSingleton(s)
|
||||
case pr: ParameterRef => visitParameterRef(pr)
|
||||
}
|
||||
}
|
||||
|
||||
def visitEmptyType() {}
|
||||
def visitSingleton(s: Singleton) {}
|
||||
def visitParameterRef(p: ParameterRef) {}
|
||||
|
||||
def visitExistential(e: Existential) = visitParameters(e.clause, e.baseType)
|
||||
def visitPolymorphic(p: Polymorphic) = visitParameters(p.parameters, p.baseType)
|
||||
def visitProjection(p: Projection) = visitType(p.prefix)
|
||||
def visitParameterized(p: Parameterized)
|
||||
{
|
||||
visitType(p.baseType)
|
||||
visitTypes(p.typeArguments)
|
||||
}
|
||||
def visitAnnotated(a: Annotated)
|
||||
{
|
||||
visitType(a.baseType)
|
||||
visitAnnotations(a.annotations)
|
||||
}
|
||||
final def visitStructure(structure: Structure) = if(visitedStructures add structure) visitStructure0(structure)
|
||||
def visitStructure0(structure: Structure)
|
||||
{
|
||||
visitTypes(structure.parents)
|
||||
visitDefinitions(structure.declared)
|
||||
visitDefinitions(structure.inherited)
|
||||
}
|
||||
def visitParameters(parameters: Seq[TypeParameter], base: Type): Unit =
|
||||
{
|
||||
visitTypeParameters(parameters)
|
||||
visitType(base)
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@ object Discovery
|
|||
apply(Set.empty, Set.empty)( definitions )
|
||||
|
||||
def isConcrete(a: Definition): Boolean = isConcrete(a.modifiers)
|
||||
def isConcrete(m: Modifiers) = !m.isAbstract && !m.isDeferred
|
||||
def isConcrete(m: Modifiers) = !m.isAbstract
|
||||
def isPublic(a: Definition): Boolean = isPublic(a.access)
|
||||
def isPublic(a: Access): Boolean = a.isInstanceOf[Public]
|
||||
def isModule(c: ClassLike) = c.definitionType == DefinitionType.Module
|
||||
|
|
@ -54,7 +54,7 @@ object Discovery
|
|||
defs.exists(isMainMethod)
|
||||
def isMainMethod(d: Definition): Boolean =
|
||||
d match {
|
||||
case d: Def => isPublic(d) && isConcrete(d) && isUnit(d.returnType) && isStringArray(d.valueParameters)
|
||||
case d: Def => d.name == "main" && isPublic(d) && isConcrete(d) && isUnit(d.returnType) && isStringArray(d.valueParameters)
|
||||
case _ => false
|
||||
}
|
||||
def isStringArray(vp: IndexedSeq[ParameterList]): Boolean = vp.length == 1 && isStringArray(vp(0).parameters)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ object ApplicationsTest extends Specification
|
|||
""" ::"""
|
||||
object Main4 extends Main2
|
||||
""" :: """
|
||||
trait Main5 { def main(args: Array[String]) {} }; trait Main5b extends Main5; trait Main5c extends Main2; abstract class Main5d { def main(args: Array[String]) {} }
|
||||
trait Main5 { def main(args: Array[String]) {} }; trait Main5b extends Main5; trait Main5c extends Main2; abstract class Main5d { def main(args: Array[String]) {} };
|
||||
trait Main5e[T] { def main(args: Array[T]) {} }
|
||||
trait Main5f[T <: String] { def main(args: Array[T]) {} }
|
||||
""" :: """
|
||||
object Main6a { var main = () }
|
||||
object Main6b { var main = (args: Array[String]) => () }
|
||||
|
|
@ -93,6 +95,9 @@ object ApplicationsTest extends Specification
|
|||
type A[T] = Array[String]
|
||||
def main[T](args: A[T]) {}
|
||||
}
|
||||
object MainE6 extends Main5e[String]
|
||||
object MainE7 extends Main5e[Int]
|
||||
object MainE8 extends Main5f[String]
|
||||
""" :: """
|
||||
object MainF1 extends Application { var x = 3; x = 5 }
|
||||
object MainF2 { def main(args: Array[java.lang.String]) {} }
|
||||
|
|
@ -107,18 +112,23 @@ object ApplicationsTest extends Specification
|
|||
for(scalaVersion <- TestCompile.allVersions)
|
||||
CallbackTest.full(scalaVersion, files) { (callback, file, scalaInstance, log) =>
|
||||
val expected = Set( main -> "Main", main4 -> "Main4", main8 -> "Main8", main9 -> "Main9", mainB -> "MainB",
|
||||
mainE -> "MainE1", mainE -> "MainE2", mainE -> "MainE3", mainE -> "MainE4", mainE -> "MainE5",
|
||||
mainE -> "MainE1", mainE -> "MainE2", mainE -> "MainE3", mainE -> "MainE4", mainE -> "MainE5", mainE -> "MainE6", mainE -> "MainE8",
|
||||
mainF -> "MainF1", mainF -> "MainF2", mainF -> "MainF4")
|
||||
// the source signature is valid for the following, but the binary signature is not, so they can't actually be run.
|
||||
// these are then known discrepancies between detected and actual entry points
|
||||
val erased = Set( mainE -> "MainE6", mainE -> "MainE8" )
|
||||
val actual = applications(callback).toSet
|
||||
(actual -- expected) must beEmpty
|
||||
(expected -- actual) must beEmpty
|
||||
val loader = new URLClassLoader(Array(file.toURI.toURL), scalaInstance.loader)
|
||||
for( (_, className) <- expected) testRun(loader, className)
|
||||
for( (_, className) <- expected filterNot erased) testRun(loader, className)
|
||||
}
|
||||
}
|
||||
}
|
||||
def applications(callback: xsbti.TestCallback): Seq[(File, String)] =
|
||||
for( (file, api) <- callback.apis.toSeq; application <- applications(api))
|
||||
for( (file, api) <- callback.apis.toSeq;
|
||||
x = println("\n" + file + ":\n" + (api.definitions.flatMap { case c: xsbti.api.ClassLike => c.structure.inherited.filter(_.name == "main"); case _ => Nil }).map(xsbt.api.DefaultShowAPI.apply).mkString("\n"));
|
||||
application <- applications(api))
|
||||
yield (file, application)
|
||||
def applications(src: xsbti.api.Source): Seq[String] =
|
||||
Discovery.applications(src.definitions) collect { case (definition, Discovered(_, _, true, _)) => definition.name }
|
||||
|
|
|
|||
|
|
@ -46,9 +46,7 @@ object DetectAnnotations extends Specification
|
|||
Nil
|
||||
val actual = subclasses(callback).toSet
|
||||
val actualOnly = (actual -- expected)
|
||||
println("Actual: " + actualOnly)
|
||||
val expectedOnly = (expected.toSet -- actual)
|
||||
println("Expected: " + expectedOnly)
|
||||
expectedOnly must beEmpty
|
||||
actualOnly must beEmpty
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ object DetectSubclasses extends Specification
|
|||
val actual = subclasses(callback).toSet
|
||||
val actualOnly = actual -- expected
|
||||
val expectedOnly = expected.toSet -- actual
|
||||
assert(actualOnly.isEmpty, "Actual only: " + actualOnly)
|
||||
assert(expectedOnly.isEmpty , "Expected only: " + expectedOnly)
|
||||
actualOnly must beEmpty
|
||||
expectedOnly must beEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,13 +38,46 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
def processUnit(unit: CompilationUnit)
|
||||
{
|
||||
val sourceFile = unit.source.file.file
|
||||
println("Traversing " + sourceFile)
|
||||
val traverser = new TopLevelHandler(sourceFile)
|
||||
traverser.apply(unit.body)
|
||||
val packages = traverser.packages.toArray[String].map(p => new xsbti.api.Package(p))
|
||||
val source = new xsbti.api.Source(packages, traverser.definitions.toArray[xsbti.api.Definition])
|
||||
forceStructures()
|
||||
callback.api(sourceFile, source)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] val structureCache = new HashMap[Symbol, xsbti.api.Structure]
|
||||
private[this] val classLikeCache = new HashMap[Symbol, xsbti.api.ClassLike]
|
||||
private[this] val pending = new HashSet[xsbti.api.Lazy[_]]
|
||||
|
||||
// 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) can 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
|
||||
}
|
||||
|
||||
// force all lazy structures. This is necessary so that we see the symbols/types at this phase and
|
||||
// so that we don't hold on to compiler objects and classes
|
||||
private def forceStructures(): Unit =
|
||||
if(pending.isEmpty)
|
||||
structureCache.clear()
|
||||
else
|
||||
{
|
||||
val toProcess = pending.toList
|
||||
pending.clear()
|
||||
toProcess foreach { _.get() }
|
||||
forceStructures()
|
||||
}
|
||||
|
||||
private def thisPath(sym: Symbol) = path(pathComponents(sym, Constants.thisPath :: Nil))
|
||||
private def path(components: List[PathComponent]) = new xsbti.api.Path(components.toArray[PathComponent])
|
||||
private def pathComponents(sym: Symbol, postfix: List[PathComponent]): List[PathComponent] =
|
||||
|
|
@ -56,21 +89,28 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
processType(t) match
|
||||
{
|
||||
case s: SimpleType => s
|
||||
case _ => error("Expected simple type: " + t)
|
||||
case x => error("Not a simple type:\n\tType: " + t + " (class " + t.getClass + ")\n\tTransformed: " + x.getClass)
|
||||
}
|
||||
private def types(t: List[Type]): Array[xsbti.api.Type] = t.toArray[Type].map(processType)
|
||||
private def projectionType(pre: Type, sym: Symbol) =
|
||||
{
|
||||
if(pre == NoPrefix)
|
||||
{
|
||||
if(sym.isLocalClass) Constants.emptyType
|
||||
else if(sym.isTypeParameterOrSkolem || isExistential(sym)) new xsbti.api.ParameterRef(sym.id)
|
||||
else error("Unknown prefixless type: " + sym + " in " + sym.owner + " in class " + sym.enclClass)
|
||||
if(sym.isLocalClass || sym.isRoot || sym.isRootPackage) Constants.emptyType
|
||||
else if(sym.isTypeParameterOrSkolem || isExistential(sym)) reference(sym)
|
||||
else {
|
||||
println("Warning: Unknown prefixless type: " + sym + " in " + sym.owner + " in " + sym.enclClass)
|
||||
println("\tFlags: " + sym.flags + ", istype: " + sym.isType + ", absT: " + sym.isAbstractType + ", alias: " + sym.isAliasType + ", nonclass: " + isNonClassType(sym))
|
||||
reference(sym)
|
||||
}
|
||||
}
|
||||
else if(sym.isRoot || sym.isRootPackage) Constants.emptyType
|
||||
else new xsbti.api.Projection(simpleType(pre), sym.nameString)
|
||||
}
|
||||
|
||||
private def reference(sym: Symbol): xsbti.api.ParameterRef = new xsbti.api.ParameterRef(sym.id)
|
||||
|
||||
|
||||
private def annotations(as: List[AnnotationInfo]): Array[xsbti.api.Annotation] = as.toArray[AnnotationInfo].map(annotation)
|
||||
private def annotation(a: AnnotationInfo) =
|
||||
new xsbti.api.Annotation(simpleType(a.atp),
|
||||
|
|
@ -79,7 +119,9 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
)
|
||||
private def annotated(as: List[AnnotationInfo], tpe: Type) = new xsbti.api.Annotated(simpleType(tpe), annotations(as))
|
||||
|
||||
private def defDef(s: Symbol) =
|
||||
private def viewer(s: Symbol) = (if(s.isModule) s.moduleClass else s).thisType
|
||||
private def printMember(label: String, in: Symbol, t: Type) = println(label + " in " + in + " : " + t + " (debug: " + debugString(t) + " )")
|
||||
private def defDef(in: Symbol, s: Symbol) =
|
||||
{
|
||||
def build(t: Type, typeParams: Array[xsbti.api.TypeParameter], valueParameters: List[xsbti.api.ParameterList]): xsbti.api.Def =
|
||||
{
|
||||
|
|
@ -107,8 +149,12 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
new xsbti.api.Def(valueParameters.reverse.toArray, processType(returnType), typeParams, simpleName(s), getAccess(s), getModifiers(s), annotations(s))
|
||||
}
|
||||
}
|
||||
def parameterS(s: Symbol): xsbti.api.MethodParameter = makeParameter(s.nameString, s.info, s.info.typeSymbol, s)
|
||||
def parameterT(t: Type): xsbti.api.MethodParameter = makeParameter("", t, t.typeSymbol, NoSymbol)
|
||||
def parameterS(s: Symbol): xsbti.api.MethodParameter =
|
||||
makeParameter(s.nameString, s.info, s.info.typeSymbol, s)
|
||||
|
||||
def parameterT(t: Type): xsbti.api.MethodParameter =
|
||||
makeParameter("", t, t.typeSymbol, NoSymbol)
|
||||
|
||||
// paramSym is only for 2.8 and is to determine if the parameter has a default
|
||||
def makeParameter(name: String, tpe: Type, ts: Symbol, paramSym: Symbol): xsbti.api.MethodParameter =
|
||||
{
|
||||
|
|
@ -122,8 +168,8 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
(tpe, Plain)
|
||||
new xsbti.api.MethodParameter(name, processType(t), hasDefault(paramSym), special)
|
||||
}
|
||||
|
||||
build(s.info, Array(), Nil)
|
||||
val t = viewer(in).memberInfo(s)
|
||||
build(t, Array(), Nil)
|
||||
}
|
||||
private def hasDefault(s: Symbol) =
|
||||
{
|
||||
|
|
@ -132,13 +178,16 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
class WithDefault { val DEFAULTPARAM = 0x00000000 }
|
||||
s != NoSymbol && s.hasFlag(Flags.DEFAULTPARAM)
|
||||
}
|
||||
private def fieldDef[T](s: Symbol, create: (xsbti.api.Type, String, xsbti.api.Access, xsbti.api.Modifiers, Array[xsbti.api.Annotation]) => T): T =
|
||||
create(processType(s.tpeHK), simpleName(s), getAccess(s), getModifiers(s), annotations(s))
|
||||
private def fieldDef[T](in: Symbol, s: Symbol, create: (xsbti.api.Type, String, xsbti.api.Access, xsbti.api.Modifiers, Array[xsbti.api.Annotation]) => T): T =
|
||||
{
|
||||
val t = viewer(in).memberType(s)
|
||||
create(processType(t), simpleName(s), getAccess(s), getModifiers(s), annotations(s))
|
||||
}
|
||||
|
||||
private def typeDef(s: Symbol): xsbti.api.TypeMember =
|
||||
private def typeDef(in: Symbol, s: Symbol): xsbti.api.TypeMember =
|
||||
{
|
||||
val (typeParams, tpe) =
|
||||
s.info match
|
||||
viewer(in).memberInfo(s) match
|
||||
{
|
||||
case PolyType(typeParams0, base) => (typeParameters(typeParams0), base)
|
||||
case t => (Array[xsbti.api.TypeParameter](), t)
|
||||
|
|
@ -159,36 +208,38 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
error("Unknown type member" + s)
|
||||
}
|
||||
|
||||
private def structure(s: Symbol): xsbti.api.Structure = structure(s.info)
|
||||
private def structure(info: Type): xsbti.api.Structure =
|
||||
private def structure(s: Symbol): xsbti.api.Structure = structure(s.info, s, true)
|
||||
private def structure(info: Type): xsbti.api.Structure = structure(info, info.typeSymbol, false)
|
||||
private def structure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure =
|
||||
structureCache.getOrElseUpdate( s, mkStructure(info, s, inherit))
|
||||
|
||||
private def mkStructure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure =
|
||||
{
|
||||
val s = info.typeSymbol
|
||||
val (declared, inherited) = info.members.reverse.partition(_.owner == s)
|
||||
// would be nice to know how to do this properly:
|
||||
// baseClasses contains symbols in proper linearization order, but tpe doesn't have type parameters applied
|
||||
// baseTypeSeq contains the types with parameters properly applied
|
||||
val bases = info.baseClasses.tail
|
||||
val bs = info.baseTypeSeq.toList.tail
|
||||
val baseTypes = bases.map(base => bs.find(_.typeSymbol eq base).get)
|
||||
structure(baseTypes, declared, inherited)
|
||||
val baseTypes = info.baseClasses.tail.map(info.baseType)
|
||||
mkStructure(s, baseTypes, declared, if(inherit) inherited filter { !_.isConstructor} else Nil)
|
||||
}
|
||||
private def structure(parents: List[Type], declared: List[Symbol], inherited: List[Symbol]): xsbti.api.Structure =
|
||||
new xsbti.api.Structure(types(parents), processDefinitions(declared), if(java.lang.Boolean.getBoolean("xsbt.api.inherited")) processDefinitions(inherited) else Array())
|
||||
private def processDefinitions(defs: List[Symbol]): Array[xsbti.api.Definition] = defs.toArray.map(definition).filter(_ ne null) // TODO remove null hack
|
||||
private def definition(sym: Symbol): xsbti.api.Definition =
|
||||
|
||||
private def mkStructure(s: Symbol, bases: List[Type], declared: List[Symbol], inherited: List[Symbol]): xsbti.api.Structure =
|
||||
new xsbti.api.Structure(lzy(types(bases)), lzy(processDefinitions(s, declared)), lzy(processDefinitions(s, inherited)))
|
||||
private def processDefinitions(in: Symbol, defs: List[Symbol]): Array[xsbti.api.Definition] =
|
||||
defs.toArray.flatMap( (d: Symbol) => definition(in, d))
|
||||
private def definition(in: Symbol, sym: Symbol): Option[xsbti.api.Definition] =
|
||||
{
|
||||
def mkVar = fieldDef(sym, new xsbti.api.Var(_,_,_,_,_))
|
||||
def mkVar = Some(fieldDef(in, sym, new xsbti.api.Var(_,_,_,_,_)))
|
||||
def mkVal = Some(fieldDef(in, sym, new xsbti.api.Val(_,_,_,_,_)))
|
||||
if(sym.isClass)
|
||||
classLike(sym)
|
||||
Some(classLike(in, sym))
|
||||
else if(isNonClassType(sym))
|
||||
typeDef(sym)
|
||||
Some(typeDef(in, sym))
|
||||
else if(sym.isVariable)
|
||||
if(isSourceField(sym)) mkVar else null
|
||||
if(isSourceField(sym)) mkVar else None
|
||||
else if(sym.isStable)
|
||||
if(isSourceField(sym)) fieldDef(sym, new xsbti.api.Val(_,_,_,_,_)) else null
|
||||
if(isSourceField(sym)) mkVal else None
|
||||
else if(sym.isSourceMethod && !sym.isSetter)
|
||||
if(sym.isGetter) mkVar else defDef(sym)
|
||||
else null
|
||||
if(sym.isGetter) mkVar else Some(defDef(in, sym))
|
||||
else
|
||||
None
|
||||
}
|
||||
// This filters private[this] vals/vars that were not in the original source.
|
||||
// The getter will be used for processing instead.
|
||||
|
|
@ -202,8 +253,8 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
private def getModifiers(s: Symbol): xsbti.api.Modifiers =
|
||||
{
|
||||
import Flags._
|
||||
new xsbti.api.Modifiers(s.hasFlag(ABSTRACT), s.hasFlag(DEFERRED), s.hasFlag(OVERRIDE),
|
||||
s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY), s.hasFlag(SYNTHETIC))
|
||||
new xsbti.api.Modifiers(s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED), s.hasFlag(OVERRIDE),
|
||||
s.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY))
|
||||
}
|
||||
private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT)
|
||||
private def getAccess(c: Symbol): xsbti.api.Access =
|
||||
|
|
@ -219,12 +270,11 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
else new xsbti.api.Private(qualifier)
|
||||
}
|
||||
}
|
||||
|
||||
private def processType(t: Type): xsbti.api.Type =
|
||||
{
|
||||
class TypeCompat { def dealias = t } // 2.7.7 compatibility: don't bother dealiasing
|
||||
implicit def compat(t: Type): TypeCompat = new TypeCompat
|
||||
t.dealias match
|
||||
def dealias(t: Type) = t match { case TypeRef(_, sym, _) if sym.isAliasType => t.normalize; case _ => t }
|
||||
|
||||
dealias(t) match
|
||||
{
|
||||
case NoPrefix => Constants.emptyType
|
||||
case ThisType(sym) => new xsbti.api.Singleton(thisPath(sym))
|
||||
|
|
@ -232,10 +282,10 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
case ConstantType(value) => error("Constant type (not implemented)")
|
||||
case TypeRef(pre, sym, args) =>
|
||||
val base = projectionType(pre, sym)
|
||||
if(args.isEmpty) base else new xsbti.api.Parameterized(base, args.map(processType).toArray[xsbti.api.Type])
|
||||
if(args.isEmpty) base else new xsbti.api.Parameterized(base, types(args))
|
||||
case SuperType(thistpe: Type, supertpe: Type) => error("Super type (not implemented)")
|
||||
case at: AnnotatedType => annotatedType(at)
|
||||
case rt: RefinedType => structure(rt)
|
||||
case rt: CompoundType => structure(rt)
|
||||
case ExistentialType(tparams, result) => new xsbti.api.Existential(processType(result), typeParameters(tparams))
|
||||
case NoType => error("NoType")
|
||||
case PolyType(typeParams, resultType) => new xsbti.api.Polymorphic(processType(resultType), typeParameters(typeParams))
|
||||
|
|
@ -257,8 +307,11 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
case x => error("Unknown type parameter info: " + x.getClass)
|
||||
}
|
||||
}
|
||||
private def selfType(s: Symbol): xsbti.api.Type = if(s.thisSym eq s) Constants.normalSelf else processType(s.typeOfThis)
|
||||
private def classLike(c: Symbol): ClassLike =
|
||||
private def selfType(s: Symbol): xsbti.api.Type =
|
||||
if(s.thisSym eq s) Constants.normalSelf else processType(s.thisSym.typeOfThis)
|
||||
|
||||
private def classLike(in: Symbol, c: Symbol): ClassLike = classLikeCache.getOrElseUpdate(c, mkClassLike(in, c))
|
||||
private def mkClassLike(in: Symbol, c: Symbol): ClassLike =
|
||||
{
|
||||
val name = fullName(c)
|
||||
val isModule = c.isModuleClass || c.isModule
|
||||
|
|
@ -270,13 +323,13 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
else DefinitionType.Module
|
||||
}
|
||||
else DefinitionType.ClassDef
|
||||
new xsbti.api.ClassLike(defType, selfType(c), structure(c), typeParameters(c), name, getAccess(c), getModifiers(c), annotations(c))
|
||||
new xsbti.api.ClassLike(defType, lzy(selfType(c)), lzy(structure(c)), typeParameters(c), name, getAccess(c), getModifiers(c), annotations(c))
|
||||
}
|
||||
private final class TopLevelHandler(sourceFile: File) extends TopLevelTraverser
|
||||
{
|
||||
val packages = new HashSet[String]
|
||||
val definitions = new ListBuffer[xsbti.api.Definition]
|
||||
def `class`(c: Symbol): Unit = definitions += classLike(c)
|
||||
def `class`(c: Symbol): Unit = definitions += classLike(c.owner, c)
|
||||
/** Record packages declared in the source file*/
|
||||
def `package`(p: Symbol)
|
||||
{
|
||||
|
|
@ -342,4 +395,4 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend
|
|||
}
|
||||
private def fullName(s: Symbol): String = nameString(s)
|
||||
private def simpleName(s: Symbol): String = s.simpleName.toString.trim
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ package xsbt
|
|||
|
||||
object Message
|
||||
{
|
||||
def apply(s: => String) = new xsbti.F0[String] { def apply() = s }
|
||||
def apply[T](s: => T) = new xsbti.F0[T] { def apply() = s }
|
||||
}
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package xsbt.api
|
||||
|
||||
import Predef.{implicitly => ??, _}
|
||||
import sbt.Using
|
||||
import xsbti.api._
|
||||
import sbinary._
|
||||
import DefaultProtocol._
|
||||
import java.io.File
|
||||
import scala.collection.mutable
|
||||
|
||||
trait FormatExtra
|
||||
{
|
||||
def p2[A,B,R](unapply: R => (A,B))( apply: (A,B) => R)(implicit fa: Format[A], fb: Format[B]): Format[R] =
|
||||
asProduct2(apply)(unapply)(fa, fb)
|
||||
def p3[A,B,C,R](unapply: R => (A,B,C))( apply: (A,B,C) => R)(implicit fa: Format[A], fb: Format[B], fc: Format[C]): Format[R] =
|
||||
asProduct3(apply)(unapply)(fa, fb, fc)
|
||||
def p4[A,B,C,D,R](unapply: R => (A,B,C,D))( apply: (A,B,C,D) => R)(implicit fa: Format[A], fb: Format[B], fc: Format[C], fd: Format[D]): Format[R] =
|
||||
asProduct4(apply)(unapply)(fa, fb, fc, fd)
|
||||
def p5[A,B,C,D,E,R](unapply: R => (A,B,C,D,E))( apply: (A,B,C,D,E) => R)(implicit fa: Format[A], fb: Format[B], fc: Format[C], fd: Format[D], fe: Format[E]): Format[R] =
|
||||
asProduct5(apply)(unapply)(fa, fb, fc, fd, fe)
|
||||
def p6[A,B,C,D,E,F,R](unapply: R => (A,B,C,D,E,F))( apply: (A,B,C,D,E,F) => R)(implicit fa: Format[A], fb: Format[B], fc: Format[C], fd: Format[D], fe: Format[E], ff: Format[F]): Format[R] =
|
||||
asProduct6(apply)(unapply)(fa, fb, fc, fd, fe, ff)
|
||||
def p7[A,B,C,D,E,F,G,R](unapply: R => (A,B,C,D,E,F,G))( apply: (A,B,C,D,E,F,G) => R)(implicit fa: Format[A], fb: Format[B], fc: Format[C], fd: Format[D], fe: Format[E], ff: Format[F], fg: Format[G]): Format[R] =
|
||||
asProduct7(apply)(unapply)(fa, fb, fc, fd, fe, ff, fg)
|
||||
def p8[A,B,C,D,E,F,G,H,R](unapply: R => (A,B,C,D,E,F,G,H))( apply: (A,B,C,D,E,F,G,H) => R)(implicit fa: Format[A], fb: Format[B], fc: Format[C], fd: Format[D], fe: Format[E], ff: Format[F], fg: Format[G], fh: Format[H]): Format[R] =
|
||||
asProduct8(apply)(unapply)(fa, fb, fc, fd, fe, ff, fg, fh)
|
||||
}
|
||||
trait APIFormats extends FormatExtra
|
||||
{
|
||||
implicit def formatStructure(implicit fi: Format[Int], references: References): Format[Structure] =
|
||||
wrap(references.id, references.structure)
|
||||
implicit def formatClassLike(implicit fi: Format[Int], references: References): Format[ClassLike] =
|
||||
wrap(references.id, references.classLike)
|
||||
|
||||
// cyclic with formatSuper, so it is not implicit by default
|
||||
def formatPath(implicit pc: Format[Array[PathComponent]]): Format[Path] =
|
||||
wrap[Path, Array[PathComponent]]( _.components, x => new Path(x))(pc)
|
||||
|
||||
implicit def formatComponent(implicit t: Format[This], s: Format[Super], i: Format[Id]): Format[PathComponent] =
|
||||
asUnion(t, s, i)
|
||||
def formatSuper(implicit p: Format[Path]): Format[Super] =
|
||||
wrap[Super, Path](_.qualifier, q => new Super(q))(p)
|
||||
implicit def formatId(implicit s: Format[String]): Format[Id] =
|
||||
wrap[Id, String](_.id, i => new Id(i))(s)
|
||||
implicit val formatThis: Format[This] = asSingleton(new This)
|
||||
|
||||
implicit def formatSource(implicit pa: Format[Array[Package]], da: Format[Array[Definition]]): Format[Source] =
|
||||
p2( (s: Source) => (s.packages, s.definitions))( (p, d) => new Source(p, d) )(pa, da)
|
||||
|
||||
implicit def formatAnnotated(implicit t: Format[SimpleType], as: Format[Array[Annotation]]): Format[Annotated] =
|
||||
p2( (a: Annotated) => (a.baseType,a.annotations))(new Annotated(_,_))(t,as)
|
||||
|
||||
implicit def formatPolymorphic(implicit t: Format[Type], tps: Format[Array[TypeParameter]]): Format[Polymorphic] =
|
||||
p2( (p: Polymorphic) => (p.baseType, p.parameters) )( new Polymorphic(_,_) )(t, tps)
|
||||
|
||||
implicit def formatExistential(implicit t: Format[Type], tps: Format[Array[TypeParameter]]): Format[Existential] =
|
||||
p2( (e: Existential) => (e.baseType, e.clause) )( new Existential(_,_) )(t,tps)
|
||||
|
||||
implicit def formatParameterRef(implicit i: Format[Int]): Format[ParameterRef] =
|
||||
wrap[ParameterRef, Int](_.id, new ParameterRef(_))(i)
|
||||
|
||||
// cyclic with many formats
|
||||
def formatType(implicit s: Format[SimpleType], a: Format[Annotated], st: Format[Structure], e: Format[Existential], po: Format[Polymorphic]): Format[Type] =
|
||||
asUnion(s, a, st, e, po)
|
||||
|
||||
implicit def formatDef(implicit acs: Format[Access], ms: Format[Modifiers], ans: Format[Array[Annotation]], tp: Format[Array[TypeParameter]], vp: Format[Array[ParameterList]], t: Format[Type], fs: Format[String]): Format[Def] =
|
||||
p7( (d: Def) => (d.valueParameters, d.returnType, d.typeParameters, d.name, d.access, d.modifiers, d.annotations))( new Def(_,_,_,_,_,_,_) )(vp, t, tp, fs, acs, ms, ans)
|
||||
|
||||
implicit def formatVal(implicit acs: Format[Access], ms: Format[Modifiers], ans: Format[Array[Annotation]], t: Format[Type], ts: Format[String]): Format[Val] =
|
||||
fieldLike( new Val(_,_,_,_,_) )(acs, ms, ans, ts, t)
|
||||
|
||||
implicit def formatVar(implicit acs: Format[Access], ms: Format[Modifiers], ans: Format[Array[Annotation]], t: Format[Type], ts: Format[String]): Format[Var] =
|
||||
fieldLike( new Var(_,_,_,_,_) )(acs, ms, ans, ts, t)
|
||||
|
||||
def fieldLike[F <: FieldLike](construct: (Type, String, Access, Modifiers, Array[Annotation]) => F)(implicit acs: Format[Access], ms: Format[Modifiers], ans: Format[Array[Annotation]], nme: Format[String], tpe: Format[Type]): Format[F] =
|
||||
asProduct5(construct)( d => (d.tpe, d.name, d.access, d.modifiers, d.annotations))(tpe, nme, acs, ms, ans)
|
||||
|
||||
def formatDefinition(implicit vl: Format[Val], vr: Format[Var], ds: Format[Def], cl: Format[ClassLike], ta: Format[TypeAlias], td: Format[TypeDeclaration]): Format[Definition] =
|
||||
asUnion(vl, vr, ds, cl, ta, td)
|
||||
|
||||
def formatSimpleType(implicit pr: Format[Projection], pa: Format[ParameterRef], si: Format[Singleton], et: Format[EmptyType], p: Format[Parameterized]): Format[SimpleType] =
|
||||
asUnion(pr, pa, si, et, p)
|
||||
|
||||
implicit def formatEmptyType: Format[EmptyType] = asSingleton(new EmptyType)
|
||||
implicit def formatSingleton(implicit p: Format[Path]): Format[Singleton] = wrap[Singleton, Path](_.path, new Singleton(_))
|
||||
|
||||
def formatProjection(implicit t: Format[SimpleType], s: Format[String]): Format[Projection] =
|
||||
p2( (p: Projection) => (p.prefix, p.id))(new Projection(_,_))(t, s)
|
||||
|
||||
def formatParameterized(implicit t: Format[SimpleType], tps: Format[Array[Type]]): Format[Parameterized] =
|
||||
p2( (p: Parameterized) => (p.baseType, p.typeArguments))(new Parameterized(_,_))(t, tps)
|
||||
|
||||
implicit def formatTypeAlias(implicit acs: Format[Access], ms: Format[Modifiers], ans: Format[Array[Annotation]], tps: Format[Array[TypeParameter]], t: Format[Type], n: Format[String]): Format[TypeAlias] =
|
||||
p6( (ta: TypeAlias) => (ta.tpe, ta.typeParameters, ta.name, ta.access, ta.modifiers, ta.annotations))( new TypeAlias(_,_,_,_,_,_) )(t, tps, n, acs, ms, ans)
|
||||
|
||||
implicit def formatTypeDeclaration(implicit acs: Format[Access], ms: Format[Modifiers], ans: Format[Array[Annotation]], tps: Format[Array[TypeParameter]], t: Format[Type], n: Format[String]): Format[TypeDeclaration] =
|
||||
p7( (td: TypeDeclaration) => (td.lowerBound, td.upperBound, td.typeParameters, td.name, td.access, td.modifiers, td.annotations))( new TypeDeclaration(_,_,_,_,_,_,_))(t,t,tps,n,acs,ms,ans)
|
||||
|
||||
// cyclic with SimpleType
|
||||
def formatAnnotation(implicit t: Format[SimpleType], af: Format[Array[AnnotationArgument]]): Format[Annotation] =
|
||||
p2( (a: Annotation) => (a.base, a.arguments) )( (a,b) => new Annotation(a,b) )(t, af)
|
||||
|
||||
implicit def formatAnnotationArgument(implicit sf: Format[String]): Format[AnnotationArgument] =
|
||||
p2( (aa: AnnotationArgument) => (aa.name, aa.value))( (a,b) => new AnnotationArgument(a,b))(sf, sf)
|
||||
|
||||
|
||||
implicit def formatVariance(implicit b: Format[Byte]): Format[Variance] =
|
||||
wrap[Variance, Byte]( v => v.ordinal.toByte, b => Variance.values.apply(b.toInt) )(b)
|
||||
|
||||
implicit def formatDefinitionType(implicit b: Format[Byte]): Format[DefinitionType] =
|
||||
wrap[DefinitionType, Byte]( dt => dt.ordinal.toByte, b => DefinitionType.values.apply(b.toInt) )(b)
|
||||
|
||||
implicit def formatPackage(implicit fs: Format[String]): Format[Package] =
|
||||
wrap[Package, String]( _.name, n => new Package(n) )(fs)
|
||||
|
||||
implicit def formatAccess(implicit fs: Format[String]): Format[Access] =
|
||||
new Format[Access]
|
||||
{
|
||||
val unqualified = new Unqualified
|
||||
val ths = new ThisQualifier
|
||||
|
||||
val public = new Public
|
||||
val privateUn = new Private(unqualified)
|
||||
val protectedUn = new Protected(unqualified)
|
||||
val privateThis = new Private(ths)
|
||||
val protectedThis = new Protected(ths)
|
||||
|
||||
def reads(in: Input): Access =
|
||||
{
|
||||
def qualifier() = new IdQualifier(fs.reads(in))
|
||||
import AccessIDs._
|
||||
AccessIDs(in.readByte) match
|
||||
{
|
||||
case PublicID => public
|
||||
case PrivateID => privateUn
|
||||
case ProtectedID => protectedUn
|
||||
case PrivateThisID => privateThis
|
||||
case ProtectedThisID => protectedThis
|
||||
case QPrivateID => new Private(qualifier())
|
||||
case QProtectedID => new Protected(qualifier())
|
||||
}
|
||||
}
|
||||
def writes(out: Output, a: Access)
|
||||
{
|
||||
import AccessIDs._
|
||||
def w(id: AccessIDs.Value) = out.writeByte(id.id.toByte)
|
||||
def qualified(un: Value, ths: Value, qual: Value, qualifier: Qualifier): Unit =
|
||||
qualifier match
|
||||
{
|
||||
case _: Unqualified => w(un)
|
||||
case _: ThisQualifier => w(ths)
|
||||
case i: IdQualifier => w(qual); fs.writes(out, i.value)
|
||||
}
|
||||
a match
|
||||
{
|
||||
case _: Public => w(PublicID)
|
||||
case q: Qualified => q match {
|
||||
case p: Private => qualified(PrivateID, PrivateThisID, QPrivateID, p.qualifier)
|
||||
case p: Protected => qualified(ProtectedID, ProtectedThisID, QProtectedID, p.qualifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import APIUtil._
|
||||
implicit def formatModifiers(implicit bf: Format[Byte]): Format[Modifiers] =
|
||||
wrap[Modifiers, Byte]( modifiersToByte, byteToModifiers )
|
||||
|
||||
def formatTypeParameter(tps: Format[TypeParameter] => Format[Array[TypeParameter]])(implicit as: Format[Array[Annotation]], t: Format[Type], v: Format[Variance], i: Format[Int]): Format[TypeParameter] =
|
||||
{
|
||||
lazy val ltps: Format[Array[TypeParameter]] = lazyFormat( tps(ltp) )
|
||||
lazy val ltp = p6( (tp: TypeParameter) => (tp.id, tp.annotations, tp.typeParameters, tp.variance, tp.lowerBound, tp.upperBound))(new TypeParameter(_,_,_,_,_,_))(i, as, ltps, v, t, t)
|
||||
ltp
|
||||
}
|
||||
|
||||
implicit def formatParameterList(implicit mp: Format[Array[MethodParameter]], bf: Format[Boolean]): Format[ParameterList] =
|
||||
p2( (pl: ParameterList) => (pl.parameters, pl.isImplicit) )( (ps, impl) => new ParameterList(ps, impl) )
|
||||
|
||||
implicit def formatMethodParameter(implicit s: Format[String], b: Format[Boolean], m: Format[ParameterModifier], t: Format[Type]): Format[MethodParameter] =
|
||||
p4( (mp: MethodParameter) => (mp.name, mp.tpe, mp.hasDefault, mp.modifier) )( new MethodParameter(_,_,_,_) )(s,t,b,m)
|
||||
|
||||
implicit def formatParameterModifier(implicit bf: Format[Byte]): Format[ParameterModifier] =
|
||||
wrap[ParameterModifier, Byte]( dt => dt.ordinal.toByte, b => ParameterModifier.values.apply(b.toInt) )(bf)
|
||||
}
|
||||
private object AccessIDs extends Enumeration
|
||||
{
|
||||
val PublicID, PrivateID, ProtectedID, QPrivateID, QProtectedID, PrivateThisID, ProtectedThisID = Value
|
||||
}
|
||||
|
||||
// construct a concrete Format[Source], handling cycles
|
||||
// ?? (Predef.implicitly) is used for implicits the compiler should infer
|
||||
class DefaultAPIFormats(implicit val references: References) extends APIFormats
|
||||
{
|
||||
import sbinary.DefaultProtocol._
|
||||
|
||||
implicit lazy val tpf: Format[TypeParameter] = formatTypeParameter(array)
|
||||
|
||||
// SimpleType is cyclic with Projection and Parameterized
|
||||
implicit lazy val stf: Format[SimpleType] = lazyFormat(formatSimpleType(projf, ??, ??, ??, paramf))
|
||||
// Type is cyclic with a lot, including SimpleType and TypeParameter
|
||||
implicit lazy val tf: Format[Type] = lazyFormat( formatType(stf, ??, ??, ??, ??) )
|
||||
|
||||
implicit lazy val df: Format[Definition] = lazyFormat( formatDefinition )
|
||||
|
||||
// Projection, Annotation, and Parameterized are cyclic with SimpleType
|
||||
// Parameterized is also cyclic with Type
|
||||
implicit lazy val projf: Format[Projection] = formatProjection(stf, ??)
|
||||
implicit lazy val af: Format[Annotation] = formatAnnotation(stf, ??)
|
||||
implicit lazy val paramf: Format[Parameterized] = formatParameterized(stf, array(tf))
|
||||
|
||||
// Super and Path are cyclic
|
||||
implicit lazy val sf: Format[Super] = lazyFormat(formatSuper(pathf))
|
||||
implicit lazy val pathf: Format[Path] = formatPath
|
||||
|
||||
implicit val srcFormat: Format[Source] = formatSource(??, array(df))
|
||||
|
||||
private[this] def array[T](format: Format[T])(implicit mf: Manifest[T]): Format[Array[T]] = arrayFormat(format, mf)
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ package sbt
|
|||
package inc
|
||||
|
||||
import xsbti.api.Source
|
||||
import xsbt.api.APIFormat
|
||||
import java.io.File
|
||||
import sbinary._
|
||||
import DefaultProtocol._
|
||||
|
|
@ -36,8 +35,7 @@ object AnalysisFormats
|
|||
implicit def relationFormat[A,B](implicit af: Format[Map[A, Set[B]]], bf: Format[Map[B, Set[A]]]): Format[Relation[A,B]] =
|
||||
asProduct2[Relation[A,B], Map[A, Set[B]], Map[B, Set[A]]]( Relation.make _ )( r => (r.forwardMap, r.reverseMap) )(af, bf)
|
||||
|
||||
implicit val sourceFormat: Format[Source] =
|
||||
wrap[Source, Array[Byte]]( APIFormat.write _, APIFormat.read _)
|
||||
implicit val sourceFormat: Format[Source] = xsbt.api.SourceFormat
|
||||
|
||||
implicit def fileFormat: Format[File] = wrap[File, String](_.getAbsolutePath, s => new File(s))
|
||||
// can't require Format[Seq[String]] because its complexity is higher than Format[CompileOptions]
|
||||
|
|
|
|||
|
|
@ -14,15 +14,19 @@ object FileBasedStore
|
|||
{
|
||||
def apply(file: File)(implicit analysisF: Format[Analysis], setupF: Format[CompileSetup]): AnalysisStore = new AnalysisStore {
|
||||
def set(analysis: Analysis, setup: CompileSetup): Unit =
|
||||
Using.fileOutputStream()(file) { out =>
|
||||
write[(Analysis, CompileSetup)](out, (analysis, setup) )
|
||||
Using.fileOutputStream()(file) { fout =>
|
||||
Using.gzipOutputStream(fout) { out =>
|
||||
write[(Analysis, CompileSetup)](out, (analysis, setup) )
|
||||
}
|
||||
}
|
||||
|
||||
def get(): Option[(Analysis, CompileSetup)] =
|
||||
try { Some(getUncaught()) } catch { case io: IOException => None }
|
||||
def getUncaught(): (Analysis, CompileSetup) =
|
||||
Using.fileInputStream(file) { in =>
|
||||
read[(Analysis, CompileSetup)]( in )
|
||||
Using.fileInputStream(file) { fin =>
|
||||
Using.gzipInputStream(fin) { in =>
|
||||
read[(Analysis, CompileSetup)]( in )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package xsbt.api
|
||||
|
||||
import xsbti.SafeLazy
|
||||
import xsbti.api._
|
||||
import sbt.Using
|
||||
import sbinary._
|
||||
import DefaultProtocol._
|
||||
import Operations.{read,write}
|
||||
import JavaIO._
|
||||
import java.io.File
|
||||
import scala.collection.mutable
|
||||
|
||||
object SourceFormat extends Format[Source]
|
||||
{
|
||||
private[this] final val StructureFields = 3
|
||||
private[this] final val ClassFields = 2
|
||||
|
||||
private[this]def toMap[T](a: Seq[T]): Map[T, Int] = a.zipWithIndex.toMap
|
||||
|
||||
def reads(in: Input): Source =
|
||||
{
|
||||
var pending: List[Lazy[_]] = Nil
|
||||
def forcePending() = pending foreach { _.get }
|
||||
|
||||
def lzyC[T <: AnyRef](i: Int, off: Int): Lazy[T] = lzy { getData[T](i, off, classData, ClassFields) }
|
||||
def lzyS[T <: AnyRef](i: Int, off: Int): Lazy[T] = lzy { getData[T](i, off, structureData, StructureFields) }
|
||||
def getData[T <: AnyRef](i: Int, off: Int, backing: Array[AnyRef], fields: Int): T = backing(i*fields+off).asInstanceOf[T]
|
||||
def lzy[T <: AnyRef](t: => T): Lazy[T] =
|
||||
{
|
||||
val z = SafeLazy(t)
|
||||
pending ::= z
|
||||
z
|
||||
}
|
||||
|
||||
val structureCount = read[Int](in)
|
||||
|
||||
lazy val refs: References = new References {
|
||||
def structure(id: Int) = structures(id)
|
||||
def classLike(id: Int) = classes(id)
|
||||
def id(s: Structure) = noID()
|
||||
def id(s: ClassLike) = noID()
|
||||
def noID() = error("References only for reading")
|
||||
}
|
||||
lazy val formats = new DefaultAPIFormats()(refs)
|
||||
import formats._
|
||||
|
||||
lazy val classesStrict = read[Array[(DefinitionType, Array[TypeParameter], String, Access, Modifiers, Array[Annotation])]](in)
|
||||
|
||||
// shells for types that can be cyclic. The actual contents are filled in after the shells are constructed so that
|
||||
// the contents can have cyclic references
|
||||
lazy val structures = Array.tabulate(structureCount)(i => new xsbti.api.Structure( lzyS(i,0), lzyS(i,1), lzyS(i,2) ) )
|
||||
lazy val classes = Array.tabulate(classesStrict.size) { i =>
|
||||
val c = classesStrict(i)
|
||||
new xsbti.api.ClassLike( c._1, lzyC(i,0), lzyC(i,1), c._2, c._3, c._4, c._5, c._6 )
|
||||
}
|
||||
|
||||
def readFlat[T <: Product](implicit r: Reads[Array[T]]): Array[AnyRef] =
|
||||
read[Array[T]](in)(r).flatMap(_.productIterator.asInstanceOf[Iterator[AnyRef]].toTraversable)
|
||||
|
||||
lazy val structureData = readFlat[(Array[Type], Array[Definition], Array[Definition])]
|
||||
lazy val classData = readFlat[(Type, Structure)]
|
||||
|
||||
// order is important here, we are forcing in the correct order
|
||||
classesStrict; // read the strict contents of ClassLikes
|
||||
structures; // force the Structure shells to be constructed
|
||||
classes; // force the ClassLike shells to be constructed, filling in the strict parts now and deferring the lazy parts
|
||||
structureData; // force loading the lazy contents of Structures
|
||||
classData; // force loading the lazy contents of ClassLikes
|
||||
|
||||
forcePending() // force the shells to be filled in
|
||||
read[Source](in)
|
||||
}
|
||||
|
||||
def writes(out: Output, s: Source)
|
||||
{
|
||||
val (structures, classes) = getStructures(s)
|
||||
val structuresMap = toMap(structures)
|
||||
val classesMap = toMap(classes)
|
||||
implicit val references = new References {
|
||||
def id(s: Structure) = structuresMap(s)
|
||||
def id(s: ClassLike) = classesMap(s)
|
||||
def noID() = error("References only for writing")
|
||||
def structure(id: Int) = noID()
|
||||
def classLike(id: Int) = noID()
|
||||
}
|
||||
val formats = new DefaultAPIFormats
|
||||
import formats._
|
||||
|
||||
val expandedStructures = structures map { s => (s.parents, s.declared, s.inherited)}
|
||||
// TODO: type parameters and annotations need to be lazy as well
|
||||
val expandedClassesStrict = classes map { c => (c.definitionType, c.typeParameters, c.name, c.access, c.modifiers, c.annotations) }
|
||||
val expandedClassesLazy = classes map { c => (c.selfType, c.structure) }
|
||||
|
||||
write(out, structures.size )
|
||||
write(out, expandedClassesStrict)
|
||||
write(out, expandedStructures)
|
||||
write(out, expandedClassesLazy)
|
||||
write(out, s)
|
||||
}
|
||||
|
||||
def getStructures(s: Source): (Array[Structure], Array[ClassLike]) =
|
||||
{
|
||||
val structures = new mutable.HashSet[Structure]
|
||||
val classes = new mutable.HashSet[ClassLike]
|
||||
val search = new Visit {
|
||||
override def visitStructure0(s: Structure)
|
||||
{
|
||||
structures += s
|
||||
super.visitStructure0(s)
|
||||
}
|
||||
override def visitClass0(c: ClassLike)
|
||||
{
|
||||
classes += c
|
||||
super.visitClass0(c: ClassLike)
|
||||
}
|
||||
}
|
||||
search.visit(s)
|
||||
(structures.toArray, classes.toArray)
|
||||
}
|
||||
}
|
||||
|
||||
trait References {
|
||||
def id(s: Structure): Int
|
||||
def id(s: ClassLike): Int
|
||||
def structure(id: Int): Structure
|
||||
def classLike(id: Int): ClassLike
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package sbt
|
||||
package compile
|
||||
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
|
||||
object CheckBasicAPI extends Specification
|
||||
{
|
||||
val basicName = new File("Basic.scala")
|
||||
val basicSource =
|
||||
"""
|
||||
package org.example {
|
||||
trait Parent[A] { def x: A }
|
||||
abstract class Child extends Parent[String]
|
||||
class S {
|
||||
def x = new { def y = new S { def z = new Child { def x = "asdf" } } }
|
||||
}
|
||||
object Basic
|
||||
trait Out {
|
||||
trait In
|
||||
def t: In
|
||||
}
|
||||
}
|
||||
package org.example2 {
|
||||
trait ZZ[S] {
|
||||
val p: S
|
||||
val q: ZZZ[S] { val what: S }
|
||||
class A extends ZZZ[S] {
|
||||
val p = error("")
|
||||
val q = error("")
|
||||
}
|
||||
}
|
||||
trait ZZZ[T] extends ZZ[List[T]]
|
||||
trait ZZZZ extends ZZZ[Int]
|
||||
trait Z extends ZZZZ
|
||||
trait P[S] {
|
||||
trait Q
|
||||
def x(s: S): S
|
||||
def q(v: Q): Q
|
||||
val u: Q
|
||||
val p: S
|
||||
}
|
||||
trait Out { self: P[String] =>
|
||||
trait In
|
||||
def z = p
|
||||
def t(i: In): In
|
||||
def y(q: Q): Q
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
"Compiling should succeed" in {
|
||||
WithFiles(basicName -> basicSource){ files =>
|
||||
for(scalaVersion <- TestCompile.allVersions)
|
||||
{
|
||||
TestCompile(scalaVersion, files){ loader => Class.forName("org.example.Basic", false, loader) }
|
||||
true must beTrue // don't know how to just check that previous line completes without exception
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,10 @@ object TestCompile
|
|||
val testCallback = new TestCallback
|
||||
WithCompiler(scalaVersion) { (compiler, log) =>
|
||||
compiler(sources, Nil, outputDirectory, options, testCallback, 5, log)
|
||||
f(testCallback, compiler.scalaInstance, log)
|
||||
val result = f(testCallback, compiler.scalaInstance, log)
|
||||
for( (file, src) <- testCallback.apis )
|
||||
xsbt.api.APIUtil.verifyTypeParameters(src)
|
||||
result
|
||||
}
|
||||
}
|
||||
/** Tests running the compiler interface with the analyzer plugin. The provided function is given a ClassLoader that can
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ Definition
|
|||
returnType: Type
|
||||
ClassLike
|
||||
definitionType: DefinitionType
|
||||
selfType: Type
|
||||
structure: Structure
|
||||
selfType: ~Type
|
||||
structure: ~Structure
|
||||
TypeMember
|
||||
TypeAlias
|
||||
tpe: Type
|
||||
|
|
|
|||
|
|
@ -20,13 +20,11 @@ Qualifier
|
|||
|
||||
Modifiers
|
||||
isAbstract: Boolean
|
||||
isDeferred: Boolean
|
||||
isOverride: Boolean
|
||||
isFinal: Boolean
|
||||
isSealed: Boolean
|
||||
isImplicit: Boolean
|
||||
isLazy: Boolean
|
||||
isSynthetic: Boolean
|
||||
|
||||
ParameterList
|
||||
parameters: MethodParameter*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package xsbti.api;
|
||||
|
||||
public interface Lazy<T>
|
||||
{
|
||||
T get();
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ Type
|
|||
SimpleType
|
||||
Projection
|
||||
prefix : SimpleType
|
||||
id : String
|
||||
id: String
|
||||
ParameterRef
|
||||
id: Int
|
||||
Singleton
|
||||
|
|
@ -16,9 +16,9 @@ Type
|
|||
baseType : SimpleType
|
||||
annotations : Annotation*
|
||||
Structure
|
||||
parents : Type*
|
||||
declared: Definition*
|
||||
inherited: Definition*
|
||||
parents : ~Type*
|
||||
declared: ~Definition*
|
||||
inherited: ~Definition*
|
||||
Existential
|
||||
baseType : Type
|
||||
clause: TypeParameter*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[scala]
|
||||
version: 2.8.1.RC2
|
||||
version: 2.8.1.RC3
|
||||
classifiers: read(scala.classifiers)[]
|
||||
|
||||
[app]
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
project.name: quick=set(test), new=prompt(Name), fill=prompt(Name)
|
||||
project.organization: new=prompt(Organization)
|
||||
project.version: quick=set(1.0), new=prompt(Version)[1.0], fill=prompt(Version)[1.0]
|
||||
build.scala.versions: quick=set(2.8.1.RC2), new=prompt(Scala version)[2.8.1.RC2], fill=prompt(Scala version)[2.8.1.RC2]
|
||||
build.scala.versions: quick=set(2.8.1.RC3), new=prompt(Scala version)[2.8.1.RC3], fill=prompt(Scala version)[2.8.1.RC3]
|
||||
sbt.version: quick=set(0.9.0-SNAPSHOT), new=prompt(sbt version)[0.9.0-SNAPSHOT], fill=prompt(sbt version)[0.9.0-SNAPSHOT]
|
||||
project.scratch: quick=set(true)
|
||||
project.initialize: quick=set(true), new=set(true)
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ project.organization=org.scala-tools.sbt
|
|||
project.name=xsbt
|
||||
sbt.version=0.7.4
|
||||
project.version=0.9.0-SNAPSHOT
|
||||
build.scala.versions=2.8.1.RC2
|
||||
build.scala.versions=2.8.1.RC3
|
||||
|
|
|
|||
|
|
@ -62,8 +62,6 @@ trait ProguardLaunch extends ProguardProject
|
|||
val name = file.getName
|
||||
name.startsWith(x) && name.endsWith(".jar")
|
||||
}
|
||||
// class body declaration for proguard that keeps all public members
|
||||
private val allPublic = " {\n public * ;\n}"
|
||||
|
||||
private val keepJLine =
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -191,6 +191,9 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
}
|
||||
class CacheProject(info: ProjectInfo) extends Base(info) with SBinaryDep
|
||||
class PersistProject(info: ProjectInfo) extends Base(info) with SBinaryDep
|
||||
{
|
||||
override def compileOptions = super.compileOptions ++ compileOptions("-Xlog-implicits")
|
||||
}
|
||||
trait SBinaryDep extends BasicManagedProject
|
||||
{
|
||||
// these compilation options are useful for debugging caches and task composition
|
||||
|
|
@ -218,10 +221,10 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
val testInterface = "org.scala-tools.testing" % "test-interface" % "0.5"
|
||||
}
|
||||
class DiscoveryProject(info: ProjectInfo) extends TestedBase(info) with TestWithCompile
|
||||
class CompileProject(info: ProjectInfo) extends Base(info) with TestWithLog with TestWithLaunch
|
||||
class CompileProject(info: ProjectInfo) extends Base(info) with TestWithLog with TestWithLaunch with TestWithAPI
|
||||
{
|
||||
override def testCompileAction = super.testCompileAction dependsOn(compileInterfaceSub.`package`, interfaceSub.`package`)
|
||||
override def testClasspath = super.testClasspath +++ compileInterfaceSub.packageSrcJar +++ interfaceSub.jarPath --- compilerInterfaceClasspath --- interfaceSub.mainCompilePath
|
||||
override def testClasspath = super.testClasspath +++ compileInterfaceSub.packageSrcJar --- compilerInterfaceClasspath --- interfaceSub.mainCompilePath +++ interfaceSub.jarPath
|
||||
}
|
||||
class IvyProject(info: ProjectInfo) extends Base(info) with TestWithIO with TestWithLog with TestWithLaunch
|
||||
{
|
||||
|
|
@ -293,8 +296,6 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
</dependency>
|
||||
</dependencies> )
|
||||
|
||||
def xTestClasspath = projectClasspath(Configurations.Test)
|
||||
|
||||
def srcID = "compiler-interface-src"
|
||||
lazy val srcArtifact = Artifact(srcID) extra("e:component" -> srcID)
|
||||
override def packageSrcJar = mkJarPath(srcID)
|
||||
|
|
@ -303,7 +304,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
|
||||
// sub projects for each version of Scala to precompile against other than the one sbt is built against
|
||||
// each sub project here will add ~100k to the download
|
||||
//lazy val precompiled28 = precompiledSub("2.8.0")
|
||||
lazy val precompiled28 = precompiledSub("2.8.0")
|
||||
lazy val precompiled27 = precompiledSub("2.7.7")
|
||||
|
||||
def precompiledSub(v: String) =
|
||||
|
|
@ -324,6 +325,9 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
override def projectClasspath(config: Configuration) = Path.emptyPathFinder
|
||||
}
|
||||
}
|
||||
trait TestWithAPI extends TestWith {
|
||||
override def testWithTestClasspath = super.testWithTestClasspath ++ Seq(apiSub)
|
||||
}
|
||||
trait TestWithCompile extends TestWith {
|
||||
override def testWithTestClasspath = super.testWithTestClasspath ++ Seq(compilerSub)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
trait Equal[-A] {
|
||||
def equal(a1: A, a2: A): Boolean
|
||||
}
|
||||
object Test {
|
||||
implicit def TraversableEqual[CC[X] <: collection.TraversableLike[X, CC[X]] with Traversable[X], A: Equal]: Equal[CC[A]] = error("")
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import sbt._
|
||||
|
||||
class P(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
override def name = "test"
|
||||
lazy val checkSame = compile map { analysis =>
|
||||
analysis.apis.internal foreach { case (_, api) =>
|
||||
assert( xsbt.api.APIUtil.verifyTypeParameters(api) )
|
||||
assert( xsbt.api.SameAPI(api, api) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
project.name=test
|
||||
project.version=1.0
|
||||
|
|
@ -0,0 +1 @@
|
|||
> check-same
|
||||
|
|
@ -38,7 +38,9 @@ trait Relation[A,B]
|
|||
def _1s: collection.Set[A]
|
||||
/** Returns the set of all _2s such that (_1, _2) is in this relation. */
|
||||
def _2s: collection.Set[B]
|
||||
|
||||
/** Returns the number of pairs in this relation */
|
||||
def size: Int
|
||||
|
||||
/** Returns all pairs in this relation.*/
|
||||
def all: Traversable[(A,B)]
|
||||
|
||||
|
|
@ -57,6 +59,8 @@ private final class MRelation[A,B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) ext
|
|||
|
||||
def _1s = fwd.keySet
|
||||
def _2s = rev.keySet
|
||||
|
||||
def size = fwd.size
|
||||
|
||||
def all: Traversable[(A,B)] = fwd.iterator.flatMap { case (a, bs) => bs.iterator.map( b => (a,b) ) }.toTraversable
|
||||
|
||||
|
|
|
|||
|
|
@ -11,15 +11,24 @@ final class ClassDef(val name: String, val parent: Option[ClassDef], val members
|
|||
{
|
||||
def allMembers = members ++ inheritedMembers
|
||||
def inheritedMembers: Seq[MemberDef] = parent.toList.flatMap(_.allMembers)
|
||||
def + (m: MemberLine) = new ClassDef(name, parent, members ++ Seq(new MemberDef(m.name, m.tpe, m.single)) )
|
||||
def + (m: MemberLine) = new ClassDef(name, parent, members ++ Seq(new MemberDef(m.name, m.tpe.stripPrefix("~"), m.single, m.tpe.startsWith("~"))) )
|
||||
}
|
||||
final class EnumDef(val name: String, val members: Seq[String]) extends Definition
|
||||
|
||||
final class MemberDef(val name: String, val tpe: String, val single: Boolean) extends NotNull
|
||||
final class MemberDef(val name: String, val tpe: String, val single: Boolean, val lzy: Boolean) extends NotNull
|
||||
{
|
||||
def javaType = tpe + (if(single) "" else "[]")
|
||||
def scalaType = if(single) tpe else "Array[" + tpe + "]"
|
||||
def asScalaDeclaration = name + ": " + scalaType
|
||||
def asJavaDeclaration = javaType + " " + name
|
||||
def mapType(f: String => String) = new MemberDef(name, f(tpe), single)
|
||||
def javaType(accessor: Boolean) =
|
||||
{
|
||||
val base = tpe + (if(single) "" else "[]")
|
||||
if(!accessor && lzy) "Lazy<" + base + ">" else base
|
||||
}
|
||||
def scalaType(accessor: Boolean) =
|
||||
{
|
||||
val base = if(single) tpe else "Array[" + tpe + "]"
|
||||
if(!accessor && lzy) "Lazy[" + base + "]" else base
|
||||
}
|
||||
def asGet = name + (if(lzy) ".get()" else "")
|
||||
def asScalaDeclaration(accessor: Boolean) = name + ": " + scalaType(accessor)
|
||||
def asJavaDeclaration(accessor: Boolean) = javaType(accessor) + " " + name
|
||||
def mapType(f: String => String) = new MemberDef(name, f(tpe), single, lzy)
|
||||
}
|
||||
|
|
@ -54,9 +54,9 @@ class ImmutableGenerator(pkgName: String, baseDir: File) extends GeneratorBase(p
|
|||
val hasParent = c.parent.isDefined
|
||||
val allMembers = c.allMembers.map(normalize)
|
||||
val normalizedMembers = c.members.map(normalize)
|
||||
val fields = normalizedMembers.map(m => "private final " + m.asJavaDeclaration + ";")
|
||||
val accessors = normalizedMembers.map(m => "public final " + m.asJavaDeclaration + "()\n\t{\n\t\treturn " + m.name + ";\n\t}")
|
||||
val parameters = allMembers.map(_.asJavaDeclaration)
|
||||
val fields = normalizedMembers.map(m => "private final " + m.asJavaDeclaration(false) + ";")
|
||||
val accessors = normalizedMembers.map(m => "public final " + m.asJavaDeclaration(true) + "()\n\t{\n\t\treturn " + m.asGet + ";\n\t}")
|
||||
val parameters = allMembers.map(_.asJavaDeclaration(false))
|
||||
val assignments = normalizedMembers.map(m => "this." + m.name + " = " + m.name + ";")
|
||||
val superConstructor =
|
||||
{
|
||||
|
|
@ -90,8 +90,8 @@ class MutableGenerator(pkgName: String, baseDir: File) extends GeneratorBase(pkg
|
|||
def interfaceContent(c: ClassDef): String =
|
||||
{
|
||||
val normalizedMembers = c.members.map(normalize)
|
||||
val getters = normalizedMembers.map(m => "public " + m.asJavaDeclaration + "();")
|
||||
val setters = normalizedMembers.map(m => "public void " + m.name + "(" + m.javaType + " newValue);")
|
||||
val getters = normalizedMembers.map(m => "public " + m.asJavaDeclaration(true) + "();")
|
||||
val setters = normalizedMembers.map(m => "public void " + m.name + "(" + m.javaType(false) + " newValue);")
|
||||
val extendsPhrase = c.parent.map(_.name).map(" extends " + _).getOrElse("")
|
||||
|
||||
("public interface " + c.name + extendsPhrase + "\n" +
|
||||
|
|
@ -102,9 +102,9 @@ class MutableGenerator(pkgName: String, baseDir: File) extends GeneratorBase(pkg
|
|||
def implContent(c: ClassDef): String =
|
||||
{
|
||||
val normalizedMembers = c.members.map(normalize)
|
||||
val fields = normalizedMembers.map(m => "private " + m.asJavaDeclaration + ";")
|
||||
val getters = normalizedMembers.map(m => "public final " + m.asJavaDeclaration + "()\n\t{\n\t\treturn " + m.name + ";\n\t}")
|
||||
val setters = normalizedMembers.map(m => "public final void " + m.name + "(" + m.javaType + " newValue)\n\t{\n\t\t" + m.name + " = newValue;\n\t}")
|
||||
val fields = normalizedMembers.map(m => "private " + m.asJavaDeclaration(false) + ";")
|
||||
val getters = normalizedMembers.map(m => "public final " + m.asJavaDeclaration(true) + "()\n\t{\n\t\treturn " + m.asGet + ";\n\t}")
|
||||
val setters = normalizedMembers.map(m => "public final void " + m.name + "(" + m.javaType(false) + " newValue)\n\t{\n\t\t" + m.name + " = newValue;\n\t}")
|
||||
val extendsClass = c.parent.map(p => implName(p.name))
|
||||
val serializable = if(c.parent.isDefined) Nil else "java.io.Serializable" :: Nil
|
||||
val implements = c.name :: serializable
|
||||
|
|
|
|||
Loading…
Reference in New Issue