Introduce DirectDexApplication.
This is the first step in a refactoring that will allow us to
mutate library and program classes freely, including changing
their names.
Bug:
Change-Id: I503bdb1516ba69d38453fc8e00175d2821ad3915
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 5a18b41..6190cbd 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -37,7 +37,8 @@
private List<String> run(AndroidApp app) throws IOException, ExecutionException {
ExecutorService executor = ThreadUtils.getExecutorService(options);
- DexApplication application = new ApplicationReader(app, options, timing).read(executor);
+ DexApplication application =
+ new ApplicationReader(app, options, timing).read(executor).toDirect();
AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
RootSet mainDexRootSet =
new RootSetBuilder(application, appInfo, options.mainDexKeepRules).run(executor);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 0b16963..d0badf2 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -222,7 +222,7 @@
+ oLevel.getName() + " and later (--min-api " + oLevel.getLevel() + ")");
}
DexApplication application =
- new ApplicationReader(inputApp, options, timing).read(executorService);
+ new ApplicationReader(inputApp, options, timing).read(executorService).toDirect();
AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
RootSet rootSet;
@@ -309,7 +309,7 @@
// Calculate the automatic main dex list according to legacy multidex constraints.
// Add those classes to an eventual manual list of classes.
- application = new DexApplication.Builder(application)
+ application = application.builder()
.addToMainDexList(new MainDexListBuilder(mainDexBaseClasses, application).run())
.build();
}
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 8c0626a..2e7d731 100644
--- a/src/main/java/com/android/tools/r8/bisect/BisectState.java
+++ b/src/main/java/com/android/tools/r8/bisect/BisectState.java
@@ -270,7 +270,7 @@
}
}
System.out.println("Class split is good: " + goodClasses + ", bad: " + badClasses);
- return new Builder(badApp).replaceProgramClasses(programClasses).build();
+ return badApp.builder().replaceProgramClasses(programClasses).build();
}
private DexProgramClass getGoodClass(DexProgramClass clazz) {
@@ -326,6 +326,7 @@
return classes;
}
+ @SuppressWarnings("deprecation")
private static String makeSignature(DexApplication app) {
List<DexProgramClass> classes = getSortedClasses(app);
StringBuilder builder = new StringBuilder();
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 3e2aba5..9aa712a 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.JarClassFileReader;
+import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.naming.ProguardMapReader;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ClassProvider;
@@ -73,7 +74,7 @@
public final DexApplication read(ExecutorService executorService)
throws IOException, ExecutionException {
timing.begin("DexApplication.read");
- final DexApplication.Builder builder = new DexApplication.Builder(itemFactory, timing);
+ final LazyLoadedDexApplication.Builder builder = DexApplication.builder(itemFactory, timing);
try {
List<Future<?>> futures = new ArrayList<>();
// Still preload some of the classes, primarily for two reasons:
@@ -142,7 +143,7 @@
}
}
- private void readMainDexList(DexApplication.Builder builder, ExecutorService executorService,
+ private void readMainDexList(DexApplication.Builder<?> builder, ExecutorService executorService,
List<Future<?>> futures) {
if (inputApp.hasMainDexList()) {
futures.add(executorService.submit(() -> {
@@ -254,7 +255,7 @@
: ClassProvider.combine(classKind, providers);
}
- void initializeLazyClassCollection(DexApplication.Builder builder) {
+ void initializeLazyClassCollection(LazyLoadedDexApplication.Builder builder) {
// Add all program classes to the builder.
for (DexProgramClass clazz : programClasses) {
builder.addProgramClass(clazz.asProgramClass());
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 afd923c..2d111f0 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -52,10 +52,6 @@
return app.classes();
}
- public Iterable<DexLibraryClass> libraryClasses() {
- return app.libraryClasses();
- }
-
public DexClass definitionFor(DexType type) {
return app.definitionFor(type);
}
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 9c87b61..54b2a51 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.graph;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
@@ -21,19 +23,29 @@
public AppInfoWithSubtyping(DexApplication application) {
super(application);
- populateSubtypeMap(application.getFullClassMap(), application.dexItemFactory);
+ populateSubtypeMap(application.asDirect(), application.dexItemFactory);
}
protected AppInfoWithSubtyping(AppInfoWithSubtyping previous) {
super(previous);
missingClasses.addAll(previous.missingClasses);
subtypeMap.putAll(previous.subtypeMap);
+ assert app instanceof DirectMappedDexApplication;
}
protected AppInfoWithSubtyping(AppInfoWithSubtyping previous, GraphLense lense) {
super(previous, lense);
// Recompute subtype map if we have modified the graph.
- populateSubtypeMap(previous.app.getFullClassMap(), dexItemFactory);
+ populateSubtypeMap(previous.getDirectApplication(), dexItemFactory);
+ }
+
+ private DirectMappedDexApplication getDirectApplication() {
+ // TODO(herhut): Remove need for cast.
+ return (DirectMappedDexApplication) app;
+ }
+
+ public Iterable<DexLibraryClass> libraryClasses() {
+ return getDirectApplication().libraryClasses();
}
public Set<DexType> getMissingClasses() {
@@ -84,17 +96,17 @@
}
}
- private void populateSubtypeMap(Map<DexType, DexClass> classes, DexItemFactory dexItemFactory) {
+ private void populateSubtypeMap(DirectMappedDexApplication app, DexItemFactory dexItemFactory) {
dexItemFactory.clearSubtypeInformation();
dexItemFactory.objectType.tagAsSubtypeRoot();
Hashtable<DexType, Set<DexType>> map = new Hashtable<>();
- for (Map.Entry<DexType, DexClass> entry : classes.entrySet()) {
- populateAllSuperTypes(map, entry.getKey(), entry.getValue(), classes::get);
+ for (DexClass clazz : Iterables.<DexClass>concat(app.classes(), app.libraryClasses())) {
+ populateAllSuperTypes(map, clazz.type, clazz, app::definitionFor);
}
for (Map.Entry<DexType, Set<DexType>> entry : map.entrySet()) {
subtypeMap.put(entry.getKey(), ImmutableSet.copyOf(entry.getValue()));
}
- assert DexType.validateLevelsAreCorrect(classes::get, dexItemFactory);
+ assert DexType.validateLevelsAreCorrect(app::definitionFor, 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 294b031..b79f30e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -6,10 +6,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.utils.ClasspathClassCollection;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.LibraryClassCollection;
import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
@@ -26,17 +25,13 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.IdentityHashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-public class DexApplication {
+public abstract class DexApplication {
// Maps type into class, may be used concurrently.
- private ProgramClassCollection programClasses;
- private ClasspathClassCollection classpathClasses;
- private LibraryClassCollection libraryClasses;
+ ProgramClassCollection programClasses;
public final ImmutableSet<DexType> mainDexList;
public final byte[] deadCode;
@@ -50,12 +45,12 @@
// Information on the lexicographically largest string referenced from code.
public final DexString highestSortingString;
- /** Constructor should only be invoked by the DexApplication.Builder. */
- private DexApplication(
+ /**
+ * Constructor should only be invoked by the DexApplication.Builder.
+ */
+ DexApplication(
ClassNameMapper proguardMap,
ProgramClassCollection programClasses,
- ClasspathClassCollection classpathClasses,
- LibraryClassCollection libraryClasses,
ImmutableSet<DexType> mainDexList,
byte[] deadCode,
DexItemFactory dexItemFactory,
@@ -64,8 +59,6 @@
assert programClasses != null;
this.proguardMap = proguardMap;
this.programClasses = programClasses;
- this.classpathClasses = classpathClasses;
- this.libraryClasses = libraryClasses;
this.mainDexList = mainDexList;
this.deadCode = deadCode;
this.dexItemFactory = dexItemFactory;
@@ -73,10 +66,7 @@
this.timing = timing;
}
- /** Force load all classes and return type -> class map containing all the classes */
- public Map<DexType, DexClass> getFullClassMap() {
- return forceLoadAllClasses();
- }
+ public abstract Builder<?> builder();
// Reorder classes randomly. Note that the order of classes in program or library
// class collections should not matter for compilation of valid code and when running
@@ -96,61 +86,14 @@
return classes;
}
- public List<DexLibraryClass> libraryClasses() {
- assert classpathClasses == null : "Operation is not supported.";
- Map<DexType, DexClass> classMap = forceLoadAllClasses();
- List<DexLibraryClass> classes = new ArrayList<>();
- for (DexClass clazz : classMap.values()) {
- if (clazz.isLibraryClass()) {
- classes.add(clazz.asLibraryClass());
- }
- }
- assert reorderClasses(classes);
- return classes;
- }
-
- private Map<DexType, DexClass> forceLoadAllClasses() {
- Map<DexType, DexClass> loaded = new IdentityHashMap<>();
-
- // program classes are supposed to be loaded, but force-loading them is no-op.
- programClasses.forceLoad(type -> true);
- programClasses.getAllClasses().forEach(clazz -> loaded.put(clazz.type, clazz));
-
- if (classpathClasses != null) {
- classpathClasses.forceLoad(type -> !loaded.containsKey(type));
- classpathClasses.getAllClasses().forEach(clazz -> loaded.putIfAbsent(clazz.type, clazz));
- }
-
- if (libraryClasses != null) {
- libraryClasses.forceLoad(type -> !loaded.containsKey(type));
- libraryClasses.getAllClasses().forEach(clazz -> loaded.putIfAbsent(clazz.type, clazz));
- }
-
- return loaded;
- }
-
- public DexClass definitionFor(DexType type) {
- if (type == null) {
- return null;
- }
- DexClass clazz = programClasses.get(type);
- if (clazz == null && classpathClasses != null) {
- clazz = classpathClasses.get(type);
- }
- if (clazz == null && libraryClasses != null) {
- clazz = libraryClasses.get(type);
- }
- return clazz;
- }
+ public abstract DexClass definitionFor(DexType type);
public DexProgramClass programDefinitionFor(DexType type) {
DexClass clazz = programClasses.get(type);
return clazz == null ? null : clazz.asProgramClass();
}
- public String toString() {
- return "Application (" + programClasses + "; " + classpathClasses + "; " + libraryClasses + ")";
- }
+ public abstract String toString();
public ClassNameMapper getProguardMap() {
return proguardMap;
@@ -208,7 +151,9 @@
}
}
- /** Return smali source for the application code. */
+ /**
+ * Return smali source for the application code.
+ */
public String smali(InternalOptions options) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os);
@@ -296,7 +241,7 @@
}
}
- public static class Builder {
+ public abstract static class Builder<T extends Builder> {
// We handle program class collection separately from classpath
// and library class collections. Since while we assume program
// class collection should always be fully loaded and thus fully
@@ -304,17 +249,15 @@
// new or removing existing classes), classpath and library
// collections will be considered monolithic collections.
- private final List<DexProgramClass> programClasses;
- private ClasspathClassCollection classpathClasses;
- private LibraryClassCollection libraryClasses;
+ final List<DexProgramClass> programClasses;
public final DexItemFactory dexItemFactory;
ClassNameMapper proguardMap;
- private final Timing timing;
+ final Timing timing;
DexString highestSortingString;
- private byte[] deadCode;
- private final Set<DexType> mainDexList = Sets.newIdentityHashSet();
+ byte[] deadCode;
+ final Set<DexType> mainDexList = Sets.newIdentityHashSet();
private final Collection<DexProgramClass> synthesizedClasses;
public Builder(DexItemFactory dexItemFactory, Timing timing) {
@@ -322,16 +265,14 @@
this.dexItemFactory = dexItemFactory;
this.timing = timing;
this.deadCode = null;
- this.classpathClasses = null;
- this.libraryClasses = null;
this.synthesizedClasses = new ArrayList<>();
}
+ abstract T self();
+
public Builder(DexApplication application) {
programClasses = application.programClasses.getAllClasses();
- classpathClasses = application.classpathClasses;
- libraryClasses = application.libraryClasses;
- proguardMap = application.proguardMap;
+ proguardMap = application.getProguardMap();
timing = application.timing;
highestSortingString = application.highestSortingString;
dexItemFactory = application.dexItemFactory;
@@ -340,53 +281,43 @@
synthesizedClasses = new ArrayList<>();
}
- public synchronized Builder setProguardMap(ClassNameMapper proguardMap) {
+ public synchronized T setProguardMap(ClassNameMapper proguardMap) {
assert this.proguardMap == null;
this.proguardMap = proguardMap;
- return this;
+ return self();
}
- public synchronized Builder replaceProgramClasses(List<DexProgramClass> newProgramClasses) {
+ public synchronized T replaceProgramClasses(List<DexProgramClass> newProgramClasses) {
assert newProgramClasses != null;
this.programClasses.clear();
this.programClasses.addAll(newProgramClasses);
- return this;
+ return self();
}
- public Builder appendDeadCode(byte[] deadCodeAtAnotherRound) {
+ public T appendDeadCode(byte[] deadCodeAtAnotherRound) {
if (deadCodeAtAnotherRound == null) {
- return this;
+ return self();
}
if (this.deadCode == null) {
this.deadCode = deadCodeAtAnotherRound;
- return this;
+ return self();
}
// Concatenate existing byte[] and the given byte[].
this.deadCode = Bytes.concat(this.deadCode, deadCodeAtAnotherRound);
- return this;
+ return self();
}
- public synchronized Builder setHighestSortingString(DexString value) {
+ public synchronized T setHighestSortingString(DexString value) {
highestSortingString = value;
- return this;
+ return self();
}
- public synchronized Builder addProgramClass(DexProgramClass clazz) {
+ public synchronized T addProgramClass(DexProgramClass clazz) {
programClasses.add(clazz);
- return this;
+ return self();
}
- public Builder setClasspathClassCollection(ClasspathClassCollection classes) {
- this.classpathClasses = classes;
- return this;
- }
-
- public Builder setLibraryClassCollection(LibraryClassCollection classes) {
- this.libraryClasses = classes;
- return this;
- }
-
- public synchronized Builder addSynthesizedClass(
+ public synchronized T addSynthesizedClass(
DexProgramClass synthesizedClass, boolean addToMainDexList) {
assert synthesizedClass.isProgramClass() : "All synthesized classes must be program classes";
addProgramClass(synthesizedClass);
@@ -394,7 +325,7 @@
if (addToMainDexList && !mainDexList.isEmpty()) {
mainDexList.add(synthesizedClass.type);
}
- return this;
+ return self();
}
public Collection<DexProgramClass> getProgramClasses() {
@@ -414,17 +345,17 @@
return this;
}
- public DexApplication build() {
- return new DexApplication(
- proguardMap,
- ProgramClassCollection.create(programClasses),
- classpathClasses,
- libraryClasses,
- ImmutableSet.copyOf(mainDexList),
- deadCode,
- dexItemFactory,
- highestSortingString,
- timing);
- }
+ public abstract DexApplication build();
}
+
+ public static LazyLoadedDexApplication.Builder builder(DexItemFactory factory, Timing timing) {
+ return new LazyLoadedDexApplication.Builder(factory, timing);
+ }
+
+ public DirectMappedDexApplication asDirect() {
+ throw new Unreachable("Cannot use a LazyDexApplication where a DirectDexApplication is"
+ + " expected.");
+ }
+
+ public abstract DirectMappedDexApplication toDirect();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
new file mode 100644
index 0000000..e2583c3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -0,0 +1,99 @@
+// 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.
+// Copyright (c) 2016, 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.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ProgramClassCollection;
+import com.android.tools.r8.utils.Timing;
+
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class DirectMappedDexApplication extends DexApplication {
+
+ private final ImmutableMap<DexType, DexLibraryClass> libraryClasses;
+
+ private DirectMappedDexApplication(ClassNameMapper proguardMap,
+ ProgramClassCollection programClasses,
+ ImmutableMap<DexType, DexLibraryClass> libraryClasses,
+ ImmutableSet<DexType> mainDexList, byte[] deadCode,
+ DexItemFactory dexItemFactory, DexString highestSortingString,
+ Timing timing) {
+ super(proguardMap, programClasses, mainDexList, deadCode,
+ dexItemFactory, highestSortingString, timing);
+ this.libraryClasses = libraryClasses;
+ }
+
+ public Collection<DexLibraryClass> libraryClasses() {
+ return libraryClasses.values();
+ }
+
+ @Override
+ public DexClass definitionFor(DexType type) {
+ DexClass result = programClasses.get(type);
+ if (result == null) {
+ result = libraryClasses.get(type);
+ }
+ return result;
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public DirectMappedDexApplication toDirect() {
+ return this;
+ }
+
+ @Override
+ public DirectMappedDexApplication asDirect() {
+ return this;
+ }
+
+ public String toString() {
+ return "DexApplication (direct)";
+ }
+
+ public static class Builder extends DexApplication.Builder<Builder> {
+
+ private Map<DexType, DexLibraryClass> libraryClasses = new IdentityHashMap<>();
+
+ Builder(LazyLoadedDexApplication application) {
+ super(application);
+ // As a side-effect, this will force-load all classes.
+ Map<DexType, DexClass> allClasses = application.getFullClassMap();
+ Iterables.filter(allClasses.values(), DexLibraryClass.class)
+ .forEach(k -> libraryClasses.put(k.type, k));
+
+ }
+
+ private Builder(DirectMappedDexApplication application) {
+ super(application);
+ this.libraryClasses.putAll(application.libraryClasses);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+
+ @Override
+ public DexApplication build() {
+ return new DirectMappedDexApplication(proguardMap,
+ ProgramClassCollection.create(programClasses),
+ ImmutableMap.copyOf(libraryClasses), ImmutableSet.copyOf(mainDexList), deadCode,
+ dexItemFactory, highestSortingString, timing);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
new file mode 100644
index 0000000..bd7b266
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -0,0 +1,134 @@
+// 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.
+// Copyright (c) 2016, 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.naming.ClassNameMapper;
+import com.android.tools.r8.utils.ClasspathClassCollection;
+import com.android.tools.r8.utils.LibraryClassCollection;
+import com.android.tools.r8.utils.ProgramClassCollection;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class LazyLoadedDexApplication extends DexApplication {
+
+ private ClasspathClassCollection classpathClasses;
+ private LibraryClassCollection libraryClasses;
+
+ /**
+ * Constructor should only be invoked by the DexApplication.Builder.
+ */
+ private LazyLoadedDexApplication(ClassNameMapper proguardMap,
+ ProgramClassCollection programClasses,
+ ClasspathClassCollection classpathClasses,
+ LibraryClassCollection libraryClasses,
+ ImmutableSet<DexType> mainDexList, byte[] deadCode,
+ DexItemFactory dexItemFactory, DexString highestSortingString,
+ Timing timing) {
+ super(proguardMap, programClasses, mainDexList, deadCode,
+ dexItemFactory, highestSortingString, timing);
+ this.classpathClasses = classpathClasses;
+ this.libraryClasses = libraryClasses;
+ }
+
+ @Override
+ public DexClass definitionFor(DexType type) {
+ if (type == null) {
+ return null;
+ }
+ DexClass clazz = programClasses.get(type);
+ if (clazz == null && classpathClasses != null) {
+ clazz = classpathClasses.get(type);
+ }
+ if (clazz == null && libraryClasses != null) {
+ clazz = libraryClasses.get(type);
+ }
+ return clazz;
+ }
+
+ private Map<DexType, DexClass> forceLoadAllClasses() {
+ Map<DexType, DexClass> loaded = new IdentityHashMap<>();
+
+ // Program classes are supposed to be loaded, but force-loading them is no-op.
+ programClasses.forceLoad(type -> true);
+ programClasses.getAllClasses().forEach(clazz -> loaded.put(clazz.type, clazz));
+
+ if (classpathClasses != null) {
+ classpathClasses.forceLoad(type -> !loaded.containsKey(type));
+ classpathClasses.getAllClasses().forEach(clazz -> loaded.putIfAbsent(clazz.type, clazz));
+ }
+
+ if (libraryClasses != null) {
+ libraryClasses.forceLoad(type -> !loaded.containsKey(type));
+ libraryClasses.getAllClasses().forEach(clazz -> loaded.putIfAbsent(clazz.type, clazz));
+ }
+
+ return loaded;
+ }
+
+ /**
+ * Force load all classes and return type -> class map containing all the classes.
+ */
+ public Map<DexType, DexClass> getFullClassMap() {
+ return forceLoadAllClasses();
+ }
+
+ public static class Builder extends DexApplication.Builder<Builder> {
+
+ private ClasspathClassCollection classpathClasses;
+ private LibraryClassCollection libraryClasses;
+
+ Builder(DexItemFactory dexItemFactory, Timing timing) {
+ super(dexItemFactory, timing);
+ this.classpathClasses = null;
+ this.libraryClasses = null;
+ }
+
+ private Builder(LazyLoadedDexApplication application) {
+ super(application);
+ this.classpathClasses = application.classpathClasses;
+ this.libraryClasses = application.libraryClasses;
+ }
+
+ Builder self() {
+ return this;
+ }
+
+ public Builder setClasspathClassCollection(ClasspathClassCollection classes) {
+ this.classpathClasses = classes;
+ return this;
+ }
+
+ public Builder setLibraryClassCollection(LibraryClassCollection classes) {
+ this.libraryClasses = classes;
+ return this;
+ }
+
+ public LazyLoadedDexApplication build() {
+ return new LazyLoadedDexApplication(proguardMap,
+ ProgramClassCollection.create(programClasses),
+ classpathClasses, libraryClasses, ImmutableSet.copyOf(mainDexList), deadCode,
+ dexItemFactory, highestSortingString, timing);
+ }
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public DirectMappedDexApplication toDirect() {
+ return new DirectMappedDexApplication.Builder(this).build().asDirect();
+ }
+
+ public String toString() {
+ return "Application (" + programClasses + "; " + classpathClasses + "; " + libraryClasses
+ + ")";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 716db5e..2ba4421 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -226,7 +226,7 @@
convertClassesToDex(application.classes(), executor);
// Build a new application with jumbo string info,
- Builder builder = new Builder(application);
+ Builder builder = application.builder();
builder.setHighestSortingString(highestSortingString);
synthesizeLambdaClasses(builder);
@@ -245,7 +245,7 @@
return builder.build();
}
- private void updateMainDexListWithSynthesizedClassMap(Builder builder) {
+ private void updateMainDexListWithSynthesizedClassMap(Builder<?> builder) {
Set<DexType> inputMainDexList = builder.getMainDexList();
if (!inputMainDexList.isEmpty()) {
Map<DexType, DexProgramClass> programClasses = builder.getProgramClasses().stream()
@@ -264,14 +264,14 @@
}
}
- private void clearSynthesizedClassMapping(Builder builder) {
+ private void clearSynthesizedClassMapping(Builder<?> builder) {
for (DexProgramClass programClass : builder.getProgramClasses()) {
programClass.annotations =
programClass.annotations.getWithout(builder.dexItemFactory.annotationSynthesizedClassMap);
}
}
- private void updateSynthesizedClassMapping(Builder builder) {
+ private void updateSynthesizedClassMapping(Builder<?> builder) {
ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized =
ArrayListMultimap.create();
for (DexProgramClass synthesized : builder.getSynthesizedClasses()) {
@@ -361,7 +361,7 @@
}
// Build a new application with jumbo string info.
- Builder builder = new Builder(application);
+ Builder builder = application.builder();
builder.setHighestSortingString(highestSortingString);
// Second inlining pass for dealing with double inline callers.
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 edf8b60..99417c3 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
@@ -254,7 +254,7 @@
}
private Map<DexProgramClass, DexProgramClass> processInterfaces(
- Builder builder, Flavor flavour) {
+ Builder<?> builder, Flavor flavour) {
InterfaceProcessor processor = new InterfaceProcessor(this);
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, true)) {
@@ -264,7 +264,7 @@
return processor.companionClasses;
}
- private Set<DexEncodedMethod> processClasses(Builder builder, Flavor flavour) {
+ private Set<DexEncodedMethod> processClasses(Builder<?> builder, Flavor flavour) {
ClassProcessor processor = new ClassProcessor(this);
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, false)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 74cec83..d4ff512 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -18,7 +19,6 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.base.Equivalence.Wrapper;
@@ -40,7 +40,7 @@
public class RootSetBuilder {
- private DexApplication application;
+ private DirectMappedDexApplication application;
private final AppInfo appInfo;
private final List<ProguardConfigurationRule> rules;
private final Map<DexItem, ProguardKeepRule> noShrinking = new IdentityHashMap<>();
@@ -59,7 +59,7 @@
public RootSetBuilder(DexApplication application, AppInfo appInfo,
List<ProguardConfigurationRule> rules) {
- this.application = application;
+ this.application = application.asDirect();
this.appInfo = appInfo;
this.rules = rules;
}
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 3decec1..f3b712f 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -56,7 +56,7 @@
}
private DexApplication.Builder removeUnused(DexApplication application) {
- return new DexApplication.Builder(application)
+ return application.builder()
.replaceProgramClasses(getNewProgramClasses(application.classes()));
}
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
index 61a049e..082b5ee 100644
--- a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -8,9 +8,9 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -33,8 +33,10 @@
AndroidApp input = AndroidApp.fromProgramFiles(SMALI_DIR.resolve(name).resolve(name + ".dex"));
ExecutorService executorService = Executors.newSingleThreadExecutor();
Timing timing = new Timing("R8UnreachableCodeTest");
- DexApplication application =
- new ApplicationReader(input, new InternalOptions(), timing).read(executorService);
+ DirectMappedDexApplication application =
+ new ApplicationReader(input, new InternalOptions(), timing)
+ .read(executorService)
+ .toDirect();
IRConverter converter =
new IRConverter(application, new AppInfoWithSubtyping(application), new InternalOptions());
converter.optimize();
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 94d52f0..09f2594 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -459,7 +459,8 @@
AndroidApp.fromProgramFiles(ListUtils.map(fileNames, Paths::get)),
new InternalOptions(),
new Timing("ToolHelper buildApplication"))
- .read();
+ .read()
+ .toDirect();
}
public static ProguardConfiguration loadProguardConfiguration(
@@ -545,10 +546,10 @@
public static DexApplication optimizeWithR8(
DexApplication application,
- AppInfoWithSubtyping appInfo,
InternalOptions options)
throws CompilationException, ExecutionException, IOException {
- return R8.optimize(application, appInfo, options);
+ application = application.toDirect();
+ return R8.optimize(application, new AppInfoWithSubtyping(application), options);
}
public static AndroidApp runD8(AndroidApp app) throws CompilationException, IOException {
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index e5581a4..a08f274 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexDebugEvent;
@@ -24,7 +25,7 @@
ObjectToOffsetMapping emptyObjectTObjectMapping() {
return new ObjectToOffsetMapping(
0,
- new Builder(new DexItemFactory(), null).build(),
+ DexApplication.builder(new DexItemFactory(), null).build(),
new DexProgramClass[] {},
new DexProto[] {},
new DexType[] {},
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index 74e4e9b..30e02b5 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -9,10 +9,10 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
@@ -29,7 +29,7 @@
static final String APP_DIR = "third_party/gmscore/v5/";
private AndroidApp app;
- private DexApplication program;
+ private DirectMappedDexApplication program;
private AppInfoWithSubtyping appInfo;
@Before
@@ -37,7 +37,8 @@
app = AndroidApp.fromProgramDirectory(Paths.get(APP_DIR));
ExecutorService executorService = Executors.newSingleThreadExecutor();
Timing timing = new Timing("ReadGMSCore");
- program = new ApplicationReader(app, new InternalOptions(), timing).read(executorService);
+ program = new ApplicationReader(app, new InternalOptions(), timing)
+ .read(executorService).toDirect();
appInfo = new AppInfoWithSubtyping(program);
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 9da8e0e..8cf6e9b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.DexOverflowException;
import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -141,7 +140,7 @@
protected static DexApplication process(DexApplication app, InternalOptions options)
throws IOException, CompilationException, ExecutionException {
- return ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), options);
+ return ToolHelper.optimizeWithR8(app, options);
}
protected DexApplication buildApplication(JasminBuilder builder, InternalOptions options) {
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 0acd445..b0a683a 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -35,6 +35,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.DebugPosition;
import com.android.tools.r8.ir.code.IRCode;
@@ -440,7 +441,7 @@
}
builder.append("]");
fail("Class " + clazz + " found in main dex, " +
- "only expected explicit main dex classes " + builder +" in main dex file");
+ "only expected explicit main dex classes " + builder + " in main dex file");
}
}
@@ -551,7 +552,7 @@
options.minApiLevel = minApi;
options.intermediate = intermediate;
DexItemFactory factory = options.itemFactory;
- DexApplication.Builder builder = new DexApplication.Builder(factory, timing);
+ DexApplication.Builder builder = DexApplication.builder(factory, timing);
for (String clazz : classes) {
DexString desc = factory.createString(DescriptorUtils.javaTypeToDescriptor(clazz));
DexType type = factory.createType(desc);
@@ -591,7 +592,7 @@
directMethods,
DexEncodedMethod.EMPTY_ARRAY));
}
- DexApplication application = builder.build();
+ DirectMappedDexApplication application = builder.build().toDirect();
AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
ApplicationWriter writer = new ApplicationWriter(
application, appInfo, options, null, null, NamingLens.getIdentityLens(), null);
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index c64e39c..1aa9be7 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -901,8 +901,7 @@
// Process the application several times. Each time will outline the previous outline.
for (int i = 0; i < count; i++) {
// Build a new application with the Outliner class.
- DexApplication.Builder appBuilder =
- new DexApplication.Builder(processedApplication);
+ DexApplication.Builder appBuilder = processedApplication.builder();
originalApplication = appBuilder.build();
processedApplication = processApplication(originalApplication, options);
assertEquals(i + 3, Iterables.size(processedApplication.classes()));
@@ -912,8 +911,7 @@
options.outline.threshold = 2;
for (int i = 0; i < count; i++) {
// Build a new application with the Outliner class.
- DexApplication.Builder appBuilder =
- new DexApplication.Builder(processedApplication);
+ DexApplication.Builder appBuilder = processedApplication.builder();
originalApplication = appBuilder.build();
processedApplication = processApplication(originalApplication, options);
assertEquals(count - 1 + 3, Iterables.size(processedApplication.classes()));
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 42e289a..c580f74 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.DexOverflowException;
import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -463,7 +462,7 @@
protected DexApplication processApplication(DexApplication application, InternalOptions options) {
try {
- return ToolHelper.optimizeWithR8(application, new AppInfoWithSubtyping(application), options);
+ return ToolHelper.optimizeWithR8(application, options);
} catch (IOException | CompilationException | ExecutionException e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
index 77366fb..3fc329c 100644
--- a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.PackedSwitch;
import com.android.tools.r8.code.SparseSwitch;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -296,7 +295,7 @@
" return");
DexApplication app = builder.read();
- app = ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), new InternalOptions());
+ app = ToolHelper.optimizeWithR8(app, new InternalOptions());
MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
DexEncodedMethod method = getMethod(app, signature);
@@ -354,7 +353,7 @@
" return");
DexApplication app = builder.read();
- app = ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), new InternalOptions());
+ app = ToolHelper.optimizeWithR8(app, new InternalOptions());
MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
DexEncodedMethod method = getMethod(app, signature);
@@ -432,7 +431,7 @@
" return");
DexApplication app = builder.read();
- app = ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), new InternalOptions());
+ app = ToolHelper.optimizeWithR8(app, new InternalOptions());
MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
DexEncodedMethod method = getMethod(app, signature);