Skip to content

Commit

Permalink
Add transitive extraBytecodeHash to handle subproject macro invalidation
Browse files Browse the repository at this point in the history
When upstream project macro is recompiled, we need to hash bytecodes the macro calls into
for child project to detect upstream macro API change.
  • Loading branch information
Friendseeker committed Oct 4, 2024
1 parent 4cd1b68 commit 8ac5c18
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ 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 create(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance, int _bytecodeHash, int _extraBytecodeHash) {
return new AnalyzedClass(_compilationTimestamp, _name, _api, _apiHash, _nameHashes, _hasMacro, _extraHash, _provenance, _bytecodeHash, _extraBytecodeHash);
}
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);
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, int _extraBytecodeHash) {
return new AnalyzedClass(_compilationTimestamp, _name, _api, _apiHash, _nameHashes, _hasMacro, _extraHash, _provenance, _bytecodeHash, _extraBytecodeHash);
}
private long compilationTimestamp;
private String name;
Expand All @@ -39,6 +39,7 @@ public static AnalyzedClass of(long _compilationTimestamp, String _name, xsbti.a
private int extraHash;
private String provenance;
private int bytecodeHash;
private int extraBytecodeHash;
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro) {
super();
compilationTimestamp = _compilationTimestamp;
Expand All @@ -50,6 +51,7 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
extraHash = apiHash;
provenance = "";
bytecodeHash = 0;
extraBytecodeHash = 0;
}
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash) {
super();
Expand All @@ -62,6 +64,7 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
extraHash = _extraHash;
provenance = "";
bytecodeHash = 0;
extraBytecodeHash = 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 @@ -74,8 +77,9 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
extraHash = _extraHash;
provenance = _provenance;
bytecodeHash = 0;
extraBytecodeHash = 0;
}
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance, int _bytecodeHash) {
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance, int _bytecodeHash, int _extraBytecodeHash) {
super();
compilationTimestamp = _compilationTimestamp;
name = _name;
Expand All @@ -86,6 +90,7 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
extraHash = _extraHash;
provenance = _provenance;
bytecodeHash = _bytecodeHash;
extraBytecodeHash = _extraBytecodeHash;
}

public long compilationTimestamp() {
Expand Down Expand Up @@ -121,32 +126,39 @@ public String provenance() {
public int bytecodeHash() {
return this.bytecodeHash;
}
/** A hash of generated bytecode of all upstream dependencies */
public int extraBytecodeHash() {
return this.extraBytecodeHash;
}
public AnalyzedClass withCompilationTimestamp(long compilationTimestamp) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withName(String name) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withApi(xsbti.api.Lazy<Companions> api) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withApiHash(int apiHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withNameHashes(NameHash[] nameHashes) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withHasMacro(boolean hasMacro) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withExtraHash(int extraHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withProvenance(String provenance) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withBytecodeHash(int bytecodeHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
public AnalyzedClass withExtraBytecodeHash(int extraBytecodeHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance, bytecodeHash, extraBytecodeHash);
}
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 @@ -37,6 +37,15 @@
"doc": [
"A hash of generated bytecode of source file hosting the class"
]
},
{
"name": "extraBytecodeHash",
"type": "int",
"default": "0",
"since": "1.11.0",
"doc": [
"A hash of generated bytecode of all upstream dependencies"
]
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ private final class AnalysisCallback(

private def getAnalysis: Analysis = {
val analysis0 = addProductsAndDeps(Analysis.empty)
addUsedNames(addCompilation(analysis0))
addUsedNames(addCompilation(addExtraBytecodeHash(analysis0)))
}

def getPostJavaAnalysis: Analysis = {
Expand All @@ -965,6 +965,24 @@ private final class AnalysisCallback(
)
}

private def addExtraBytecodeHash(base: Analysis): Analysis = {
import base.{ apis, relations }
val findUpstream = relations.memberRef.internal.forward _
val internalAPIs = apis.internal.map { case (className, analyzedClass) =>
if (!analyzedClass.hasMacro) {
(className, analyzedClass)
} else {
val upstreamClasses =
IncrementalCommon.transitiveDeps(Set(className), log, logging = false)(findUpstream)
val upstreamAnalyzedClasses = upstreamClasses.map(apis.internalAPI)
val upstreamHashes = upstreamAnalyzedClasses.map(_.bytecodeHash())
(className, analyzedClass.withExtraBytecodeHash(upstreamHashes.hashCode()))
}
}
val APIs = new MAPIs(internalAPIs, apis.external)
base.copy(apis = APIs)
}

private def companionsWithHash(className: String): (Companions, HashAPI.Hash, HashAPI.Hash) = {
val emptyHash = -1
val emptyClass =
Expand Down Expand Up @@ -1007,6 +1025,7 @@ private final class AnalysisCallback(
extraHash,
provenance,
bytecodeHash,
0,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ private[inc] abstract class IncrementalCommon(
options: IncOptions,
profiler: RunProfiler
) extends InvalidationProfilerUtils {
private final val TIMESTAMP_2020 = 1577854800000L

// Work around bugs in classpath handling such as the "currently" problematic -javabootclasspath
private[this] def enableShallowLookup: Boolean =
java.lang.Boolean.getBoolean("xsbt.skip.cp.lookup")
Expand Down Expand Up @@ -309,11 +307,16 @@ private[inc] abstract class IncrementalCommon(
newAPI: String => AnalyzedClass
): APIChanges = {
// log.debug(s"[zinc] detectAPIChanges(recompiledClasses = $recompiledClasses)")
def hashesMatch(a: AnalyzedClass, b: AnalyzedClass, hasMacro: Boolean): Boolean = {
(a.bytecodeHash() == b.bytecodeHash()) &&
(a.apiHash == b.apiHash) &&
(a.extraHash == b.extraHash) &&
(!hasMacro || a.extraBytecodeHash() == b.extraBytecodeHash())
}
def classDiff(className: String, a: AnalyzedClass, b: AnalyzedClass): Option[APIChange] = {
// log.debug(s"[zinc] classDiff($className, ${a.name}, ${b.name})")
if (
(a.bytecodeHash() == b.bytecodeHash()) && (a.apiHash == b.apiHash) && (a.extraHash == b.extraHash)
) None
val hasMacro = a.hasMacro || b.hasMacro
if (hashesMatch(a, b, hasMacro)) None
else {
val hasMacro = a.hasMacro || b.hasMacro
if (hasMacro && IncOptions.getRecompileOnMacroDef(options)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class ConsistentAnalysisFormat(val mappers: ReadWriteMappers, sort: Boolean) {
out.string(ac.provenance())
out.int(ac.extraHash())
out.int(ac.bytecodeHash())
out.int(ac.extraBytecodeHash())
val nh0 = ac.nameHashes()
val nh = if (nh0.length > 1 && sort) {
val nh = nh0.clone()
Expand All @@ -173,6 +174,7 @@ class ConsistentAnalysisFormat(val mappers: ReadWriteMappers, sort: Boolean) {
val p = in.string()
val eh = in.int()
val bh = in.int()
val ebh = 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 @@ -185,7 +187,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, eh, p, bh)
AnalyzedClass.of(ts, name, SafeLazyProxy.strict(comp), ah, nameHashes, hm, eh, p, bh, ebh)
}
}

Expand Down

0 comments on commit 8ac5c18

Please sign in to comment.