
xsbt.api.HashAPI.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zinc-apiinfo_2.13 Show documentation
Show all versions of zinc-apiinfo_2.13 Show documentation
Incremental compiler of Scala
The newest version!
/*
* Zinc - The incremental compiler for Scala.
* Copyright Scala Center, Lightbend, and Mark Harrah
*
* Licensed under Apache License 2.0
* SPDX-License-Identifier: Apache-2.0
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package xsbt.api
import xsbti.api._
import scala.util.hashing.MurmurHash3
import HashAPI.Hash
object HashAPI {
type Hash = Int
def apply(api: ClassLike): Hash = apply(_.hashAPI(api))
def apply(x: Def): Hash = apply(_.hashDefinition(x))
def apply(
doHashing: HashAPI => Unit,
includePrivate: Boolean = false,
includeParamNames: Boolean = true,
includeDefinitions: Boolean = true,
includeSealedChildren: Boolean = true,
includePrivateDefsInTrait: Boolean = false
): Hash = {
val hasher = new HashAPI(
includePrivate,
includeParamNames,
includeDefinitions,
includeSealedChildren,
includePrivateDefsInTrait
)
doHashing(hasher)
hasher.finalizeHash
}
}
/**
* Implements hashing of public API.
*
* @param includePrivate should private definitions be included in a hash sum
* @param includeParamNames should parameter names for methods be included in a hash sum
* @param includeDefinitions when hashing a structure (e.g. of a class) should hashes of definitions (members)
* be included in a hash sum. Structure can appear as a type (in structural type).
* In that case we always include definitions in a hash sum.
* @param includeSealedChildren Controls if types of children of sealed class should be included in hash.
*/
final class HashAPI private (
includePrivate: Boolean,
includeParamNames: Boolean,
includeDefinitions: Boolean,
includeSealedChildren: Boolean,
includeTraitBreakers: Boolean
) {
// this constructor variant is for source and binary backwards compatibility with sbt 0.13.0
def this(includePrivate: Boolean, includeParamNames: Boolean) = {
// in the old logic we used to always include definitions hence
// includeDefinitions=true
this(
includePrivate,
includeParamNames,
includeDefinitions = true,
includeSealedChildren = true,
includeTraitBreakers = false
)
}
import scala.collection.mutable
import MurmurHash3.{ mix, stringHash, unorderedHash }
private[this] val visitedStructures = visitedMap[Structure]
private[this] val visitedClassLike = visitedMap[ClassLike]
private[this] def visitedMap[T] = new mutable.HashMap[T, List[Hash]]
private[this] def visit[T](map: mutable.Map[T, List[Hash]], t: T)(hashF: T => Unit): Unit = {
map.put(t, hash :: map.getOrElse(t, Nil)) match {
case Some(x :: _) => extend(x)
case _ =>
hashF(t)
for (hs <- map(t))
extend(hs)
map.put(t, hash :: Nil)
()
}
}
private[this] final val ValHash = 1
private[this] final val VarHash = 2
private[this] final val DefHash = 3
private[this] final val ClassDefHash = 4
private[this] final val TypeDeclHash = 5
private[this] final val TypeAliasHash = 6
private[this] final val PublicHash = 30
private[this] final val ProtectedHash = 31
private[this] final val PrivateHash = 32
private[this] final val UnqualifiedHash = 33
private[this] final val ThisQualifierHash = 34
private[this] final val IdQualifierHash = 35
private[this] final val IdPathHash = 20
private[this] final val SuperHash = 21
private[this] final val ThisPathHash = 22
private[this] final val ValueParamsHash = 40
private[this] final val ClassPendingHash = 41
private[this] final val StructurePendingHash = 42
private[this] final val EmptyTypeHash = 51
private[this] final val ParameterRefHash = 52
private[this] final val SingletonHash = 53
private[this] final val ProjectionHash = 54
private[this] final val ParameterizedHash = 55
private[this] final val AnnotatedHash = 56
private[this] final val PolymorphicHash = 57
private[this] final val ConstantHash = 58
private[this] final val ExistentialHash = 59
private[this] final val StructureHash = 60
private[this] val ClassHash = 70
private[this] final val TrueHash = 97
private[this] final val FalseHash = 98
private[this] var hash: Hash = 0
final def hashString(s: String): Unit = extend(stringHash(s))
final def hashBoolean(b: Boolean): Unit = extend(if (b) TrueHash else FalseHash)
@inline final def hashArray[T <: AnyRef](s: Array[T], hashF: T => Unit): Unit = {
extend(s.length)
var i = 0
while (i < s.length) {
hashF(s(i))
i += 1
}
}
final def hashSymmetric[T](ts: TraversableOnce[T], hashF: T => Unit): Unit = {
val current = hash
val tsHash: Hash = ts match {
case ts: collection.Iterable[T] =>
// Avoid creation of a temporary collection and avoid boxing of hashCodes by passing
// this iterator to `unorderedHash`. It returns itself each time with a different hashCode.
class HashIterator(delegate: Iterator[T]) extends Iterator[AnyRef] {
override def hasNext: Boolean = delegate.hasNext
def next(): AnyRef = { hash = 1; hashF(delegate.next()); this }
override def hashCode: Int = finalizeHash
}
unorderedHash(new HashIterator(ts.iterator))
case _ =>
unorderedHash(ts.toList.map { t =>
hash = 1
hashF(t)
finalizeHash
})
}
hash = current
extend(tsHash)
}
@inline final def extend(a: Hash): Unit = {
hash = mix(hash, a)
}
// TODO: Figure out what should be the length
def finalizeHash: Hash = MurmurHash3.finalizeHash(hash, 1)
def hashModifiers(m: Modifiers) = extend(m.raw.toInt)
def hashAPI(c: ClassLike): Unit = {
hash = 1
hashClass(c)
}
def hashPackage(p: Package) = hashString(p.name)
def hashDefinitions(ds: Seq[Definition], topLevel: Boolean, isTrait: Boolean): Unit = {
// Any private definition in a trait is required to be concrete
def isPrivate(d: Definition): Boolean =
d.access match { case _: xsbti.api.Private => true; case _ => false }
def isTraitBreaker(d: Definition): Boolean = d match {
// Vars and vals in traits introduce getters, setters and fields in the implementing classes.
// See test `source-dependencies/trait-private-var
case _: FieldLike => true
// Objects in traits introduce fields in the implementing classes.
// See test `source-dependencies/trait-private-object`
case cl: ClassLikeDef => cl.definitionType == DefinitionType.Module
// super calls introduce accessors that are not part of the public API
case d: Def => d.modifiers.isSuperAccessor
case _ => false
}
val requiresPrivateDefinitions = isTrait && includeTraitBreakers && !topLevel
// Get private definitions that break trait properties in incremental compilation
val extraPrivateDefinitions =
if (!requiresPrivateDefinitions) Nil
else ds.filter(x => isTraitBreaker(x) && isPrivate(x))
val defs = SameAPI.filterDefinitions(ds, topLevel, includePrivate)
hashSymmetric(defs ++ extraPrivateDefinitions, hashDefinition)
}
/**
* Hashes a sequence of definitions by combining each definition's own
* hash with extra one supplied.
*
* It's useful when one wants to influence hash of a definition by some
* external (to definition) factor (e.g. location of definition).
*
* NOTE: This method doesn't perform any filtering of passed definitions.
*/
def hashDefinitionsWithExtraHash(ds: TraversableOnce[Definition], extraHash: Hash): Unit = {
def hashDefinitionCombined(d: Definition): Unit = {
hashDefinition(d)
extend(extraHash)
}
hashSymmetric(ds, hashDefinitionCombined _)
}
def hashDefinition(d: Definition): Unit = {
hashString(d.name)
hashAnnotations(d.annotations)
hashModifiers(d.modifiers)
hashAccess(d.access)
d match {
case c: ClassLikeDef => hashClassDef(c)
case c: ClassLike => hashClass(c)
case f: FieldLike => hashField(f)
case d: Def => hashDef(d)
case t: TypeDeclaration => hashTypeDeclaration(t)
case t: TypeAlias => hashTypeAlias(t)
}
}
final def hashClassDef(c: ClassLikeDef): Unit = {
extend(ClassDefHash)
hashParameterizedDefinition(c)
}
final def hashClass(c: ClassLike): Unit = visit(visitedClassLike, c)(hashClass0)
def hashClass0(c: ClassLike): Unit = {
extend(ClassHash)
hashTypeParameters(c.typeParameters)
hashType(c.selfType)
if (includeSealedChildren)
hashTypesSymmetric(c.childrenOfSealedClass, includeDefinitions)
val isTrait = c.definitionType() == DefinitionType.Trait
hashStructure(c.structure, includeDefinitions, isTrait)
}
def hashField(f: FieldLike): Unit = {
f match {
case _: Var => extend(VarHash)
case _: Val => extend(ValHash)
}
hashType(f.tpe)
}
def hashDef(d: Def): Unit = {
extend(DefHash)
hashParameterizedDefinition(d)
hashValueParameters(d.valueParameters)
hashType(d.returnType)
}
def hashAccess(a: Access): Unit =
a match {
case _: Public => extend(PublicHash)
case qual: Qualified => hashQualified(qual)
}
def hashQualified(qual: Qualified): Unit = {
qual match {
case _: Protected => extend(ProtectedHash)
case _: Private => extend(PrivateHash)
}
hashQualifier(qual.qualifier)
}
def hashQualifier(qual: Qualifier): Unit =
qual match {
case _: Unqualified => extend(UnqualifiedHash)
case _: ThisQualifier => extend(ThisQualifierHash)
case id: IdQualifier =>
extend(IdQualifierHash)
hashString(id.value)
}
def hashValueParameters(valueParameters: Array[ParameterList]) =
hashArray(valueParameters, hashValueParameterList)
def hashValueParameterList(list: ParameterList) = {
extend(ValueParamsHash)
hashBoolean(list.isImplicit)
hashArray(list.parameters, hashValueParameter)
}
def hashValueParameter(parameter: MethodParameter) = {
hashString(parameter.name)
hashType(parameter.tpe)
extend(parameter.modifier.ordinal)
hashBoolean(parameter.hasDefault)
}
def hashParameterizedDefinition[T <: ParameterizedDefinition](d: T): Unit = {
hashTypeParameters(d.typeParameters)
}
def hashTypeDeclaration(d: TypeDeclaration): Unit = {
extend(TypeDeclHash)
hashParameterizedDefinition(d)
hashType(d.lowerBound)
hashType(d.upperBound)
}
def hashTypeAlias(d: TypeAlias): Unit = {
extend(TypeAliasHash)
hashParameterizedDefinition(d)
hashType(d.tpe)
}
def hashTypeParameters(parameters: Array[TypeParameter]) =
hashArray(parameters, hashTypeParameter)
def hashTypeParameter(parameter: TypeParameter): Unit = {
hashString(parameter.id)
extend(parameter.variance.ordinal)
hashTypeParameters(parameter.typeParameters)
hashType(parameter.lowerBound)
hashType(parameter.upperBound)
hashAnnotations(parameter.annotations)
}
def hashAnnotations(annotations: Array[Annotation]) = hashArray(annotations, hashAnnotation)
def hashAnnotation(annotation: Annotation) = {
hashType(annotation.base)
hashAnnotationArguments(annotation.arguments)
}
def hashAnnotationArguments(args: Array[AnnotationArgument]) =
hashArray(args, hashAnnotationArgument)
def hashAnnotationArgument(arg: AnnotationArgument): Unit = {
hashString(arg.name)
hashString(arg.value)
}
def hashTypes(ts: Array[Type], includeDefinitions: Boolean = true) =
hashArray(ts, (t: Type) => hashType(t, includeDefinitions))
def hashTypesSymmetric(ts: Array[Type], includeDefinitions: Boolean = true) =
hashSymmetric(ts, hashType(_, includeDefinitions))
def hashType(t: Type, includeDefinitions: Boolean = true): Unit =
t match {
case s: Structure => hashStructure(s, includeDefinitions, false)
case e: Existential => hashExistential(e)
case c: Constant => hashConstant(c)
case p: Polymorphic => hashPolymorphic(p)
case a: Annotated => hashAnnotated(a)
case p: Parameterized => hashParameterized(p)
case p: Projection => hashProjection(p)
case _: EmptyType => extend(EmptyTypeHash)
case s: Singleton => hashSingleton(s)
case pr: ParameterRef => hashParameterRef(pr)
}
def hashParameterRef(p: ParameterRef): Unit = {
extend(ParameterRefHash)
hashString(p.id)
}
def hashSingleton(s: Singleton): Unit = {
extend(SingletonHash)
hashPath(s.path)
}
def hashPath(path: Path) = {
hashArray(path.components, hashPathComponent)
}
def hashPathComponent(pc: PathComponent) = pc match {
case _: This => extend(ThisPathHash)
case s: Super => hashSuperPath(s)
case id: Id => hashIdPath(id)
}
def hashSuperPath(s: Super): Unit = {
extend(SuperHash)
hashPath(s.qualifier)
}
def hashIdPath(id: Id): Unit = {
extend(IdPathHash)
hashString(id.id)
}
def hashConstant(c: Constant) = {
extend(ConstantHash)
hashString(c.value)
hashType(c.baseType)
}
def hashExistential(e: Existential) = {
extend(ExistentialHash)
hashParameters(e.clause, e.baseType)
}
def hashPolymorphic(p: Polymorphic) = {
extend(PolymorphicHash)
hashParameters(p.parameters, p.baseType)
}
def hashProjection(p: Projection) = {
extend(ProjectionHash)
hashString(p.id)
hashType(p.prefix)
}
def hashParameterized(p: Parameterized): Unit = {
extend(ParameterizedHash)
hashType(p.baseType)
hashTypes(p.typeArguments)
}
def hashAnnotated(a: Annotated): Unit = {
extend(AnnotatedHash)
hashType(a.baseType)
hashAnnotations(a.annotations)
}
def hashStructure(structure: Structure, includeDefinitions: Boolean, isTrait: Boolean) =
visit(visitedStructures, structure)(hashStructure0(includeDefinitions, isTrait))
// Hoisted functions to reduce allocation.
private def hashStructure0(includeDefinitions: Boolean, isTrait: Boolean): (Structure => Unit) = {
if (isTrait && includeTraitBreakers) hashStructure0WithDefsTrait
else if (includeDefinitions) hashStructure0WithDefs
else hashStructure0NoDefs
}
private[this] final val hashStructure0WithDefsTrait = (s: Structure) =>
hashStructure0(s, includeDefinitions = true, isTrait = true)
private[this] final val hashStructure0WithDefs = (s: Structure) =>
hashStructure0(s, includeDefinitions = true, isTrait = false)
private[this] final val hashStructure0NoDefs = (s: Structure) =>
hashStructure0(s, includeDefinitions = false, isTrait = false)
def hashStructure0(structure: Structure, includeDefinitions: Boolean, isTrait: Boolean): Unit = {
extend(StructureHash)
hashTypes(structure.parents, includeDefinitions)
if (includeDefinitions) {
hashDefinitions(structure.declared.toIndexedSeq, false, isTrait)
hashDefinitions(structure.inherited.toIndexedSeq, false, isTrait)
}
}
def hashParameters(parameters: Array[TypeParameter], base: Type): Unit = {
hashTypeParameters(parameters)
hashType(base)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy