/* 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 }