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:
Mark Harrah 2010-10-22 21:55:16 -04:00
parent 3c5d8ab29b
commit 5ed8f3c042
36 changed files with 872 additions and 168 deletions

View File

@ -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)

View File

@ -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())
}

45
compile/api/APIUtil.scala Normal file
View File

@ -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)
}
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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)

View File

@ -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) }

View File

@ -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)

124
compile/api/Visit.scala Normal file
View File

@ -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)
}
}

View File

@ -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)

View File

@ -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 }

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}
}

View File

@ -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 }
}

View File

@ -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)
}

View File

@ -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]

View File

@ -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 )
}
}
}
}

View File

@ -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
}

View File

@ -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
}
}
}
}

View File

@ -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

View File

@ -14,8 +14,8 @@ Definition
returnType: Type
ClassLike
definitionType: DefinitionType
selfType: Type
structure: Structure
selfType: ~Type
structure: ~Structure
TypeMember
TypeAlias
tpe: Type

View File

@ -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*

View File

@ -0,0 +1,9 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package xsbti.api;
public interface Lazy<T>
{
T get();
}

View File

@ -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*

View File

@ -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)

View File

@ -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

View File

@ -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 =
"""

View File

@ -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)
}

View File

@ -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("")
}

View File

@ -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) )
}
}
}

View File

@ -0,0 +1,2 @@
project.name=test
project.version=1.0

View File

@ -0,0 +1 @@
> check-same

View File

@ -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

View File

@ -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)
}

View File

@ -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