Merge "Removed DexClassPromise and LazyClassFileLoader."
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index 3497778..85c430a 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -5,13 +5,12 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.OutputMode;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OutputMode;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
-import java.util.List;
abstract class BaseCommand {
@@ -147,7 +146,7 @@
}
/** Add library file resources. */
- public B addLibraryFiles(List<Path> files) throws IOException {
+ public B addLibraryFiles(Collection<Path> files) throws IOException {
app.addLibraryFiles(files);
return self();
}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 0ad793c..ceefac5 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -3,15 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.FileUtils.isArchive;
+
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OffOrAuto;
import com.android.tools.r8.utils.OutputMode;
+import com.android.tools.r8.utils.PreloadedResourceProvider;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
/**
* Immutable command structure for an invocation of the {@code D8} compiler.
@@ -39,6 +44,40 @@
super(app, CompilationMode.DEBUG);
}
+ /** Add classpath file resources. */
+ public Builder addClasspathFiles(Path... files) throws IOException {
+ return addClasspathFiles(Arrays.asList(files));
+ }
+
+ /** Add classpath file resources. */
+ public Builder addClasspathFiles(Collection<Path> files) throws IOException {
+ for (Path file : files) {
+ if (isArchive(file)) {
+ addClasspathResourceProvider(PreloadedResourceProvider.fromArchive(file));
+ } else {
+ super.addClasspathFiles(file);
+ }
+ }
+ return this;
+ }
+
+ /** Add library file resources. */
+ public Builder addLibraryFiles(Path... files) throws IOException {
+ return addLibraryFiles(Arrays.asList(files));
+ }
+
+ /** Add library file resources. */
+ public Builder addLibraryFiles(Collection<Path> files) throws IOException {
+ for (Path file : files) {
+ if (isArchive(file)) {
+ addLibraryResourceProvider(PreloadedResourceProvider.fromArchive(file));
+ } else {
+ super.addLibraryFiles(file);
+ }
+ }
+ return this;
+ }
+
public Builder addClasspathResourceProvider(ResourceProvider provider) {
getAppBuilder().addClasspathResourceProvider(provider);
return this;
@@ -171,8 +210,6 @@
internal.removeSwitchMaps = false;
assert internal.outline.enabled;
internal.outline.enabled = false;
- internal.lazyClasspathLoading = true;
- internal.lazyLibraryLoading = true;
internal.outputMode = getOutputMode();
return internal;
}
diff --git a/src/main/java/com/android/tools/r8/ResourceProvider.java b/src/main/java/com/android/tools/r8/ResourceProvider.java
index 2a5b0dc..bed36e3 100644
--- a/src/main/java/com/android/tools/r8/ResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ResourceProvider.java
@@ -5,13 +5,22 @@
/**
* Represents a provider for application resources. All resources returned
- * via this provider should be class file resources and will be loaded on-demand
- * or not loaded at all.
+ * via this provider should be class file resources, other resource kinds
+ * are not yet supported.
*
- * NOTE: currently only resources representing Java class files can be loaded
- * with lazy resource providers.
+ * Note that the classes will only be created for resources provided by
+ * resource providers on-demand when they are needed by the tool. If
+ * never needed, the resource will never be loaded.
*/
public interface ResourceProvider {
- /** Get the class resource associated with the descriptor, or null. */
+ // TODO: Consider adding support for DEX resources.
+
+ /**
+ * Get the class resource associated with the descriptor, or null if
+ * this provider does not have one.
+ *
+ * Method may be called several times for the same resource, and should
+ * support concurrent calls from different threads.
+ */
Resource getResource(String descriptor);
}
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectState.java b/src/main/java/com/android/tools/r8/bisect/BisectState.java
index 945578a..183e513 100644
--- a/src/main/java/com/android/tools/r8/bisect/BisectState.java
+++ b/src/main/java/com/android/tools/r8/bisect/BisectState.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexApplication.Builder;
-import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -261,7 +261,7 @@
System.out.println("Next bisection range: " + nextRange);
int goodClasses = 0;
int badClasses = 0;
- Map<DexType, DexClassPromise> classes = new HashMap<>();
+ Map<DexType, DexClass> classes = new HashMap<>();
for (DexLibraryClass clazz : badApp.libraryClasses()) {
classes.put(clazz.type, clazz);
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index af6bebe..208d0f8 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -16,10 +16,8 @@
import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.JarClassFileReader;
-import com.android.tools.r8.graph.LazyClassFileLoader;
import com.android.tools.r8.naming.ProguardMapReader;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -32,7 +30,6 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -95,18 +92,10 @@
reader.read(DEFAULT_DEX_FILENAME, ClassKind.PROGRAM, input.getStream(closer));
}
for (Resource input : inputApp.getClassClasspathResources()) {
- if (options.lazyClasspathLoading && getResourceClassDescriptorOrNull(input) != null) {
- addLazyLoader(application, ClassKind.CLASSPATH, builder, input);
- } else {
- reader.read(DEFAULT_DEX_FILENAME, ClassKind.CLASSPATH, input.getStream(closer));
- }
+ reader.read(DEFAULT_DEX_FILENAME, ClassKind.CLASSPATH, input.getStream(closer));
}
for (Resource input : inputApp.getClassLibraryResources()) {
- if (options.lazyLibraryLoading && getResourceClassDescriptorOrNull(input) != null) {
- addLazyLoader(application, ClassKind.LIBRARY, builder, input);
- } else {
- reader.read(DEFAULT_DEX_FILENAME, ClassKind.LIBRARY, input.getStream(closer));
- }
+ reader.read(DEFAULT_DEX_FILENAME, ClassKind.LIBRARY, input.getStream(closer));
}
}
@@ -119,16 +108,6 @@
}
}
- private void addLazyLoader(JarApplicationReader application,
- ClassKind classKind, DexApplication.Builder builder, Resource resource) {
- // Generate expected DEX type.
- String classDescriptor = getResourceClassDescriptorOrNull(resource);
- assert classDescriptor != null;
- DexType type = options.itemFactory.createType(classDescriptor);
- LazyClassFileLoader newLoader = new LazyClassFileLoader(type, resource, classKind, application);
- builder.addClassPromise(newLoader, true);
- }
-
private void readDexSources(DexApplication.Builder builder, ExecutorService executorService,
List<Future<?>> futures, Closer closer)
throws IOException, ExecutionException {
@@ -162,7 +141,7 @@
for (DexFileReader reader : fileReaders) {
futures.add(executorService.submit(() -> {
reader.addCodeItemsTo(); // Depends on Everything for parsing.
- reader.addClassDefsTo(builder::addClassPromise); // Depends on Methods, Code items etc.
+ reader.addClassDefsTo(builder::addClass); // Depends on Methods, Code items etc.
}));
}
}
@@ -231,10 +210,4 @@
}));
}
}
-
- private static String getResourceClassDescriptorOrNull(Resource resource) {
- Set<String> descriptors = resource.getClassDescriptors();
- return (descriptors == null) || (descriptors.size() != 1)
- ? null : descriptors.iterator().next();
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index a558b3e..a5c8ff8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -40,9 +40,8 @@
private Map<Descriptor, KeyedDexItem> computeDefinitions(DexType type) {
Builder<Descriptor, KeyedDexItem> builder = ImmutableMap.builder();
- DexClassPromise promise = app.definitionFor(type);
- if (promise != null) {
- DexClass clazz = promise.get();
+ DexClass clazz = app.definitionFor(type);
+ if (clazz != null) {
registerDefinitions(builder, clazz.directMethods());
registerDefinitions(builder, clazz.virtualMethods());
registerDefinitions(builder, clazz.instanceFields());
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 9330fdd..73c0533 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -84,22 +84,17 @@
}
}
- private void populateSubtypeMap(
- Map<DexType, DexClassPromise> classes, DexItemFactory dexItemFactory) {
+ private void populateSubtypeMap(Map<DexType, DexClass> classes, DexItemFactory dexItemFactory) {
dexItemFactory.clearSubtypeInformation();
dexItemFactory.objectType.tagAsSubtypeRoot();
Hashtable<DexType, Set<DexType>> map = new Hashtable<>();
- Function<DexType, DexClass> typeToClass = type -> {
- DexClassPromise promise = classes.get(type);
- return promise == null ? null : promise.get();
- };
- for (Map.Entry<DexType, DexClassPromise> entry : classes.entrySet()) {
- populateAllSuperTypes(map, entry.getKey(), entry.getValue().get(), typeToClass);
+ for (Map.Entry<DexType, DexClass> entry : classes.entrySet()) {
+ populateAllSuperTypes(map, entry.getKey(), entry.getValue(), classes::get);
}
for (Map.Entry<DexType, Set<DexType>> entry : map.entrySet()) {
subtypeMap.put(entry.getKey(), ImmutableSet.copyOf(entry.getValue()));
}
- assert DexType.validateLevelsAreCorrect(typeToClass, dexItemFactory);
+ assert DexType.validateLevelsAreCorrect(classes::get, dexItemFactory);
}
// For mapping invoke virtual instruction to target methods.
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index acbc205..33849e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -34,8 +34,8 @@
public class DexApplication {
- // Maps type into class promise, may be used concurrently.
- private final ImmutableMap<DexType, DexClassPromise> classMap;
+ // Maps type into class, may be used concurrently.
+ private final ImmutableMap<DexType, DexClass> classMap;
// Lazily loaded classes.
//
@@ -60,7 +60,7 @@
/** Constructor should only be invoked by the DexApplication.Builder. */
private DexApplication(
ClassNameMapper proguardMap,
- ImmutableMap<DexType, DexClassPromise> classMap,
+ ImmutableMap<DexType, DexClass> classMap,
LazyClassCollection lazyClassCollection,
ImmutableSet<DexType> mainDexList,
DexItemFactory dexItemFactory,
@@ -75,7 +75,7 @@
this.timing = timing;
}
- ImmutableMap<DexType, DexClassPromise> getClassMap() {
+ ImmutableMap<DexType, DexClass> getClassMap() {
assert lazyClassCollection == null : "Only allowed in non-lazy scenarios.";
return classMap;
}
@@ -84,9 +84,9 @@
List<DexProgramClass> result = new ArrayList<>();
// Note: we ignore lazy class collection because it
// is never supposed to be used for program classes.
- for (DexClassPromise promise : classMap.values()) {
- if (promise.isProgramClass()) {
- result.add(promise.get().asProgramClass());
+ for (DexClass clazz : classMap.values()) {
+ if (clazz.isProgramClass()) {
+ result.add(clazz.asProgramClass());
}
}
return result;
@@ -95,28 +95,28 @@
public Iterable<DexLibraryClass> libraryClasses() {
assert lazyClassCollection == null : "Only allowed in non-lazy scenarios.";
List<DexLibraryClass> result = new ArrayList<>();
- for (DexClassPromise promise : classMap.values()) {
- if (promise.isLibraryClass()) {
- result.add(promise.get().asLibraryClass());
+ for (DexClass clazz : classMap.values()) {
+ if (clazz.isLibraryClass()) {
+ result.add(clazz.asLibraryClass());
}
}
return result;
}
public DexClass definitionFor(DexType type) {
- DexClassPromise promise = classMap.get(type);
+ DexClass clazz = classMap.get(type);
// In presence of lazy class collection we also reach out to it
// as well unless the class found is already a program class.
- if (lazyClassCollection != null && (promise == null || !promise.isProgramClass())) {
- promise = lazyClassCollection.get(type, promise);
+ if (lazyClassCollection != null && (clazz == null || !clazz.isProgramClass())) {
+ clazz = lazyClassCollection.get(type, clazz);
}
- return promise == null ? null : promise.get();
+ return clazz;
}
public DexProgramClass programDefinitionFor(DexType type) {
- DexClassPromise promise = classMap.get(type);
+ DexClass clazz = classMap.get(type);
// Don't bother about lazy class collection, it should never load program classes.
- return (promise == null || !promise.isProgramClass()) ? null : promise.get().asProgramClass();
+ return (clazz == null || !clazz.isProgramClass()) ? null : clazz.asProgramClass();
}
public String toString() {
@@ -239,7 +239,7 @@
public static class Builder {
- private final Hashtable<DexType, DexClassPromise> classMap = new Hashtable<>();
+ private final Hashtable<DexType, DexClass> classMap = new Hashtable<>();
private LazyClassCollection lazyClassCollection;
public final Hashtable<DexCode, DexCode> codeItems = new Hashtable<>();
@@ -261,7 +261,7 @@
this(application, application.classMap);
}
- public Builder(DexApplication application, Map<DexType, DexClassPromise> classMap) {
+ public Builder(DexApplication application, Map<DexType, DexClass> classMap) {
this.classMap.putAll(classMap);
this.lazyClassCollection = application.lazyClassCollection;
proguardMap = application.proguardMap;
@@ -282,8 +282,8 @@
return this;
}
- public Builder addClassPromise(DexClassPromise promise) {
- addClassPromise(promise, false);
+ public Builder addClass(DexClass clazz) {
+ addClass(clazz, false);
return this;
}
@@ -299,7 +299,7 @@
public Builder addSynthesizedClass(DexProgramClass synthesizedClass, boolean addToMainDexList) {
assert synthesizedClass.isProgramClass() : "All synthesized classes must be program classes";
- addClassPromise(synthesizedClass);
+ addClass(synthesizedClass);
if (addToMainDexList && !mainDexList.isEmpty()) {
mainDexList.add(synthesizedClass.type);
}
@@ -310,28 +310,24 @@
List<DexProgramClass> result = new ArrayList<>();
// Note: we ignore lazy class collection because it
// is never supposed to be used for program classes.
- for (DexClassPromise promise : classMap.values()) {
- if (promise.isProgramClass()) {
- result.add(promise.get().asProgramClass());
+ for (DexClass clazz : classMap.values()) {
+ if (clazz.isProgramClass()) {
+ result.add(clazz.asProgramClass());
}
}
return result;
}
// Callback from FileReader when parsing a DexProgramClass (multi-threaded).
- private void addClass(DexClass clazz, boolean skipLibDups) {
- addClassPromise(clazz, skipLibDups);
- }
-
- public synchronized void addClassPromise(DexClassPromise promise, boolean skipLibDups) {
- assert promise != null;
- DexType type = promise.getType();
- DexClassPromise oldPromise = classMap.get(type);
- if (oldPromise != null) {
- promise = chooseClass(promise, oldPromise, skipLibDups);
+ public synchronized void addClass(DexClass newClass, boolean skipLibDups) {
+ assert newClass != null;
+ DexType type = newClass.type;
+ DexClass oldClass = classMap.get(type);
+ if (oldClass != null) {
+ newClass = chooseClass(newClass, oldClass, skipLibDups);
}
- if (oldPromise != promise) {
- classMap.put(type, promise);
+ if (oldClass != newClass) {
+ classMap.put(type, newClass);
}
}
@@ -352,53 +348,49 @@
}
}
- public static DexClassPromise chooseClass(
- DexClassPromise a, DexClassPromise b, boolean skipLibDups) {
+ public static DexClass chooseClass(DexClass a, DexClass b, boolean skipLibDups) {
// NOTE: We assume that there should not be any conflicting names in user defined
// classes and/or linked jars. If we ever want to allow 'keep first'-like policy
// to resolve this kind of conflict between program and/or classpath classes, we'll
// need to make sure we choose the class we keep deterministically.
if (a.isProgramClass() && b.isProgramClass()) {
- if (allowProgramClassConflict(a, b)) {
+ if (allowProgramClassConflict(a.asProgramClass(), b.asProgramClass())) {
return a;
}
- throw new CompilationError(
- "Program type already present: " + a.getType().toSourceString());
+ throw new CompilationError("Program type already present: " + a.type.toSourceString());
}
if (a.isProgramClass()) {
- return chooseBetweenProgramAndOtherClass(a, b);
+ return chooseClass(a.asProgramClass(), b);
}
if (b.isProgramClass()) {
- return chooseBetweenProgramAndOtherClass(b, a);
+ return chooseClass(b.asProgramClass(), a);
}
if (a.isClasspathClass() && b.isClasspathClass()) {
- throw new CompilationError(
- "Classpath type already present: " + a.getType().toSourceString());
+ throw new CompilationError("Classpath type already present: " + a.type.toSourceString());
}
if (a.isClasspathClass()) {
- return chooseBetweenClasspathAndLibraryClass(a, b);
+ return chooseClass(a.asClasspathClass(), b.asLibraryClass());
}
if (b.isClasspathClass()) {
- return chooseBetweenClasspathAndLibraryClass(b, a);
+ return chooseClass(b.asClasspathClass(), a.asLibraryClass());
}
- return chooseBetweenLibraryClasses(b, a, skipLibDups);
+ return chooseClasses(b.asLibraryClass(), a.asLibraryClass(), skipLibDups);
}
- private static boolean allowProgramClassConflict(DexClassPromise a, DexClassPromise b) {
+ private static boolean allowProgramClassConflict(DexProgramClass a, DexProgramClass b) {
// Currently only allow collapsing synthetic lambda classes.
return a.getOrigin() == Resource.Kind.DEX
&& b.getOrigin() == Resource.Kind.DEX
- && a.get().accessFlags.isSynthetic()
- && b.get().accessFlags.isSynthetic()
- && LambdaRewriter.hasLambdaClassPrefix(a.getType())
- && LambdaRewriter.hasLambdaClassPrefix(b.getType());
+ && a.accessFlags.isSynthetic()
+ && b.accessFlags.isSynthetic()
+ && LambdaRewriter.hasLambdaClassPrefix(a.type)
+ && LambdaRewriter.hasLambdaClassPrefix(b.type);
}
- private static DexClassPromise chooseBetweenProgramAndOtherClass(
- DexClassPromise selected, DexClassPromise ignored) {
- assert selected.isProgramClass() && !ignored.isProgramClass();
+ private static DexClass chooseClass(DexProgramClass selected, DexClass ignored) {
+ assert !ignored.isProgramClass();
if (ignored.isLibraryClass()) {
logIgnoredClass(ignored, "Class `%s` was specified as library and program type.");
}
@@ -406,27 +398,24 @@
return selected;
}
- private static DexClassPromise chooseBetweenClasspathAndLibraryClass(
- DexClassPromise selected, DexClassPromise ignored) {
- assert selected.isClasspathClass() && ignored.isLibraryClass();
+ private static DexClass chooseClass(DexClasspathClass selected, DexLibraryClass ignored) {
logIgnoredClass(ignored, "Class `%s` was specified as library and classpath type.");
return selected;
}
- private static DexClassPromise chooseBetweenLibraryClasses(
- DexClassPromise selected, DexClassPromise ignored, boolean skipDups) {
- assert selected.isLibraryClass() && ignored.isLibraryClass();
+ private static DexClass chooseClasses(
+ DexLibraryClass selected, DexLibraryClass ignored, boolean skipDups) {
if (!skipDups) {
throw new CompilationError(
- "Library type already present: " + selected.getType().toSourceString());
+ "Library type already present: " + selected.type.toSourceString());
}
logIgnoredClass(ignored, "Class `%s` was specified twice as a library type.");
return selected;
}
- private static void logIgnoredClass(DexClassPromise ignored, String message) {
+ private static void logIgnoredClass(DexClass ignored, String message) {
if (Log.ENABLED) {
- Log.warn(DexApplication.class, message, ignored.getType().toSourceString());
+ Log.warn(DexApplication.class, message, ignored.type.toSourceString());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index f684309..3deec04 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.google.common.base.MoreObjects;
-public abstract class DexClass extends DexItem implements DexClassPromise {
+public abstract class DexClass extends DexItem {
private static final DexEncodedMethod[] NO_METHODS = {};
private static final DexEncodedField[] NO_FIELDS = {};
@@ -118,7 +118,6 @@
public abstract void addDependencies(MixedSectionCollection collector);
- @Override
public boolean isProgramClass() {
return false;
}
@@ -127,7 +126,6 @@
return null;
}
- @Override
public boolean isClasspathClass() {
return false;
}
@@ -136,7 +134,6 @@
return null;
}
- @Override
public boolean isLibraryClass() {
return false;
}
@@ -154,21 +151,10 @@
return null;
}
- @Override
public Resource.Kind getOrigin() {
return this.origin;
}
- @Override
- public DexClass get() {
- return this;
- }
-
- @Override
- public DexType getType() {
- return type;
- }
-
public boolean hasClassInitializer() {
return getClassInitializer() != null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassPromise.java b/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
deleted file mode 100644
index c0bdf20..0000000
--- a/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.graph;
-
-import com.android.tools.r8.Resource;
-
-/**
- * Provides a way for delayed DexClass discovery.
- *
- * Provides minimal class details of the promised class and
- * provides the class when asked by calling method get().
- *
- * Note that DexClass also implements this interface, since it
- * represents a 'materialized' promise for a class.
- */
-public interface DexClassPromise {
- DexType getType();
-
- Resource.Kind getOrigin();
-
- boolean isProgramClass();
-
- boolean isClasspathClass();
-
- boolean isLibraryClass();
-
- DexClass get();
-}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java b/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
deleted file mode 100644
index e5792cd..0000000
--- a/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.graph;
-
-import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
-
-import com.android.tools.r8.Resource;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.Unreachable;
-import com.google.common.io.Closer;
-import java.io.IOException;
-
-// Lazily loads a class file represented by resource.
-public final class LazyClassFileLoader implements DexClassPromise {
- // Resource representing file definition.
- private final Resource resource;
- // Class kind to be created.
- private final ClassKind classKind;
-
- // Application reader to be used. Note that the reader may be reused in
- // many loaders and may be used concurrently, it is considered to be
- // thread-safe since its only state is internal options which we
- // consider immutable after they are initialized (barring dex factory
- // which is thread-safe).
- private final JarApplicationReader reader;
-
- // Dex type of the class to be created.
- private final DexType type;
-
- // Cached loaded class if get(...) method has already been called, this
- // field is only accessed in context synchronized on `this`.
- private DexClass loadedClass = null;
-
- public LazyClassFileLoader(DexType type,
- Resource resource, ClassKind classKind, JarApplicationReader reader) {
- this.resource = resource;
- this.reader = reader;
- this.type = type;
- this.classKind = classKind;
- assert classKind != ClassKind.PROGRAM;
- }
-
- // Callback method for JarClassFileReader, is always called in synchronized context.
- private void addClass(DexClass clazz) {
- assert clazz != null;
- assert loadedClass == null;
- loadedClass = clazz;
- }
-
- @Override
- public DexType getType() {
- return type;
- }
-
- @Override
- public Resource.Kind getOrigin() {
- return Resource.Kind.CLASSFILE;
- }
-
- @Override
- public boolean isProgramClass() {
- return false;
- }
-
- @Override
- public boolean isClasspathClass() {
- return classKind == ClassKind.CLASSPATH;
- }
-
- @Override
- public boolean isLibraryClass() {
- return classKind == ClassKind.LIBRARY;
- }
-
- // Loads the class from the resource. Synchronized on `this` to avoid
- // unnecessary complications, thus all threads trying to load a class with
- // this loader will wait for the first load to finish.
- @Override
- public synchronized DexClass get() {
- if (loadedClass != null) {
- return loadedClass;
- }
-
- try (Closer closer = Closer.create()) {
- JarClassFileReader reader = new JarClassFileReader(this.reader, this::addClass);
- reader.read(DEFAULT_DEX_FILENAME, classKind, resource.getStream(closer));
- } catch (IOException e) {
- throw new CompilationError("Failed to load class: " + type.toSourceString(), e);
- }
-
- if (loadedClass == null) {
- throw new Unreachable("Class is supposed to be loaded: " + type.toSourceString());
- }
-
- if (loadedClass.type != type) {
- throw new CompilationError("Class content provided for type descriptor "
- + type.toSourceString() + " actually defines class " + loadedClass.type.toSourceString());
- }
-
- return loadedClass;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 01be50d..baaf7d0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -226,7 +226,7 @@
InterfaceProcessor processor = new InterfaceProcessor(this);
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, true)) {
- processor.process(clazz.get().asProgramClass());
+ processor.process(clazz.asProgramClass());
}
}
return processor.companionClasses;
@@ -236,7 +236,7 @@
ClassProcessor processor = new ClassProcessor(this);
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, false)) {
- processor.process(clazz.get());
+ processor.process(clazz);
}
}
return processor.getForwardMethods();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 37bfdaa..cb518a3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexAnnotationSetRefList;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassPromise;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -388,6 +387,10 @@
DexClass definitionFor(DexType type) {
return rewriter.converter.appInfo.app.definitionFor(type);
}
+
+ DexProgramClass programDefinitionFor(DexType type) {
+ return rewriter.converter.appInfo.app.programDefinitionFor(type);
+ }
}
// Used for targeting methods referenced directly without creating accessors.
@@ -415,9 +418,7 @@
// We already found the static method to be called, just relax its accessibility.
assert descriptor.getAccessibility() != null;
descriptor.getAccessibility().unsetPrivate();
- DexClassPromise promise = definitionFor(descriptor.implHandle.asMethod().holder);
- assert promise != null;
- DexClass implMethodHolder = promise.get();
+ DexClass implMethodHolder = definitionFor(descriptor.implHandle.asMethod().holder);
if (implMethodHolder.isInterface()) {
descriptor.getAccessibility().setPublic();
}
@@ -438,9 +439,7 @@
// For all instantiation points for which compiler creates lambda$
// methods, it creates these methods in the same class/interface.
DexMethod implMethod = descriptor.implHandle.asMethod();
- DexClassPromise promise = definitionFor(implMethod.holder);
- assert promise != null;
- DexClass implMethodHolder = promise.get();
+ DexClass implMethodHolder = definitionFor(implMethod.holder);
DexEncodedMethod[] directMethods = implMethodHolder.directMethods;
for (int i = 0; i < directMethods.length; i++) {
@@ -481,9 +480,8 @@
@Override
boolean ensureAccessibility() {
// Create a static accessor with proper accessibility.
- DexClassPromise promise = definitionFor(callTarget.holder);
- assert promise != null && promise.isProgramClass();
- DexClass accessorClass = promise.get();
+ DexProgramClass accessorClass = programDefinitionFor(callTarget.holder);
+ assert accessorClass != null;
DexAccessFlags accessorFlags = new DexAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC |
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index ee0614f..88d371b 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -162,12 +162,12 @@
}
DexProgramClass newHolder = null;
// Recurse through supertype chain.
- if (originalClass.superType.isSubtypeOf(targetClass.getType(), appInfo)) {
+ if (originalClass.superType.isSubtypeOf(targetClass.type, appInfo)) {
DexClass superClass = appInfo.definitionFor(originalClass.superType);
newHolder = findBridgeMethodHolder(superClass, targetClass, packageDescriptor);
} else {
for (DexType iface : originalClass.interfaces.values) {
- if (iface.isSubtypeOf(targetClass.getType(), appInfo)) {
+ if (iface.isSubtypeOf(targetClass.type, appInfo)) {
DexClass interfaceClass = appInfo.definitionFor(iface);
newHolder = findBridgeMethodHolder(interfaceClass, targetClass, packageDescriptor);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index f801cdd..21ab0d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassPromise;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexLibraryClass;
@@ -55,8 +54,8 @@
return new DexApplication.Builder(application, removeUnusedClassStructure(application));
}
- private Map<DexType, DexClassPromise> removeUnusedClassStructure(DexApplication application) {
- Map<DexType, DexClassPromise> classMap = new IdentityHashMap<>();
+ private Map<DexType, DexClass> removeUnusedClassStructure(DexApplication application) {
+ Map<DexType, DexClass> classMap = new IdentityHashMap<>();
for (DexLibraryClass clazz : application.libraryClasses()) {
classMap.put(clazz.type, clazz);
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 5945741..bb8bc7c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -35,9 +35,6 @@
public boolean skipDebugLineNumberOpt = false;
public boolean skipClassMerging = true;
- public boolean lazyClasspathLoading = false;
- public boolean lazyLibraryLoading = false;
-
// Number of threads to use while processing the dex files.
public int numberOfThreads = NOT_SPECIFIED;
// Print smali disassembly.
diff --git a/src/main/java/com/android/tools/r8/utils/LazyClassCollection.java b/src/main/java/com/android/tools/r8/utils/LazyClassCollection.java
index 0e71973..d7379ef 100644
--- a/src/main/java/com/android/tools/r8/utils/LazyClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/LazyClassCollection.java
@@ -3,41 +3,41 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
+
import com.android.tools.r8.Resource;
import com.android.tools.r8.ResourceProvider;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassPromise;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.JarApplicationReader;
-import com.android.tools.r8.graph.LazyClassFileLoader;
+import com.android.tools.r8.graph.JarClassFileReader;
import com.google.common.collect.ImmutableList;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
/**
* Represents a collection of classes loaded lazily from a set of lazy resource
- * providers. The collection is autonomous, it lazily collects promises but
+ * providers. The collection is autonomous, it lazily collects classes but
* does not choose the classes in cases of conflicts, delaying it until
* the class is asked for.
*
* NOTE: only java class resources are allowed to be lazy loaded.
*/
public final class LazyClassCollection {
- // Stores promises for types which have ever been asked for before. Special value
- // EmptyPromise.INSTANCE indicates there were no resources for this type in any of
- // the resource providers.
- //
- // Promises are potentially coming from different providers and chained, but in majority
- // of the cases there will only be one promise per type. We store promises for all
- // the resource providers and resolve the classes at the time it is queried.
+ // For each type which has ever been queried stores one or several classes loaded
+ // from resources provided by different resource providers. In majority of the
+ // cases there will only be one class per type. We store classes for all the
+ // resource providers and resolve the classes at the time it is queried.
//
// Must be synchronized on `classes`.
- private final Map<DexType, DexClassPromise> classes = new IdentityHashMap<>();
+ private final Map<DexType, DexClass[]> classes = new IdentityHashMap<>();
// Available lazy resource providers.
private final List<ResourceProvider> classpathProviders;
@@ -59,150 +59,112 @@
/**
* Returns a definition for a class or `null` if there is no such class.
- * Parameter `dominator` represents the class promise that is considered
+ * Parameter `dominator` represents a class that is considered
* to be already loaded, it may be null but if specified it may affect
* the conflict resolution. For example non-lazy loaded classpath class
* provided as `dominator` will conflict with lazy-loaded classpath classes.
*/
- public DexClassPromise get(DexType type, DexClassPromise dominator) {
- DexClassPromise promise;
-
- // Check if the promise already exists.
+ public DexClass get(DexType type, DexClass dominator) {
+ DexClass[] candidates;
synchronized (classes) {
- promise = classes.get(type);
+ candidates = classes.get(type);
}
- if (promise == null) {
- // Building a promise may be time consuming, we do it outside
- // the lock so others don't have to wait.
- promise = buildPromiseChain(type);
+ if (candidates == null) {
+ String descriptor = type.descriptor.toString();
+ // Loading resources and constructing classes may be time consuming, we do it
+ // outside the global lock so others don't have to wait.
+ List<Resource> classpathResources = collectResources(classpathProviders, descriptor);
+ List<Resource> libraryResources = collectResources(libraryProviders, descriptor);
+
+ candidates = new DexClass[classpathResources.size() + libraryResources.size()];
+
+ // Check if someone else has already added the array for this type.
synchronized (classes) {
- DexClassPromise existing = classes.get(type);
+ DexClass[] existing = classes.get(type);
if (existing != null) {
- promise = existing;
+ assert candidates.length == existing.length;
+ candidates = existing;
} else {
- classes.put(type, promise);
+ classes.put(type, candidates);
}
}
- assert promise != EmptyPromise.INSTANCE;
+ if (candidates.length > 0) {
+ // Load classes in synchronized content unique for the type.
+ synchronized (candidates) {
+ // Either all or none of the array classes will be loaded, so we use this
+ // as a criteria for checking if we need to load classes.
+ if (candidates[0] == null) {
+ new ClassLoader(type, candidates, reader, classpathResources, libraryResources).load();
+ }
+ }
+ }
}
- return promise == EmptyPromise.INSTANCE ? dominator : chooseClass(dominator, promise);
+ // Choose class in case there are conflicts.
+ DexClass candidate = dominator;
+ for (DexClass clazz : candidates) {
+ candidate = (candidate == null) ? clazz
+ : DexApplication.chooseClass(candidate, clazz, /* skipLibDups: */ true);
+ }
+ return candidate;
}
- // Build chain of lazy promises or `null` if none of the providers
- // provided resource for this type.
- private DexClassPromise buildPromiseChain(DexType type) {
- String descriptor = type.descriptor.toString();
- DexClassPromise promise = buildPromiseChain(
- type, descriptor, null, classpathProviders, ClassKind.CLASSPATH);
- promise = buildPromiseChain(
- type, descriptor, promise, libraryProviders, ClassKind.LIBRARY);
- return promise == null ? EmptyPromise.INSTANCE : promise;
- }
-
- private DexClassPromise buildPromiseChain(DexType type, String descriptor,
- DexClassPromise promise, List<ResourceProvider> providers, ClassKind classKind) {
+ private List<Resource> collectResources(List<ResourceProvider> providers, String descriptor) {
+ List<Resource> resources = new ArrayList<>();
for (ResourceProvider provider : providers) {
Resource resource = provider.getResource(descriptor);
- if (resource == null) {
- continue;
+ if (resource != null) {
+ resources.add(resource);
}
+ }
+ return resources;
+ }
- if (resource.kind != Resource.Kind.CLASSFILE) {
- throw new CompilationError("Resource returned by resource provider for type " +
- type.toSourceString() + " must be a class file resource.");
+ private static final class ClassLoader {
+ int index = 0;
+ final DexType type;
+ final DexClass[] classes;
+ final JarApplicationReader reader;
+ final List<Resource> classpathResources;
+ final List<Resource> libraryResources;
+
+ ClassLoader(DexType type, DexClass[] classes, JarApplicationReader reader,
+ List<Resource> classpathResources, List<Resource> libraryResources) {
+ this.type = type;
+ this.classes = classes;
+ this.reader = reader;
+ this.classpathResources = classpathResources;
+ this.libraryResources = libraryResources;
+ }
+
+ void addClass(DexClass clazz) {
+ assert index < classes.length;
+ assert clazz != null;
+ if (clazz.type != type) {
+ throw new CompilationError("Class content provided for type descriptor "
+ + type.toSourceString() + " actually defines class " + clazz.type
+ .toSourceString());
}
-
- LazyClassFileLoader loader = new LazyClassFileLoader(type, resource, classKind, reader);
- promise = (promise == null) ? loader : new DexClassPromiseChain(loader, promise);
- }
- return promise;
- }
-
- // Chooses the proper promise. Recursion is not expected to be deep.
- private DexClassPromise chooseClass(DexClassPromise dominator, DexClassPromise candidate) {
- DexClassPromise best = (dominator == null) ? candidate
- : DexApplication.chooseClass(dominator, candidate, /* skipLibDups: */ true);
- return (candidate instanceof DexClassPromiseChain)
- ? chooseClass(best, ((DexClassPromiseChain) candidate).next) : best;
- }
-
- private static final class EmptyPromise implements DexClassPromise {
- static final EmptyPromise INSTANCE = new EmptyPromise();
-
- @Override
- public DexType getType() {
- throw new Unreachable();
+ classes[index++] = clazz;
}
- @Override
- public Resource.Kind getOrigin() {
- throw new Unreachable();
- }
-
- @Override
- public boolean isProgramClass() {
- throw new Unreachable();
- }
-
- @Override
- public boolean isClasspathClass() {
- throw new Unreachable();
- }
-
- @Override
- public boolean isLibraryClass() {
- throw new Unreachable();
- }
-
- @Override
- public DexClass get() {
- throw new Unreachable();
- }
- }
-
- private static final class DexClassPromiseChain implements DexClassPromise {
- final DexClassPromise promise;
- final DexClassPromise next;
-
- private DexClassPromiseChain(DexClassPromise promise, DexClassPromise next) {
- assert promise != null;
- assert next != null;
- this.promise = promise;
- this.next = next;
- }
-
- @Override
- public DexType getType() {
- return promise.getType();
- }
-
- @Override
- public Resource.Kind getOrigin() {
- return promise.getOrigin();
- }
-
- @Override
- public boolean isProgramClass() {
- return promise.isProgramClass();
- }
-
- @Override
- public boolean isClasspathClass() {
- return promise.isClasspathClass();
- }
-
- @Override
- public boolean isLibraryClass() {
- return promise.isLibraryClass();
- }
-
- @Override
- public DexClass get() {
- return promise.get();
+ void load() {
+ try (Closer closer = Closer.create()) {
+ for (Resource resource : classpathResources) {
+ JarClassFileReader classReader = new JarClassFileReader(reader, this::addClass);
+ classReader.read(DEFAULT_DEX_FILENAME, ClassKind.CLASSPATH, resource.getStream(closer));
+ }
+ for (Resource resource : libraryResources) {
+ JarClassFileReader classReader = new JarClassFileReader(reader, this::addClass);
+ classReader.read(DEFAULT_DEX_FILENAME, ClassKind.LIBRARY, resource.getStream(closer));
+ }
+ } catch (IOException e) {
+ throw new CompilationError("Failed to load class: " + type.toSourceString(), e);
+ }
+ assert index == classes.length;
}
}
}
diff --git a/src/test/java/com/android/tools/r8/D8ResourceProviderRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/D8ResourceProviderRunExamplesAndroidOTest.java
rename to src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index 7cef1a4..8449bd9 100644
--- a/src/test/java/com/android/tools/r8/D8ResourceProviderRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -9,7 +9,7 @@
import java.io.IOException;
import java.nio.file.Path;
-public class D8ResourceProviderRunExamplesAndroidOTest
+public class D8LazyRunExamplesAndroidOTest
extends D8IncrementalRunExamplesAndroidOTest {
class D8LazyTestRunner extends D8IncrementalTestRunner {
diff --git a/src/test/java/com/android/tools/r8/D8RegularLazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/D8RegularLazyRunExamplesAndroidOTest.java
rename to src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
index 38acabe..42d3c47 100644
--- a/src/test/java/com/android/tools/r8/D8RegularLazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
@@ -8,7 +8,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
-public class D8RegularLazyRunExamplesAndroidOTest
+public class D8NonLazyRunExamplesAndroidOTest
extends D8IncrementalRunExamplesAndroidOTest {
class D8LazyTestRunner extends D8IncrementalTestRunner {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 44c2156..dc49caf 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -287,7 +287,7 @@
method.setCode(ir, allocator, factory);
virtualMethods[i] = method;
}
- builder.addClassPromise(
+ builder.addClass(
new DexProgramClass(
type,
null,