Skip to content

Commit

Permalink
Cache is a final class
Browse files Browse the repository at this point in the history
  • Loading branch information
JaroslavTulach committed Feb 2, 2024
1 parent b7fd968 commit e570e37
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 90 deletions.
99 changes: 36 additions & 63 deletions engine/runtime/src/main/java/org/enso/interpreter/caches/Cache.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,10 @@
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.logger.masking.MaskedPath;
import org.enso.pkg.SourceFile;
import org.enso.text.Hex;

/**
* Cache encapsulates a common functionality needed to serialize and de-serialize objects, while
Expand All @@ -29,14 +23,14 @@
* @param <T> type of the cached data
* @param <M> type of the metadata associated with the data
*/
public class Cache<T, M extends Cache.Metadata> {
public final class Cache<T, M extends Cache.Metadata> {
private final Object LOCK = new Object();

/** implementation of the serialize/deserialize operations */
protected /* final & private */ Spi<T, M> spi;
private final Spi<T, M> spi;

/** Returns a default level of logging for this Cache. */
protected final Level logLevel;
private final Level logLevel;

/** Log name to use in log messages */
private final String logName;
Expand Down Expand Up @@ -64,16 +58,39 @@ public class Cache<T, M extends Cache.Metadata> {
* @param needsDataDigestVerification Flag indicating if the de-serialization process should
* compute the hash of the stored cache and compare it with the stored metadata entry.
*/
protected Cache(
private Cache(
Cache.Spi<T, M> spi,
Level logLevel,
String logName,
boolean needsSourceDigestVerification,
boolean needsDataDigestVerification) {
this.spi = spi;
this.logLevel = logLevel;
this.logName = logName;
this.needsDataDigestVerification = needsDataDigestVerification;
this.needsSourceDigestVerification = needsSourceDigestVerification;
this.spi = null;
}

/**
* Factory method to create new cache instance.
*
* @param spi the implementation logic of the cache
* @param logLevel logging level
* @param logName name to use in logs
* @param needsSourceDigestVerification Flag indicating if the de-serialization process should
* compute the hash of the sources from which the cache was created and compare it with the
* stored metadata entry.
* @param needsDataDigestVerification Flag indicating if the de-serialization process should
* compute the hash of the stored cache and compare it with the stored metadata entry.
*/
static <T, M extends Cache.Metadata> Cache<T, M> create(
Cache.Spi<T, M> spi,
Level logLevel,
String logName,
boolean needsSourceDigestVerification,
boolean needsDataDigestVerification) {
return new Cache<>(
spi, logLevel, logName, needsSourceDigestVerification, needsDataDigestVerification);
}

/**
Expand Down Expand Up @@ -133,7 +150,7 @@ private boolean saveCacheTo(
if (ensureRoot(cacheRoot)) {
byte[] bytesToWrite = spi.serialize(context, entry);

String blobDigest = computeDigestFromBytes(ByteBuffer.wrap(bytesToWrite));
String blobDigest = CacheUtils.computeDigestFromBytes(ByteBuffer.wrap(bytesToWrite));
String sourceDigest = spi.computeDigest(entry, logger).get();
if (sourceDigest == null) {
throw new ClassNotFoundException("unable to compute digest");
Expand Down Expand Up @@ -259,7 +276,8 @@ private Optional<T> loadCacheFrom(
blobBytes = ByteBuffer.wrap(dataPath.readAllBytes());
}
boolean blobDigestValid =
!needsDataDigestVerification || computeDigestFromBytes(blobBytes).equals(meta.blobHash());
!needsDataDigestVerification
|| CacheUtils.computeDigestFromBytes(blobBytes).equals(meta.blobHash());

if (sourceDigestValid && blobDigestValid) {
T cachedObject = null;
Expand Down Expand Up @@ -317,55 +335,6 @@ private Optional<M> loadCacheMetadata(TruffleFile path, TruffleLogger logger) th
}
}

/**
* Computes digest from an array of bytes using a default hashing algorithm.
*
* @param bytes bytes for which hash will be computed
* @return string representation of bytes' hash
*/
protected final String computeDigestFromBytes(ByteBuffer bytes) {
var sha = messageDigest();
sha.update(bytes);
return Hex.toHexString(sha.digest());
}

/**
* Computes digest from package sources using a default hashing algorithm.
*
* @param pkgSources the list of package sources
* @param logger the truffle logger
* @return string representation of bytes' hash
*/
protected final String computeDigestOfLibrarySources(
List<SourceFile<TruffleFile>> pkgSources, TruffleLogger logger) {
pkgSources.sort(Comparator.comparing(o -> o.qualifiedName().toString()));

var digest = messageDigest();
pkgSources.forEach(
source -> {
try {
digest.update(source.file().readAllBytes());
} catch (IOException e) {
logger.log(
logLevel, "failed to compute digest for " + source.qualifiedName().toString(), e);
}
});
return Hex.toHexString(digest.digest());
}

/**
* Returns a default hashing algorithm used for Enso caches.
*
* @return digest used for computing hashes
*/
protected MessageDigest messageDigest() {
try {
return MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException("Unreachable", ex);
}
}

/**
* Gets the path to the cache data within the `cacheRoot`.
*
Expand Down Expand Up @@ -473,7 +442,11 @@ private static MaskedPath toMaskedPath(TruffleFile truffleFile) {
return new MaskedPath(Path.of(truffleFile.getPath()));
}

/** Set of methods to be implemented by those who want to cache something. */
/**
* Set of methods to be implemented by those who want to cache something.
*
* @param <T>
*/
public static interface Spi<T, M> {
/**
* Deserializes and validates data by returning the expected cached entry, or {@code null}.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package org.enso.interpreter.caches;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;

import org.enso.compiler.context.CompilerContext;
import org.enso.compiler.core.ir.ProcessingPass;
import org.enso.pkg.SourceFile;
import org.enso.text.Hex;

import com.oracle.truffle.api.TruffleFile;

final class CacheUtils {
private CacheUtils() {
Expand Down Expand Up @@ -35,6 +44,53 @@ static Function<Object, Object> readResolve(CompilerContext context) {
};
}

/**
* Returns a default hashing algorithm used for Enso caches.
*
* @return digest used for computing hashes
*/
private static MessageDigest messageDigest() {
try {
return MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException ex) {
throw raise(RuntimeException.class, ex);
}
}

/**
* Computes digest from an array of bytes using a default hashing algorithm.
*
* @param bytes bytes for which hash will be computed
* @return string representation of bytes' hash
*/
static String computeDigestFromBytes(ByteBuffer bytes) {
var sha = messageDigest();
sha.update(bytes);
return Hex.toHexString(sha.digest());
}

/**
* Computes digest from package sources using a default hashing algorithm.
*
* @param pkgSources the list of package sources
* @return string representation of bytes' hash
*/
static final String computeDigestOfLibrarySources(
List<SourceFile<TruffleFile>> pkgSources
) {
pkgSources.sort(Comparator.comparing(o -> o.qualifiedName().toString()));

try {
var digest = messageDigest();
for (var source : pkgSources) {
digest.update(source.file().readAllBytes());
}
return Hex.toHexString(digest.digest());
} catch (IOException ex) {
throw raise(RuntimeException.class, ex);
}
}

@SuppressWarnings("unchecked")
static <T extends Exception> T raise(Class<T> cls, Exception e) throws T {
throw (T) e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,22 @@
import org.openide.util.lookup.ServiceProvider;

@Persistable(clazz = QualifiedName.class, id = 30300)
public final class ImportExportCache
extends Cache<ImportExportCache.CachedBindings, ImportExportCache.Metadata> {
public final class ImportExportCache {

private final LibraryName libraryName;

private ImportExportCache(LibraryName libraryName) {
super(Level.FINEST, libraryName.toString(), true, false);
this.spi = new Impl();
this.libraryName = libraryName;
}

public static Cache<ImportExportCache.CachedBindings, ImportExportCache.Metadata> create(
LibraryName libraryName) {
return new ImportExportCache(libraryName);
var impl = new ImportExportCache(libraryName).new Impl();
return Cache.create(impl, Level.FINEST, libraryName.toString(), true, false);
}

private final class Impl
implements Spi<ImportExportCache.CachedBindings, ImportExportCache.Metadata> {
implements Cache.Spi<ImportExportCache.CachedBindings, ImportExportCache.Metadata> {

@Override
public String metadataSuffix() {
Expand Down Expand Up @@ -90,7 +88,7 @@ public Optional<Metadata> metadataFromBytes(byte[] bytes, TruffleLogger logger)

@Override
public Optional<String> computeDigest(CachedBindings entry, TruffleLogger logger) {
return entry.sources().map(sources -> computeDigestOfLibrarySources(sources, logger));
return entry.sources().map(sources -> CacheUtils.computeDigestOfLibrarySources(sources));
}

@Override
Expand All @@ -99,7 +97,7 @@ public Optional<String> computeDigestFromSource(EnsoContext context, TruffleLogg
return context
.getPackageRepository()
.getPackageForLibraryJava(libraryName)
.map(pkg -> computeDigestOfLibrarySources(pkg.listSourcesJava(), logger));
.map(pkg -> CacheUtils.computeDigestOfLibrarySources(pkg.listSourcesJava()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,26 @@
import org.enso.persist.Persistance;
import org.enso.polyglot.CompilationStage;

public final class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCache.Metadata> {

public final class ModuleCache {
private final org.enso.interpreter.runtime.Module module;
private final Impl spi;

private ModuleCache(org.enso.interpreter.runtime.Module module) {
super(Level.FINEST, module.getName().toString(), true, false);
this.spi = new Impl();
this.module = module;
}

public static Cache<ModuleCache.CachedModule, ModuleCache.Metadata> create(
org.enso.interpreter.runtime.Module module) {
return new ModuleCache(module);
var mc = new ModuleCache(module);
return Cache.create(mc.spi, Level.FINEST, module.getName().toString(), true, false);
}

static ModuleCache.Impl find(Cache<?, ?> c) {
var mc = (ModuleCache) c;
return (ModuleCache.Impl) mc.spi;
throw new IllegalStateException();
}

final class Impl implements Spi<ModuleCache.CachedModule, ModuleCache.Metadata> {
final class Impl implements Cache.Spi<ModuleCache.CachedModule, ModuleCache.Metadata> {

@Override
public String metadataSuffix() {
Expand Down Expand Up @@ -96,7 +95,7 @@ private Optional<String> computeDigestOfModuleSources(Source source) {
} else {
sourceBytes = source.getCharacters().toString().getBytes(StandardCharsets.UTF_8);
}
return Optional.of(computeDigestFromBytes(ByteBuffer.wrap(sourceBytes)));
return Optional.of(CacheUtils.computeDigestFromBytes(ByteBuffer.wrap(sourceBytes)));
} else {
return Optional.empty();
}
Expand All @@ -112,7 +111,7 @@ public Optional<String> computeDigestFromSource(EnsoContext context, TruffleLogg
try {
return computeDigestOfModuleSources(module.getSource());
} catch (IOException e) {
logger.log(logLevel, "failed to retrieve the source of " + module.getName(), e);
logger.log(Level.FINEST, "failed to retrieve the source of " + module.getName(), e);
return Optional.empty();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,24 @@
import org.enso.pkg.SourceFile;
import org.enso.polyglot.Suggestion;

public final class SuggestionsCache
extends Cache<SuggestionsCache.CachedSuggestions, SuggestionsCache.Metadata> {

public final class SuggestionsCache {
private static final String SUGGESTIONS_CACHE_DATA_EXTENSION = ".suggestions";
private static final String SUGGESTIONS_CACHE_METADATA_EXTENSION = ".suggestions.meta";

final LibraryName libraryName;

private SuggestionsCache(LibraryName libraryName) {
super(Level.FINEST, libraryName.toString(), true, false);
this.spi = new Impl();
this.libraryName = libraryName;
}

public static Cache<SuggestionsCache.CachedSuggestions, SuggestionsCache.Metadata> create(
LibraryName libraryName) {
return new SuggestionsCache(libraryName);
var impl = new SuggestionsCache(libraryName).new Impl();
return Cache.create(impl, Level.FINEST, libraryName.toString(), true, false);
}

private final class Impl
implements Spi<SuggestionsCache.CachedSuggestions, SuggestionsCache.Metadata> {
implements Cache.Spi<SuggestionsCache.CachedSuggestions, SuggestionsCache.Metadata> {

@Override
public String metadataSuffix() {
Expand Down Expand Up @@ -86,19 +83,19 @@ public Optional<Metadata> metadataFromBytes(byte[] bytes, TruffleLogger logger)

@Override
public Optional<String> computeDigest(CachedSuggestions entry, TruffleLogger logger) {
return entry.getSources().map(sources -> computeDigestOfLibrarySources(sources, logger));
return entry.getSources().map(sources -> CacheUtils.computeDigestOfLibrarySources(sources));
}

@Override
public Optional<String> computeDigestFromSource(EnsoContext context, TruffleLogger logger) {
return context
.getPackageRepository()
.getPackageForLibraryJava(libraryName)
.map(pkg -> computeDigestOfLibrarySources(pkg.listSourcesJava(), logger));
.map(pkg -> CacheUtils.computeDigestOfLibrarySources(pkg.listSourcesJava()));
}

@Override
public Optional<Roots> getCacheRoots(EnsoContext context) {
public Optional<Cache.Roots> getCacheRoots(EnsoContext context) {
return context
.getPackageRepository()
.getPackageForLibraryJava(libraryName)
Expand Down

0 comments on commit e570e37

Please sign in to comment.