Merge "Revert "Revert "Ensure lookupDescriptor do not introduce duplicate minified types"""
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 364a794..e124571 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -18,6 +18,7 @@
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;
+import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -150,7 +151,8 @@
return DescriptorUtils.getClassBinaryNameFromDescriptor(classDescriptor) + CLASS_EXTENSION;
}
- public static void writeResources(Path archive, List<ProgramResource> resources)
+ public static void writeResources(
+ Path archive, List<ProgramResource> resources, Set<DataEntryResource> dataResources)
throws IOException, ResourceException {
OpenOption[] options =
new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
@@ -163,6 +165,11 @@
byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
}
+ for (DataEntryResource dataResource : dataResources) {
+ String entryName = dataResource.getName();
+ byte[] bytes = ByteStreams.toByteArray(closer.register(dataResource.getByteStream()));
+ ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 466a26f..2273c4d 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -204,6 +204,7 @@
new ApplicationWriter(
app,
+ null,
options,
marker == null ? null : ImmutableList.copyOf(markers),
null,
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index b6bb4e0..6974d46 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -95,6 +95,7 @@
ApplicationWriter writer =
new ApplicationWriter(
app,
+ null,
options,
markers,
null,
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index 8731d2a..3ca4bf2 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ZipUtils;
-import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import java.io.IOException;
@@ -180,13 +179,8 @@
outputBuilder.close(handler);
}
- public static void writeResources(Path archive, List<ProgramResource> resources)
- throws IOException, ResourceException {
- writeResources(archive, resources, ImmutableList.of());
- }
-
public static void writeResources(
- Path archive, List<ProgramResource> resources, List<DataEntryResource> dataResources)
+ Path archive, List<ProgramResource> resources, Set<DataEntryResource> dataResources)
throws IOException, ResourceException {
OpenOption[] options =
new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 27c564a..6826f63 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -96,6 +96,7 @@
try {
new ApplicationWriter(
featureApp,
+ null,
options,
markers,
null,
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 4f472c6..065ad10 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -11,7 +11,9 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.AppliedGraphLens;
import com.android.tools.r8.graph.DexApplication;
@@ -184,6 +186,7 @@
static void writeApplication(
ExecutorService executorService,
DexApplication application,
+ AppView<? extends AppInfo> appView,
String deadCode,
GraphLense graphLense,
NamingLens namingLens,
@@ -197,6 +200,7 @@
if (options.isGeneratingClassFiles()) {
new CfApplicationWriter(
application,
+ appView,
options,
marker,
deadCode,
@@ -208,6 +212,7 @@
} else {
new ApplicationWriter(
application,
+ appView,
options,
Collections.singletonList(marker),
deadCode,
@@ -263,6 +268,8 @@
AppView<AppInfoWithSubtyping> appView =
new AppView<>(
new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
+ appView.setAppServices(AppServices.builder(appView).build());
+
List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
RootSet rootSet;
String proguardSeedsData = null;
@@ -483,6 +490,8 @@
new EnumOrdinalMapCollector(appViewWithLiveness, options).run());
}
+ appView.setAppServices(appView.appServices().rewrittenWithLens(appView.graphLense()));
+
timing.begin("Create IR");
Set<DexCallSite> desugaredCallSites;
CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
@@ -619,9 +628,7 @@
NamingLens namingLens;
if (options.enableMinification) {
timing.begin("Minification");
- namingLens =
- new Minifier(appView.appInfo().withLiveness(), rootSet, desugaredCallSites, options)
- .run(timing);
+ namingLens = new Minifier(appView.withLiveness(), rootSet, desugaredCallSites).run(timing);
timing.end();
} else {
namingLens = NamingLens.getIdentityLens();
@@ -665,6 +672,7 @@
writeApplication(
executorService,
application,
+ appView,
application.deadCode,
appView.graphLense(),
namingLens,
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 60167c5..2b661d0 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -178,7 +178,7 @@
InternalOptions options = new InternalOptions();
AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
ApplicationWriter writer =
- new ApplicationWriter(app, options, null, null, null, null, null, null);
+ new ApplicationWriter(app, null, options, null, null, null, null, null, null);
writer.write(executor);
compatSink.build().writeToDirectory(output, OutputMode.DexIndexed);
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 26398d8..7b2c688 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -17,6 +17,8 @@
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationDirectory;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -60,6 +62,7 @@
public class ApplicationWriter {
public final DexApplication application;
+ public final AppView<? extends AppInfo> appView;
public final String deadCode;
public final GraphLense graphLense;
public final NamingLens namingLens;
@@ -129,6 +132,7 @@
public ApplicationWriter(
DexApplication application,
+ AppView<? extends AppInfo> appView,
InternalOptions options,
List<Marker> markers,
String deadCode,
@@ -138,6 +142,7 @@
ProguardMapSupplier proguardMapSupplier) {
this(
application,
+ appView,
options,
markers,
deadCode,
@@ -150,6 +155,7 @@
public ApplicationWriter(
DexApplication application,
+ AppView<? extends AppInfo> appView,
InternalOptions options,
List<Marker> markers,
String deadCode,
@@ -160,6 +166,7 @@
DexIndexedConsumer consumer) {
assert application != null;
this.application = application;
+ this.appView = appView;
assert options != null;
this.options = options;
this.markers = markers;
@@ -298,6 +305,7 @@
// Supply info to all additional resource consumers.
supplyAdditionalConsumers(
application,
+ appView,
graphLense,
namingLens,
options,
@@ -311,6 +319,7 @@
public static void supplyAdditionalConsumers(
DexApplication application,
+ AppView<? extends AppInfo> appView,
GraphLense graphLense,
NamingLens namingLens,
InternalOptions options,
@@ -349,7 +358,7 @@
.collect(Collectors.toList());
ResourceAdapter resourceAdapter =
- new ResourceAdapter(application.dexItemFactory, graphLense, namingLens, options);
+ new ResourceAdapter(appView, application.dexItemFactory, graphLense, namingLens, options);
Set<String> generatedResourceNames = new HashSet<>();
for (DataResourceProvider dataResourceProvider : dataResourceProviders) {
@@ -367,6 +376,10 @@
@Override
public void visit(DataEntryResource file) {
+ if (resourceAdapter.shouldBeDeleted(file)) {
+ return;
+ }
+
DataEntryResource adapted = resourceAdapter.adaptIfNeeded(file);
if (generatedResourceNames.add(adapted.getName())) {
dataResourceConsumer.accept(adapted, options.reporter);
diff --git a/src/main/java/com/android/tools/r8/dex/ResourceAdapter.java b/src/main/java/com/android/tools/r8/dex/ResourceAdapter.java
index ae59576..630f25d 100644
--- a/src/main/java/com/android/tools/r8/dex/ResourceAdapter.java
+++ b/src/main/java/com/android/tools/r8/dex/ResourceAdapter.java
@@ -8,11 +8,15 @@
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardPathFilter;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionDiagnostic;
@@ -27,16 +31,19 @@
public class ResourceAdapter {
+ private final AppView<? extends AppInfo> appView;
private final DexItemFactory dexItemFactory;
private final GraphLense graphLense;
private final NamingLens namingLense;
private final InternalOptions options;
public ResourceAdapter(
+ AppView<? extends AppInfo> appView,
DexItemFactory dexItemFactory,
GraphLense graphLense,
NamingLens namingLense,
InternalOptions options) {
+ this.appView = appView;
this.dexItemFactory = dexItemFactory;
this.graphLense = graphLense;
this.namingLense = namingLense;
@@ -88,8 +95,40 @@
return DataDirectoryResource.fromName(adaptDirectoryName(directory), directory.getOrigin());
}
+ // Returns true for files in META-INF/services/ that are never used by the application.
+ public boolean shouldBeDeleted(DataEntryResource file) {
+ if (appView != null && appView.appInfo().hasLiveness()) {
+ AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
+ if (file.getName().startsWith(AppServices.SERVICE_DIRECTORY_NAME)) {
+ String serviceName = file.getName().substring(AppServices.SERVICE_DIRECTORY_NAME.length());
+ if (!DescriptorUtils.isValidJavaType(serviceName)) {
+ return false;
+ }
+
+ DexString serviceDescriptor =
+ dexItemFactory.lookupString(DescriptorUtils.javaTypeToDescriptor(serviceName));
+ if (serviceDescriptor == null) {
+ return false;
+ }
+
+ DexType serviceType = appView.dexItemFactory().lookupType(serviceDescriptor);
+ if (serviceType == null) {
+ return false;
+ }
+
+ DexType rewrittenServiceType = appView.graphLense().lookupType(serviceType);
+ assert appView.appServices().allServiceTypes().contains(rewrittenServiceType);
+ return !appInfo.instantiatedAppServices.contains(rewrittenServiceType);
+ }
+ }
+ return false;
+ }
+
private String adaptFileName(DataEntryResource file) {
- FileNameAdapter adapter = new FileNameAdapter(file.getName());
+ FileNameAdapter adapter =
+ file.getName().startsWith(AppServices.SERVICE_DIRECTORY_NAME)
+ ? new ServiceFileNameAdapter(file.getName())
+ : new DefaultFileNameAdapter(file.getName());
if (adapter.run()) {
return adapter.getResult();
}
@@ -307,7 +346,7 @@
private void outputRangeFromInput(int from, int toExclusive) {
if (from < toExclusive) {
- result.append(contents.substring(from, toExclusive));
+ result.append(contents, from, toExclusive);
}
}
@@ -362,8 +401,8 @@
}
}
- private abstract class FileNameAdapterBase extends StringAdapter {
- public FileNameAdapterBase(String filename) {
+ private abstract class FileNameAdapter extends StringAdapter {
+ public FileNameAdapter(String filename) {
super(filename);
}
@@ -391,8 +430,8 @@
}
}
- private class FileNameAdapter extends FileNameAdapterBase {
- public FileNameAdapter(String filename) {
+ private class DefaultFileNameAdapter extends FileNameAdapter {
+ public DefaultFileNameAdapter(String filename) {
super(filename);
}
@@ -402,7 +441,28 @@
}
}
- private class DirectoryNameAdapter extends FileNameAdapterBase {
+ private class ServiceFileNameAdapter extends FileNameAdapter {
+ public ServiceFileNameAdapter(String filename) {
+ super(filename);
+ }
+
+ @Override
+ public char getClassNameSeparator() {
+ return '.';
+ }
+
+ @Override
+ public boolean allowRenamingOfPrefixes() {
+ return false;
+ }
+
+ @Override
+ public boolean isRenamingCandidate(int from, int toExclusive) {
+ return from == AppServices.SERVICE_DIRECTORY_NAME.length() && eof(toExclusive);
+ }
+ }
+
+ private class DirectoryNameAdapter extends FileNameAdapter {
public DirectoryNameAdapter(String filename) {
super(filename);
}
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 e1be490..db83f6a 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -10,6 +10,7 @@
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -22,6 +23,10 @@
public final DexItemFactory dexItemFactory;
private final ConcurrentHashMap<DexType, Map<Descriptor<?,?>, KeyedDexItem<?>>> definitions =
new ConcurrentHashMap<>();
+ // For some optimizations, e.g. optimizing synthetic classes, we may need to resolve the current
+ // class being optimized.
+ private ConcurrentHashMap<DexType, DexProgramClass> synthesizedClasses =
+ new ConcurrentHashMap<>();
public AppInfo(DexApplication application) {
this.app = application;
@@ -42,6 +47,16 @@
this(application);
}
+ public void addSynthesizedClass(DexProgramClass clazz) {
+ assert clazz.type.isD8R8SynthesizedClassType();
+ DexProgramClass previous = synthesizedClasses.put(clazz.type, clazz);
+ assert previous == null || previous == clazz;
+ }
+
+ public Collection<DexProgramClass> getSynthesizedClassesForSanityCheck() {
+ return Collections.unmodifiableCollection(synthesizedClasses.values());
+ }
+
private Map<Descriptor<?,?>, KeyedDexItem<?>> computeDefinitions(DexType type) {
Builder<Descriptor<?,?>, KeyedDexItem<?>> builder = ImmutableMap.builder();
DexClass clazz = app.definitionFor(type);
@@ -72,6 +87,11 @@
}
public DexClass definitionFor(DexType type) {
+ DexProgramClass cached = synthesizedClasses.get(type);
+ if (cached != null) {
+ assert app.definitionFor(type) == null;
+ return cached;
+ }
return app.definitionFor(type);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
new file mode 100644
index 0000000..ec22d85
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -0,0 +1,178 @@
+// Copyright (c) 2019, 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.DataDirectoryResource;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceProvider;
+import com.android.tools.r8.DataResourceProvider.Visitor;
+import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/** A description of the services and their implementations found in META-INF/services/. */
+public class AppServices {
+
+ public static final String SERVICE_DIRECTORY_NAME = "META-INF/services/";
+
+ private final AppView<? extends AppInfo> appView;
+
+ // Mapping from service types to service implementation types.
+ private final Map<DexType, Set<DexType>> services;
+
+ private AppServices(AppView<? extends AppInfo> appView, Map<DexType, Set<DexType>> services) {
+ this.appView = appView;
+ this.services = services;
+ }
+
+ public Set<DexType> allServiceTypes() {
+ assert verifyRewrittenWithLens();
+ return services.keySet();
+ }
+
+ public Set<DexType> serviceImplementationsFor(DexType serviceType) {
+ assert verifyRewrittenWithLens();
+ assert services.containsKey(serviceType);
+ Set<DexType> serviceImplementationTypes = services.get(serviceType);
+ if (serviceImplementationTypes == null) {
+ assert false
+ : "Unexpected attempt to get service implementations for non-service type `"
+ + serviceType.toSourceString()
+ + "`";
+ return ImmutableSet.of();
+ }
+ return serviceImplementationTypes;
+ }
+
+ public AppServices rewrittenWithLens(GraphLense graphLens) {
+ ImmutableMap.Builder<DexType, Set<DexType>> rewrittenServices = ImmutableMap.builder();
+ for (Entry<DexType, Set<DexType>> entry : services.entrySet()) {
+ DexType rewrittenServiceType = graphLens.lookupType(entry.getKey());
+ ImmutableSet.Builder<DexType> rewrittenServiceImplementationTypes = ImmutableSet.builder();
+ for (DexType serviceImplementationType : entry.getValue()) {
+ rewrittenServiceImplementationTypes.add(graphLens.lookupType(serviceImplementationType));
+ }
+ rewrittenServices.put(rewrittenServiceType, rewrittenServiceImplementationTypes.build());
+ }
+ return new AppServices(appView, rewrittenServices.build());
+ }
+
+ private boolean verifyRewrittenWithLens() {
+ for (Entry<DexType, Set<DexType>> entry : services.entrySet()) {
+ assert entry.getKey() == appView.graphLense().lookupType(entry.getKey());
+ for (DexType type : entry.getValue()) {
+ assert type == appView.graphLense().lookupType(type);
+ }
+ }
+ return true;
+ }
+
+ public static Builder builder(AppView<? extends AppInfo> appView) {
+ return new Builder(appView);
+ }
+
+ public static class Builder {
+
+ private final AppView<? extends AppInfo> appView;
+ private final Map<DexType, Set<DexType>> services = new IdentityHashMap<>();
+
+ private Builder(AppView<? extends AppInfo> appView) {
+ this.appView = appView;
+ }
+
+ public AppServices build() {
+ Iterable<ProgramResourceProvider> programResourceProviders =
+ appView.appInfo().app.programResourceProviders;
+ for (ProgramResourceProvider programResourceProvider : programResourceProviders) {
+ DataResourceProvider dataResourceProvider =
+ programResourceProvider.getDataResourceProvider();
+ if (dataResourceProvider != null) {
+ readServices(dataResourceProvider);
+ }
+ }
+ return new AppServices(appView, services);
+ }
+
+ private void readServices(DataResourceProvider dataResourceProvider) {
+ try {
+ dataResourceProvider.accept(new DataResourceProviderVisitor());
+ } catch (ResourceException e) {
+ throw new CompilationError(e.getMessage(), e);
+ }
+ }
+
+ private class DataResourceProviderVisitor implements Visitor {
+
+ @Override
+ public void visit(DataDirectoryResource directory) {
+ // Ignore.
+ }
+
+ @Override
+ public void visit(DataEntryResource file) {
+ try {
+ String name = file.getName();
+ if (name.startsWith(SERVICE_DIRECTORY_NAME)) {
+ String serviceName = name.substring(SERVICE_DIRECTORY_NAME.length());
+ if (DescriptorUtils.isValidJavaType(serviceName)) {
+ String serviceDescriptor = DescriptorUtils.javaTypeToDescriptor(serviceName);
+ DexType serviceType = appView.dexItemFactory().createType(serviceDescriptor);
+ byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+ String contents = new String(bytes, Charset.defaultCharset());
+ services.put(
+ serviceType, readServiceImplementationsForService(contents, file.getOrigin()));
+ }
+ }
+ } catch (IOException | ResourceException e) {
+ throw new CompilationError(e.getMessage(), e);
+ }
+ }
+
+ private Set<DexType> readServiceImplementationsForService(String contents, Origin origin) {
+ if (contents != null) {
+ return Arrays.stream(contents.split(System.lineSeparator()))
+ .map(String::trim)
+ .filter(line -> !line.isEmpty())
+ .filter(DescriptorUtils::isValidJavaType)
+ .map(DescriptorUtils::javaTypeToDescriptor)
+ .map(appView.dexItemFactory()::createType)
+ .filter(
+ serviceImplementationType -> {
+ if (!serviceImplementationType.isClassType()) {
+ // Should never happen.
+ appView
+ .options()
+ .reporter
+ .warning(
+ new StringDiagnostic(
+ "Unexpected service implementation found in META-INF/services/: `"
+ + serviceImplementationType.toSourceString()
+ + "`.",
+ origin));
+ return false;
+ }
+ return true;
+ })
+ .collect(Collectors.toSet());
+ }
+ return ImmutableSet.of();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index e232cb9..084ad80 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -11,6 +11,7 @@
public class AppView<T extends AppInfo> {
private T appInfo;
+ private AppServices appServices;
private final DexItemFactory dexItemFactory;
private GraphLense graphLense;
private final InternalOptions options;
@@ -31,6 +32,14 @@
this.appInfo = appInfo;
}
+ public AppServices appServices() {
+ return appServices;
+ }
+
+ public void setAppServices(AppServices appServices) {
+ this.appServices = appServices;
+ }
+
public DexItemFactory dexItemFactory() {
return dexItemFactory;
}
@@ -86,6 +95,16 @@
}
@Override
+ public AppServices appServices() {
+ return AppView.this.appServices();
+ }
+
+ @Override
+ public void setAppServices(AppServices appServices) {
+ AppView.this.setAppServices(appServices);
+ }
+
+ @Override
public DexItemFactory dexItemFactory() {
return AppView.this.dexItemFactory();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 0efc380..96f34bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -114,7 +114,7 @@
// The only way to figure out whether the DexValue contains the final value
// is ensure the value is not the default or check <clinit> is not present.
boolean isEffectivelyFinal =
- (accessFlags.isFinal() || !appInfo.fieldsWritten.contains(field))
+ (accessFlags.isFinal() || !appInfo.isFieldWritten(field))
&& !appInfo.isPinned(field);
if (!isEffectivelyFinal) {
return null;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index e7b3967..3ce4a75 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -170,6 +170,7 @@
public final DexString objectDescriptor = createString("Ljava/lang/Object;");
public final DexString objectArrayDescriptor = createString("[Ljava/lang/Object;");
public final DexString classDescriptor = createString("Ljava/lang/Class;");
+ public final DexString classLoaderDescriptor = createString("Ljava/lang/ClassLoader;");
public final DexString autoCloseableDescriptor = createString("Ljava/lang/AutoCloseable;");
public final DexString classArrayDescriptor = createString("[Ljava/lang/Class;");
public final DexString fieldDescriptor = createString("Ljava/lang/reflect/Field;");
@@ -187,6 +188,7 @@
public final DexString methodTypeDescriptor = createString("Ljava/lang/invoke/MethodType;");
public final DexString npeDescriptor = createString("Ljava/lang/NullPointerException;");
+ public final DexString serviceLoaderDescriptor = createString("Ljava/util/ServiceLoader;");
public final DexString intFieldUpdaterDescriptor =
createString("Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater;");
@@ -237,6 +239,7 @@
public final DexType exceptionInInitializerErrorType =
createType(exceptionInInitializerErrorDescriptor);
public final DexType classType = createType(classDescriptor);
+ public final DexType classLoaderType = createType(classLoaderDescriptor);
public final DexType autoCloseableType = createType(autoCloseableDescriptor);
public final DexType stringBuilderType = createType(stringBuilderDescriptor);
@@ -247,6 +250,7 @@
public final DexType methodTypeType = createType(methodTypeDescriptor);
public final DexType npeType = createType(npeDescriptor);
+ public final DexType serviceLoaderType = createType(serviceLoaderDescriptor);
public final StringBuildingMethods stringBuilderMethods =
new StringBuildingMethods(stringBuilderType);
@@ -266,6 +270,7 @@
new AtomicFieldUpdaterMethods();
public final Kotlin kotlin;
public final PolymorphicMethods polymorphicMethods = new PolymorphicMethods();
+ public final ServiceLoaderMethods serviceLoaderMethods = new ServiceLoaderMethods();
public final DexString twrCloseResourceMethodName = createString("$closeResource");
public final DexProto twrCloseResourceMethodProto =
@@ -790,6 +795,32 @@
}
}
+ public class ServiceLoaderMethods {
+
+ public final DexMethod load;
+ public final DexMethod loadWithClassLoader;
+ public final DexMethod loadInstalled;
+
+ private ServiceLoaderMethods() {
+ DexString loadName = createString("load");
+ load = createMethod(serviceLoaderType, createProto(serviceLoaderType, classType), loadName);
+ loadWithClassLoader =
+ createMethod(
+ serviceLoaderType,
+ createProto(serviceLoaderType, classType, classLoaderType),
+ loadName);
+ loadInstalled =
+ createMethod(
+ serviceLoaderType,
+ createProto(serviceLoaderType, classType),
+ createString("loadInstalled"));
+ }
+
+ public boolean isLoadMethod(DexMethod method) {
+ return method == load || method == loadWithClassLoader || method == loadInstalled;
+ }
+ }
+
private static <T extends DexItem> T canonicalize(ConcurrentHashMap<T, T> map, T item) {
assert item != null;
assert !DexItemFactory.isInternalSentinel(item);
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 2ebccfe..249a2b5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -10,7 +10,8 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.ir.desugar.Java8MethodRewriter;
+import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
@@ -221,14 +222,6 @@
}
while (other.hierarchyLevel < self.hierarchyLevel) {
DexClass holder = appInfo.definitionFor(self);
- // TODO(b/113374256): even synthesized class should be available ATM.
- if (holder == null) {
- assert self.isD8R8SynthesizedClassType();
- if (Log.ENABLED) {
- Log.debug(getClass(), "%s is not in AppInfo yet.", self.toSourceString());
- }
- return orElse;
- }
assert holder != null && !holder.isInterface();
self = holder.superType;
}
@@ -483,7 +476,9 @@
|| name.contains(DISPATCH_CLASS_NAME_SUFFIX)
|| name.contains(LAMBDA_CLASS_NAME_PREFIX)
|| name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX)
- || name.contains(OutlineOptions.CLASS_NAME);
+ || name.contains(OutlineOptions.CLASS_NAME)
+ || name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME)
+ || name.contains(Java8MethodRewriter.UTILITY_CLASS_NAME_PREFIX);
}
public int elementSizeForPrimitiveArrayType() {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 5fd473f..b964dd3 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -388,9 +388,7 @@
public abstract DexMethod getRenamedMethodSignature(DexMethod originalMethod);
public DexEncodedMethod mapDexEncodedMethod(
- DexEncodedMethod originalEncodedMethod,
- AppInfo appInfo,
- Map<DexType, DexProgramClass> synthesizedClasses) {
+ DexEncodedMethod originalEncodedMethod, AppInfo appInfo) {
DexMethod newMethod = getRenamedMethodSignature(originalEncodedMethod.method);
// Note that:
// * Even if `newMethod` is the same as `originalEncodedMethod.method`, we still need to look it
@@ -398,14 +396,7 @@
// * We can't directly use AppInfo#definitionFor(DexMethod) since definitions may not be
// updated either yet.
DexClass newHolder = appInfo.definitionFor(newMethod.holder);
-
- // TODO(b/120130831): Need to ensure that all synthesized classes are part of the application.
- if (newHolder == null) {
- newHolder = synthesizedClasses.get(newMethod.holder);
- }
-
assert newHolder != null;
-
DexEncodedMethod newEncodedMethod = newHolder.lookupMethod(newMethod);
assert newEncodedMethod != null;
return newEncodedMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
index b4ff421..ad95b05 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
@@ -45,7 +45,7 @@
public void run() {
- BasicBlock firstBlock = code.blocks.get(0);
+ BasicBlock firstBlock = code.entryBlock();
visitInstructions(firstBlock);
while (!flowEdges.isEmpty() || !ssaEdges.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 04f56f5..1ab0670 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -303,7 +303,7 @@
for (int i = 0; i < inlinee.blocks.size(); i++) {
blocksIterator.previous();
}
- assert IteratorUtils.peekNext(blocksIterator) == inlinee.blocks.getFirst();
+ assert IteratorUtils.peekNext(blocksIterator) == inlinee.entryBlock();
// Iterate through the inlined blocks.
for (BasicBlock inlinedBlock : inlinee.blocks) {
BasicBlock expected = blocksIterator.next();
@@ -364,7 +364,7 @@
List<Value> arguments = inlinee.collectArguments();
assert invoke.inValues().size() == arguments.size();
- BasicBlock entryBlock = inlinee.blocks.getFirst();
+ BasicBlock entryBlock = inlinee.entryBlock();
InstructionListIterator entryBlockIterator;
int i = 0;
@@ -425,7 +425,7 @@
new TypeAnalysis(appInfo, inlinee.method).narrowing(argumentUsers);
// The inline entry is the first block now the argument instructions are gone.
- BasicBlock inlineEntry = inlinee.blocks.getFirst();
+ BasicBlock inlineEntry = inlinee.entryBlock();
BasicBlock inlineExit = null;
List<BasicBlock> normalExits = inlinee.computeNormalExitBlocks();
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 870ae6f..0d96f0d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -136,6 +136,10 @@
this.hasConstString |= inlinee.hasConstString;
}
+ public BasicBlock entryBlock() {
+ return blocks.getFirst();
+ }
+
/**
* Compute the set of live values at the entry to each block using a backwards data-flow analysis.
*/
@@ -416,7 +420,7 @@
ArrayList<BasicBlock> reverseOrdered = new ArrayList<>(blocks.size());
Set<BasicBlock> visitedBlocks = new HashSet<>(blocks.size());
Deque<Object> worklist = new ArrayDeque<>(blocks.size());
- worklist.addLast(blocks.getFirst());
+ worklist.addLast(entryBlock());
while (!worklist.isEmpty()) {
Object item = worklist.removeLast();
if (item instanceof BlockMarker) {
@@ -768,7 +772,7 @@
public List<Value> collectArguments(boolean ignoreReceiver) {
final List<Value> arguments = new ArrayList<>();
- Iterator<Instruction> iterator = blocks.get(0).iterator();
+ Iterator<Instruction> iterator = entryBlock().iterator();
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
if (instruction.isArgument()) {
@@ -787,7 +791,7 @@
if (method.accessFlags.isStatic()) {
return null;
}
- Instruction firstArg = blocks.getFirst().listIterator().nextUntil(Instruction::isArgument);
+ Instruction firstArg = entryBlock().listIterator().nextUntil(Instruction::isArgument);
assert firstArg != null;
Value thisValue = firstArg.asArgument().outValue();
assert thisValue.isThis();
@@ -910,7 +914,7 @@
public Set<BasicBlock> getUnreachableBlocks() {
Set<BasicBlock> unreachableBlocks = Sets.newIdentityHashSet();
int color = reserveMarkingColor();
- markTransitiveSuccessors(blocks.getFirst(), color);
+ markTransitiveSuccessors(entryBlock(), color);
for (BasicBlock block : blocks) {
if (!block.isMarked(color)) {
unreachableBlocks.add(block);
@@ -923,7 +927,7 @@
public Set<Value> removeUnreachableBlocks() {
ImmutableSet.Builder<Value> affectedValueBuilder = ImmutableSet.builder();
int color = reserveMarkingColor();
- markTransitiveSuccessors(blocks.getFirst(), color);
+ markTransitiveSuccessors(entryBlock(), color);
ListIterator<BasicBlock> blockIterator = listIterator();
while (blockIterator.hasNext()) {
BasicBlock current = blockIterator.next();
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 cb7a5d5..ff6abd3 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
@@ -89,12 +89,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -149,10 +147,6 @@
private final OptimizationFeedback simpleOptimizationFeedback = new OptimizationFeedbackSimple();
private DexString highestSortingString;
- // For some optimizations, e.g. optimizing synthetic classes, we may need to resolve
- // the current class being optimized.
- private ConcurrentHashMap<DexType, DexProgramClass> cachedClasses = new ConcurrentHashMap<>();
-
// The argument `appView` is only available when full program optimizations are allowed
// (i.e., when running R8).
private IRConverter(
@@ -352,30 +346,24 @@
InterfaceMethodRewriter.Flavor includeAllResources,
ExecutorService executorService)
throws ExecutionException {
- desugarInterfaceMethods(builder, includeAllResources, executorService, null);
- }
-
- private void desugarInterfaceMethods(
- Builder<?> builder,
- InterfaceMethodRewriter.Flavor includeAllResources,
- ExecutorService executorService,
- Map<DexType, DexProgramClass> synthesizedClasses)
- throws ExecutionException {
if (interfaceMethodRewriter != null) {
interfaceMethodRewriter.desugarInterfaceMethods(
- builder, includeAllResources, executorService, synthesizedClasses);
+ builder, includeAllResources, executorService);
}
}
- private void synthesizeTwrCloseResourceUtilityClass(Builder<?> builder) {
+ private void synthesizeTwrCloseResourceUtilityClass(
+ Builder<?> builder, ExecutorService executorService)
+ throws ExecutionException {
if (twrCloseResourceRewriter != null) {
- twrCloseResourceRewriter.synthesizeUtilityClass(builder, options);
+ twrCloseResourceRewriter.synthesizeUtilityClass(builder, executorService, options);
}
}
- private void synthesizeJava8UtilityClass(Builder<?> builder) {
+ private void synthesizeJava8UtilityClass(
+ Builder<?> builder, ExecutorService executorService) throws ExecutionException {
if (java8MethodRewriter != null) {
- java8MethodRewriter.synthesizeUtilityClass(builder, options);
+ java8MethodRewriter.synthesizeUtilityClass(builder, executorService, options);
}
}
@@ -398,8 +386,8 @@
synthesizeLambdaClasses(builder, executor);
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
- synthesizeTwrCloseResourceUtilityClass(builder);
- synthesizeJava8UtilityClass(builder);
+ synthesizeTwrCloseResourceUtilityClass(builder, executor);
+ synthesizeJava8UtilityClass(builder, executor);
processCovariantReturnTypeAnnotations(builder);
handleSynthesizedClassMapping(builder);
@@ -590,21 +578,20 @@
synthesizeLambdaClasses(builder, executorService);
printPhase("Interface method desugaring");
- Map<DexType, DexProgramClass> synthesizedClasses = new IdentityHashMap<>();
- desugarInterfaceMethods(builder, IncludeAllResources, executorService, synthesizedClasses);
+ desugarInterfaceMethods(builder, IncludeAllResources, executorService);
printPhase("Twr close resource utility class synthesis");
- synthesizeTwrCloseResourceUtilityClass(builder);
- synthesizeJava8UtilityClass(builder);
+ synthesizeTwrCloseResourceUtilityClass(builder, executorService);
+ synthesizeJava8UtilityClass(builder, executorService);
handleSynthesizedClassMapping(builder);
printPhase("Lambda merging finalization");
- finalizeLambdaMerging(application, feedback, builder, executorService, synthesizedClasses);
+ finalizeLambdaMerging(application, feedback, builder, executorService);
if (outliner != null) {
printPhase("Outlining");
timing.begin("IR conversion phase 2");
- if (outliner.selectMethodsForOutlining(synthesizedClasses)) {
+ if (outliner.selectMethodsForOutlining()) {
forEachSelectedOutliningMethod(
executorService,
(code, method) -> {
@@ -612,7 +599,8 @@
outliner.identifyOutlineSites(code, method);
});
DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
- optimizeSynthesizedClass(outlineClass);
+ appInfo.addSynthesizedClass(outlineClass);
+ optimizeSynthesizedClass(outlineClass, executorService);
forEachSelectedOutliningMethod(
executorService,
(code, method) -> {
@@ -636,6 +624,12 @@
uninstantiatedTypeOptimization.logResults();
}
+ // Check if what we've added to the application builder as synthesized classes are same as
+ // what we've added and used through AppInfo.
+ assert appInfo.getSynthesizedClassesForSanityCheck()
+ .containsAll(builder.getSynthesizedClasses())
+ && builder.getSynthesizedClasses()
+ .containsAll(appInfo.getSynthesizedClassesForSanityCheck());
return builder.build();
}
@@ -687,14 +681,13 @@
private void finalizeLambdaMerging(
DexApplication application,
- OptimizationFeedback directFeedback,
+ OptimizationFeedback feedback,
Builder<?> builder,
- ExecutorService executorService,
- Map<DexType, DexProgramClass> synthesizedClasses)
+ ExecutorService executorService)
throws ExecutionException {
if (lambdaMerger != null) {
lambdaMerger.applyLambdaClassMapping(
- application, this, directFeedback, builder, executorService, synthesizedClasses);
+ application, this, feedback, builder, executorService);
}
}
@@ -744,49 +737,24 @@
return result;
}
- public DexClass definitionFor(DexType type) {
- DexProgramClass cached = cachedClasses.get(type);
- return cached != null ? cached : appInfo.definitionFor(type);
- }
-
- public void optimizeSynthesizedClass(DexProgramClass clazz) {
- try {
- enterCachedClass(clazz);
- // Process the generated class, but don't apply any outlining.
- clazz.forEachMethod(this::optimizeSynthesizedMethod);
- } finally {
- leaveCachedClass(clazz);
- }
+ public void optimizeSynthesizedClass(
+ DexProgramClass clazz, ExecutorService executorService)
+ throws ExecutionException {
+ Set<DexEncodedMethod> methods = Sets.newIdentityHashSet();
+ clazz.forEachMethod(methods::add);
+ // Process the generated class, but don't apply any outlining.
+ optimizeSynthesizedMethodsConcurrently(methods, executorService);
}
public void optimizeSynthesizedClasses(
Collection<DexProgramClass> classes, ExecutorService executorService)
throws ExecutionException {
Set<DexEncodedMethod> methods = Sets.newIdentityHashSet();
- try {
- for (DexProgramClass clazz : classes) {
- enterCachedClass(clazz);
- clazz.forEachMethod(methods::add);
- }
- // Process the generated class, but don't apply any outlining.
- optimizeSynthesizedMethods(methods, executorService);
- } finally {
- for (DexProgramClass clazz : classes) {
- leaveCachedClass(clazz);
- }
+ for (DexProgramClass clazz : classes) {
+ clazz.forEachMethod(methods::add);
}
- }
-
- public void optimizeMethodOnSynthesizedClass(DexProgramClass clazz, DexEncodedMethod method) {
- if (!method.isProcessed()) {
- try {
- enterCachedClass(clazz);
- // Process the generated method, but don't apply any outlining.
- optimizeSynthesizedMethod(method);
- } finally {
- leaveCachedClass(clazz);
- }
- }
+ // Process the generated class, but don't apply any outlining.
+ optimizeSynthesizedMethodsConcurrently(methods, executorService);
}
public void optimizeSynthesizedMethod(DexEncodedMethod method) {
@@ -801,7 +769,7 @@
}
}
- public void optimizeSynthesizedMethods(
+ public void optimizeSynthesizedMethodsConcurrently(
Collection<DexEncodedMethod> methods, ExecutorService executorService)
throws ExecutionException {
List<Future<?>> futures = new ArrayList<>();
@@ -821,16 +789,6 @@
ThreadUtils.awaitFutures(futures);
}
- private void enterCachedClass(DexProgramClass clazz) {
- DexProgramClass previous = cachedClasses.put(clazz.type, clazz);
- assert previous == null;
- }
-
- private void leaveCachedClass(DexProgramClass clazz) {
- DexProgramClass existing = cachedClasses.remove(clazz.type);
- assert existing == clazz;
- }
-
private String logCode(InternalOptions options, DexEncodedMethod method) {
return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
}
@@ -1198,7 +1156,8 @@
// original method signature (this could have changed as a result of, for example, class
// merging). Then, we find the type that now corresponds to the the original holder.
DexMethod originalSignature = graphLense().getOriginalMethodSignature(method.method);
- DexClass originalHolder = definitionFor(graphLense().lookupType(originalSignature.holder));
+ DexClass originalHolder = appInfo.definitionFor(
+ graphLense().lookupType(originalSignature.holder));
if (originalHolder.hasKotlinInfo()) {
KotlinInfo kotlinInfo = originalHolder.getKotlinInfo();
if (kotlinInfo.hasNonNullParameterHints()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index ec788a4..401feaa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -60,7 +60,7 @@
DexType superType = clazz.superType;
// If superClass definition is missing, just skip this part and let real processing of its
// subclasses report the error if it is required.
- DexClass superClass = superType == null ? null : rewriter.findDefinitionFor(superType);
+ DexClass superClass = superType == null ? null : rewriter.appInfo.definitionFor(superType);
if (superClass != null && superType != rewriter.factory.objectType) {
if (superClass.isInterface()) {
throw new CompilationError("Interface `" + superClass.toSourceString()
@@ -96,7 +96,7 @@
private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
DexMethod method = defaultMethod.method;
- DexClass target = rewriter.findDefinitionFor(method.holder);
+ DexClass target = rewriter.appInfo.definitionFor(method.holder);
// NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
// even if this results in invalid code, these classes are never desugared.
assert target != null && !target.isLibraryClass();
@@ -168,7 +168,7 @@
if (current.superType == null) {
break;
} else {
- DexClass superClass = rewriter.findDefinitionFor(current.superType);
+ DexClass superClass = rewriter.appInfo.definitionFor(current.superType);
if (superClass != null) {
current = superClass;
} else {
@@ -206,7 +206,7 @@
DexType superType = current.superType;
DexClass superClass = null;
if (superType != null) {
- superClass = rewriter.findDefinitionFor(superType);
+ superClass = rewriter.appInfo.definitionFor(superType);
// It's available or we would have failed while analyzing the hierarchy for interfaces.
assert superClass != null;
}
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 e79017d..abd38a5 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication.Builder;
@@ -80,6 +81,7 @@
public static final String PRIVATE_METHOD_PREFIX = "$private$";
private final AppView<? extends AppInfoWithLiveness> appView;
+ final AppInfo appInfo;
private final IRConverter converter;
private final InternalOptions options;
final DexItemFactory factory;
@@ -121,6 +123,7 @@
assert converter != null;
this.appView = appView;
this.converter = converter;
+ this.appInfo = converter.appInfo;
this.options = options;
this.factory = options.itemFactory;
}
@@ -156,7 +159,7 @@
if (instruction.isInvokeStatic()) {
InvokeStatic invokeStatic = instruction.asInvokeStatic();
DexMethod method = invokeStatic.getInvokedMethod();
- DexClass clazz = findDefinitionFor(method.holder);
+ DexClass clazz = appInfo.definitionFor(method.holder);
if (Java8MethodRewriter.hasJava8MethodRewritePrefix(method.holder)) {
// We did not create this code yet, but it will not require rewriting.
continue;
@@ -187,7 +190,7 @@
invokeStatic.outValue(), invokeStatic.arguments()));
requiredDispatchClasses
.computeIfAbsent(clazz.asLibraryClass(), k -> Sets.newConcurrentHashSet())
- .add(findDefinitionFor(encodedMethod.method.holder).asProgramClass());
+ .add(appInfo.definitionFor(encodedMethod.method.holder).asProgramClass());
}
} else {
instructions.replaceCurrentInstruction(
@@ -201,7 +204,7 @@
if (instruction.isInvokeSuper()) {
InvokeSuper invokeSuper = instruction.asInvokeSuper();
DexMethod method = invokeSuper.getInvokedMethod();
- DexClass clazz = findDefinitionFor(method.holder);
+ DexClass clazz = appInfo.definitionFor(method.holder);
if (clazz == null) {
// NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
// exception but we can not report it as error since it can also be the intended
@@ -218,7 +221,7 @@
// WARNING: This may result in incorrect code on older platforms!
// Retarget call to an appropriate method of companion class.
DexMethod amendedMethod = amendDefaultMethod(
- findDefinitionFor(encodedMethod.method.holder), method);
+ appInfo.definitionFor(encodedMethod.method.holder), method);
instructions.replaceCurrentInstruction(
new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
invokeSuper.outValue(), invokeSuper.arguments()));
@@ -233,7 +236,7 @@
continue;
}
- DexClass clazz = findDefinitionFor(method.holder);
+ DexClass clazz = appInfo.definitionFor(method.holder);
if (clazz == null) {
// Report missing class since we don't know if it is an interface.
warnMissingType(encodedMethod.method, method.holder);
@@ -279,7 +282,7 @@
private void reportStaticInterfaceMethodHandle(DexMethod referencedFrom, DexMethodHandle handle) {
if (handle.type.isInvokeStatic()) {
- DexClass holderClass = findDefinitionFor(handle.asMethod().holder);
+ DexClass holderClass = appInfo.definitionFor(handle.asMethod().holder);
// NOTE: If the class definition is missing we can't check. Let it be handled as any other
// missing call target.
if (holderClass == null) {
@@ -292,15 +295,6 @@
}
}
- /**
- * Returns the class definition for the specified type.
- *
- * @return may return null if no definition for the given type is available.
- */
- final DexClass findDefinitionFor(DexType type) {
- return converter.definitionFor(type);
- }
-
// Gets the companion class for the interface `type`.
final DexType getCompanionClassType(DexType type) {
assert type.isClassType();
@@ -334,7 +328,7 @@
}
private boolean isInMainDexList(DexType iface) {
- return converter.appInfo.isInMainDexList(iface);
+ return appInfo.isInMainDexList(iface);
}
// Represent a static interface method as a method of companion class.
@@ -393,8 +387,7 @@
public void desugarInterfaceMethods(
Builder<?> builder,
Flavor flavour,
- ExecutorService executorService,
- Map<DexType, DexProgramClass> synthesizedClasses)
+ ExecutorService executorService)
throws ExecutionException {
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
@@ -409,13 +402,10 @@
// are just moved from interfaces and don't need to be re-processed.
DexProgramClass synthesizedClass = entry.getValue();
builder.addSynthesizedClass(synthesizedClass, isInMainDexList(entry.getKey()));
-
- if (synthesizedClasses != null) {
- synthesizedClasses.put(synthesizedClass.type, synthesizedClass);
- }
+ appInfo.addSynthesizedClass(synthesizedClass);
}
- converter.optimizeSynthesizedMethods(synthesizedMethods, executorService);
+ converter.optimizeSynthesizedMethodsConcurrently(synthesizedMethods, executorService);
// Cached data is not needed any more.
clear();
@@ -528,7 +518,7 @@
if (isCompanionClassType(holder)) {
holder = getInterfaceClassType(holder);
}
- DexClass clazz = converter.appInfo.definitionFor(holder);
+ DexClass clazz = appInfo.definitionFor(holder);
return clazz == null ? Origin.unknown() : clazz.getOrigin();
}
@@ -550,7 +540,7 @@
DexClass implementing,
DexType iface) {
DefaultMethodsHelper helper = new DefaultMethodsHelper();
- DexClass definedInterface = findDefinitionFor(iface);
+ DexClass definedInterface = appInfo.definitionFor(iface);
if (definedInterface == null) {
warnMissingInterface(classToDesugar, implementing, iface);
return helper.wrapInCollection();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 054b950..e4d4b01 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -322,7 +322,7 @@
if (!seenBefore.add(superType)) {
continue;
}
- DexClass clazz = rewriter.findDefinitionFor(superType);
+ DexClass clazz = rewriter.appInfo.definitionFor(superType);
if (clazz != null) {
if (clazz.lookupVirtualMethod(method.method) != null) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
index 9e2e9ab..e930bcf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
@@ -37,9 +37,12 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
public final class Java8MethodRewriter {
+ public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$java8methods$utility";
private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L$r8$java8methods$utility";
private final Set<DexType> holders = Sets.newConcurrentHashSet();
private final IRConverter converter;
@@ -88,7 +91,9 @@
return clazz.descriptor.toString().startsWith(UTILITY_CLASS_DESCRIPTOR_PREFIX);
}
- public void synthesizeUtilityClass(Builder<?> builder, InternalOptions options) {
+ public void synthesizeUtilityClass(
+ Builder<?> builder, ExecutorService executorService, InternalOptions options)
+ throws ExecutionException {
if (holders.isEmpty()) {
return;
}
@@ -134,7 +139,8 @@
code.setUpContext(utilityClass);
boolean addToMainDexList = referencingClasses.stream()
.anyMatch(clazz -> converter.appInfo.isInMainDexList(clazz.type));
- converter.optimizeSynthesizedClass(utilityClass);
+ converter.appInfo.addSynthesizedClass(utilityClass);
+ converter.optimizeSynthesizedClass(utilityClass, executorService);
builder.addSynthesizedClass(utilityClass, addToMainDexList);
}
}
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 865a62c..cff5392 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
@@ -153,8 +153,9 @@
synthesizeVirtualMethods(mainMethod),
rewriter.factory.getSkipNameValidationForTesting());
// Optimize main method.
- rewriter.converter.optimizeMethodOnSynthesizedClass(
- clazz, clazz.lookupVirtualMethod(mainMethod));
+ rewriter.converter.appInfo.addSynthesizedClass(clazz);
+ rewriter.converter.optimizeSynthesizedMethod(clazz.lookupVirtualMethod(mainMethod));
+
// The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent-
// ModificationException we must use synchronization.
synchronized (synthesizedFrom) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 4e29e6f..a1bb373 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -181,14 +181,15 @@
/** Generates lambda classes and adds them to the builder. */
public void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
throws ExecutionException {
+ for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
+ DexProgramClass synthesizedClass = lambdaClass.getLambdaClass();
+ appInfo.addSynthesizedClass(synthesizedClass);
+ builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
+ }
converter.optimizeSynthesizedClasses(
knownLambdaClasses.values().stream()
.map(LambdaClass::getLambdaClass).collect(ImmutableSet.toImmutableSet()),
executorService);
- for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
- DexProgramClass synthesizedClass = lambdaClass.getLambdaClass();
- builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
- }
}
public Set<DexCallSite> getDesugaredCallSites() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 568e021..965d44e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -31,6 +31,8 @@
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
// Try with resources outlining processor. Handles $closeResource methods
// synthesized by java 9 compiler.
@@ -45,6 +47,7 @@
// tree shaking to remove them since now they should not be referenced.
//
public final class TwrCloseResourceRewriter {
+ public static final String UTILITY_CLASS_NAME = "$r8$twr$utility";
public static final String UTILITY_CLASS_DESCRIPTOR = "L$r8$twr$utility;";
private final IRConverter converter;
@@ -107,7 +110,9 @@
&& original.proto == converter.appInfo.dexItemFactory.twrCloseResourceMethodProto;
}
- public void synthesizeUtilityClass(Builder<?> builder, InternalOptions options) {
+ public void synthesizeUtilityClass(
+ Builder<?> builder, ExecutorService executorService, InternalOptions options)
+ throws ExecutionException {
if (referencingClasses.isEmpty()) {
return;
}
@@ -144,7 +149,8 @@
// Process created class and method.
boolean addToMainDexList = referencingClasses.stream()
.anyMatch(clazz -> converter.appInfo.isInMainDexList(clazz.type));
- converter.optimizeSynthesizedClass(utilityClass);
+ converter.appInfo.addSynthesizedClass(utilityClass);
+ converter.optimizeSynthesizedClass(utilityClass, executorService);
builder.addSynthesizedClass(utilityClass, addToMainDexList);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index f7baf3e..b5f3a2d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -201,7 +201,7 @@
BasicBlock blk = block; // Additional local for lambda below.
assert !block.isTrivialGoto()
|| block.exit().asGoto().getTarget() == block
- || code.blocks.get(0) == block
+ || code.entryBlock() == block
|| block.getPredecessors().stream().anyMatch((b) -> b.exit().fallthroughBlock() == blk);
// Trivial goto blocks never target the next block (in that case there should just be a
// fallthrough).
@@ -242,7 +242,7 @@
// Not targeting the fallthrough block, determine if we need this goto. We need it if
// a fallthrough can hit this block. That is the case if the block is the entry block
// or if one of the predecessors fall through to the block.
- needed = code.blocks.get(0) == block || isFallthroughBlock(block);
+ needed = code.entryBlock() == block || isFallthroughBlock(block);
}
if (!needed) {
@@ -1535,7 +1535,7 @@
final int color = code.reserveMarkingColor();
try {
ArrayDeque<BasicBlock> worklist = new ArrayDeque<>();
- final BasicBlock entry = code.blocks.getFirst();
+ final BasicBlock entry = code.entryBlock();
worklist.add(entry);
entry.mark(color);
@@ -1886,7 +1886,7 @@
return;
}
- DexClass clazz = definitionFor(method.method.getHolder());
+ DexClass clazz = appInfo.definitionFor(method.method.getHolder());
if (clazz == null) {
return;
}
@@ -2032,7 +2032,7 @@
Set<StaticPut> puts, Map<DexField, StaticPut> finalFieldPut) {
final int color = code.reserveMarkingColor();
try {
- BasicBlock block = code.blocks.getFirst();
+ BasicBlock block = code.entryBlock();
while (!block.isMarked(color) && block.getPredecessors().size() <= 1) {
block.mark(color);
InstructionListIterator it = block.listIterator();
@@ -2087,10 +2087,6 @@
}
}
- DexClass definitionFor(DexType type) {
- return converter.definitionFor(type);
- }
-
public void removeTrivialCheckCastAndInstanceOfInstructions(
IRCode code, boolean enableWholeProgramOptimizations) {
if (!enableWholeProgramOptimizations) {
@@ -2196,7 +2192,7 @@
if (baseType.isPrimitiveType()) {
return false;
}
- DexClass clazz = definitionFor(baseType);
+ DexClass clazz = appInfo.definitionFor(baseType);
if (clazz == null) {
// Conservatively say yes.
return true;
@@ -3874,7 +3870,7 @@
if (type == dexItemFactory.throwableType) {
return true;
}
- DexClass dexClass = definitionFor(type);
+ DexClass dexClass = appInfo.definitionFor(type);
if (dexClass == null) {
throw new CompilationError("Class or interface " + type.toSourceString() +
" required for desugaring of try-with-resources is not found.");
@@ -3901,7 +3897,7 @@
*/
public void logArgumentTypes(DexEncodedMethod method, IRCode code) {
List<Value> arguments = code.collectArguments();
- BasicBlock block = code.blocks.getFirst();
+ BasicBlock block = code.entryBlock();
InstructionListIterator iterator = block.listIterator();
// Attach some synthetic position to all inserted code.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 4c437bd..53da564 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -90,7 +90,7 @@
// Double-check the entry block does not have catch handlers.
// Otherwise, we need to split it before moving canonicalized const-string, which may throw.
- assert !code.blocks.getFirst().hasCatchHandlers();
+ assert !code.entryBlock().hasCatchHandlers();
Position firstNonNonePosition = findFirstNonNonePosition(code);
FastSortedEntrySet<ConstInstruction, List<Value>> entries =
valuesDefinedByConstant.object2ObjectEntrySet();
@@ -124,7 +124,7 @@
private static void insertCanonicalizedConstant(
IRCode code, ConstInstruction canonicalizedConstant) {
- BasicBlock entryBlock = code.blocks.get(0);
+ BasicBlock entryBlock = code.entryBlock();
// Insert the constant instruction at the start of the block right after the argument
// instructions. It is important that the const instruction is put before any instruction
// that can throw exceptions (since the value could be used on the exceptional edge).
@@ -139,7 +139,7 @@
}
private static Position findFirstNonNonePosition(IRCode code) {
- BasicBlock entryBlock = code.blocks.get(0);
+ BasicBlock entryBlock = code.entryBlock();
Instruction rightAfterArguments =
entryBlock.listIterator().nextUntil(instr -> !instr.isArgument());
Position firstNonArgumentPosition = rightAfterArguments.getPosition();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 229611d..2e31219 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -183,7 +183,7 @@
}
private static void insertCanonicalizedInvoke(IRCode code, Invoke canonicalizedInvoke) {
- BasicBlock entryBlock = code.blocks.get(0);
+ BasicBlock entryBlock = code.entryBlock();
// Insert the canonicalized invoke after in values.
int numberOfInValuePassed = 0;
InstructionListIterator it = entryBlock.listIterator();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 2c14153..22c3da8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -286,7 +286,7 @@
? appInfo.lookupInstanceTarget(field.getHolder(), field)
: appInfo.lookupStaticTarget(field.getHolder(), field);
// TODO(b/123857022): Should be possible to use `!isFieldRead(field)`.
- if (target != null && !isFieldRead(target.field)) {
+ if (target != null && !appInfo.isFieldRead(target.field)) {
// Remove writes to dead (i.e. never read) fields.
iterator.removeOrReplaceByDebugLocalRead();
}
@@ -327,17 +327,4 @@
}
assert code.isConsistentSSA();
}
-
- private boolean isFieldRead(DexField field) {
- return appInfo.fieldsRead.contains(field)
- // TODO(b/121354886): Pinned fields should be in `fieldsRead`.
- || appInfo.isPinned(field)
- // For library classes we don't know whether a field is read.
- || isLibraryField(field);
- }
-
- private boolean isLibraryField(DexField field) {
- DexClass holder = appInfo.definitionFor(field.clazz);
- return holder == null || holder.isLibraryClass();
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index cbdeaac..dd84122 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -422,8 +422,7 @@
// Stop traversing upwards if we hit the entry block: if the entry block has an non-null,
// this case should be handled already by A) because the entry block surely dominates all
// normal exits.
- // TODO(b/120787963): code.entryBlock()
- if (uncoveredPath == code.blocks.getFirst()) {
+ if (uncoveredPath == code.entryBlock()) {
return false;
}
// Make sure we're not visiting the same block over and over again.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 73f2a91..27ec7dc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1232,7 +1232,7 @@
}
}
- public boolean selectMethodsForOutlining(Map<DexType, DexProgramClass> synthesizedClasses) {
+ public boolean selectMethodsForOutlining() {
assert methodsSelectedForOutlining.size() == 0;
assert outlineSites.size() == 0;
for (List<DexEncodedMethod> outlineMethods : candidateMethodLists) {
@@ -1241,7 +1241,7 @@
methodsSelectedForOutlining.add(
converter
.graphLense()
- .mapDexEncodedMethod(outlineMethod, appInfo, synthesizedClasses));
+ .mapDexEncodedMethod(outlineMethod, appInfo));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 1f036b6..a0ba213 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -117,12 +117,14 @@
if (!eligibleClass.isClassType()) {
return false;
}
- eligibleClassDefinition = appInfo.definitionFor(eligibleClass);
- if (eligibleClassDefinition == null && lambdaRewriter != null) {
+ if (lambdaRewriter != null) {
// Check if the class is synthesized for a desugared lambda
eligibleClassDefinition = lambdaRewriter.getLambdaClass(eligibleClass);
isDesugaredLambda = eligibleClassDefinition != null;
}
+ if (eligibleClassDefinition == null) {
+ eligibleClassDefinition = appInfo.definitionFor(eligibleClass);
+ }
return eligibleClassDefinition != null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index b988a89..ab6f635 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.ir.conversion.CallSiteInformation;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.Outliner;
import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
@@ -36,6 +37,7 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
+import com.google.common.base.Predicates;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.IdentityHashMap;
@@ -194,8 +196,7 @@
IRConverter converter,
OptimizationFeedback feedback,
Builder<?> builder,
- ExecutorService executorService,
- Map<DexType, DexProgramClass> synthesizedClasses)
+ ExecutorService executorService)
throws ExecutionException {
if (lambdas.isEmpty()) {
return;
@@ -218,18 +219,29 @@
this.strategyFactory = ApplyStrategy::new;
// Add synthesized lambda group classes to the builder.
- converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
for (Entry<LambdaGroup, DexProgramClass> entry : lambdaGroupsClasses.entrySet()) {
DexProgramClass synthesizedClass = entry.getValue();
- synthesizedClasses.put(synthesizedClass.type, synthesizedClass);
+ converter.appInfo.addSynthesizedClass(synthesizedClass);
builder.addSynthesizedClass(
synthesizedClass, entry.getKey().shouldAddToMainDex(converter.appInfo));
+ // Eventually, we need to process synthesized methods in the lambda group.
+ // Otherwise, abstract SynthesizedCode will be flown to Enqueuer.
+ // But that process should not see the holder. Otherwise, lambda calls in the main dispatch
+ // method became recursive calls via the lense rewriter. They should remain, then inliner
+ // will inline methods from mergee lambdas to the main dispatch method.
+ // Then, there is a dilemma: other sub optimizations trigger subtype lookup that will throw
+ // NPE if it cannot find the holder for this synthesized lambda group.
+ // One hack here is to mark those methods `processed` so that the lense rewriter is skipped.
+ synthesizedClass.forEachMethod(encodedMethod -> {
+ encodedMethod.markProcessed(ConstraintWithTarget.NEVER);
+ });
}
+ converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
// Rewrite lambda class references into lambda group class
// references inside methods from the processing queue.
- rewriteLambdaReferences(converter, synthesizedClasses, feedback);
+ rewriteLambdaReferences(converter, feedback);
this.strategyFactory = null;
}
@@ -304,10 +316,7 @@
}
}
- private void rewriteLambdaReferences(
- IRConverter converter,
- Map<DexType, DexProgramClass> synthesizedClasses,
- OptimizationFeedback feedback) {
+ private void rewriteLambdaReferences(IRConverter converter, OptimizationFeedback feedback) {
List<DexEncodedMethod> methods =
methodsToReprocess
.stream()
@@ -315,9 +324,9 @@
.collect(Collectors.toList());
for (DexEncodedMethod method : methods) {
DexEncodedMethod mappedMethod =
- converter.graphLense().mapDexEncodedMethod(method, converter.appInfo, synthesizedClasses);
+ converter.graphLense().mapDexEncodedMethod(method, converter.appInfo);
converter.processMethod(mappedMethod, feedback,
- x -> false, CallSiteInformation.empty(), Outliner::noProcessing);
+ Predicates.alwaysFalse(), CallSiteInformation.empty(), Outliner::noProcessing);
assert mappedMethod.isProcessed();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 170c39e..51f9de0 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -189,7 +189,7 @@
this.code = code;
this.options = options;
int argumentRegisters = 0;
- for (Instruction instruction : code.blocks.getFirst().getInstructions()) {
+ for (Instruction instruction : code.entryBlock().getInstructions()) {
if (instruction.isArgument()) {
argumentRegisters += instruction.outValue().requiredRegisters();
}
@@ -2506,7 +2506,7 @@
// VMs we block the receiver register throughout the method.
if ((options.canHaveThisTypeVerifierBug() || options.canHaveThisJitCodeDebuggingBug())
&& !code.method.accessFlags.isStatic()) {
- for (Instruction instruction : code.blocks.get(0).getInstructions()) {
+ for (Instruction instruction : code.entryBlock().getInstructions()) {
if (instruction.isArgument() && instruction.outValue().isThis()) {
Value thisValue = instruction.outValue();
LiveIntervals thisIntervals = thisValue.getLiveIntervals();
@@ -2674,8 +2674,8 @@
// be original, consecutive arguments of the enclosing method (and importantly, not values that
// have been defined by a check-cast instruction).
private void transformBridgeMethod() {
- assert implementationIsBridge(this.code);
- BasicBlock entry = this.code.blocks.getFirst();
+ assert implementationIsBridge(code);
+ BasicBlock entry = code.entryBlock();
InstructionListIterator iterator = entry.listIterator();
// Create a mapping from argument values to their index, while scanning over the arguments.
Reference2IntMap<Value> argumentIndices = new Reference2IntArrayMap<>();
@@ -2733,7 +2733,7 @@
if (code.blocks.size() > 1) {
return false;
}
- InstructionListIterator iterator = code.blocks.getFirst().listIterator();
+ InstructionListIterator iterator = code.entryBlock().listIterator();
// Move forward to the first instruction after the definition of the arguments.
while (iterator.hasNext() && iterator.peekNext().isArgument()) {
iterator.next();
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
index 8489866..0d25746 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -166,7 +166,7 @@
public int scheduleAndInsertMoves(int tempRegister) {
for (BasicBlock block : code.blocks) {
InstructionListIterator insertAt = block.listIterator();
- if (block == code.blocks.getFirst()) {
+ if (block == code.entryBlock()) {
// Move insertAt iterator to the first non-argument, such that moves for the arguments will
// be inserted after the last argument.
while (insertAt.hasNext() && insertAt.peekNext().isArgument()) {
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 8d55a59..6a9e056 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
@@ -42,7 +44,6 @@
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
-import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.ExecutorService;
@@ -67,6 +68,7 @@
public static final int MARKER_STRING_CONSTANT_POOL_INDEX = 2;
private final DexApplication application;
+ private final AppView<? extends AppInfo> appView;
private final GraphLense graphLense;
private final NamingLens namingLens;
private final InternalOptions options;
@@ -78,6 +80,7 @@
public CfApplicationWriter(
DexApplication application,
+ AppView<? extends AppInfo> appView,
InternalOptions options,
Marker marker,
String deadCode,
@@ -86,6 +89,7 @@
String proguardSeedsData,
ProguardMapSupplier proguardMapSupplier) {
this.application = application;
+ this.appView = appView;
this.graphLense = graphLense;
this.namingLens = namingLens;
this.options = options;
@@ -96,7 +100,7 @@
this.proguardSeedsData = proguardSeedsData;
}
- public void write(ClassFileConsumer consumer, ExecutorService executor) throws IOException {
+ public void write(ClassFileConsumer consumer, ExecutorService executor) {
application.timing.begin("CfApplicationWriter.write");
try {
writeApplication(consumer, executor);
@@ -105,8 +109,7 @@
}
}
- private void writeApplication(ClassFileConsumer consumer, ExecutorService executor)
- throws IOException {
+ private void writeApplication(ClassFileConsumer consumer, ExecutorService executor) {
ProguardMapSupplier.ProguardMapAndId proguardMapAndId = null;
if (proguardMapSupplier != null && options.proguardMapConsumer != null) {
proguardMapAndId = proguardMapSupplier.getProguardMapAndId();
@@ -124,6 +127,7 @@
}
ApplicationWriter.supplyAdditionalConsumers(
application,
+ appView,
graphLense,
namingLens,
options,
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index bc81538..b59b79b 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
import static com.android.tools.r8.utils.DescriptorUtils.getPackageBinaryNameFromJavaType;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
@@ -50,6 +51,7 @@
class ClassNameMinifier {
+ private final AppView<AppInfoWithLiveness> appView;
private final AppInfoWithLiveness appInfo;
private final Reporter reporter;
private final PackageObfuscationMode packageObfuscationMode;
@@ -74,11 +76,10 @@
private final GenericSignatureParser<DexType> genericSignatureParser =
new GenericSignatureParser<>(genericSignatureRewriter);
- ClassNameMinifier(
- AppInfoWithLiveness appInfo,
- RootSet rootSet,
- InternalOptions options) {
- this.appInfo = appInfo;
+ ClassNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+ this.appView = appView;
+ this.appInfo = appView.appInfo();
+ InternalOptions options = appView.options();
this.reporter = options.reporter;
this.packageObfuscationMode = options.getProguardConfiguration().getPackageObfuscationMode();
this.isAccessModificationAllowed =
@@ -546,6 +547,7 @@
@Override
public DexType parsedTypeName(String name) {
DexType type = appInfo.dexItemFactory.createType(getDescriptorFromClassBinaryName(name));
+ type = appView.graphLense().lookupType(type);
DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
return type;
@@ -564,6 +566,7 @@
String enclosingRenamedBinaryName =
getClassBinaryNameFromDescriptor(
renaming.getOrDefault(enclosingType, enclosingType.descriptor).toString());
+ type = appView.graphLense().lookupType(type);
DexString renamedDescriptor = renaming.get(type);
if (renamedDescriptor != null) {
// Pick the renamed inner class from the fully renamed binary name.
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index be339b6..e2127dd 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
@@ -10,7 +11,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
@@ -19,8 +19,8 @@
class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
- FieldNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options) {
- super(appInfo, rootSet, options);
+ FieldNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+ super(appView, rootSet);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
index 419bfbb..53957e4 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CachedHashValueDexItem;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -18,6 +19,7 @@
abstract class MemberNameMinifier<MemberType, StateType extends CachedHashValueDexItem> {
+ protected final AppView<AppInfoWithLiveness> appView;
protected final AppInfoWithLiveness appInfo;
protected final RootSet rootSet;
protected final InternalOptions options;
@@ -34,10 +36,11 @@
// which is useful for debugging.
private final BiMap<DexType, NamingState<StateType, ?>> states = HashBiMap.create();
- MemberNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options) {
- this.appInfo = appInfo;
+ MemberNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+ this.appView = appView;
+ this.appInfo = appView.appInfo();
this.rootSet = rootSet;
- this.options = options;
+ this.options = appView.options();
this.dictionary = options.getProguardConfiguration().getObfuscationDictionary();
this.useUniqueMemberNames = options.getProguardConfiguration().isUseUniqueClassMemberNames();
this.overloadAggressively =
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 8864271..2cd8712 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -91,11 +92,8 @@
private final FrontierState frontierState = new FrontierState();
- MethodNameMinifier(
- AppInfoWithLiveness appInfo,
- RootSet rootSet,
- InternalOptions options) {
- super(appInfo, rootSet, options);
+ MethodNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+ super(appView, rootSet);
equivalence =
overloadAggressively
? MethodSignatureEquivalence.get()
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 5699d03..7f389a4 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -36,26 +37,27 @@
static final char INNER_CLASS_SEPARATOR = '$';
+ private final AppView<AppInfoWithLiveness> appView;
private final AppInfoWithLiveness appInfo;
private final RootSet rootSet;
private final Set<DexCallSite> desugaredCallSites;
private final InternalOptions options;
public Minifier(
- AppInfoWithLiveness appInfo,
+ AppView<AppInfoWithLiveness> appView,
RootSet rootSet,
- Set<DexCallSite> desugaredCallSites,
- InternalOptions options) {
- this.appInfo = appInfo;
+ Set<DexCallSite> desugaredCallSites) {
+ this.appView = appView;
+ this.appInfo = appView.appInfo();
this.rootSet = rootSet;
this.desugaredCallSites = desugaredCallSites;
- this.options = options;
+ this.options = appView.options();
}
public NamingLens run(Timing timing) {
assert options.enableMinification;
timing.begin("MinifyClasses");
- ClassNameMinifier classNameMinifier = new ClassNameMinifier(appInfo, rootSet, options);
+ ClassNameMinifier classNameMinifier = new ClassNameMinifier(appView, rootSet);
ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
timing.end();
@@ -65,16 +67,14 @@
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
- new MethodNameMinifier(appInfo, rootSet, options)
- .computeRenaming(desugaredCallSites, timing);
+ new MethodNameMinifier(appView, rootSet).computeRenaming(desugaredCallSites, timing);
timing.end();
assert new MinifiedRenaming(classRenaming, methodRenaming, FieldRenaming.empty(), appInfo)
.verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
timing.begin("MinifyFields");
- FieldRenaming fieldRenaming =
- new FieldNameMinifier(appInfo, rootSet, options).computeRenaming(timing);
+ FieldRenaming fieldRenaming = new FieldNameMinifier(appView, rootSet).computeRenaming(timing);
timing.end();
NamingLens lens = new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 1a19a39..8de2695 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -51,6 +51,7 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
@@ -220,6 +221,12 @@
new SetWithReason<>(this::registerField);
/**
+ * Set of service types (from META-INF/services/) that may have been instantiated reflectively via
+ * ServiceLoader.load() or ServiceLoader.loadInstalled().
+ */
+ private final Set<DexType> instantiatedAppServices = Sets.newIdentityHashSet();
+
+ /**
* Set of interface types for which a lambda expression can be reached. These never have a single
* interface implementation.
*/
@@ -492,6 +499,10 @@
if (method == appInfo.dexItemFactory.enumMethods.valueOf) {
pendingReflectiveUses.add(currentMethod);
}
+ // Handling of application services.
+ if (appInfo.dexItemFactory.serviceLoaderMethods.isLoadMethod(method)) {
+ pendingReflectiveUses.add(currentMethod);
+ }
if (!registerItemWithTargetAndContext(staticInvokes, method, currentMethod)) {
return false;
}
@@ -829,8 +840,8 @@
// If this type has deferred annotations, we have to process those now, too.
Set<DexAnnotation> annotations = deferredAnnotations.remove(type);
if (annotations != null && !annotations.isEmpty()) {
- assert !holder.accessFlags.isAnnotation()
- || annotations.stream().allMatch(a -> a.annotation.type == holder.type);
+ assert holder.accessFlags.isAnnotation();
+ assert annotations.stream().allMatch(a -> a.annotation.type == holder.type);
annotations.forEach(annotation -> handleAnnotation(holder, annotation));
}
} else {
@@ -1688,6 +1699,13 @@
collectReachedFields(staticFields, this::tryLookupStaticField)));
}
+ private void markClassAsInstantiatedWithReason(DexClass clazz, KeepReason reason) {
+ workList.add(Action.markInstantiated(clazz, reason));
+ if (clazz.hasDefaultInitializer()) {
+ workList.add(Action.markMethodLive(clazz.getDefaultInitializer(), reason));
+ }
+ }
+
private void markClassAsInstantiatedWithCompatRule(DexClass clazz) {
ProguardKeepRule rule = ProguardConfigurationUtils.buildDefaultInitializerKeepRule(clazz);
proguardCompatibilityWorkList.add(
@@ -1728,6 +1746,10 @@
handleJavaLangEnumValueOf(method, invoke);
return;
}
+ if (appInfo.dexItemFactory.serviceLoaderMethods.isLoadMethod(invokedMethod)) {
+ handleServiceLoaderInvocation(method, invoke);
+ return;
+ }
if (!isReflectionMethod(appInfo.dexItemFactory, invokedMethod)) {
return;
}
@@ -1797,6 +1819,55 @@
}
}
+ private void handleServiceLoaderInvocation(DexEncodedMethod method, InvokeMethod invoke) {
+ if (invoke.inValues().size() == 0) {
+ // Should never happen.
+ return;
+ }
+
+ Value argument = invoke.inValues().get(0).getAliasedValue();
+ if (!argument.isPhi() && argument.definition.isConstClass()) {
+ DexType serviceType = argument.definition.asConstClass().getValue();
+ if (!appView.appServices().allServiceTypes().contains(serviceType)) {
+ // Should never happen.
+ options.reporter.warning(
+ new StringDiagnostic(
+ "The type `"
+ + serviceType.toSourceString()
+ + "` is being passed to the method `"
+ + invoke.getInvokedMethod()
+ + "`, but could was found in `META-INF/services/`.",
+ appInfo.originFor(method.method.holder)));
+ return;
+ }
+
+ handleServiceInstantiation(serviceType, KeepReason.reflectiveUseIn(method));
+ } else {
+ KeepReason reason = KeepReason.reflectiveUseIn(method);
+ for (DexType serviceType : appView.appServices().allServiceTypes()) {
+ handleServiceInstantiation(serviceType, reason);
+ }
+ }
+ }
+
+ private void handleServiceInstantiation(DexType serviceType, KeepReason reason) {
+ instantiatedAppServices.add(serviceType);
+
+ Set<DexType> serviceImplementationTypes =
+ appView.appServices().serviceImplementationsFor(serviceType);
+ for (DexType serviceImplementationType : serviceImplementationTypes) {
+ if (!serviceImplementationType.isClassType()) {
+ // Should never happen.
+ continue;
+ }
+
+ DexClass serviceImplementationClass = appInfo.definitionFor(serviceImplementationType);
+ if (serviceImplementationClass != null && serviceImplementationClass.isProgramClass()) {
+ markClassAsInstantiatedWithReason(serviceImplementationClass, reason);
+ }
+ }
+ }
+
private static class Action {
final Kind kind;
@@ -1868,6 +1939,11 @@
/** Set of annotation types that are instantiated. */
final SortedSet<DexType> instantiatedAnnotationTypes;
/**
+ * Set of service types (from META-INF/services/) that may have been instantiated reflectively
+ * via ServiceLoader.load() or ServiceLoader.loadInstalled().
+ */
+ public final SortedSet<DexType> instantiatedAppServices;
+ /**
* Set of types that are actually instantiated. These cannot be abstract.
*/
final SortedSet<DexType> instantiatedTypes;
@@ -1909,11 +1985,11 @@
/**
* Set of all fields which may be touched by a get operation. This is actual field definitions.
*/
- public final SortedSet<DexField> fieldsRead;
+ private final SortedSet<DexField> fieldsRead;
/**
* Set of all fields which may be touched by a put operation. This is actual field definitions.
*/
- public final SortedSet<DexField> fieldsWritten;
+ private final SortedSet<DexField> fieldsWritten;
/**
* Set of all field ids used in instance field reads, along with access context.
*/
@@ -2029,6 +2105,9 @@
ImmutableSortedSet.orderedBy(PresortedComparable<DexType>::slowCompareTo);
enqueuer.liveAnnotations.items.forEach(annotation -> builder.add(annotation.annotation.type));
this.instantiatedAnnotationTypes = builder.build();
+ this.instantiatedAppServices =
+ ImmutableSortedSet.copyOf(
+ PresortedComparable<DexType>::slowCompareTo, enqueuer.instantiatedAppServices);
this.instantiatedTypes = ImmutableSortedSet.copyOf(
PresortedComparable<DexType>::slowCompareTo, enqueuer.instantiatedTypes.getItems());
this.instantiatedLambdas =
@@ -2087,6 +2166,7 @@
super(application);
this.liveTypes = previous.liveTypes;
this.instantiatedAnnotationTypes = previous.instantiatedAnnotationTypes;
+ this.instantiatedAppServices = previous.instantiatedAppServices;
this.instantiatedTypes = previous.instantiatedTypes;
this.instantiatedLambdas = previous.instantiatedLambdas;
this.targetedMethods = previous.targetedMethods;
@@ -2136,6 +2216,8 @@
this.liveTypes = rewriteItems(previous.liveTypes, lense::lookupType);
this.instantiatedAnnotationTypes =
rewriteItems(previous.instantiatedAnnotationTypes, lense::lookupType);
+ this.instantiatedAppServices =
+ rewriteItems(previous.instantiatedAppServices, lense::lookupType);
this.instantiatedTypes = rewriteItems(previous.instantiatedTypes, lense::lookupType);
this.instantiatedLambdas = rewriteItems(previous.instantiatedLambdas, lense::lookupType);
this.targetedMethods = lense.rewriteMethodsConservatively(previous.targetedMethods);
@@ -2214,6 +2296,7 @@
super(previous);
this.liveTypes = previous.liveTypes;
this.instantiatedAnnotationTypes = previous.instantiatedAnnotationTypes;
+ this.instantiatedAppServices = previous.instantiatedAppServices;
this.instantiatedTypes = previous.instantiatedTypes;
this.instantiatedLambdas = previous.instantiatedLambdas;
this.targetedMethods = previous.targetedMethods;
@@ -2276,7 +2359,8 @@
public boolean isInstantiatedDirectly(DexType type) {
assert type.isClassType();
- return instantiatedTypes.contains(type)
+ return type.isD8R8SynthesizedClassType()
+ || instantiatedTypes.contains(type)
|| instantiatedLambdas.contains(type)
|| instantiatedAnnotationTypes.contains(type);
}
@@ -2303,6 +2387,31 @@
return isInstantiatedDirectly(type) || isInstantiatedIndirectly(type);
}
+ public boolean isFieldRead(DexField field) {
+ return fieldsRead.contains(field)
+ // TODO(b/121354886): Pinned fields should be in `fieldsRead`.
+ || isPinned(field)
+ // Fields in the class that is synthesized by D8/R8 would be used soon.
+ || field.getHolder().isD8R8SynthesizedClassType()
+ // For library classes we don't know whether a field is read.
+ || isLibraryField(field);
+ }
+
+ public boolean isFieldWritten(DexField field) {
+ return fieldsWritten.contains(field)
+ // TODO(b/121354886): Pinned fields should be in `fieldsWritten`.
+ || isPinned(field)
+ // Fields in the class that is synthesized by D8/R8 would be used soon.
+ || field.clazz.isD8R8SynthesizedClassType()
+ // For library classes we don't know whether a field is rewritten.
+ || isLibraryField(field);
+ }
+
+ private boolean isLibraryField(DexField field) {
+ DexClass holder = definitionFor(field.clazz);
+ return holder == null || holder.isLibraryClass();
+ }
+
private Object2BooleanMap<DexReference> joinIdentifierNameStrings(
Set<DexReference> explicit, Set<DexReference> implicit) {
Object2BooleanMap<DexReference> result = new Object2BooleanArrayMap<>();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index eb9916e..a767c86 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -57,9 +57,11 @@
private Position keepParameterNamesOptionPosition;
private final ProguardClassFilter.Builder adaptClassStrings = ProguardClassFilter.builder();
private final ProguardPathFilter.Builder adaptResourceFilenames =
- ProguardPathFilter.builder().disable();
+ ProguardPathFilter.builder()
+ .addPattern(ProguardPathList.builder().addFileName("META-INF/services/*").build());
private final ProguardPathFilter.Builder adaptResourceFileContents =
- ProguardPathFilter.builder().disable();
+ ProguardPathFilter.builder()
+ .addPattern(ProguardPathList.builder().addFileName("META-INF/services/*").build());
private final ProguardPathFilter.Builder keepDirectories =
ProguardPathFilter.builder().disable();
private boolean forceProguardCompatibility = false;
@@ -243,18 +245,10 @@
adaptClassStrings.addPattern(pattern);
}
- public void enableAdaptResourceFilenames() {
- adaptResourceFilenames.enable();
- }
-
public void addAdaptResourceFilenames(ProguardPathList pattern) {
adaptResourceFilenames.addPattern(pattern);
}
- public void enableAdaptResourceFileContents() {
- adaptResourceFileContents.enable();
- }
-
public void addAdaptResourceFileContents(ProguardPathList pattern) {
adaptResourceFileContents.addPattern(pattern);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 5acd2a8..eaad9a4 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -384,10 +384,8 @@
} else if (acceptString("adaptclassstrings")) {
parseClassFilter(configurationBuilder::addAdaptClassStringsPattern);
} else if (acceptString("adaptresourcefilenames")) {
- configurationBuilder.enableAdaptResourceFilenames();
parsePathFilter(configurationBuilder::addAdaptResourceFilenames);
} else if (acceptString("adaptresourcefilecontents")) {
- configurationBuilder.enableAdaptResourceFileContents();
parsePathFilter(configurationBuilder::addAdaptResourceFileContents);
} else if (acceptString("identifiernamestring")) {
configurationBuilder.addRule(parseIdentifierNameStringRule(optionStart));
@@ -1710,7 +1708,7 @@
if (fileFilter == null) {
throw parseError("Path filter expected");
}
- builder.addFileName(negated, fileFilter);
+ builder.addFileName(fileFilter, negated);
skipWhitespace();
while (acceptChar(',')) {
skipWhitespace();
@@ -1720,7 +1718,7 @@
if (fileFilter == null) {
throw parseError("Path filter expected");
}
- builder.addFileName(negated, fileFilter);
+ builder.addFileName(fileFilter, negated);
skipWhitespace();
}
return builder.build();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java b/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java
index 6766128..edcc4f6 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java
@@ -26,7 +26,11 @@
private Builder() {
}
- public Builder addFileName(boolean isNegated, String path) {
+ public Builder addFileName(String path) {
+ return addFileName(path, false);
+ }
+
+ public Builder addFileName(String path, boolean isNegated) {
matchers.add(new FileNameMatcher(isNegated, path));
return this;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index dc8925a..a59a08c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -386,24 +386,43 @@
wildcard = wildcards.get(wildcardIndex);
assert wildcard.isPattern();
wildcardPattern = wildcard.asPattern();
+
boolean includeSeparators = pattern.length() > (i + 1) && pattern.charAt(i + 1) == '*';
- int nextPatternIndex = i + (includeSeparators ? 2 : 1);
- // Fast cases for the common case where a pattern ends with '**' or '*'.
+ boolean includeAll = pattern.length() > (i + 2) && pattern.charAt(i + 2) == '*';
+ int nextPatternIndex = i + 1;
+ if (includeAll) {
+ nextPatternIndex += 2;
+ } else if (includeSeparators) {
+ nextPatternIndex += 1;
+ }
+
+ // Fast cases for the common case where a pattern ends with '*', '**', or '***'.
if (nextPatternIndex == pattern.length()) {
- wildcardPattern.setCaptured(name.substring(nameIndex, name.length()));
+ wildcardPattern.setCaptured(name.substring(nameIndex));
+ if (includeAll) {
+ return true;
+ }
if (includeSeparators) {
return kind == ClassOrType.CLASS || !isArrayType(name);
}
boolean hasSeparators = containsSeparatorsStartingAt(name, nameIndex);
return !hasSeparators && (kind == ClassOrType.CLASS || !isArrayType(name));
}
+
// Match the rest of the pattern against the (non-empty) rest of the class name.
for (int nextNameIndex = nameIndex; nextNameIndex < name.length(); nextNameIndex++) {
wildcardPattern.setCaptured(name.substring(nameIndex, nextNameIndex));
- if (!includeSeparators && name.charAt(nextNameIndex) == '.') {
- return matchClassOrTypeNameImpl(
- pattern, nextPatternIndex, name, nextNameIndex, wildcards, wildcardIndex + 1,
- kind);
+ if (!includeSeparators) {
+ if (name.charAt(nextNameIndex) == '.') {
+ return matchClassOrTypeNameImpl(
+ pattern,
+ nextPatternIndex,
+ name,
+ nextNameIndex,
+ wildcards,
+ wildcardIndex + 1,
+ kind);
+ }
}
if (kind == ClassOrType.TYPE && name.charAt(nextNameIndex) == '[') {
return matchClassOrTypeNameImpl(
@@ -416,11 +435,12 @@
return true;
}
}
- // Finally, check the case where the '*' or '**' eats all of the class name.
- wildcardPattern.setCaptured(name.substring(nameIndex, name.length()));
+
+ // Finally, check the case where the '*', '**', or '***' eats all of the class name.
+ wildcardPattern.setCaptured(name.substring(nameIndex));
return matchClassOrTypeNameImpl(
- pattern, nextPatternIndex, name, name.length(), wildcards, wildcardIndex + 1,
- kind);
+ pattern, nextPatternIndex, name, name.length(), wildcards, wildcardIndex + 1, kind);
+
case '?':
wildcard = wildcards.get(wildcardIndex);
assert wildcard.isPattern();
@@ -432,6 +452,7 @@
nameIndex++;
wildcardIndex++;
break;
+
case '<':
wildcard = wildcards.get(wildcardIndex);
assert wildcard.isBackReference();
@@ -446,6 +467,7 @@
wildcardIndex++;
i = pattern.indexOf(">", i);
break;
+
default:
if (nameIndex == name.length() || patternChar != name.charAt(nameIndex++)) {
return false;
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 dd8bcd6..786d026 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -228,8 +228,8 @@
Predicate<DexField> isReachableOrReferencedField =
field ->
appInfo.liveFields.contains(field)
- || appInfo.fieldsRead.contains(field)
- || appInfo.fieldsWritten.contains(field);
+ || appInfo.isFieldRead(field)
+ || appInfo.isFieldWritten(field);
int firstUnreachable =
firstUnreachableIndex(Arrays.asList(fields), isReachableOrReferencedField);
// Return the original array if all fields are used.
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 369367e..4c15524 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.shaking.FilteredClassPath;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@@ -39,10 +40,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
/**
* Collection of program files needed for processing.
@@ -214,8 +217,8 @@
}
}
- public List<DataEntryResource> getDataEntryResourcesForTesting() throws ResourceException {
- List<DataEntryResource> out = new ArrayList<>();
+ public Set<DataEntryResource> getDataEntryResourcesForTesting() throws ResourceException {
+ Set<DataEntryResource> out = new TreeSet<>(Comparator.comparing(DataResource::getName));
for (ProgramResourceProvider programResourceProvider : getProgramResourceProviders()) {
DataResourceProvider dataResourceProvider = programResourceProvider.getDataResourceProvider();
if (dataResourceProvider != null) {
@@ -229,7 +232,14 @@
@Override
public void visit(DataEntryResource file) {
- out.add(file);
+ try {
+ byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+ DataEntryResource copy =
+ DataEntryResource.fromBytes(bytes, file.getName(), file.getOrigin());
+ out.add(copy);
+ } catch (IOException | ResourceException e) {
+ throw new RuntimeException(e);
+ }
}
});
}
@@ -355,8 +365,8 @@
DexFilePerClassFileConsumer.ArchiveConsumer.writeResources(
archive, resources, programResourcesMainDescriptor);
} else if (outputMode == OutputMode.ClassFile) {
- List<ProgramResource> resources = getClassProgramResourcesForTesting();
- ClassFileConsumer.ArchiveConsumer.writeResources(archive, resources);
+ ClassFileConsumer.ArchiveConsumer.writeResources(
+ archive, getClassProgramResourcesForTesting(), getDataEntryResourcesForTesting());
} else {
throw new Unreachable("Unsupported output-mode for writing: " + outputMode);
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
index c7078ec..b7beb82 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -14,10 +14,13 @@
import com.android.tools.r8.DexIndexedConsumer.ForwardingConsumer;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.origin.Origin;
+import com.google.common.io.ByteStreams;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -110,23 +113,38 @@
@Override
public DataResourceConsumer getDataResourceConsumer() {
- assert consumer.getDataResourceConsumer() == null;
+ DataResourceConsumer dataResourceConsumer =
+ consumer != null ? consumer.getDataResourceConsumer() : null;
return new DataResourceConsumer() {
@Override
public void accept(
DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler) {
- // Ignore.
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.accept(directory, diagnosticsHandler);
+ }
}
@Override
public void accept(DataEntryResource file, DiagnosticsHandler diagnosticsHandler) {
- builder.addDataResource(file);
+ try {
+ byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+ DataEntryResource copy =
+ DataEntryResource.fromBytes(bytes, file.getName(), file.getOrigin());
+ builder.addDataResource(copy);
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.accept(copy, diagnosticsHandler);
+ }
+ } catch (IOException | ResourceException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
public void finished(DiagnosticsHandler handler) {
- // Ignore.
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.finished(handler);
+ }
}
};
}
@@ -174,6 +192,44 @@
assert getDataResourceConsumer() != null;
}
}
+
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ DataResourceConsumer dataResourceConsumer =
+ consumer != null ? consumer.getDataResourceConsumer() : null;
+ return new DataResourceConsumer() {
+
+ @Override
+ public void accept(
+ DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler) {
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.accept(directory, diagnosticsHandler);
+ }
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler diagnosticsHandler) {
+ try {
+ byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+ DataEntryResource copy =
+ DataEntryResource.fromBytes(bytes, file.getName(), file.getOrigin());
+ builder.addDataResource(copy);
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.accept(copy, diagnosticsHandler);
+ }
+ } catch (IOException | ResourceException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.finished(handler);
+ }
+ }
+ };
+ }
};
programConsumer = wrapped;
return wrapped;
@@ -208,6 +264,44 @@
assert getDataResourceConsumer() != null;
}
}
+
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ DataResourceConsumer dataResourceConsumer =
+ consumer != null ? consumer.getDataResourceConsumer() : null;
+ return new DataResourceConsumer() {
+
+ @Override
+ public void accept(
+ DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler) {
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.accept(directory, diagnosticsHandler);
+ }
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler diagnosticsHandler) {
+ try {
+ byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+ DataEntryResource copy =
+ DataEntryResource.fromBytes(bytes, file.getName(), file.getOrigin());
+ builder.addDataResource(copy);
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.accept(copy, diagnosticsHandler);
+ }
+ } catch (IOException | ResourceException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.finished(handler);
+ }
+ }
+ };
+ }
};
programConsumer = wrapped;
return wrapped;
diff --git a/src/test/java/com/android/tools/r8/TestBaseResult.java b/src/test/java/com/android/tools/r8/TestBaseResult.java
index e7623a3..390e017 100644
--- a/src/test/java/com/android/tools/r8/TestBaseResult.java
+++ b/src/test/java/com/android/tools/r8/TestBaseResult.java
@@ -4,6 +4,9 @@
package com.android.tools.r8;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
public abstract class TestBaseResult<CR extends TestBaseResult<CR, RR>, RR extends TestRunResult> {
final TestState state;
@@ -12,4 +15,13 @@
}
public abstract CR self();
+
+ public <S> S map(Function<CR, S> fn) {
+ return fn.apply(self());
+ }
+
+ public CR apply(Consumer<CR> fn) {
+ fn.accept(self());
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index c67c05c..9ba822d 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -9,6 +9,8 @@
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
+import java.util.function.Consumer;
+import java.util.function.Function;
public abstract class TestBuilder<RR extends TestRunResult, T extends TestBuilder<RR, T>> {
@@ -24,6 +26,15 @@
abstract T self();
+ public <S> S map(Function<T, S> fn) {
+ return fn.apply(self());
+ }
+
+ public T apply(Consumer<T> fn) {
+ fn.accept(self());
+ return self();
+ }
+
public abstract RR run(String mainClass)
throws IOException, CompilationFailedException;
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index 89d0fff..e2f4239 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -9,12 +9,12 @@
import static org.junit.Assert.assertThat;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.graph.invokesuper.Consumer;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
import java.io.PrintStream;
import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
import java.util.function.Function;
import org.hamcrest.Matcher;
@@ -29,6 +29,15 @@
abstract RR self();
+ public <S> S map(Function<RR, S> fn) {
+ return fn.apply(self());
+ }
+
+ public RR apply(Consumer<RR> fn) {
+ fn.accept(self());
+ return self();
+ }
+
public AndroidApp app() {
return app;
}
@@ -74,10 +83,6 @@
return self();
}
- public <R> R map(Function<RR, R> mapper) {
- return mapper.apply(self());
- }
-
public CodeInspector inspector() throws IOException, ExecutionException {
// Inspection post run implies success. If inspection of an invalid program is needed it should
// be done on the compilation result or on the input.
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 4f80d5e..c0b06f4 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1784,6 +1784,7 @@
Executors.newSingleThreadExecutor(),
application,
null,
+ null,
GraphLense.getIdentityLense(),
NamingLens.getIdentityLens(),
null,
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
index 926a22d..b730618 100644
--- a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -91,7 +91,7 @@
if (!code.method.qualifiedName().equals(TryRangeTestLimitRange.class.getName() + ".main")) {
return;
}
- BasicBlock entryBlock = code.blocks.get(0);
+ BasicBlock entryBlock = code.entryBlock();
BasicBlock tryBlock = code.blocks.get(1);
assertTrue(tryBlock.hasCatchHandlers());
ListIterator<Instruction> it = entryBlock.getInstructions().listIterator();
diff --git a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
index f8faf9c..e72cf0a 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -58,7 +58,7 @@
// the source file or the desguared interface is to make it an inner class.
assertEquals('$', InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX.charAt(0));
defaultMethodName = InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX + "doSomething";
- defaultMethodThisName = "-this";
+ defaultMethodThisName = "_this";
}
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 8d6953b..298ffe1 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -139,6 +139,7 @@
ApplicationWriter writer =
new ApplicationWriter(
application,
+ null,
options,
null,
null,
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index a459a9c..68b466a 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -101,7 +101,7 @@
// Run code inlining a.
test = codeForMethodReplaceTest(a, b);
- iterator = test.code.blocks.get(0).listIterator();
+ iterator = test.code.entryBlock().listIterator();
iterator.nextUntil(instruction -> instruction.isInvoke());
iterator.previous();
iterator.inlineInvoke(test.appInfo, test.code, test.additionalCode.get(0));
@@ -110,7 +110,7 @@
// Run code inlining b (where a is actually called).
test = codeForMethodReplaceTest(a, b);
- iterator = test.code.blocks.get(0).listIterator();
+ iterator = test.code.entryBlock().listIterator();
iterator.nextUntil(instruction -> instruction.isInvoke());
iterator.previous();
iterator.inlineInvoke(test.appInfo, test.code, test.additionalCode.get(1));
@@ -183,7 +183,7 @@
// Run code inlining a.
test = codeForMethodReplaceReturnVoidTest(1, 2);
- iterator = test.code.blocks.get(0).listIterator();
+ iterator = test.code.entryBlock().listIterator();
iterator.nextUntil(instruction -> instruction.isInvoke());
iterator.previous();
iterator.inlineInvoke(test.appInfo, test.code, test.additionalCode.get(0));
@@ -519,7 +519,7 @@
// Run code inlining a.
test = codeForInlineCanThrow(a, b, twoGuards);
- iterator = test.code.blocks.get(0).listIterator();
+ iterator = test.code.entryBlock().listIterator();
iterator.nextUntil(instruction -> instruction.isInvoke());
iterator.previous();
iterator.inlineInvoke(test.appInfo, test.code, test.additionalCode.get(0));
@@ -528,7 +528,7 @@
// Run code inlining b (where a is actually called).
test = codeForInlineCanThrow(a, b, twoGuards);
- iterator = test.code.blocks.get(0).listIterator();
+ iterator = test.code.entryBlock().listIterator();
iterator.nextUntil(instruction -> instruction.isInvoke());
iterator.previous();
iterator.inlineInvoke(test.appInfo, test.code, test.additionalCode.get(1));
@@ -633,7 +633,7 @@
// Run code inlining a.
test = codeForInlineAlwaysThrows(twoGuards);
- iterator = test.code.blocks.get(0).listIterator();
+ iterator = test.code.entryBlock().listIterator();
iterator.nextUntil(Instruction::isInvoke);
iterator.previous();
iterator.inlineInvoke(test.appInfo, test.code, test.additionalCode.get(0));
@@ -643,7 +643,7 @@
// Run code inlining b (where a is actually called).
test = codeForInlineAlwaysThrows(twoGuards);
- iterator = test.code.blocks.get(0).listIterator();
+ iterator = test.code.entryBlock().listIterator();
iterator.nextUntil(Instruction::isInvoke);
iterator.previous();
iterator.inlineInvoke(test.appInfo, test.code, test.additionalCode.get(1));
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 1152903..066c51a 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -104,7 +104,7 @@
public int countArgumentInstructions() {
int count = 0;
- ListIterator<Instruction> iterator = code.blocks.get(0).listIterator();
+ ListIterator<Instruction> iterator = code.entryBlock().listIterator();
while (iterator.next().isArgument()) {
count++;
}
diff --git a/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java b/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java
index 3001b98..0a11f41 100644
--- a/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/LinearFlowIteratorTest.java
@@ -107,7 +107,7 @@
@Test
public void hasNextWillCheckNextBlock() throws Exception {
IRCode code = simpleCode();
- InstructionListIterator it = new LinearFlowInstructionIterator(code.blocks.get(0));
+ InstructionListIterator it = new LinearFlowInstructionIterator(code.entryBlock());
Instruction current = it.next();
current = it.next();
assert it.hasNext();
@@ -116,7 +116,7 @@
@Test
public void nextWillContinueThroughGotoBlocks() throws Exception {
IRCode code = simpleCode();
- InstructionListIterator it = new LinearFlowInstructionIterator(code.blocks.get(0));
+ InstructionListIterator it = new LinearFlowInstructionIterator(code.entryBlock());
Instruction current = it.next();
current = it.next();
current = it.next();
@@ -142,7 +142,7 @@
public void GoToFrontAndBackIsSameAmountOfInstructions() throws Exception {
IRCode code = simpleCode();
int moves = 0;
- InstructionListIterator it = new LinearFlowInstructionIterator(code.blocks.get(0));
+ InstructionListIterator it = new LinearFlowInstructionIterator(code.entryBlock());
while (it.hasNext()) {
it.next();
moves++;
@@ -169,7 +169,7 @@
@Test
public void doNotChangeToNextBlockWhenNotLinearFlow() throws Exception {
IRCode code = branchingCode();
- InstructionListIterator it = new LinearFlowInstructionIterator(code.blocks.get(0));
+ InstructionListIterator it = new LinearFlowInstructionIterator(code.entryBlock());
it.nextUntil((i) -> !i.isArgument());
Instruction current = it.next();
assert !it.hasNext();
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index c416c6e..2a6e8c8 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -83,7 +83,7 @@
IRCode code = test.code;
assertEquals(initialBlockCount, code.blocks.size());
- BasicBlock block = code.blocks.get(0);
+ BasicBlock block = code.entryBlock();
int instructionCount = block.getInstructions().size();
assertEquals(firstBlockInstructions, instructionCount);
@@ -96,7 +96,7 @@
assertTrue(code.isConsistentSSA());
assertEquals(initialBlockCount + 1, code.blocks.size());
- assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+ assertEquals(i + 1, code.entryBlock().getInstructions().size());
assertEquals(instructionCount - i, code.blocks.get(1).getInstructions().size());
assertSame(newBlock, code.blocks.get(1));
@@ -117,7 +117,7 @@
IRCode code = test.code;
assertEquals(initialBlockCount, code.blocks.size());
- BasicBlock block = code.blocks.get(0);
+ BasicBlock block = code.entryBlock();
int instructionCount = block.getInstructions().size();
assertEquals(firstBlockInstructions, instructionCount);
@@ -130,7 +130,7 @@
assertTrue(code.isConsistentSSA());
assertEquals(initialBlockCount + 2, code.blocks.size());
- assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+ assertEquals(i + 1, code.entryBlock().getInstructions().size());
assertEquals(2, code.blocks.get(1).getInstructions().size());
assertEquals(instructionCount - i - 1, code.blocks.get(2).getInstructions().size());
assertSame(newBlock, code.blocks.get(1));
@@ -329,7 +329,7 @@
IRCode code = test.code;
assertEquals(initialBlockCount, code.blocks.size());
- BasicBlock block = code.blocks.get(0);
+ BasicBlock block = code.entryBlock();
int instructionCount = block.getInstructions().size();
assertEquals(firstBlockInstructions, instructionCount);
@@ -342,7 +342,7 @@
assertTrue(code.isConsistentSSA());
assertEquals(initialBlockCount + 1, code.blocks.size());
- assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+ assertEquals(i + 1, code.entryBlock().getInstructions().size());
assertEquals(instructionCount - i, newBlock.getInstructions().size());
assertSame(newBlock, code.blocks.get(1));
@@ -457,7 +457,7 @@
IRCode code = test.code;
assertEquals(initialBlockCount, code.blocks.size());
- BasicBlock block = code.blocks.get(0);
+ BasicBlock block = code.entryBlock();
int instructionCount = block.getInstructions().size();
assertEquals(firstBlockInstructions, instructionCount);
@@ -470,7 +470,7 @@
assertTrue(code.isConsistentSSA());
assertEquals(initialBlockCount + 1, code.blocks.size());
- assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+ assertEquals(i + 1, code.entryBlock().getInstructions().size());
assertEquals(instructionCount - i, newBlock.getInstructions().size());
assertSame(newBlock, code.blocks.get(1));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
index 927bff2..0df0f97 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -145,6 +145,6 @@
// Check that all four constant number instructions remain.
assertEquals(
4,
- code.blocks.get(0).getInstructions().stream().filter(Instruction::isConstNumber).count());
+ code.entryBlock().getInstructions().stream().filter(Instruction::isConstNumber).count());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/RedundantConstNumberRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/RedundantConstNumberRemovalTest.java
index 6b281cb..e2f47cd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/RedundantConstNumberRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/RedundantConstNumberRemovalTest.java
@@ -102,7 +102,7 @@
// Only a single basic block.
assertEquals(1, code.blocks.size());
// The block only has three instructions.
- BasicBlock entryBlock = code.blocks.get(0);
+ BasicBlock entryBlock = code.entryBlock();
assertEquals(3, entryBlock.getInstructions().size());
// The first one is the `argument` instruction.
Instruction argument = entryBlock.getInstructions().getFirst();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index 3582403..a9e2157 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -83,7 +83,7 @@
false,
Origin.unknown());
CodeRewriter.collapseTrivialGotos(null, code);
- assertTrue(code.blocks.get(0).isTrivialGoto());
+ assertTrue(code.entryBlock().isTrivialGoto());
assertTrue(blocks.contains(block0));
assertTrue(blocks.contains(block1));
assertTrue(blocks.contains(block2));
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 5103951..30db56e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -779,6 +779,7 @@
ApplicationWriter writer =
new ApplicationWriter(
application,
+ null,
options,
null,
null,
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
index 0f3accb..15599ef 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.DataResourceProvider.Visitor;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestCompileResult;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
import com.android.tools.r8.utils.ArchiveResourceProvider;
@@ -60,13 +59,24 @@
this.backend = backend;
}
- protected static class CustomDataResourceConsumer implements DataResourceConsumer {
+ public static class DataResourceConsumerForTesting implements DataResourceConsumer {
+ private final DataResourceConsumer inner;
private final Map<String, ImmutableList<String>> resources = new HashMap<>();
+ public DataResourceConsumerForTesting() {
+ this(null);
+ }
+
+ public DataResourceConsumerForTesting(DataResourceConsumer inner) {
+ this.inner = inner;
+ }
+
@Override
public void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler) {
- throw new Unreachable();
+ if (inner != null) {
+ inner.accept(directory, diagnosticsHandler);
+ }
}
@Override
@@ -79,6 +89,9 @@
} catch (Exception e) {
throw new RuntimeException(e);
}
+ if (inner != null) {
+ inner.accept(file, diagnosticsHandler);
+ }
}
@Override
@@ -88,6 +101,10 @@
return resources.get(name);
}
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
public int size() {
return resources.size();
}
@@ -175,7 +192,7 @@
@Test
public void testEnabled() throws Exception {
String pgConf = getProguardConfigWithNeverInline(true, null);
- CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer();
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
CodeInspector inspector = compileWithR8(pgConf, dataResourceConsumer).inspector();
// Check that the data resources have changed as expected.
@@ -257,7 +274,7 @@
@Test
public void testEnabledWithFilter() throws Exception {
- CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer();
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
compileWithR8(getProguardConfigWithNeverInline(true, "*.md"), dataResourceConsumer);
// Check that the file matching the filter has changed as expected.
@@ -279,7 +296,7 @@
@Test
public void testDisabled() throws Exception {
- CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer();
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
compileWithR8(getProguardConfigWithNeverInline(false, null), dataResourceConsumer);
// Check that all data resources are unchanged.
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 437f2dd..40f4249 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.naming.AdaptResourceFileContentsTest.CustomDataResourceConsumer;
+import com.android.tools.r8.naming.AdaptResourceFileContentsTest.DataResourceConsumerForTesting;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
import com.android.tools.r8.utils.AndroidApp;
@@ -107,7 +107,7 @@
@Test
public void testEnabled() throws Exception {
- CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer();
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
compileWithR8(
getProguardConfigWithNeverInline(true, null), dataResourceConsumer, this::checkR8Renamings);
// Check that the generated resources have the expected names.
@@ -120,7 +120,7 @@
@Test
public void testEnabledWithFilter() throws Exception {
- CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer();
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
compileWithR8(
getProguardConfigWithNeverInline(true, "**.md"),
dataResourceConsumer,
@@ -138,7 +138,7 @@
@Test
public void testDisabled() throws Exception {
- CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer();
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
compileWithR8(getProguardConfigWithNeverInline(false, null), dataResourceConsumer);
// Check that none of the resources were renamed.
for (DataEntryResource dataResource : getOriginalDataResources()) {
@@ -150,7 +150,7 @@
@Test
public void testCollisionBehavior() throws Exception {
- CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer();
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
compileWithR8(
getProguardConfigWithNeverInline(true, null),
dataResourceConsumer,
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index 7b6052a..4dcb52f 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.RootSetBuilder;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
@@ -74,9 +75,12 @@
new RootSetBuilder(appView, program, configuration.getRules(), options).run(executor);
Enqueuer enqueuer = new Enqueuer(appView, options, null, options.forceProguardCompatibility);
- AppInfoWithSubtyping appInfo =
+ AppInfoWithLiveness appInfo =
enqueuer.traceApplication(rootSet, configuration.getDontWarnPatterns(), executor, timing);
- return new Minifier(appInfo.withLiveness(), rootSet, Collections.emptySet(), options)
+ return new Minifier(
+ new AppView<>(appInfo, GraphLense.getIdentityLense(), options),
+ rootSet,
+ Collections.emptySet())
.run(timing);
}
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index 3112ae7..659a8fc 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -7,19 +7,21 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
@@ -27,7 +29,7 @@
public class B124357885Test extends TestBase {
- private void checkSignatureAnnotation(AnnotationSubject signature) {
+ private void checkSignatureAnnotation(CodeInspector inspector, AnnotationSubject signature) {
DexAnnotationElement[] elements = signature.getAnnotation().elements;
assertEquals(1, elements.length);
assertEquals("value", elements[0].name.toString());
@@ -38,20 +40,22 @@
assertTrue(value instanceof DexValueString);
builder.append(((DexValueString) value).value);
}
- // TODO(124357885): This should be the minified name for FooImpl instead of Foo.
- String fooDescriptor = DescriptorUtils.javaTypeToDescriptor(Foo.class.getTypeName());
+ String fooImplFinalDescriptor =
+ DescriptorUtils.javaTypeToDescriptor(inspector.clazz(FooImpl.class).getFinalName());
StringBuilder expected =
new StringBuilder()
.append("()")
- .append(fooDescriptor.substring(0, fooDescriptor.length() - 1)) // Remove the ;.
+ // Remove the final ; from the descriptor to add the generic type.
+ .append(fooImplFinalDescriptor.substring(0, fooImplFinalDescriptor.length() - 1))
.append("<Ljava/lang/String;>")
- .append(";"); // Add the ; here.
+ // Add the ; after the generic type.
+ .append(";");
assertEquals(expected.toString(), builder.toString());
}
@Test
public void test() throws Exception {
- testForR8(Backend.DEX)
+ R8TestCompileResult compileResult = testForR8(Backend.DEX)
.addProgramClasses(Main.class, Service.class, Foo.class, FooImpl.class)
.addKeepMainRule(Main.class)
.addKeepRules("-keepattributes Signature,InnerClasses,EnclosingMethod")
@@ -65,13 +69,14 @@
assertEquals(1, inspector.clazz(Service.class).allMethods().size());
MethodSubject fooList = inspector.clazz(Service.class).allMethods().get(0);
AnnotationSubject signature = fooList.annotation("dalvik.annotation.Signature");
- checkSignatureAnnotation(signature);
- })
- .run(Main.class)
- .assertFailureWithErrorThatMatches(
- containsString(
- "java.lang.ClassNotFoundException: "
- + "Didn't find class \"com.android.tools.r8.naming.b124357885.Foo\""));
+ checkSignatureAnnotation(inspector, signature);
+ });
+
+ String fooImplFinalName = compileResult.inspector().clazz(FooImpl.class).getFinalName();
+
+ compileResult
+ .run(Main.class)
+ .assertSuccessWithOutput(StringUtils.lines(fooImplFinalName, fooImplFinalName));
}
}
@@ -84,7 +89,7 @@
// Convince R8 we only use subtypes to get class merging of Foo into FooImpl.
Foo<String> foo = new FooImpl<>();
- System.out.println(foo);
+ System.out.println(foo.getClass().getTypeName());
}
}
@@ -94,4 +99,4 @@
interface Foo<T> {}
-class FooImpl<T> implements Foo<T> {}
\ No newline at end of file
+class FooImpl<T> implements Foo<T> {}
diff --git a/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
new file mode 100644
index 0000000..d5f456b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2019, 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.proguard.rules;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+
+/** Regression test for b/124584385. */
+public class ProguardMatchAllRuleWithPrefixTest extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .addProgramClasses(TestClass.class)
+ .addKeepRules(
+ "-keep,allowobfuscation class com.android.tools.r8.*** {",
+ " com.android.tools.r8.*** methodA();",
+ " com.android.tools.r8.***Class methodB();",
+ " com.android.tools.r8.***[] methodC();",
+ " com.android.tools.r8.***Class[] methodD();",
+ "}")
+ .compile()
+ .inspector();
+
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+ assertThat(classSubject.uniqueMethodWithName("methodA"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("methodB"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("methodC"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("methodD"), isPresent());
+ }
+
+ static class TestClass {
+
+ TestClass methodA() {
+ return new TestClass();
+ }
+
+ TestClass methodB() {
+ return new TestClass();
+ }
+
+ TestClass[] methodC() {
+ return new TestClass[0];
+ }
+
+ TestClass[] methodD() {
+ return new TestClass[0];
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
index e005a2c..29e5bcd 100644
--- a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -4,11 +4,20 @@
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.naming.AdaptResourceFileContentsTest.DataResourceConsumerForTesting;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.Lists;
import java.util.List;
@@ -21,14 +30,18 @@
@RunWith(Parameterized.class)
public class ServiceLoaderTest extends TestBase {
+ private final Backend backend;
private final boolean includeWorldGreeter;
- @Parameters(name = "Include WorldGreeter: {0}")
- public static Boolean[] data() {
- return BooleanUtils.values();
+ private DataResourceConsumerForTesting dataResourceConsumer;
+
+ @Parameters(name = "Backend: {0}, include WorldGreeter: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(Backend.values(), BooleanUtils.values());
}
- public ServiceLoaderTest(boolean includeWorldGreeter) {
+ public ServiceLoaderTest(Backend backend, boolean includeWorldGreeter) {
+ this.backend = backend;
this.includeWorldGreeter = includeWorldGreeter;
}
@@ -42,26 +55,84 @@
}
CodeInspector inspector =
- testForR8(Backend.DEX)
+ testForR8(backend)
.addInnerClasses(ServiceLoaderTest.class)
.addKeepMainRule(TestClass.class)
- // TODO(b/124181030): Test should work without the following keep-all-rules.
- .addKeepAllClassesRule()
- .addKeepAllInterfacesRule()
.addDataEntryResources(
DataEntryResource.fromBytes(
StringUtils.lines(serviceImplementations).getBytes(),
"META-INF/services/" + Greeter.class.getTypeName(),
Origin.unknown()))
+ .addOptionsModification(
+ options -> {
+ dataResourceConsumer =
+ new DataResourceConsumerForTesting(options.dataResourceConsumer);
+ options.dataResourceConsumer = dataResourceConsumer;
+ })
.run(TestClass.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
- // TODO(b/124181030): Verify that Greeter is merged into HelloGreeter when `includeWorldGreeter`
- // is false.
+ ClassSubject greeterSubject = inspector.clazz(Greeter.class);
+ assertEquals(includeWorldGreeter, greeterSubject.isPresent());
- // TODO(b/124181030): Verify that META-INF/services/...WorldGreeter is removed when
- // `includeWorldGreeter` is false.
+ ClassSubject helloGreeterSubject = inspector.clazz(HelloGreeter.class);
+ assertThat(helloGreeterSubject, isPresent());
+
+ ClassSubject worldGreeterSubject = inspector.clazz(WorldGreeter.class);
+ assertEquals(includeWorldGreeter, worldGreeterSubject.isPresent());
+
+ String serviceFileName =
+ includeWorldGreeter ? greeterSubject.getFinalName() : helloGreeterSubject.getFinalName();
+ List<String> lines =
+ dataResourceConsumer.get(AppServices.SERVICE_DIRECTORY_NAME + serviceFileName);
+ assertEquals(includeWorldGreeter ? 2 : 1, lines.size());
+ assertEquals(helloGreeterSubject.getFinalName(), lines.get(0));
+ if (includeWorldGreeter) {
+ assertEquals(worldGreeterSubject.getFinalName(), lines.get(1));
+ }
+
+ // TODO(b/124181030): Verify that -whyareyoukeeping works as intended.
+ }
+
+ @Test
+ public void testResourceElimination() throws Exception {
+ String expectedOutput = "Hello world!";
+
+ List<String> serviceImplementations = Lists.newArrayList(HelloGreeter.class.getTypeName());
+ if (includeWorldGreeter) {
+ serviceImplementations.add(WorldGreeter.class.getTypeName());
+ }
+
+ CodeInspector inspector =
+ testForR8(backend)
+ .addInnerClasses(ServiceLoaderTest.class)
+ .addKeepMainRule(OtherTestClass.class)
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines(serviceImplementations).getBytes(),
+ "META-INF/services/" + Greeter.class.getTypeName(),
+ Origin.unknown()))
+ .addOptionsModification(
+ options -> {
+ dataResourceConsumer =
+ new DataResourceConsumerForTesting(options.dataResourceConsumer);
+ options.dataResourceConsumer = dataResourceConsumer;
+ })
+ .run(OtherTestClass.class)
+ .assertSuccessWithOutput(expectedOutput)
+ .inspector();
+
+ ClassSubject greeterSubject = inspector.clazz(Greeter.class);
+ assertThat(greeterSubject, not(isPresent()));
+
+ ClassSubject helloGreeterSubject = inspector.clazz(HelloGreeter.class);
+ assertThat(helloGreeterSubject, not(isPresent()));
+
+ ClassSubject worldGreeterSubject = inspector.clazz(WorldGreeter.class);
+ assertThat(worldGreeterSubject, not(isPresent()));
+
+ assertTrue(dataResourceConsumer.isEmpty());
}
static class TestClass {
@@ -73,6 +144,13 @@
}
}
+ static class OtherTestClass {
+
+ public static void main(String[] args) {
+ System.out.print("Hello world!");
+ }
+ }
+
public interface Greeter {
String greeting();
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index fb61a57..b99cbdc 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -113,6 +113,7 @@
ApplicationWriter writer =
new ApplicationWriter(
dexApp,
+ null,
options,
null,
null,
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index aa083da..f585226 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -321,6 +321,14 @@
return obfuscatedTypeName != null ? obfuscatedTypeName : originalTypeName;
}
+ String getOriginalTypeName(String minifiedTypeName) {
+ String originalTypeName = null;
+ if (mapping != null) {
+ originalTypeName = mapType(obfuscatedToOriginalMapping, minifiedTypeName);
+ }
+ return originalTypeName != null ? originalTypeName : minifiedTypeName;
+ }
+
InstructionSubject createInstructionSubject(Instruction instruction) {
DexInstructionSubject dexInst = new DexInstructionSubject(instruction);
if (dexInst.isInvoke()) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 6581a47..dd7cd85 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -143,18 +143,14 @@
// X method(X) -> a
//
// whereas the final signature is for X.a is "a (a)"
- String[] OriginalParameters = new String[signature.parameters.length];
- for (int i = 0; i < OriginalParameters.length; i++) {
- String obfuscated = signature.parameters[i];
- String original = codeInspector.obfuscatedToOriginalMapping.get(obfuscated);
- OriginalParameters[i] = original != null ? original : obfuscated;
+ String[] originalParameters = new String[signature.parameters.length];
+ for (int i = 0; i < originalParameters.length; i++) {
+ originalParameters[i] = codeInspector.getOriginalTypeName(signature.parameters[i]);
}
- String obfuscatedReturnType = signature.type;
- String originalReturnType = codeInspector.obfuscatedToOriginalMapping.get(obfuscatedReturnType);
- String returnType = originalReturnType != null ? originalReturnType : obfuscatedReturnType;
+ String returnType = codeInspector.getOriginalTypeName(signature.type);
MethodSignature lookupSignature =
- new MethodSignature(signature.name, returnType, OriginalParameters);
+ new MethodSignature(signature.name, returnType, originalParameters);
MemberNaming memberNaming = clazz.naming.lookup(lookupSignature);
return memberNaming != null ? (MethodSignature) memberNaming.getOriginalSignature() : signature;