Skip to content

Commit

Permalink
Implement content hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
Friendseeker committed Oct 3, 2024
1 parent 7496345 commit 4efdef3
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public static AnalyzedClass create(long _compilationTimestamp, String _name, xsb
public static AnalyzedClass of(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance) {
return new AnalyzedClass(_compilationTimestamp, _name, _api, _apiHash, _nameHashes, _hasMacro, _extraHash, _provenance);
}
public static AnalyzedClass create(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance, int _bytecodeHash) {
return new AnalyzedClass(_compilationTimestamp, _name, _api, _apiHash, _nameHashes, _hasMacro, _extraHash, _provenance, _bytecodeHash);
}
public static AnalyzedClass of(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance, int _bytecodeHash) {
return new AnalyzedClass(_compilationTimestamp, _name, _api, _apiHash, _nameHashes, _hasMacro, _extraHash, _provenance, _bytecodeHash);
}
private long compilationTimestamp;
private String name;
private xsbti.api.Lazy<Companions> api;
Expand All @@ -32,6 +38,7 @@ public static AnalyzedClass of(long _compilationTimestamp, String _name, xsbti.a
private boolean hasMacro;
private int extraHash;
private String provenance;
private int bytecodeHash;
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro) {
super();
compilationTimestamp = _compilationTimestamp;
Expand All @@ -42,6 +49,7 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
hasMacro = _hasMacro;
extraHash = apiHash;
provenance = "";
bytecodeHash = 0;
}
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash) {
super();
Expand All @@ -53,6 +61,7 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
hasMacro = _hasMacro;
extraHash = _extraHash;
provenance = "";
bytecodeHash = 0;
}
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance) {
super();
Expand All @@ -64,6 +73,19 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
hasMacro = _hasMacro;
extraHash = _extraHash;
provenance = _provenance;
bytecodeHash = 0;
}
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance, int _bytecodeHash) {
super();
compilationTimestamp = _compilationTimestamp;
name = _name;
api = _api;
apiHash = _apiHash;
nameHashes = _nameHashes;
hasMacro = _hasMacro;
extraHash = _extraHash;
provenance = _provenance;
bytecodeHash = _bytecodeHash;
}

public long compilationTimestamp() {
Expand Down Expand Up @@ -95,29 +117,36 @@ public int extraHash() {
public String provenance() {
return this.provenance;
}
/** A hash of generated bytecode of source file hosting the class */
public int bytecodeHash() {
return this.bytecodeHash;
}
public AnalyzedClass withCompilationTimestamp(long compilationTimestamp) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public AnalyzedClass withName(String name) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public AnalyzedClass withApi(xsbti.api.Lazy<Companions> api) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public AnalyzedClass withApiHash(int apiHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public AnalyzedClass withNameHashes(NameHash[] nameHashes) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public AnalyzedClass withHasMacro(boolean hasMacro) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public AnalyzedClass withExtraHash(int extraHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public AnalyzedClass withProvenance(String provenance) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public AnalyzedClass withBytecodeHash(int bytecodeHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
}
public boolean equals(Object obj) {
return this == obj; // We have lazy members, so use object identity to avoid circularity.
Expand Down
9 changes: 9 additions & 0 deletions internal/compiler-interface/src/main/contraband/other.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
"Combined with a way to tell if the provenance has changed,",
"it can be used to short-circuit the 'lookupAnalyzedClass' operation."
]
},
{
"name": "bytecodeHash",
"type": "int",
"default": "0",
"since": "1.11.0",
"doc": [
"A hash of generated bytecode of source file hosting the class"
]
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ object Analysis {

case class Sources(java: Set[String], scala: Set[String])

def computeBytecodeHash(
localProducts: Iterable[LocalProduct],
nonLocalProduct: Iterable[NonLocalProduct]
): Int = {
val hashes =
localProducts.map(_.classFileStamp.getHash) ++ nonLocalProduct.map(_.classFileStamp.getHash)
hashes.hashCode()
}

def sources(a: Analysis): Sources = {
def sourceFileForClass(className: String): VirtualFileRef =
a.relations.definesClass(className).headOption.getOrElse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import java.nio.file.{ Files, Path, Paths }
import java.{ util => ju }
import ju.{ EnumSet, Optional, UUID }
import ju.concurrent.atomic.AtomicBoolean
import sbt.internal.inc.Analysis.{ LocalProduct, NonLocalProduct }
import sbt.internal.inc.Analysis.{ LocalProduct, NonLocalProduct, computeBytecodeHash }
import sbt.internal.inc.JavaInterfaceUtil.EnrichOption
import sbt.util.{ InterfaceUtil, Level, Logger }
import sbt.util.InterfaceUtil.{ jl2l, jo2o, l2jl, t2 }
Expand Down Expand Up @@ -992,7 +992,7 @@ private final class AnalysisCallback(
}
}

private def analyzeClass(name: String): AnalyzedClass = {
private def analyzeClass(name: String, bytecodeHash: Int): AnalyzedClass = {
val hasMacro: Boolean = macroClasses.contains(name)
val (companions, apiHash, extraHash) = companionsWithHash(name)
val nameHashes = nameHashesForCompanions(name)
Expand All @@ -1018,7 +1018,6 @@ private final class AnalysisCallback(
.getOrElse(src, ConcurrentHashMap.newKeySet[(String, String)]())
.asScala
.map(_._1)
val analyzedApis = classesInSrc.map(analyzeClass)
val info = SourceInfos.makeInfo(
getOrNil(reporteds.iterator.map { case (k, v) => k -> v.asScala.toSeq }.toMap, src),
getOrNil(unreporteds.iterator.map { case (k, v) => k -> v.asScala.toSeq }.toMap, src),
Expand Down Expand Up @@ -1053,6 +1052,9 @@ private final class AnalysisCallback(
)
val libDeps = libraries.map(d => (d, binaryClassName(d), stampReader.library(d)))

val bytecodeHash = computeBytecodeHash(localProds, nonLocalProds)
val analyzedApis = classesInSrc.map(analyzeClass(_, bytecodeHash))

a.addSource(
src,
analyzedApis,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,15 +308,11 @@ private[inc] abstract class IncrementalCommon(
oldAPI: String => AnalyzedClass,
newAPI: String => AnalyzedClass
): APIChanges = {
// ignore timestamp pre-2020 since that likely means that we have a hardcoded 2010 timestamp
def timeStampIsSame(ts1: Long, ts2: Long): Boolean = {
(ts1 < TIMESTAMP_2020) || (ts2 < TIMESTAMP_2020) || (ts1 == ts2)
}
// log.debug(s"[zinc] detectAPIChanges(recompiledClasses = $recompiledClasses)")
def classDiff(className: String, a: AnalyzedClass, b: AnalyzedClass): Option[APIChange] = {
// log.debug(s"[zinc] classDiff($className, ${a.name}, ${b.name})")
if (
timeStampIsSame(a.compilationTimestamp(), b.compilationTimestamp()) && (a.apiHash == b.apiHash)
(a.bytecodeHash() == b.bytecodeHash()) && (a.apiHash == b.apiHash) && (a.extraHash == b.extraHash)
) None
else {
val hasMacro = a.hasMacro || b.hasMacro
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Compat._
class ConsistentAnalysisFormat(val mappers: ReadWriteMappers, sort: Boolean) {
import ConsistentAnalysisFormat._

private[this] final val VERSION = 1100029
private[this] final val VERSION = 1100030
private[this] final val readMapper = mappers.getReadMapper
private[this] final val writeMapper = mappers.getWriteMapper

Expand Down Expand Up @@ -145,6 +145,8 @@ class ConsistentAnalysisFormat(val mappers: ReadWriteMappers, sort: Boolean) {
out.int(ac.apiHash())
out.bool(ac.hasMacro)
out.string(ac.provenance())
out.int(ac.extraHash())
out.int(ac.bytecodeHash())
val nh0 = ac.nameHashes()
val nh = if (nh0.length > 1 && sort) {
val nh = nh0.clone()
Expand All @@ -169,6 +171,8 @@ class ConsistentAnalysisFormat(val mappers: ReadWriteMappers, sort: Boolean) {
val ah = in.int()
val hm = in.bool()
val p = in.string()
val eh = in.int()
val bh = in.int()
val nhNames = in.readStringArray()
val nhScopes = in.readArray[UseScope]() { UseScope.values()(in.byte().toInt) }
val nhHashes = in.readArray[Int]() { in.int() }
Expand All @@ -181,7 +185,7 @@ class ConsistentAnalysisFormat(val mappers: ReadWriteMappers, sort: Boolean) {
val comp =
if (storeApis) Companions.of(readClassLike(in), readClassLike(in))
else APIs.emptyCompanions
AnalyzedClass.of(ts, name, SafeLazyProxy.strict(comp), ah, nameHashes, hm, ah, p)
AnalyzedClass.of(ts, name, SafeLazyProxy.strict(comp), ah, nameHashes, hm, eh, p, bh)
}
}

Expand Down

0 comments on commit 4efdef3

Please sign in to comment.