2009-11-16 08:46:47 -05:00
/* sbt -- Simple Build Tool
2011-03-11 16:57:15 -05:00
* Copyright 2008, 2009, 2010, 2011 Mark Harrah
2009-11-16 08:46:47 -05:00
*/
package xsbt
import java.io.File
2012-04-30 20:34:48 -04:00
import java.util. { Arrays , Comparator }
2009-11-16 08:46:47 -05:00
import scala.tools.nsc. { io , plugins , symtab , Global , Phase }
import io. { AbstractFile , PlainFile , ZipArchive }
import plugins. { Plugin , PluginComponent }
import symtab.Flags
import scala.collection.mutable. { HashMap , HashSet , ListBuffer }
2010-06-15 20:38:18 -04:00
import xsbti.api. { ClassLike , DefinitionType , PathComponent , SimpleType }
2009-11-16 08:46:47 -05:00
object API
{
val name = "xsbt-api"
}
2011-02-08 20:30:15 -05:00
2012-04-28 18:58:38 -04:00
final class API ( val global : CallbackGlobal ) extends Compat
2009-11-16 08:46:47 -05:00
{
import global._
def error ( msg : String ) = throw new RuntimeException ( msg )
2012-11-19 18:36:10 -08:00
@inline def debug ( msg : => String ) = if ( settings . verbose . value ) inform ( msg )
2009-11-16 08:46:47 -05:00
def newPhase ( prev : Phase ) = new ApiPhase ( prev )
class ApiPhase ( prev : Phase ) extends Phase ( prev )
{
override def description = "Extracts the public API from source files."
def name = API . name
2009-11-22 22:54:17 -05:00
def run : Unit =
{
2010-07-02 06:57:03 -04:00
val start = System . currentTimeMillis
currentRun . units . foreach ( processUnit )
val stop = System . currentTimeMillis
2011-05-29 19:17:31 -04:00
debug ( "API phase took : " + (( stop - start )/ 1000.0 ) + " s" )
2009-11-22 22:54:17 -05:00
}
2011-04-26 21:19:56 -04:00
def processUnit ( unit : CompilationUnit ) = if (! unit . isJava ) processScalaUnit ( unit )
def processScalaUnit ( unit : CompilationUnit )
2009-11-16 08:46:47 -05:00
{
val sourceFile = unit . source . file . file
2011-05-29 19:17:31 -04:00
debug ( "Traversing " + sourceFile )
2009-11-16 08:46:47 -05:00
val traverser = new TopLevelHandler ( sourceFile )
traverser . apply ( unit . body )
val packages = traverser . packages . toArray [ String ]. map ( p => new xsbti . api . Package ( p ))
2011-06-01 02:19:46 -04:00
val source = new xsbti . api . SourceAPI ( packages , traverser . definitions . toArray [ xsbti.api.Definition ])
2010-10-22 21:55:16 -04:00
forceStructures ()
2010-10-30 17:46:56 -04:00
clearCaches ()
2009-11-16 08:46:47 -05:00
callback . api ( sourceFile , source )
2010-06-15 20:38:18 -04:00
}
2009-11-16 08:46:47 -05:00
}
2010-10-22 21:55:16 -04:00
2010-10-30 17:46:56 -04:00
// this cache reduces duplicate work both here and when persisting
// caches on other structures had minimal effect on time and cache size
// (tried: Definition, Modifier, Path, Id, String)
2010-11-09 20:49:23 -05:00
private [ this ] val typeCache = new HashMap [( Symbol ,Type ) , xsbti.api.Type ]
2010-10-30 17:46:56 -04:00
// these caches are necessary for correctness
2010-10-22 21:55:16 -04:00
private [ this ] val structureCache = new HashMap [ Symbol , xsbti.api.Structure ]
2010-11-09 20:49:23 -05:00
private [ this ] val classLikeCache = new HashMap [( Symbol ,Symbol ) , xsbti.api.ClassLike ]
2010-10-22 21:55:16 -04:00
private [ this ] val pending = new HashSet [ xsbti.api.Lazy [ _ ]]
2011-10-19 22:23:47 -04:00
private [ this ] val emptyStringArray = new Array [ String ]( 0 )
2010-10-30 17:46:56 -04:00
// to mitigate "temporary leaks" like that caused by NoPhase in 2.8.0,
// this ensures this class is not retaining objects
private def clearCaches ()
{
typeCache . clear ()
structureCache . clear ()
classLikeCache . clear ()
}
2010-10-22 21:55:16 -04:00
// 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
2011-03-11 16:57:15 -05:00
// (those in this subproject) to be garbage collected after compilation.
2010-10-22 21:55:16 -04:00
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 ()
}
2010-06-15 20:38:18 -04:00
private def thisPath ( sym : Symbol ) = path ( pathComponents ( sym , Constants . thisPath : : Nil ))
2009-11-16 08:46:47 -05:00
private def path ( components : List [ PathComponent ]) = new xsbti . api . Path ( components . toArray [ PathComponent ])
private def pathComponents ( sym : Symbol , postfix : List [ PathComponent ]) : List [ PathComponent ] =
{
2010-07-02 06:57:03 -04:00
if ( sym == NoSymbol || sym . isRoot || sym . isEmptyPackageClass || sym . isRootPackage ) postfix
2010-01-07 21:41:20 -05:00
else pathComponents ( sym . owner , new xsbti . api . Id ( simpleName ( sym )) : : postfix )
2009-11-16 08:46:47 -05:00
}
2010-11-09 20:49:23 -05:00
private def simpleType ( in : Symbol , t : Type ) : SimpleType =
processType ( in , t ) match
2009-11-16 08:46:47 -05:00
{
case s : SimpleType => s
2012-01-04 16:20:22 +01:00
case x => warning ( "Not a simple type:\n\tType: " + t + " (" + t . getClass + ")\n\tTransformed: " + x . getClass ); Constants . emptyType
2009-11-16 08:46:47 -05:00
}
2010-11-09 20:49:23 -05:00
private def types ( in : Symbol , t : List [ Type ]) : Array [ xsbti.api.Type ] = t . toArray [ Type ]. map ( processType ( in , _ ))
private def projectionType ( in : Symbol , pre : Type , sym : Symbol ) =
2009-11-16 08:46:47 -05:00
{
2010-01-07 21:41:20 -05:00
if ( pre == NoPrefix )
{
2010-10-22 21:55:16 -04:00
if ( sym . isLocalClass || sym . isRoot || sym . isRootPackage ) Constants . emptyType
2011-08-17 21:50:46 -04:00
else if ( sym . isTypeParameterOrSkolem || sym . isExistentiallyBound ) reference ( sym )
2010-10-22 21:55:16 -04:00
else {
2010-11-09 20:49:23 -05:00
// this appears to come from an existential type in an inherited member- not sure why isExistential is false here
/*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))*/
2010-10-22 21:55:16 -04:00
reference ( sym )
}
2010-01-07 21:41:20 -05:00
}
2009-11-16 08:46:47 -05:00
else if ( sym . isRoot || sym . isRootPackage ) Constants . emptyType
2012-10-15 12:42:27 -04:00
else new xsbti . api . Projection ( simpleType ( in , pre ), simpleName ( sym ))
2009-11-16 08:46:47 -05:00
}
2012-05-12 23:12:29 -04:00
private def reference ( sym : Symbol ) : xsbti.api.ParameterRef = new xsbti . api . ParameterRef ( tparamID ( sym ))
2010-10-22 21:55:16 -04:00
2010-11-09 20:49:23 -05:00
private def annotations ( in : Symbol , as : List [ AnnotationInfo ]) : Array [ xsbti.api.Annotation ] = as . toArray [ AnnotationInfo ]. map ( annotation ( in , _ ))
private def annotation ( in : Symbol , a : AnnotationInfo ) =
2011-05-23 18:40:03 -04:00
new xsbti . api . Annotation ( processType ( in , a . atp ),
2010-01-24 00:11:43 -05:00
if ( a . assocs . isEmpty ) Array ( new xsbti . api . AnnotationArgument ( "" , a . args . mkString ( "(" , "," , ")" ))) // what else to do with a Tree?
else a . assocs . map { case ( name , value ) => new xsbti . api . AnnotationArgument ( name . toString , value . toString ) }. toArray [ xsbti.api.AnnotationArgument ]
)
2010-11-09 20:49:23 -05:00
private def annotated ( in : Symbol , as : List [ AnnotationInfo ], tpe : Type ) = new xsbti . api . Annotated ( simpleType ( in , tpe ), annotations ( in , as ))
2009-11-16 08:46:47 -05:00
2010-10-22 21:55:16 -04:00
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 ) =
2009-11-16 08:46:47 -05:00
{
def build ( t : Type , typeParams : Array [ xsbti.api.TypeParameter ], valueParameters : List [ xsbti.api.ParameterList ]) : xsbti.api.Def =
{
2011-08-17 21:50:46 -04:00
def parameterList ( syms : List [ Symbol ]) : xsbti.api.ParameterList =
2009-11-16 08:46:47 -05:00
{
2009-11-24 23:01:05 -05:00
val isImplicitList = syms match { case head : : _ => isImplicit ( head ); case _ => false }
2009-11-16 08:46:47 -05:00
new xsbti . api . ParameterList ( syms . map ( parameterS ). toArray , isImplicitList )
}
t match
{
case PolyType ( typeParams0 , base ) =>
assert ( typeParams . isEmpty )
assert ( valueParameters . isEmpty )
2010-11-09 20:49:23 -05:00
build ( base , typeParameters ( in , typeParams0 ), Nil )
2011-08-17 21:50:46 -04:00
case MethodType ( params , resultType ) =>
build ( resultType , typeParams , parameterList ( params ) : : valueParameters )
2011-02-08 20:30:15 -05:00
case Nullary ( resultType ) => // 2.9 and later
build ( resultType , typeParams , valueParameters )
2009-11-16 08:46:47 -05:00
case returnType =>
2011-02-14 18:59:54 -05:00
val t2 = processType ( in , dropConst ( returnType ))
new xsbti . api . Def ( valueParameters . reverse . toArray , t2 , typeParams , simpleName ( s ), getAccess ( s ), getModifiers ( s ), annotations ( in , s ))
2009-11-16 08:46:47 -05:00
}
}
2010-10-22 21:55:16 -04:00
def parameterS ( s : Symbol ) : xsbti.api.MethodParameter =
2012-10-15 12:42:27 -04:00
makeParameter ( simpleName ( s ), s . info , s . info . typeSymbol , s )
2010-10-22 21:55:16 -04:00
2010-09-27 18:48:12 -04:00
// 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 =
2009-11-16 08:46:47 -05:00
{
import xsbti.api.ParameterModifier._
val ( t , special ) =
if ( ts == definitions . RepeatedParamClass ) // || s == definitions.JavaRepeatedParamClass)
( tpe . typeArgs ( 0 ), Repeated )
else if ( ts == definitions . ByNameParamClass )
( tpe . typeArgs ( 0 ), ByName )
else
( tpe , Plain )
2010-11-09 20:49:23 -05:00
new xsbti . api . MethodParameter ( name , processType ( in , t ), hasDefault ( paramSym ), special )
2009-11-16 08:46:47 -05:00
}
2010-10-22 21:55:16 -04:00
val t = viewer ( in ). memberInfo ( s )
build ( t , Array (), Nil )
2009-11-16 08:46:47 -05:00
}
2011-08-17 21:50:46 -04:00
private def hasDefault ( s : Symbol ) = s != NoSymbol && s . hasFlag ( Flags . DEFAULTPARAM )
2011-02-14 18:59:54 -05:00
private def fieldDef [ T ]( in : Symbol , s : Symbol , keepConst : Boolean , create : ( xsbti.api.Type , String , xsbti . api . Access , xsbti . api . Modifiers , Array [ xsbti.api.Annotation ]) => T ) : T =
2010-10-22 21:55:16 -04:00
{
2011-02-08 20:30:15 -05:00
val t = dropNullary ( viewer ( in ). memberType ( s ))
2011-02-14 18:59:54 -05:00
val t2 = if ( keepConst ) t else dropConst ( t )
create ( processType ( in , t2 ), simpleName ( s ), getAccess ( s ), getModifiers ( s ), annotations ( in , s ))
}
private def dropConst ( t : Type ) : Type = t match {
case ConstantType ( constant ) => constant . tpe
case _ => t
2010-10-22 21:55:16 -04:00
}
2011-02-08 20:30:15 -05:00
private def dropNullary ( t : Type ) : Type = t match {
case Nullary ( un ) => un
case _ => t
}
2010-01-24 00:11:43 -05:00
2010-10-22 21:55:16 -04:00
private def typeDef ( in : Symbol , s : Symbol ) : xsbti.api.TypeMember =
2009-11-22 22:54:17 -05:00
{
val ( typeParams , tpe ) =
2010-10-22 21:55:16 -04:00
viewer ( in ). memberInfo ( s ) match
2009-11-22 22:54:17 -05:00
{
2010-11-09 20:49:23 -05:00
case PolyType ( typeParams0 , base ) => ( typeParameters ( in , typeParams0 ), base )
2009-11-22 22:54:17 -05:00
case t => ( Array [ xsbti.api.TypeParameter ](), t )
}
2010-01-07 21:41:20 -05:00
val name = simpleName ( s )
2009-11-22 22:54:17 -05:00
val access = getAccess ( s )
val modifiers = getModifiers ( s )
2010-11-09 20:49:23 -05:00
val as = annotations ( in , s )
2009-11-22 22:54:17 -05:00
if ( s . isAliasType )
2010-11-09 20:49:23 -05:00
new xsbti . api . TypeAlias ( processType ( in , tpe ), typeParams , name , access , modifiers , as )
2009-11-22 22:54:17 -05:00
else if ( s . isAbstractType )
{
val bounds = tpe . bounds
2010-11-09 20:49:23 -05:00
new xsbti . api . TypeDeclaration ( processType ( in , bounds . lo ), processType ( in , bounds . hi ), typeParams , name , access , modifiers , as )
2009-11-22 22:54:17 -05:00
}
else
error ( "Unknown type member" + s )
}
2009-11-16 08:46:47 -05:00
2010-11-09 20:49:23 -05:00
private def structure ( in : Symbol , s : Symbol ) : xsbti.api.Structure = structure ( viewer ( in ). memberInfo ( s ), s , true )
2010-10-22 21:55:16 -04:00
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 ))
2010-11-09 20:49:23 -05:00
private def removeConstructors ( ds : List [ Symbol ]) : List [ Symbol ] = ds filter { ! _ . isConstructor }
2010-10-22 21:55:16 -04:00
private def mkStructure ( info : Type , s : Symbol , inherit : Boolean ) : xsbti.api.Structure =
2009-11-22 22:54:17 -05:00
{
2010-09-17 21:30:47 -04:00
val ( declared , inherited ) = info . members . reverse . partition ( _ . owner == s )
2010-10-22 21:55:16 -04:00
val baseTypes = info . baseClasses . tail . map ( info . baseType )
2010-11-09 20:49:23 -05:00
val ds = if ( s . isModuleClass ) removeConstructors ( declared ) else declared
val is = if ( inherit ) removeConstructors ( inherited ) else Nil
mkStructure ( s , baseTypes , ds , is )
2009-11-22 22:54:17 -05:00
}
2010-10-22 21:55:16 -04:00
private def mkStructure ( s : Symbol , bases : List [ Type ], declared : List [ Symbol ], inherited : List [ Symbol ]) : xsbti.api.Structure =
2010-11-09 20:49:23 -05:00
new xsbti . api . Structure ( lzy ( types ( s , bases )), lzy ( processDefinitions ( s , declared )), lzy ( processDefinitions ( s , inherited )))
2010-10-22 21:55:16 -04:00
private def processDefinitions ( in : Symbol , defs : List [ Symbol ]) : Array [ xsbti.api.Definition ] =
2012-04-30 20:34:48 -04:00
sort ( defs . toArray ). flatMap ( ( d : Symbol ) => definition ( in , d ))
private [ this ] def sort ( defs : Array [ Symbol ]) : Array [ Symbol ] = {
Arrays . sort ( defs , sortClasses )
defs
}
2010-10-22 21:55:16 -04:00
private def definition ( in : Symbol , sym : Symbol ) : Option [ xsbti.api.Definition ] =
2009-11-16 08:46:47 -05:00
{
2011-02-14 18:59:54 -05:00
def mkVar = Some ( fieldDef ( in , sym , false , new xsbti . api . Var ( _ , _ , _ , _ , _ )))
def mkVal = Some ( fieldDef ( in , sym , true , new xsbti . api . Val ( _ , _ , _ , _ , _ )))
2012-04-30 20:34:48 -04:00
if ( isClass ( sym ))
2010-11-09 20:49:23 -05:00
if ( ignoreClass ( sym )) None else Some ( classLike ( in , sym ))
2011-08-17 21:50:46 -04:00
else if ( sym . isNonClassType )
2010-10-22 21:55:16 -04:00
Some ( typeDef ( in , sym ))
2010-09-17 22:14:48 -04:00
else if ( sym . isVariable )
2010-10-22 21:55:16 -04:00
if ( isSourceField ( sym )) mkVar else None
2010-09-17 22:14:48 -04:00
else if ( sym . isStable )
2010-10-22 21:55:16 -04:00
if ( isSourceField ( sym )) mkVal else None
2010-09-17 22:14:48 -04:00
else if ( sym . isSourceMethod && ! sym . isSetter )
2010-10-22 21:55:16 -04:00
if ( sym . isGetter ) mkVar else Some ( defDef ( in , sym ))
else
None
2010-09-17 22:14:48 -04:00
}
2010-11-09 20:49:23 -05:00
private def ignoreClass ( sym : Symbol ) : Boolean =
2012-02-04 21:10:30 -05:00
sym . isLocalClass || sym . isAnonymousClass || sym . fullName . endsWith ( LocalChild . toString )
2010-11-09 20:49:23 -05:00
2010-09-17 22:14:48 -04:00
// This filters private[this] vals/vars that were not in the original source.
// The getter will be used for processing instead.
private def isSourceField ( sym : Symbol ) : Boolean =
{
val getter = sym . getter ( sym . enclClass )
// the check `getter eq sym` is a precaution against infinite recursion
// `isParamAccessor` does not exist in all supported versions of Scala, so the flag check is done directly
( getter == NoSymbol && ! sym . hasFlag ( Flags . PARAMACCESSOR )) || ( getter eq sym )
2009-11-16 08:46:47 -05:00
}
private def getModifiers ( s : Symbol ) : xsbti.api.Modifiers =
{
2012-03-04 19:07:33 +01:00
import Flags._
2013-04-15 14:12:15 -04:00
val absOver = s . hasFlag ( ABSOVERRIDE )
val abs = s . hasFlag ( ABSTRACT ) || s . hasFlag ( DEFERRED ) || absOver
val over = s . hasFlag ( OVERRIDE ) || absOver
new xsbti . api . Modifiers ( abs , over , s . isFinal , s . hasFlag ( SEALED ), isImplicit ( s ), s . hasFlag ( LAZY ), hasMacro ( s ))
2009-11-16 08:46:47 -05:00
}
2010-10-30 17:46:56 -04:00
2009-11-16 08:46:47 -05:00
private def isImplicit ( s : Symbol ) = s . hasFlag ( Flags . IMPLICIT )
private def getAccess ( c : Symbol ) : xsbti.api.Access =
{
if ( c . isPublic ) Constants . public
else if ( c . isPrivateLocal ) Constants . privateLocal
else if ( c . isProtectedLocal ) Constants . protectedLocal
else
{
val within = c . privateWithin
2011-08-17 21:50:46 -04:00
val qualifier = if ( within == NoSymbol ) Constants . unqualified else new xsbti . api . IdQualifier ( within . fullName )
2010-09-17 21:29:29 -04:00
if ( c . hasFlag ( Flags . PROTECTED )) new xsbti . api . Protected ( qualifier )
else new xsbti . api . Private ( qualifier )
2009-11-16 08:46:47 -05:00
}
}
2012-11-19 18:36:10 -08:00
/**
* Replace all types that directly refer to the `forbidden` symbol by `NoType`.
* (a specialized version of substThisAndSym)
*/
class SuppressSymbolRef ( forbidden : Symbol ) extends TypeMap {
def apply ( tp : Type ) =
if ( tp . typeSymbolDirect == forbidden ) NoType
else mapOver ( tp )
}
2010-11-09 20:49:23 -05:00
private def processType ( in : Symbol , t : Type ) : xsbti.api.Type = typeCache . getOrElseUpdate (( in , t ), makeType ( in , t ))
private def makeType ( in : Symbol , t : Type ) : xsbti.api.Type =
2009-11-16 08:46:47 -05:00
{
2010-10-22 21:55:16 -04:00
2013-04-27 00:22:58 +02:00
val dealiased = t match {
case TypeRef ( _ , sym , _ ) if sym . isAliasType => t . dealias
case _ => t
}
dealiased match
2009-11-16 08:46:47 -05:00
{
case NoPrefix => Constants . emptyType
case ThisType ( sym ) => new xsbti . api . Singleton ( thisPath ( sym ))
2010-11-09 20:49:23 -05:00
case SingleType ( pre , sym ) => projectionType ( in , pre , sym )
2011-02-14 18:59:54 -05:00
case ConstantType ( constant ) => new xsbti . api . Constant ( processType ( in , constant . tpe ), constant . stringValue )
2012-11-19 18:36:10 -08:00
/* explaining the special-casing of references to refinement classes (https://support.typesafe.com/tickets/1882)
*
* goal: a representation of type references to refinement classes that's stable across compilation runs
* (and thus insensitive to typing from source or unpickling from bytecode)
*
* problem: the current representation, which corresponds to the owner chain of the refinement:
* 1. is affected by pickling, so typing from source or using unpickled symbols give different results (because the unpickler "localizes" owners -- this could be fixed in the compiler)
* 2. can't distinguish multiple refinements in the same owner (this is a limitation of SBT's internal representation and cannot be fixed in the compiler)
*
* potential solutions:
* - simply drop the reference: won't work as collapsing all refinement types will cause recompilation to be skipped when a refinement is changed to another refinement
* - represent the symbol in the api: can't think of a stable way of referring to an anonymous symbol whose owner changes when pickled
* + expand the reference to the corresponding refinement type: doing that recursively may not terminate, but we can deal with that by approximating recursive references
* (all we care about is being sound for recompilation: recompile iff a dependency changes, and this will happen as long as we have one unrolling of the reference to the refinement)
*/
case TypeRef ( pre , sym , Nil ) if sym . isRefinementClass =>
// Since we only care about detecting changes reliably, we unroll a reference to a refinement class once.
// Recursive references are simply replaced by NoType -- changes to the type will be seen in the first unrolling.
// The API need not be type correct, so this truncation is acceptable. Most of all, the API should be compact.
val unrolling = pre . memberInfo ( sym ) // this is a refinement type
// in case there are recursive references, suppress them -- does this ever happen?
// we don't have a test case for this, so warn and hope we'll get a contribution for it :-)
val withoutRecursiveRefs = new SuppressSymbolRef ( sym ). mapOver ( unrolling )
if ( unrolling ne withoutRecursiveRefs )
reporter . warning ( sym . pos , "sbt-api: approximated refinement ref" + t + " (== " + unrolling + ") to " + withoutRecursiveRefs + "\nThis is currently untested, please report the code you were compiling." )
structure ( withoutRecursiveRefs )
2012-05-12 23:12:29 -04:00
case tr @ TypeRef ( pre , sym , args ) =>
2010-11-09 20:49:23 -05:00
val base = projectionType ( in , pre , sym )
2012-05-12 23:12:29 -04:00
if ( args . isEmpty )
2013-01-16 10:26:32 -05:00
if ( isRawType ( tr ))
2012-05-12 23:12:29 -04:00
processType ( in , rawToExistential ( tr ))
else
base
else
new xsbti . api . Parameterized ( base , types ( in , args ))
2011-05-25 08:07:34 -04:00
case SuperType ( thistpe : Type , supertpe : Type ) => warning ( "sbt-api: Super type (not implemented): this=" + thistpe + ", super=" + supertpe ); Constants . emptyType
2010-11-09 20:49:23 -05:00
case at : AnnotatedType => annotatedType ( in , at )
2010-10-22 21:55:16 -04:00
case rt : CompoundType => structure ( rt )
2010-11-09 20:49:23 -05:00
case ExistentialType ( tparams , result ) => new xsbti . api . Existential ( processType ( in , result ), typeParameters ( in , tparams ))
2011-05-25 07:57:14 -04:00
case NoType => Constants . emptyType // this can happen when there is an error that will be reported by a later phase
2010-11-09 20:49:23 -05:00
case PolyType ( typeParams , resultType ) => new xsbti . api . Polymorphic ( processType ( in , resultType ), typeParameters ( in , typeParams ))
2011-05-25 08:07:34 -04:00
case Nullary ( resultType ) => warning ( "sbt-api: Unexpected nullary method type " + in + " in " + in . owner ); Constants . emptyType
case _ => warning ( "sbt-api: Unhandled type " + t . getClass + " : " + t ); Constants . emptyType
2009-11-16 08:46:47 -05:00
}
}
2010-11-09 20:49:23 -05:00
private def typeParameters ( in : Symbol , s : Symbol ) : Array [ xsbti.api.TypeParameter ] = typeParameters ( in , s . typeParams )
private def typeParameters ( in : Symbol , s : List [ Symbol ]) : Array [ xsbti.api.TypeParameter ] = s . map ( typeParameter ( in , _ )). toArray [ xsbti.api.TypeParameter ]
private def typeParameter ( in : Symbol , s : Symbol ) : xsbti.api.TypeParameter =
2009-11-16 08:46:47 -05:00
{
val varianceInt = s . variance
import xsbti.api.Variance._
2010-11-09 20:49:23 -05:00
val annots = annotations ( in , s )
2009-11-16 08:46:47 -05:00
val variance = if ( varianceInt < 0 ) Contravariant else if ( varianceInt > 0 ) Covariant else Invariant
2010-11-09 20:49:23 -05:00
viewer ( in ). memberInfo ( s ) match
2009-11-16 08:46:47 -05:00
{
2012-05-12 23:12:29 -04:00
case TypeBounds ( low , high ) => new xsbti . api . TypeParameter ( tparamID ( s ), annots , typeParameters ( in , s ), variance , processType ( in , low ), processType ( in , high ) )
case PolyType ( typeParams , base ) => new xsbti . api . TypeParameter ( tparamID ( s ), annots , typeParameters ( in , typeParams ), variance , processType ( in , base . bounds . lo ), processType ( in , base . bounds . hi ))
2009-11-16 08:46:47 -05:00
case x => error ( "Unknown type parameter info: " + x . getClass )
}
}
2012-05-12 23:12:29 -04:00
private def tparamID ( s : Symbol ) = s . fullName
2011-03-11 16:57:15 -05:00
private def selfType ( in : Symbol , s : Symbol ) : xsbti.api.Type = processType ( in , s . thisSym . typeOfThis )
2010-10-22 21:55:16 -04:00
2010-11-09 20:49:23 -05:00
private def classLike ( in : Symbol , c : Symbol ) : ClassLike = classLikeCache . getOrElseUpdate ( ( in , c ), mkClassLike ( in , c ))
2010-10-22 21:55:16 -04:00
private def mkClassLike ( in : Symbol , c : Symbol ) : ClassLike =
2009-11-16 08:46:47 -05:00
{
2011-08-17 21:50:46 -04:00
val name = c . fullName
2009-11-16 08:46:47 -05:00
val isModule = c . isModuleClass || c . isModule
2010-11-09 20:49:23 -05:00
val struct = if ( isModule ) c . moduleClass else c
2009-11-16 08:46:47 -05:00
val defType =
if ( c . isTrait ) DefinitionType . Trait
else if ( isModule )
{
if ( c . isPackage ) DefinitionType . PackageModule
else DefinitionType . Module
}
else DefinitionType . ClassDef
2011-10-19 22:23:47 -04:00
new xsbti . api . ClassLike ( defType , lzy ( selfType ( in , c )), lzy ( structure ( in , struct )), emptyStringArray , typeParameters ( in , c ), name , getAccess ( c ), getModifiers ( c ), annotations ( in , c ))
2009-11-16 08:46:47 -05:00
}
private final class TopLevelHandler ( sourceFile : File ) extends TopLevelTraverser
{
val packages = new HashSet [ String ]
val definitions = new ListBuffer [ xsbti.api.Definition ]
2010-10-22 21:55:16 -04:00
def `class` ( c : Symbol ) : Unit = definitions += classLike ( c . owner , c )
2009-11-16 08:46:47 -05:00
/** Record packages declared in the source file*/
def `package` ( p : Symbol )
{
if ( ( p eq null ) || p == NoSymbol || p . isRoot || p . isRootPackage || p . isEmptyPackageClass || p . isEmptyPackage )
()
else
{
2011-08-17 21:50:46 -04:00
packages += p . fullName
2009-11-16 08:46:47 -05:00
`package` ( p . enclosingPackage )
}
}
}
2012-04-30 20:34:48 -04:00
private [ this ] def isClass ( s : Symbol ) = s . isClass || s . isModule
// necessary to ensure a stable ordering of classes in the definitions list:
// modules and classes come first and are sorted by name
// all other definitions come later and are not sorted
private [ this ] val sortClasses = new Comparator [ Symbol ] {
def compare ( a : Symbol , b : Symbol ) = {
val aIsClass = isClass ( a )
val bIsClass = isClass ( b )
if ( aIsClass == bIsClass )
if ( aIsClass )
if ( a . isModule == b . isModule )
a . fullName . compareTo ( b . fullName )
else if ( a . isModule )
- 1
else
1
else
0 // substantial performance hit if fullNames are compared here
else if ( aIsClass )
- 1
else
1
}
}
2009-11-16 08:46:47 -05:00
private object Constants
{
2009-12-31 18:55:35 -05:00
val local = new xsbti . api . ThisQualifier
2009-11-16 08:46:47 -05:00
val public = new xsbti . api . Public
val privateLocal = new xsbti . api . Private ( local )
val protectedLocal = new xsbti . api . Protected ( local )
val unqualified = new xsbti . api . Unqualified
val emptyPath = new xsbti . api . Path ( Array ())
val thisPath = new xsbti . api . This
val emptyType = new xsbti . api . EmptyType
}
private abstract class TopLevelTraverser extends Traverser
{
def `class` ( s : Symbol )
def `package` ( s : Symbol )
override def traverse ( tree : Tree )
{
tree match
{
case ( _: ClassDef | _ : ModuleDef ) if isTopLevel ( tree . symbol ) => `class` ( tree . symbol )
case p : PackageDef =>
`package` ( p . symbol )
super . traverse ( tree )
case _ =>
}
}
def isTopLevel ( sym : Symbol ) : Boolean =
( sym ne null ) && ( sym != NoSymbol ) && ! sym . isImplClass && ! sym . isNestedClass && sym . isStatic &&
2010-07-14 19:24:50 -04:00
! sym . hasFlag ( Flags . SYNTHETIC ) && ! sym . hasFlag ( Flags . JAVA )
2009-11-16 08:46:47 -05:00
}
2009-11-24 23:01:05 -05:00
2010-11-09 20:49:23 -05:00
private def annotations ( in : Symbol , s : Symbol ) : Array [ xsbti.api.Annotation ] =
2010-01-24 00:11:43 -05:00
atPhase ( currentRun . typerPhase ) {
2010-09-17 22:14:48 -04:00
val base = if ( s . hasFlag ( Flags . ACCESSOR )) s . accessed else NoSymbol
val b = if ( base == NoSymbol ) s else base
// annotations from bean methods are not handled because:
// a) they are recorded as normal source methods anyway
// b) there is no way to distinguish them from user-defined methods
val associated = List ( b , b . getter ( b . enclClass ), b . setter ( b . enclClass )). filter ( _ != NoSymbol )
2011-08-17 21:50:46 -04:00
associated . flatMap ( ss => annotations ( in , ss . annotations ) ). distinct . toArray ;
2010-01-24 00:11:43 -05:00
}
2010-11-09 20:49:23 -05:00
private def annotatedType ( in : Symbol , at : AnnotatedType ) : xsbti.api.Type =
2010-01-07 21:41:20 -05:00
{
2011-08-17 21:50:46 -04:00
val annots = at . annotations
2010-11-09 20:49:23 -05:00
if ( annots . isEmpty ) processType ( in , at . underlying ) else annotated ( in , annots , at . underlying )
2010-01-07 21:41:20 -05:00
}
2011-03-11 16:57:15 -05:00
private def simpleName ( s : Symbol ) : String =
{
val n = s . originalName
val n2 = if ( n . toString == "<init>" ) n else n . decode
n2 . toString . trim
}
2010-10-22 21:55:16 -04:00
}