From 38dbb1d23c4574f3966b6d810a3092ea044ed5d7 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sun, 22 Nov 2009 22:54:17 -0500 Subject: [PATCH] Type member support, linearization instead of parents and add inherited members for structure --- compile/interface/API.scala | 51 ++++++++++++++++--- interface/definition | 4 +- .../src/main/java/xsbti/AnalysisCallback.java | 2 +- util/datatype/Generator.scala | 30 ++++++++--- 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/compile/interface/API.scala b/compile/interface/API.scala index 4ff296381..47fa307e2 100644 --- a/compile/interface/API.scala +++ b/compile/interface/API.scala @@ -27,7 +27,14 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend { override def description = "Extracts the public API from source files." def name = API.name - def run: Unit = currentRun.units.foreach(processUnit) + def run: Unit = + { + val start = System.currentTimeMillis + if(java.lang.Boolean.getBoolean("sbt.api.enable")) + currentRun.units.foreach(processUnit) + val stop = System.currentTimeMillis + println("API phase took : " + ((stop - start)/1000.0) + " s") + } def processUnit(unit: CompilationUnit) { val sourceFile = unit.source.file.file @@ -116,11 +123,39 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend } private def fieldDef[T](s: Symbol, create: (xsbti.api.Type, String, xsbti.api.Access, xsbti.api.Modifiers) => T): T = create(processType(s.tpe), s.fullNameString, getAccess(s), getModifiers(s)) - private def typeDef(s: Symbol) = error("type members not implemented yet") + private def typeDef(s: Symbol): xsbti.api.TypeMember = + { + val (typeParams, tpe) = + s.info match + { + case PolyType(typeParams0, base) => (typeParameters(typeParams0), base) + case t => (Array[xsbti.api.TypeParameter](), t) + } + val name = s.fullNameString + val access = getAccess(s) + val modifiers = getModifiers(s) - private def classStructure(s: Symbol) = structure(s.info.parents, s.info.decls) - private def structure(parents: List[Type], defs: Scope) = new xsbti.api.Structure(types(parents), processDefinitions(defs)) - private def processDefinitions(defs: Scope): Array[xsbti.api.Definition] = defs.toList.toArray.map(definition) + if(s.isAliasType) + new xsbti.api.TypeAlias(processType(tpe), typeParams, name, access, modifiers) + else if(s.isAbstractType) + { + val bounds = tpe.bounds + new xsbti.api.TypeDeclaration(processType(bounds.lo), processType(bounds.hi), typeParams, name, access, modifiers) + } + else + error("Unknown type member" + s) + } + + private def structure(s: Symbol): xsbti.api.Structure = structure(s.info) + private def structure(info: Type): xsbti.api.Structure = + { + val s = info.typeSymbol + val (declared, inherited) = info.members.partition(_.owner == s) + structure(info.baseClasses.map(_.tpe), declared, inherited) // linearization instead of parents + } + private def structure(parents: List[Type], declared: List[Symbol], inherited: List[Symbol]): xsbti.api.Structure = + new xsbti.api.Structure(types(parents), processDefinitions(declared), processDefinitions(inherited)) + private def processDefinitions(defs: List[Symbol]): Array[xsbti.api.Definition] = defs.toArray.map(definition) private def definition(sym: Symbol): xsbti.api.Definition = { if(sym.isClass) classLike(sym) @@ -133,7 +168,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend { 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.isFinal, s.hasFlag(SEALED), isImplicit(s), s.hasFlag(LAZY), s.hasFlag(SYNTHETIC)) } private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT) private def getAccess(c: Symbol): xsbti.api.Access = @@ -164,7 +199,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend if(args.isEmpty) base else new xsbti.api.Parameterized(base, args.map(simpleType).toArray[SimpleType]) case SuperType(thistpe: Type, supertpe: Type) => error("Super type (not implemented)") case at: AnnotatedType => annotatedType(at) - case RefinedType(parents, defs) => structure(parents, defs) + case rt: RefinedType/*(parents, defs)*/ => structure(rt)//parents, defs.toList) case ExistentialType(tparams, result) => new xsbti.api.Existential(processType(result), typeParameters(tparams)) case NoType => error("NoType") case PolyType(typeParams, resultType) => println("polyType(" + typeParams + " , " + resultType + ")"); error("polyType") @@ -207,7 +242,7 @@ final class API(val global: Global, val callback: xsbti.AnalysisCallback) extend else DefinitionType.Module } else DefinitionType.ClassDef - new xsbti.api.ClassLike(defType, selfType(c), classStructure(c), typeParameters(c), name, access, modifiers) + new xsbti.api.ClassLike(defType, selfType(c), structure(c), typeParameters(c), name, access, modifiers) } private final class TopLevelHandler(sourceFile: File) extends TopLevelTraverser { diff --git a/interface/definition b/interface/definition index 1ce6fe02a..5269e50f7 100644 --- a/interface/definition +++ b/interface/definition @@ -26,8 +26,8 @@ Definition TypeAlias tpe: Type TypeDeclaration - upperBound: Type lowerBound: Type + upperBound: Type Type SimpleType @@ -48,6 +48,7 @@ Type Structure parents : Type* declarations: Definition* + inherited: Definition* Existential baseType : Type clause: TypeParameter* @@ -74,6 +75,7 @@ Modifiers isSealed: Boolean isImplicit: Boolean isLazy: Boolean + isSynthetic: Boolean ParameterList parameters: MethodParameter* diff --git a/interface/src/main/java/xsbti/AnalysisCallback.java b/interface/src/main/java/xsbti/AnalysisCallback.java index 4c7bb8689..2372a408b 100644 --- a/interface/src/main/java/xsbti/AnalysisCallback.java +++ b/interface/src/main/java/xsbti/AnalysisCallback.java @@ -34,6 +34,6 @@ public interface AnalysisCallback public void endSource(File sourcePath); /** Called when a module with a public 'main' method with the right signature is found.*/ public void foundApplication(File source, String className); - + /** Called when the public API of a source file is extracted. */ public void api(File sourceFile, xsbti.api.Source source); } \ No newline at end of file diff --git a/util/datatype/Generator.scala b/util/datatype/Generator.scala index 70a9ae5f6..bae5a3b0f 100644 --- a/util/datatype/Generator.scala +++ b/util/datatype/Generator.scala @@ -3,11 +3,23 @@ package xsbt.api import java.io.File import xsbt.FileUtilities +/** Creates immutable datatype classes in Java from the intermediate Definition representation. +* +* A ClassDef is written as a class with an optional parent class. The class has a single constructor with +* parameters for all declared and inherited members. Declared members are listed first in the constructor. +* Inherited members are passed to the super constructor. The value of each member is held in a private +* final field and.is accessed by a method of the same name. +* +* A toString method is generated for debugging. +* The classes implement java.io.Serializable. +* +*.@param baseDirectory output directory for sources +* @param pkgName package that classes will be defined in*/ class Generator(pkgName: String, baseDirectory: File) { def writeDefinitions(ds: Iterable[Definition]) = { - val (nameSet, duplicates) = + val (_, duplicates) = ( (Set[String](), Set[String]()) /: ds.map(_.name)) { case ((nameSet, duplicates), name) => if(nameSet.contains(name)) (nameSet, duplicates + name) else (nameSet + name, duplicates) @@ -29,10 +41,11 @@ class Generator(pkgName: String, baseDirectory: File) def write(c: ClassDef): Unit = writeSource(c.name, classContent(c)) def classContent(c: ClassDef): String = { + 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 accessors = normalizedMembers.map(m => "public final " + m.asJavaDeclaration + "()\n\t{\n\t\treturn " + m.name + ";\n\t}") val parameters = allMembers.map(_.asJavaDeclaration) val assignments = normalizedMembers.map(m => "this." + m.name + " = " + m.name + ";") val superConstructor = @@ -41,23 +54,24 @@ class Generator(pkgName: String, baseDirectory: File) if(inherited.isEmpty) "" else "super(" + inherited.map(_.name).mkString(", ") + ");\n\t\t" } val parametersString = if(allMembers.isEmpty) "\"\"" else allMembers.map(m => fieldToString(m.name, m.single)).mkString(" + \", \" + ") - val toStringMethod = "public String toString()\n\t{\n\t\t" + - "return \"" + c.name + "(\" + " + parametersString + "+ \")\";\n\t" + - "}\n" + val toStringMethod = method("public", "String", "toString", "", "\"" + c.name + "(\" + " + parametersString + "+ \")\"") val constructor = "public " + c.name + "(" + parameters.mkString(", ") + ")\n\t" + "{\n\t\t" + superConstructor + assignments.mkString("\n\t\t") + "\n\t" + "}" - "\nimport java.util.Arrays;\n" + - "public class " + c.name + c.parent.map(" extends " + _.name + " ").getOrElse("") + "\n" + + "import java.util.Arrays;\n" + + "import java.util.List;\n" + + "public class " + c.name + c.parent.map(" extends " + _.name + " ").getOrElse(" implements java.io.Serializable") + "\n" + "{\n\t" + constructor + "\n\t" + (fields ++ accessors).mkString("\n\t") + "\n\t" + - toStringMethod + "\n" + + toStringMethod + "\n\t" + "}" } + def method(modifiers: String, returnType: String, name: String, parameters: String, content: String) = + modifiers + " " + returnType + " " + name + "(" + parameters + ")\n\t{\n\t\treturn " + content + ";\n\t}" def fieldToString(name: String, single: Boolean) = "\"" + name + ": \" + " + fieldString(name + "()", single) def fieldString(arg: String, single: Boolean) = if(single) arg else "Arrays.toString(" + arg + ")" def normalize(m: MemberDef): MemberDef =