Revert "Ensure no service loader rewriting if it crosses a feature split"
This reverts commit 8e258a371de31374fd3da8f79291dfb49f6df6cf.
Reason for revert: NPE
Change-Id: I3810807e815e3e59eaaeb3e870fccdf2207bd096
diff --git a/src/main/java/com/android/tools/r8/FeatureSplit.java b/src/main/java/com/android/tools/r8/FeatureSplit.java
index 9bb637e..5947786 100644
--- a/src/main/java/com/android/tools/r8/FeatureSplit.java
+++ b/src/main/java/com/android/tools/r8/FeatureSplit.java
@@ -29,9 +29,6 @@
*/
@Keep
public final class FeatureSplit {
-
- public static final FeatureSplit BASE = new FeatureSplit(null, null);
-
private final ProgramConsumer programConsumer;
private final List<ProgramResourceProvider> programResourceProviders;
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index f010b85..5c1c7ad 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -8,8 +8,6 @@
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.DataResourceProvider.Visitor;
-import com.android.tools.r8.FeatureSplit;
-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;
@@ -23,7 +21,7 @@
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.LinkedHashMap;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -38,9 +36,9 @@
private final AppView<?> appView;
// Mapping from service types to service implementation types.
- private final Map<DexType, Map<FeatureSplit, List<DexType>>> services;
+ private final Map<DexType, List<DexType>> services;
- private AppServices(AppView<?> appView, Map<DexType, Map<FeatureSplit, List<DexType>>> services) {
+ private AppServices(AppView<?> appView, Map<DexType, List<DexType>> services) {
this.appView = appView;
this.services = services;
}
@@ -56,124 +54,66 @@
public List<DexType> serviceImplementationsFor(DexType serviceType) {
assert verifyRewrittenWithLens();
- Map<FeatureSplit, List<DexType>> featureSplitListMap = services.get(serviceType);
- if (featureSplitListMap == null) {
+ assert services.containsKey(serviceType);
+ List<DexType> serviceImplementationTypes = services.get(serviceType);
+ if (serviceImplementationTypes == null) {
assert false
: "Unexpected attempt to get service implementations for non-service type `"
+ serviceType.toSourceString()
+ "`";
return ImmutableList.of();
}
- ImmutableList.Builder<DexType> builder = ImmutableList.builder();
- for (List<DexType> implementations : featureSplitListMap.values()) {
- builder.addAll(implementations);
- }
- return builder.build();
- }
-
- public boolean hasServiceImplementationsInFeature(DexType serviceType) {
- if (appView.options().featureSplitConfiguration == null) {
- return false;
- }
- Map<FeatureSplit, List<DexType>> featureImplementations = services.get(serviceType);
- if (featureImplementations == null || featureImplementations.isEmpty()) {
- assert false
- : "Unexpected attempt to get service implementations for non-service type `"
- + serviceType.toSourceString()
- + "`";
- return true;
- }
- if (featureImplementations.size() > 1
- || !featureImplementations.containsKey(FeatureSplit.BASE)) {
- return true;
- }
- // Check if service is defined feature
- DexProgramClass serviceClass = appView.definitionForProgramType(serviceType);
- if (appView.options().featureSplitConfiguration.isInFeature(serviceClass)) {
- return true;
- }
- for (DexType dexType : featureImplementations.get(FeatureSplit.BASE)) {
- DexProgramClass implementationClass = appView.definitionForProgramType(dexType);
- if (appView.options().featureSplitConfiguration.isInFeature(implementationClass)) {
- return true;
- }
- }
- return false;
+ return serviceImplementationTypes;
}
public AppServices rewrittenWithLens(GraphLense graphLens) {
- ImmutableMap.Builder<DexType, Map<FeatureSplit, List<DexType>>> rewrittenFeatureMappings =
- ImmutableMap.builder();
- for (Entry<DexType, Map<FeatureSplit, List<DexType>>> entry : services.entrySet()) {
+ ImmutableMap.Builder<DexType, List<DexType>> rewrittenServices = ImmutableMap.builder();
+ for (Entry<DexType, List<DexType>> entry : services.entrySet()) {
DexType rewrittenServiceType = graphLens.lookupType(entry.getKey());
- ImmutableMap.Builder<FeatureSplit, List<DexType>> rewrittenFeatureImplementations =
- ImmutableMap.builder();
- for (Entry<FeatureSplit, List<DexType>> featureSplitImpls : entry.getValue().entrySet()) {
- ImmutableList.Builder<DexType> rewrittenServiceImplementationTypes =
- ImmutableList.builder();
- for (DexType serviceImplementationType : featureSplitImpls.getValue()) {
- rewrittenServiceImplementationTypes.add(graphLens.lookupType(serviceImplementationType));
- }
- rewrittenFeatureImplementations.put(
- featureSplitImpls.getKey(), rewrittenServiceImplementationTypes.build());
+ ImmutableList.Builder<DexType> rewrittenServiceImplementationTypes = ImmutableList.builder();
+ for (DexType serviceImplementationType : entry.getValue()) {
+ rewrittenServiceImplementationTypes.add(graphLens.lookupType(serviceImplementationType));
}
- rewrittenFeatureMappings.put(rewrittenServiceType, rewrittenFeatureImplementations.build());
+ rewrittenServices.put(rewrittenServiceType, rewrittenServiceImplementationTypes.build());
}
- return new AppServices(appView, rewrittenFeatureMappings.build());
+ return new AppServices(appView, rewrittenServices.build());
}
public AppServices prunedCopy(Collection<DexType> removedClasses) {
- ImmutableMap.Builder<DexType, Map<FeatureSplit, List<DexType>>> rewrittenServicesBuilder =
- ImmutableMap.builder();
- for (Entry<DexType, Map<FeatureSplit, List<DexType>>> entry : services.entrySet()) {
- if (removedClasses.contains(entry.getKey())) {
- continue;
- }
- ImmutableMap.Builder<FeatureSplit, List<DexType>> prunedFeatureSplitImpls =
- ImmutableMap.builder();
- for (Entry<FeatureSplit, List<DexType>> featureSplitEntry : entry.getValue().entrySet()) {
+ ImmutableMap.Builder<DexType, List<DexType>> rewrittenServicesBuilder = ImmutableMap.builder();
+ for (Entry<DexType, List<DexType>> entry : services.entrySet()) {
+ if (!removedClasses.contains(entry.getKey())) {
+ DexType serviceType = entry.getKey();
ImmutableList.Builder<DexType> rewrittenServiceImplementationTypesBuilder =
ImmutableList.builder();
- for (DexType serviceImplementationType : featureSplitEntry.getValue()) {
+ for (DexType serviceImplementationType : entry.getValue()) {
if (!removedClasses.contains(serviceImplementationType)) {
rewrittenServiceImplementationTypesBuilder.add(serviceImplementationType);
}
}
- List<DexType> prunedFeatureSplitImplementations =
+ List<DexType> rewrittenServiceImplementationTypes =
rewrittenServiceImplementationTypesBuilder.build();
- if (prunedFeatureSplitImplementations.size() > 0) {
- prunedFeatureSplitImpls.put(
- featureSplitEntry.getKey(), rewrittenServiceImplementationTypesBuilder.build());
+ if (rewrittenServiceImplementationTypes.size() > 0) {
+ rewrittenServicesBuilder.put(
+ serviceType, rewrittenServiceImplementationTypesBuilder.build());
}
}
- ImmutableMap<FeatureSplit, List<DexType>> prunedServiceImplementations =
- prunedFeatureSplitImpls.build();
- if (prunedServiceImplementations.size() > 0) {
- rewrittenServicesBuilder.put(entry.getKey(), prunedServiceImplementations);
- }
}
return new AppServices(appView, rewrittenServicesBuilder.build());
}
private boolean verifyRewrittenWithLens() {
- for (Entry<DexType, Map<FeatureSplit, List<DexType>>> entry : services.entrySet()) {
+ for (Entry<DexType, List<DexType>> entry : services.entrySet()) {
assert entry.getKey() == appView.graphLense().lookupType(entry.getKey());
- for (Entry<FeatureSplit, List<DexType>> featureEntry : entry.getValue().entrySet()) {
- for (DexType type : featureEntry.getValue()) {
- assert type == appView.graphLense().lookupType(type);
- }
+ for (DexType type : entry.getValue()) {
+ assert type == appView.graphLense().lookupType(type);
}
}
return true;
}
public void visit(BiConsumer<DexType, List<DexType>> consumer) {
- services.forEach(
- (type, featureImpls) -> {
- ImmutableList.Builder<DexType> builder = ImmutableList.builder();
- featureImpls.values().forEach(builder::addAll);
- consumer.accept(type, builder.build());
- });
+ services.forEach(consumer);
}
public static Builder builder(AppView<?> appView) {
@@ -183,7 +123,7 @@
public static class Builder {
private final AppView<?> appView;
- private final Map<DexType, Map<FeatureSplit, List<DexType>>> services = new LinkedHashMap<>();
+ private final Map<DexType, List<DexType>> services = new IdentityHashMap<>();
private Builder(AppView<?> appView) {
this.appView = appView;
@@ -191,22 +131,14 @@
public AppServices build() {
for (DataResourceProvider provider : appView.appInfo().app().dataResourceProviders) {
- readServices(provider, FeatureSplit.BASE);
- }
- List<FeatureSplit> featureSplits =
- appView.options().featureSplitConfiguration.getFeatureSplits();
- for (FeatureSplit featureSplit : featureSplits) {
- for (ProgramResourceProvider provider : featureSplit.getProgramResourceProviders()) {
- readServices(provider.getDataResourceProvider(), featureSplit);
- }
+ readServices(provider);
}
return new AppServices(appView, services);
}
- private void readServices(
- DataResourceProvider dataResourceProvider, FeatureSplit featureSplit) {
+ private void readServices(DataResourceProvider dataResourceProvider) {
try {
- dataResourceProvider.accept(new DataResourceProviderVisitor(featureSplit));
+ dataResourceProvider.accept(new DataResourceProviderVisitor());
} catch (ResourceException e) {
throw new CompilationError(e.getMessage(), e);
}
@@ -214,12 +146,6 @@
private class DataResourceProviderVisitor implements Visitor {
- private final FeatureSplit featureSplit;
-
- public DataResourceProviderVisitor(FeatureSplit featureSplit) {
- this.featureSplit = featureSplit;
- }
-
@Override
public void visit(DataDirectoryResource directory) {
// Ignore.
@@ -236,10 +162,8 @@
DexType serviceType = appView.dexItemFactory().createType(serviceDescriptor);
byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
String contents = new String(bytes, Charset.defaultCharset());
- Map<FeatureSplit, List<DexType>> featureSplitImplementations =
- services.computeIfAbsent(serviceType, k -> new LinkedHashMap<>());
List<DexType> serviceImplementations =
- featureSplitImplementations.computeIfAbsent(featureSplit, f -> new ArrayList<>());
+ services.computeIfAbsent(serviceType, (key) -> new ArrayList<>());
readServiceImplementationsForService(
contents, file.getOrigin(), serviceImplementations);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 71a2a18..263cadd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -132,11 +132,6 @@
continue;
}
- // Check that we are not service loading anything from a feature into base.
- if (appView.appServices().hasServiceImplementationsInFeature(constClass.getValue())) {
- continue;
- }
-
// Check that ClassLoader used is the ClassLoader defined for the the service configuration
// that we are instantiating or NULL.
InvokeVirtual classLoaderInvoke =
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitServiceLoaderTest.java b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitServiceLoaderTest.java
index c290f26..39a1b03 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitServiceLoaderTest.java
@@ -5,14 +5,11 @@
package com.android.tools.r8.dexsplitter;
import static com.android.tools.r8.rewrite.ServiceLoaderRewritingTest.getServiceLoaderLoads;
-import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.DataEntryResource;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringUtils;
@@ -39,15 +36,18 @@
}
@Test
- public void testR8AllServiceConfigurationInBaseAndNoTypesInFeatures() throws Exception {
+ public void testR8AllServiceConfigurationInBase() throws Exception {
Path base = temp.newFile("base.zip").toPath();
Path feature1Path = temp.newFile("feature1.zip").toPath();
+ Path feature2Path = temp.newFile("feature2.zip").toPath();
testForR8(parameters.getBackend())
- .addProgramClasses(Base.class, I.class, Feature1I.class, Feature2I.class)
+ .addProgramClasses(Base.class, I.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Base.class)
.addFeatureSplit(
- builder -> simpleSplitProvider(builder, feature1Path, temp, Feature3Dummy.class))
+ builder -> simpleSplitProvider(builder, feature1Path, temp, Feature1I.class))
+ .addFeatureSplit(
+ builder -> simpleSplitProvider(builder, feature2Path, temp, Feature2I.class))
.addDataEntryResources(
DataEntryResource.fromBytes(
StringUtils.lines(Feature1I.class.getTypeName(), Feature2I.class.getTypeName())
@@ -57,99 +57,59 @@
.compile()
.inspect(
inspector -> {
+ // TODO(b/157426812): This should be 1.
assertEquals(0, getServiceLoaderLoads(inspector, Base.class));
})
.writeToZip(base)
+ .addRunClasspathFiles(feature1Path, feature2Path)
.run(parameters.getRuntime(), Base.class)
.assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
}
@Test
- public void testR8AllServiceConfigurationInBase() throws Exception {
- Path base = temp.newFile("base.zip").toPath();
- Path feature1Path = temp.newFile("feature1.zip").toPath();
- Path feature2Path = temp.newFile("feature2.zip").toPath();
- R8TestRunResult runResult =
- testForR8(parameters.getBackend())
- .addProgramClasses(Base.class, I.class)
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Base.class)
- .addFeatureSplit(
- builder -> simpleSplitProvider(builder, feature1Path, temp, Feature1I.class))
- .addFeatureSplit(
- builder -> simpleSplitProvider(builder, feature2Path, temp, Feature2I.class))
- .addDataEntryResources(
- DataEntryResource.fromBytes(
- StringUtils.lines(Feature1I.class.getTypeName(), Feature2I.class.getTypeName())
- .getBytes(),
- "META-INF/services/" + I.class.getTypeName(),
- Origin.unknown()))
- .compile()
- .inspect(
- inspector -> {
- assertEquals(1, getServiceLoaderLoads(inspector, Base.class));
- })
- .writeToZip(base)
- .addRunClasspathFiles(feature1Path, feature2Path)
- .run(parameters.getRuntime(), Base.class);
- // TODO(b/160888348): This is failing on 7.0
- if (parameters.getRuntime().isDex()
- && parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
- runResult.assertFailureWithErrorThatMatches(containsString("ServiceConfigurationError"));
- } else {
- runResult.assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
- }
- }
-
- @Test
public void testR8AllLoaded() throws Exception {
Path base = temp.newFile("base.zip").toPath();
Path feature1Path = temp.newFile("feature1.zip").toPath();
Path feature2Path = temp.newFile("feature2.zip").toPath();
- R8TestRunResult runResult =
- testForR8(parameters.getBackend())
- .addProgramClasses(Base.class, I.class)
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Base.class)
- .addFeatureSplit(
- builder ->
- splitWithNonJavaFile(
- builder,
- feature1Path,
- temp,
- ImmutableList.of(
- new Pair<>(
- "META-INF/services/" + I.class.getTypeName(),
- StringUtils.lines(Feature1I.class.getTypeName()))),
- true,
- Feature1I.class))
- .addFeatureSplit(
- builder ->
- splitWithNonJavaFile(
- builder,
- feature2Path,
- temp,
- ImmutableList.of(
- new Pair<>(
- "META-INF/services/" + I.class.getTypeName(),
- StringUtils.lines(Feature2I.class.getTypeName()))),
- true,
- Feature2I.class))
- .compile()
- .inspect(
- inspector -> {
- assertEquals(1, getServiceLoaderLoads(inspector, Base.class));
- })
- .writeToZip(base)
- .addRunClasspathFiles(feature1Path, feature2Path)
- .run(parameters.getRuntime(), Base.class);
- // TODO(b/160888348): This is failing on 7.0
- if (parameters.getRuntime().isDex()
- && parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
- runResult.assertFailureWithErrorThatMatches(containsString("ServiceConfigurationError"));
- } else {
- runResult.assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Base.class, I.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Base.class)
+ .addFeatureSplit(
+ builder ->
+ splitWithNonJavaFile(
+ builder,
+ feature1Path,
+ temp,
+ ImmutableList.of(
+ new Pair<>(
+ "META-INF/services/" + I.class.getTypeName(),
+ StringUtils.lines(Feature1I.class.getTypeName()))),
+ true,
+ Feature1I.class))
+ .addFeatureSplit(
+ builder ->
+ splitWithNonJavaFile(
+ builder,
+ feature2Path,
+ temp,
+ ImmutableList.of(
+ new Pair<>(
+ "META-INF/services/" + I.class.getTypeName(),
+ StringUtils.lines(Feature2I.class.getTypeName()))),
+ true,
+ Feature2I.class))
+ .compile()
+ .inspect(
+ inspector -> {
+ assertEquals(1, getServiceLoaderLoads(inspector, Base.class));
+ })
+ .writeToZip(base)
+ .addRunClasspathFiles(feature1Path, feature2Path)
+ .run(parameters.getRuntime(), Base.class)
+ // TODO(b/157426812): Should output
+ // .assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
+ .assertSuccessWithOutput("");
}
@Test
@@ -158,47 +118,42 @@
Path feature1Path = temp.newFile("feature1.zip").toPath();
Path feature2Path = temp.newFile("feature2.zip").toPath();
Path feature3Path = temp.newFile("feature3.zip").toPath();
- R8TestRunResult runResult =
- testForR8(parameters.getBackend())
- .addProgramClasses(Base.class, I.class)
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Base.class)
- .addFeatureSplit(
- builder -> simpleSplitProvider(builder, feature1Path, temp, Feature1I.class))
- .addFeatureSplit(
- builder -> simpleSplitProvider(builder, feature2Path, temp, Feature2I.class))
- .addFeatureSplit(
- builder ->
- splitWithNonJavaFile(
- builder,
- feature3Path,
- temp,
- ImmutableList.of(
- new Pair<>(
- "META-INF/services/" + I.class.getTypeName(),
- StringUtils.lines(
- Feature1I.class.getTypeName(), Feature2I.class.getTypeName()))),
- true,
- Feature3Dummy.class))
- .compile()
- .inspect(
- inspector -> {
- assertEquals(1, getServiceLoaderLoads(inspector, Base.class));
- })
- .writeToZip(base)
- .addRunClasspathFiles(feature1Path, feature2Path, feature3Path)
- .run(parameters.getRuntime(), Base.class);
- // TODO(b/160888348): This is failing on 7.0
- if (parameters.getRuntime().isDex()
- && parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
- runResult.assertFailureWithErrorThatMatches(containsString("ServiceConfigurationError"));
- } else {
- runResult.assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Base.class, I.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Base.class)
+ .addFeatureSplit(
+ builder -> simpleSplitProvider(builder, feature1Path, temp, Feature1I.class))
+ .addFeatureSplit(
+ builder -> simpleSplitProvider(builder, feature2Path, temp, Feature2I.class))
+ .addFeatureSplit(
+ builder ->
+ splitWithNonJavaFile(
+ builder,
+ feature3Path,
+ temp,
+ ImmutableList.of(
+ new Pair<>(
+ "META-INF/services/" + I.class.getTypeName(),
+ StringUtils.lines(
+ Feature1I.class.getTypeName(), Feature2I.class.getTypeName()))),
+ true,
+ Feature3Dummy.class))
+ .compile()
+ .inspect(
+ inspector -> {
+ assertEquals(1, getServiceLoaderLoads(inspector, Base.class));
+ })
+ .writeToZip(base)
+ .addRunClasspathFiles(feature1Path, feature2Path, feature3Path)
+ .run(parameters.getRuntime(), Base.class)
+ // TODO(b/157426812): Should output
+ // .assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
+ .assertSuccessWithOutput("");
}
@Test
- public void testR8OnlyFeature2() throws Exception {
+ public void testR8NotFound() throws Exception {
Path base = temp.newFile("base.zip").toPath();
Path feature1Path = temp.newFile("feature1.zip").toPath();
Path feature2Path = temp.newFile("feature2.zip").toPath();
@@ -238,8 +193,9 @@
.writeToZip(base)
.addRunClasspathFiles(feature2Path)
.run(parameters.getRuntime(), Base.class)
- // TODO(b/160889305): This should work.
- .assertFailureWithErrorThatMatches(containsString("java.lang.ClassNotFoundException"));
+ // TODO(b/157426812): Should output
+ // .assertSuccessWithOutputLines("Feature2I.foo()");
+ .assertSuccessWithOutput("");
}
public interface I {