Read services from META-INF/services/
Bug: 124181030
Change-Id: Icc929973524669ed388991d582b5d54f01861182
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 4f472c6..77968e6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
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;
@@ -263,6 +264,8 @@
AppView<AppInfoWithSubtyping> appView =
new AppView<>(
new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
+ appView.setAppServices(AppServices.builder(application).build());
+
List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
RootSet rootSet;
String proguardSeedsData = null;
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..cfa5898
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -0,0 +1,108 @@
+// 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.utils.DescriptorUtils;
+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.Set;
+import java.util.stream.Collectors;
+
+/** A description of the services and their implementations found in META-INF/services/. */
+public class AppServices {
+
+ private final Map<DexType, Set<DexType>> services;
+
+ private AppServices(Map<DexType, Set<DexType>> services) {
+ this.services = services;
+ }
+
+ public static Builder builder(DexApplication application) {
+ return new Builder(application);
+ }
+
+ public static class Builder {
+
+ private static final String SERVICE_DIRECTORY_NAME = "META-INF/services/";
+
+ private final DexApplication app;
+ private final Map<DexType, Set<DexType>> services = new IdentityHashMap<>();
+
+ private Builder(DexApplication app) {
+ this.app = app;
+ }
+
+ public AppServices build() {
+ for (ProgramResourceProvider programResourceProvider : app.programResourceProviders) {
+ DataResourceProvider dataResourceProvider =
+ programResourceProvider.getDataResourceProvider();
+ if (dataResourceProvider != null) {
+ readServices(dataResourceProvider);
+ }
+ }
+ return new AppServices(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 = app.dexItemFactory.createType(serviceDescriptor);
+ byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+ String contents = new String(bytes, Charset.defaultCharset());
+ services.put(serviceType, readServiceImplementationsForService(contents));
+ }
+ }
+ } catch (IOException | ResourceException e) {
+ throw new CompilationError(e.getMessage(), e);
+ }
+ }
+
+ private Set<DexType> readServiceImplementationsForService(String contents) {
+ if (contents != null) {
+ return Arrays.stream(contents.split(System.lineSeparator()))
+ .map(String::trim)
+ .filter(line -> !line.isEmpty())
+ .filter(DescriptorUtils::isValidJavaType)
+ .map(DescriptorUtils::javaTypeToDescriptor)
+ .map(app.dexItemFactory::createType)
+ .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();
}