diff --git a/compile/AnalyzingCompiler.scala b/compile/AnalyzingCompiler.scala index 24132ca74..82f85ae54 100644 --- a/compile/AnalyzingCompiler.scala +++ b/compile/AnalyzingCompiler.scala @@ -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) diff --git a/compile/api/APIFormat.scala b/compile/api/APIFormat.scala deleted file mode 100644 index f420a33e9..000000000 --- a/compile/api/APIFormat.scala +++ /dev/null @@ -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()) -} \ No newline at end of file diff --git a/compile/api/APIUtil.scala b/compile/api/APIUtil.scala new file mode 100644 index 000000000..98bec00b0 --- /dev/null +++ b/compile/api/APIUtil.scala @@ -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) + } + } +} \ No newline at end of file diff --git a/compile/api/ClassToAPI.scala b/compile/api/ClassToAPI.scala index fdd785c96..c2e6af2d5 100644 --- a/compile/api/ClassToAPI.scala +++ b/compile/api/ClassToAPI.scala @@ -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) -} \ No newline at end of file +} diff --git a/compile/api/SafeLazy.scala b/compile/api/SafeLazy.scala new file mode 100644 index 000000000..99630629f --- /dev/null +++ b/compile/api/SafeLazy.scala @@ -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 + } +} \ No newline at end of file diff --git a/compile/api/SameAPI.scala b/compile/api/SameAPI.scala index 2c57aaa76..9ee2ec47f 100644 --- a/compile/api/SameAPI.scala +++ b/compile/api/SameAPI.scala @@ -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) diff --git a/compile/api/ShowAPI.scala b/compile/api/ShowAPI.scala index 38fbdaa00..1e38ef57e 100644 --- a/compile/api/ShowAPI.scala +++ b/compile/api/ShowAPI.scala @@ -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) } diff --git a/compile/api/TagTypeVariables.scala b/compile/api/TagTypeVariables.scala index a2eca2e83..65f4b852f 100644 --- a/compile/api/TagTypeVariables.scala +++ b/compile/api/TagTypeVariables.scala @@ -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) diff --git a/compile/api/Visit.scala b/compile/api/Visit.scala new file mode 100644 index 000000000..61ffc0438 --- /dev/null +++ b/compile/api/Visit.scala @@ -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) + } +} \ No newline at end of file diff --git a/compile/discover/Discovery.scala b/compile/discover/Discovery.scala index f7f8842a2..a0fbfc920 100644 --- a/compile/discover/Discovery.scala +++ b/compile/discover/Discovery.scala @@ -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) diff --git a/compile/discover/src/test/scala/ApplicationsTest.scala b/compile/discover/src/test/scala/ApplicationsTest.scala index d8be01580..dbc9b5429 100644 --- a/compile/discover/src/test/scala/ApplicationsTest.scala +++ b/compile/discover/src/test/scala/ApplicationsTest.scala @@ -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 } diff --git a/compile/discover/src/test/scala/DetectAnnotations.scala b/compile/discover/src/test/scala/DetectAnnotations.scala index 56993bb30..b29829f95 100644 --- a/compile/discover/src/test/scala/DetectAnnotations.scala +++ b/compile/discover/src/test/scala/DetectAnnotations.scala @@ -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 } diff --git a/compile/discover/src/test/scala/DetectSubclasses.scala b/compile/discover/src/test/scala/DetectSubclasses.scala index 7985d2014..f2eaa88f5 100644 --- a/compile/discover/src/test/scala/DetectSubclasses.scala +++ b/compile/discover/src/test/scala/DetectSubclasses.scala @@ -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 } } } diff --git a/compile/interface/API.scala b/compile/interface/API.scala index 34b09b673..9985f4c67 100644 --- a/compile/interface/API.scala +++ b/compile/interface/API.scala @@ -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 -} \ No newline at end of file +} diff --git a/compile/interface/Message.scala b/compile/interface/Message.scala index b3bc4330e..3db251747 100644 --- a/compile/interface/Message.scala +++ b/compile/interface/Message.scala @@ -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 } } \ No newline at end of file diff --git a/compile/persist/APIFormats.scala b/compile/persist/APIFormats.scala new file mode 100644 index 000000000..fb36d3712 --- /dev/null +++ b/compile/persist/APIFormats.scala @@ -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) +} \ No newline at end of file diff --git a/compile/persist/AnalysisFormats.scala b/compile/persist/AnalysisFormats.scala index 57cd5521a..7916b7e03 100644 --- a/compile/persist/AnalysisFormats.scala +++ b/compile/persist/AnalysisFormats.scala @@ -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] diff --git a/compile/persist/FileBasedStore.scala b/compile/persist/FileBasedStore.scala index 43bf41ffb..3e2cc4653 100644 --- a/compile/persist/FileBasedStore.scala +++ b/compile/persist/FileBasedStore.scala @@ -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 ) + } } } } diff --git a/compile/persist/SourceFormat.scala b/compile/persist/SourceFormat.scala new file mode 100644 index 000000000..d5183e9f3 --- /dev/null +++ b/compile/persist/SourceFormat.scala @@ -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 +} \ No newline at end of file diff --git a/compile/src/test/scala/BasicAPI.scala b/compile/src/test/scala/BasicAPI.scala new file mode 100644 index 000000000..0d879e275 --- /dev/null +++ b/compile/src/test/scala/BasicAPI.scala @@ -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 + } + } + } +} \ No newline at end of file diff --git a/compile/src/test/scala/TestCompile.scala b/compile/src/test/scala/TestCompile.scala index 9ca5c7211..ad0f840a2 100644 --- a/compile/src/test/scala/TestCompile.scala +++ b/compile/src/test/scala/TestCompile.scala @@ -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 diff --git a/interface/definition b/interface/definition index 9220a9a0e..2dcd4025b 100644 --- a/interface/definition +++ b/interface/definition @@ -14,8 +14,8 @@ Definition returnType: Type ClassLike definitionType: DefinitionType - selfType: Type - structure: Structure + selfType: ~Type + structure: ~Structure TypeMember TypeAlias tpe: Type diff --git a/interface/other b/interface/other index 3ef1d4461..bba27dc7f 100644 --- a/interface/other +++ b/interface/other @@ -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* diff --git a/interface/src/main/java/xsbti/api/Lazy.java b/interface/src/main/java/xsbti/api/Lazy.java new file mode 100644 index 000000000..4a5642a01 --- /dev/null +++ b/interface/src/main/java/xsbti/api/Lazy.java @@ -0,0 +1,9 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009 Mark Harrah + */ +package xsbti.api; + +public interface Lazy +{ + T get(); +} \ No newline at end of file diff --git a/interface/type b/interface/type index c516b62b9..a605f4cd4 100644 --- a/interface/type +++ b/interface/type @@ -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* diff --git a/launch/src/main/resources/sbt/sbt.boot.properties b/launch/src/main/resources/sbt/sbt.boot.properties index 64b9d4471..e95e8cb46 100644 --- a/launch/src/main/resources/sbt/sbt.boot.properties +++ b/launch/src/main/resources/sbt/sbt.boot.properties @@ -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) diff --git a/project/build.properties b/project/build.properties index bb804e1bc..1dd3aeb8e 100644 --- a/project/build.properties +++ b/project/build.properties @@ -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 diff --git a/project/build/LauncherProguard.scala b/project/build/LauncherProguard.scala index 17c6eeef3..432439a6d 100644 --- a/project/build/LauncherProguard.scala +++ b/project/build/LauncherProguard.scala @@ -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 = """ diff --git a/project/build/XSbt.scala b/project/build/XSbt.scala index 3739db53c..560294723 100644 --- a/project/build/XSbt.scala +++ b/project/build/XSbt.scala @@ -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 ) - 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) } diff --git a/sbt/src/sbt-test/source-dependencies/inherited_type_params/A.scala b/sbt/src/sbt-test/source-dependencies/inherited_type_params/A.scala new file mode 100644 index 000000000..9de61dd5f --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/inherited_type_params/A.scala @@ -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("") +} \ No newline at end of file diff --git a/sbt/src/sbt-test/source-dependencies/inherited_type_params/project/P.scala b/sbt/src/sbt-test/source-dependencies/inherited_type_params/project/P.scala new file mode 100644 index 000000000..a784a62f8 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/inherited_type_params/project/P.scala @@ -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) ) + } + } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/source-dependencies/inherited_type_params/project/build.properties b/sbt/src/sbt-test/source-dependencies/inherited_type_params/project/build.properties new file mode 100644 index 000000000..3f2496999 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/inherited_type_params/project/build.properties @@ -0,0 +1,2 @@ +project.name=test +project.version=1.0 \ No newline at end of file diff --git a/sbt/src/sbt-test/source-dependencies/inherited_type_params/test b/sbt/src/sbt-test/source-dependencies/inherited_type_params/test new file mode 100644 index 000000000..8434347c5 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/inherited_type_params/test @@ -0,0 +1 @@ +> check-same \ No newline at end of file diff --git a/util/collection/Relation.scala b/util/collection/Relation.scala index ed6046f6e..a282bf7e2 100644 --- a/util/collection/Relation.scala +++ b/util/collection/Relation.scala @@ -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 diff --git a/util/datatype/Definition.scala b/util/datatype/Definition.scala index 2b60f75c7..b5a2b59c5 100644 --- a/util/datatype/Definition.scala +++ b/util/datatype/Definition.scala @@ -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) } \ No newline at end of file diff --git a/util/datatype/Generator.scala b/util/datatype/Generator.scala index 521473360..5b573c9e5 100644 --- a/util/datatype/Generator.scala +++ b/util/datatype/Generator.scala @@ -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