Skip to content

Commit

Permalink
Lazily create BytecodeBoundClass objects
Browse files Browse the repository at this point in the history
Most of the classes on the classpath are never loaded, and `BytecodeBoundClass` has a relatively expensive constructor that creates a bunch of suppliers to try to defer work until it's needed. Putting yet another lazy supplier in front of the entire `BytecodeBoundClass` makes classes that are never loaded cheaper.

PiperOrigin-RevId: 690677848
  • Loading branch information
cushon authored and Javac Team committed Oct 28, 2024
1 parent 81d79e3 commit b7b5536
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 52 deletions.
51 changes: 14 additions & 37 deletions java/com/google/turbine/binder/ClassPathBinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package com.google.turbine.binder;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.bound.ModuleInfo;
import com.google.turbine.binder.bytecode.BytecodeBinder;
Expand All @@ -35,7 +34,6 @@
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;

/** Sets up an environment for symbols on the classpath. */
Expand All @@ -55,33 +53,31 @@ public final class ClassPathBinder {

/** Creates an environment containing symbols in the given classpath. */
public static ClassPath bindClasspath(Collection<Path> paths) throws IOException {
// TODO(cushon): this is going to require an env eventually,
// e.g. to look up type parameters in enclosing declarations
Map<ClassSymbol, BytecodeBoundClass> transitive = new LinkedHashMap<>();
Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>();
Map<ClassSymbol, Supplier<BytecodeBoundClass>> transitive = new LinkedHashMap<>();
Map<ClassSymbol, Supplier<BytecodeBoundClass>> map = new HashMap<>();
Map<ModuleSymbol, ModuleInfo> modules = new HashMap<>();
Map<String, Supplier<byte[]>> resources = new HashMap<>();
Env<ClassSymbol, BytecodeBoundClass> benv =
Env<ClassSymbol, BytecodeBoundClass> env =
new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
public @Nullable BytecodeBoundClass get(ClassSymbol sym) {
return map.get(sym);
Supplier<BytecodeBoundClass> supplier = map.get(sym);
return supplier == null ? null : supplier.get();
}
};
for (Path path : paths) {
try {
bindJar(path, map, modules, benv, transitive, resources);
bindJar(path, map, modules, env, transitive, resources);
} catch (IOException e) {
throw new IOException("error reading " + path, e);
}
}
for (Map.Entry<ClassSymbol, BytecodeBoundClass> entry : transitive.entrySet()) {
for (Map.Entry<ClassSymbol, Supplier<BytecodeBoundClass>> entry : transitive.entrySet()) {
ClassSymbol symbol = entry.getKey();
map.putIfAbsent(symbol, entry.getValue());
}
SimpleEnv<ClassSymbol, BytecodeBoundClass> env = new SimpleEnv<>(ImmutableMap.copyOf(map));
SimpleEnv<ModuleSymbol, ModuleInfo> moduleEnv = new SimpleEnv<>(ImmutableMap.copyOf(modules));
TopLevelIndex index = SimpleTopLevelIndex.of(env.asMap().keySet());
TopLevelIndex index = SimpleTopLevelIndex.of(map.keySet());
return new ClassPath() {
@Override
public Env<ClassSymbol, BytecodeBoundClass> env() {
Expand All @@ -107,10 +103,10 @@ public TopLevelIndex index() {

private static void bindJar(
Path path,
Map<ClassSymbol, BytecodeBoundClass> env,
Map<ClassSymbol, Supplier<BytecodeBoundClass>> env,
Map<ModuleSymbol, ModuleInfo> modules,
Env<ClassSymbol, BytecodeBoundClass> benv,
Map<ClassSymbol, BytecodeBoundClass> transitive,
Map<ClassSymbol, Supplier<BytecodeBoundClass>> transitive,
Map<String, Supplier<byte[]>> resources)
throws IOException {
// TODO(cushon): don't leak file descriptors
Expand All @@ -124,41 +120,22 @@ private static void bindJar(
new ClassSymbol(
name.substring(
TRANSITIVE_PREFIX.length(), name.length() - TRANSITIVE_SUFFIX.length()));
transitive.computeIfAbsent(
sym,
new Function<ClassSymbol, BytecodeBoundClass>() {
@Override
public BytecodeBoundClass apply(ClassSymbol sym) {
return new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, path.toString());
}
});
transitive.putIfAbsent(sym, BytecodeBoundClass.lazy(sym, ze, benv, path));
continue;
}
if (!name.endsWith(".class")) {
resources.put(name, toByteArrayOrDie(ze));
resources.put(name, ze);
continue;
}
if (name.substring(name.lastIndexOf('/') + 1).equals("module-info.class")) {
ModuleInfo moduleInfo =
BytecodeBinder.bindModuleInfo(path.toString(), toByteArrayOrDie(ze));
ModuleInfo moduleInfo = BytecodeBinder.bindModuleInfo(path.toString(), ze);
modules.put(new ModuleSymbol(moduleInfo.name()), moduleInfo);
continue;
}
ClassSymbol sym = new ClassSymbol(name.substring(0, name.length() - ".class".length()));
env.putIfAbsent(
sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, path.toString()));
env.putIfAbsent(sym, BytecodeBoundClass.lazy(sym, ze, benv, path));
}
}

private static Supplier<byte[]> toByteArrayOrDie(Zip.Entry ze) {
return Suppliers.memoize(
new Supplier<byte[]>() {
@Override
public byte[] get() {
return ze.data();
}
});
}

private ClassPathBinder() {}
}
16 changes: 2 additions & 14 deletions java/com/google/turbine/binder/CtSymClassBinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.bound.ModuleInfo;
import com.google.turbine.binder.bytecode.BytecodeBinder;
Expand Down Expand Up @@ -86,13 +85,12 @@ public final class CtSymClassBinder {
// JDK >= 12 includes the module name as a prefix
idx = name.indexOf('/', idx + 1);
if (name.substring(name.lastIndexOf('/') + 1).equals("module-info.sig")) {
ModuleInfo moduleInfo = BytecodeBinder.bindModuleInfo(name, toByteArrayOrDie(ze));
ModuleInfo moduleInfo = BytecodeBinder.bindModuleInfo(name, ze);
modules.put(new ModuleSymbol(moduleInfo.name()), moduleInfo);
continue;
}
ClassSymbol sym = new ClassSymbol(name.substring(idx + 1, name.length() - ".sig".length()));
map.putIfAbsent(
sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, ctSym + "!" + ze.name()));
map.putIfAbsent(sym, new BytecodeBoundClass(sym, ze, benv, ctSym + "!" + ze.name()));
}
if (map.isEmpty()) {
// we didn't find any classes for the desired release
Expand Down Expand Up @@ -124,16 +122,6 @@ public TopLevelIndex index() {
};
}

private static Supplier<byte[]> toByteArrayOrDie(Zip.Entry ze) {
return Suppliers.memoize(
new Supplier<byte[]>() {
@Override
public byte[] get() {
return ze.data();
}
});
}

// ct.sym contains directories whose names are the concatenation of a list of target versions
// formatted as a single character 0-9 or A-Z (e.g. 789A) and which contain interface class
// files with a .sig extension.
Expand Down
15 changes: 15 additions & 0 deletions java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import com.google.turbine.type.Type.ClassTy;
import com.google.turbine.type.Type.IntersectionTy;
import java.lang.annotation.RetentionPolicy;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import org.jspecify.annotations.Nullable;
Expand All @@ -72,6 +73,20 @@
*/
public class BytecodeBoundClass implements TypeBoundClass {

public static Supplier<BytecodeBoundClass> lazy(
ClassSymbol sym,
Supplier<byte[]> bytes,
Env<ClassSymbol, BytecodeBoundClass> env,
Path path) {
return Suppliers.memoize(
new Supplier<BytecodeBoundClass>() {
@Override
public BytecodeBoundClass get() {
return new BytecodeBoundClass(sym, bytes, env, path.toString());
}
});
}

private final ClassSymbol sym;
private final Env<ClassSymbol, BytecodeBoundClass> env;
private final Supplier<ClassFile> classFile;
Expand Down
8 changes: 7 additions & 1 deletion java/com/google/turbine/zip/Zip.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.base.Supplier;
import com.google.common.primitives.UnsignedInts;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
Expand Down Expand Up @@ -251,7 +252,7 @@ public void close() throws IOException {
}

/** An entry in a zip archive. */
public static class Entry {
public static class Entry implements Supplier<byte[]> {

private final Path path;
private final FileChannel chan;
Expand Down Expand Up @@ -351,6 +352,11 @@ private byte[] getBytes(
throw new IOError(e);
}
}

@Override
public byte[] get() {
return data();
}
}

static void checkSignature(
Expand Down

0 comments on commit b7b5536

Please sign in to comment.