Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce "provenance" to AnalyzedClass #786

Merged
merged 3 commits into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,20 @@ 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) {
return new AnalyzedClass(_compilationTimestamp, _name, _api, _apiHash, _nameHashes, _hasMacro, _extraHash);
}
public static AnalyzedClass create(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 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);
}
private long compilationTimestamp;
private String name;
private xsbti.api.Lazy<Companions> api;
private int apiHash;
private NameHash[] nameHashes;
private boolean hasMacro;
private int extraHash;
private String provenance;
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro) {
super();
compilationTimestamp = _compilationTimestamp;
Expand All @@ -34,6 +41,7 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
nameHashes = _nameHashes;
hasMacro = _hasMacro;
extraHash = apiHash;
provenance = "";
}
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash) {
super();
Expand All @@ -44,6 +52,18 @@ protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy
nameHashes = _nameHashes;
hasMacro = _hasMacro;
extraHash = _extraHash;
provenance = "";
}
protected AnalyzedClass(long _compilationTimestamp, String _name, xsbti.api.Lazy<Companions> _api, int _apiHash, NameHash[] _nameHashes, boolean _hasMacro, int _extraHash, String _provenance) {
super();
compilationTimestamp = _compilationTimestamp;
name = _name;
api = _api;
apiHash = _apiHash;
nameHashes = _nameHashes;
hasMacro = _hasMacro;
extraHash = _extraHash;
provenance = _provenance;
}

public long compilationTimestamp() {
Expand All @@ -67,26 +87,37 @@ public boolean hasMacro() {
public int extraHash() {
return this.extraHash;
}
/**
* An identifier of the 'provenance' of a class, such as the jar that contained it.
* Combined with a way to tell if the provenance has changed,
* it can be used to short-circuit the 'lookupAnalyzedClass' operation.
*/
public String provenance() {
return this.provenance;
}
public AnalyzedClass withCompilationTimestamp(long compilationTimestamp) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
}
public AnalyzedClass withName(String name) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
}
public AnalyzedClass withApi(xsbti.api.Lazy<Companions> api) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
}
public AnalyzedClass withApiHash(int apiHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
}
public AnalyzedClass withNameHashes(NameHash[] nameHashes) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
}
public AnalyzedClass withHasMacro(boolean hasMacro) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
}
public AnalyzedClass withExtraHash(int extraHash) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash);
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
}
public AnalyzedClass withProvenance(String provenance) {
return new AnalyzedClass(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance);
}
public boolean equals(Object obj) {
return this == obj; // We have lazy members, so use object identity to avoid circularity.
Expand Down
11 changes: 11 additions & 0 deletions internal/compiler-interface/src/main/contraband/other.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@
"type": "int",
"since": "1.2.0",
"default": "apiHash"
},
{
"name": "provenance",
"type": "String",
"since": "1.4.0",
"default": "\"\"",
"doc": [
"An identifier of the 'provenance' of a class, such as the jar that contained it.",
"Combined with a way to tell if the provenance has changed,",
"it can be used to short-circuit the 'lookupAnalyzedClass' operation."
]
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,20 @@
public class DefaultExternalHooks implements ExternalHooks {
private Optional<ExternalHooks.Lookup> lookup = Optional.empty();
private Optional<ClassFileManager> classFileManager = Optional.empty();
private GetProvenance getProvenance = NoProvenance.INSTANCE;

public DefaultExternalHooks(Optional<ExternalHooks.Lookup> lookup, Optional<ClassFileManager> classFileManager) {
public DefaultExternalHooks(
Optional<ExternalHooks.Lookup> lookup,
Optional<ClassFileManager> classFileManager,
GetProvenance getProvenance
) {
this.lookup = lookup;
this.classFileManager = classFileManager;
this.getProvenance = getProvenance;
}

public DefaultExternalHooks(Optional<ExternalHooks.Lookup> lookup, Optional<ClassFileManager> classFileManager) {
this(lookup, classFileManager, NoProvenance.INSTANCE);
}

@Override
Expand All @@ -32,17 +42,25 @@ public Optional<ClassFileManager> getExternalClassFileManager() {
return classFileManager;
}

@Override public GetProvenance getProvenance() { return getProvenance; }

@Override
public ExternalHooks withExternalClassFileManager(ClassFileManager externalClassFileManager) {
Optional<ClassFileManager> external = Optional.of(externalClassFileManager);
Optional<ClassFileManager> mixedManager = classFileManager.isPresent()
? Optional.of(WrappedClassFileManager.of(classFileManager.get(), external))
: external;
return new DefaultExternalHooks(lookup, mixedManager);
return new DefaultExternalHooks(lookup, mixedManager, getProvenance);
}

@Override
public ExternalHooks withExternalLookup(ExternalHooks.Lookup externalLookup) {
return new DefaultExternalHooks(Optional.of(externalLookup), classFileManager);
Optional<Lookup> externalLookup1 = Optional.of(externalLookup);
return new DefaultExternalHooks(externalLookup1, classFileManager, getProvenance);
}

@Override
public ExternalHooks withGetProvenance(GetProvenance getProvenance) {
return new DefaultExternalHooks(lookup, classFileManager, getProvenance);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

package xsbti.compile;

import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
import xsbti.VirtualFileRef;
Expand Down Expand Up @@ -60,6 +61,15 @@ interface Lookup {
Optional<FileHash[]> hashClasspath(VirtualFile[] classpath);
}

interface GetProvenance {
String get(Path path);
}

enum NoProvenance implements GetProvenance {
INSTANCE;
@Override public String get(Path path) { return ""; }
}

/**
* Returns the implementation of a lookup mechanism to be used instead of
* the internal lookup provided by the default implementation.
Expand All @@ -75,6 +85,8 @@ interface Lookup {
*/
Optional<ClassFileManager> getExternalClassFileManager();

default GetProvenance getProvenance() { return NoProvenance.INSTANCE; }

/**
* Returns an instance of hooks that executes the external passed class file manager.
*
Expand All @@ -93,4 +105,6 @@ interface Lookup {
* @return An instance of {@link ExternalHooks} with the specified lookup.
*/
ExternalHooks withExternalLookup(Lookup externalLookup);

default ExternalHooks withGetProvenance(GetProvenance getProvenance) { return this; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ private final class AnalysisCallback(
private[this] val compileStartTime: Long = System.currentTimeMillis()
private[this] val compilation: Compilation = Compilation(compileStartTime, output)

private val hooks = options.externalHooks
private val provenance = jo2o(output.getSingleOutput).fold("")(hooks.getProvenance.get(_)).intern

override def toString =
(List("Class APIs", "Object APIs", "Library deps", "Products", "Source deps") zip
List(classApis, objectApis, libraryDeps, nonLocalClasses, intSrcDeps))
Expand Down Expand Up @@ -581,7 +584,8 @@ private final class AnalysisCallback(
apiHash,
nameHashes,
hasMacro,
extraHash
extraHash,
provenance
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ trait Lookup extends ExternalLookup {
def lookupAnalysis(binaryClassName: String): Option[CompileAnalysis]

def lookupAnalyzedClass(binaryClassName: String): Option[AnalyzedClass] = {
// This is the default, slow, route, via Analysis; overridden in LookupImpl for the fast-track.
for {
analysis0 <- lookupAnalysis(binaryClassName)
analysis = analysis0 match { case a: Analysis => a }
Expand All @@ -71,6 +72,9 @@ trait ExternalLookup extends ExternalHooks.Lookup {
* Find the external `AnalyzedClass` (from another analysis) given a class name.
*
* @return The `AnalyzedClass` associated with the given class name, if one is found.
* Optional.empty() => Found class somewhere outside of project. No analysis possible.
* Optional.of(analyzed) if analyzed.provenance.isEmpty => Couldn't find it.
* Optional.of(analyzed) => good
*/
def lookupAnalyzedClass(binaryClassName: String): Option[AnalyzedClass]

Expand Down
1 change: 1 addition & 0 deletions internal/zinc-persist/src/main/protobuf/schema.proto
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ message AnalyzedClass {
repeated NameHash nameHashes = 5;
bool hasMacro = 6;
sint32 extraHash = 7;
string provenance = 8;
}

message APIs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ final class ProtobufReaders(mapper: ReadMapper, currentVersion: schema.Version)

import SafeLazyProxy.{ strict => mkLazy }
import ReadersFeedback.ExpectedCompanionsInAnalyzedClass
val compilationTimestamp = analyzedClass.compilationTimestamp
val compilationTs = analyzedClass.compilationTimestamp
val name = analyzedClass.name
val api =
if (!shouldStoreApis) EmptyLazyCompanions
Expand All @@ -575,7 +575,8 @@ final class ProtobufReaders(mapper: ReadMapper, currentVersion: schema.Version)
val extraHash = if (currentVersion == schema.Version.V1) 0 else analyzedClass.extraHash
val nameHashes = analyzedClass.nameHashes.toZincArray(fromNameHash)
val hasMacro = analyzedClass.hasMacro
AnalyzedClass.of(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro, extraHash)
val provenance = analyzedClass.provenance.intern
AnalyzedClass.of(compilationTs, name, api, apiHash, nameHashes, hasMacro, extraHash, provenance)
}

private final val stringId = identity[String] _
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -601,14 +601,16 @@ final class ProtobufWriters(mapper: WriteMapper) {
val hasMacro = analyzedClass.hasMacro
val name = analyzedClass.name()
val nameHashes = analyzedClass.nameHashes().toSchemaList(toNameHash)
val provenance = analyzedClass.provenance
schema.AnalyzedClass(
compilationTimestamp = compilationTimestamp,
name = name,
api = companions,
apiHash = apiHash,
nameHashes = nameHashes,
hasMacro = hasMacro,
extraHash = extraHash
extraHash = extraHash,
provenance = provenance
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ object AnalyzedClassFormats {
implicit ev0: Format[Compilation],
ev1: Format[NameHash]
): Format[AnalyzedClass] =
wrap[AnalyzedClass, (Long, String, Int, Array[NameHash], Boolean)](
a => (a.compilationTimestamp(), a.name, a.apiHash, a.nameHashes, a.hasMacro),
(x: (Long, String, Int, Array[NameHash], Boolean)) =>
wrap[AnalyzedClass, (Long, String, Int, Array[NameHash], Boolean, String)](
a => (a.compilationTimestamp(), a.name, a.apiHash, a.nameHashes, a.hasMacro, a.provenance),
(x: (Long, String, Int, Array[NameHash], Boolean, String)) =>
x match {
case (
compilationTimestamp: Long,
name: String,
apiHash: Int,
nameHashes: Array[NameHash],
hasMacro: Boolean
hasMacro: Boolean,
provenance: String
) =>
val _ = ev0.toString
AnalyzedClass.of(
Expand All @@ -42,7 +43,8 @@ object AnalyzedClassFormats {
apiHash,
nameHashes,
hasMacro,
apiHash
apiHash,
provenance
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ object AnalysisGenerators {
apiHash <- arbitrary[Int]
hasMacro <- arbitrary[Boolean]
nameHash <- genNameHash(name)
provenance <- arbitrary[String]
} yield {
AnalyzedClass.of(
startTime,
Expand All @@ -121,7 +122,8 @@ object AnalysisGenerators {
apiHash,
Array(nameHash),
hasMacro,
apiHash // The default is to use the public API hash
apiHash, // The default is to use the public API hash
provenance
)
}

Expand Down
9 changes: 7 additions & 2 deletions zinc/src/main/scala/sbt/internal/inc/LookupImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,13 @@ class LookupImpl(compileConfiguration: CompileConfiguration, previousSetup: Opti

override def lookupAnalyzedClass(binaryClassName: String): Option[AnalyzedClass] = {
externalLookup match { // not flatMap so that external lookup can fast-track returning None
case Some(externalLookup) => externalLookup.lookupAnalyzedClass(binaryClassName)
case _ => super.lookupAnalyzedClass(binaryClassName)
case Some(externalLookup) =>
externalLookup.lookupAnalyzedClass(binaryClassName) match {
case Some(api) if api.provenance.isEmpty => // found but w/o provenance, so go slow route
super.lookupAnalyzedClass(binaryClassName)
case x => x // fast-track success: either found w/ provenance or not found at all
}
case _ => super.lookupAnalyzedClass(binaryClassName)
}
}

Expand Down