Revert "Rewrite of ServiceLoader.load to NewInstance when possible"
This reverts commit b027e3c0b123bd4a9397ff210e40293c1381d1a8.
Reason for revert: Failing kotlinreflecttest
Change-Id: I09c2c405b1a309f607aaaaca624447b16365e08a
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 0122834..fab313f 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -13,15 +13,13 @@
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.ImmutableList;
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.HashSet;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -35,9 +33,9 @@
private final AppView<? extends AppInfo> appView;
// Mapping from service types to service implementation types.
- private final Map<DexType, List<DexType>> services;
+ private final Map<DexType, Set<DexType>> services;
- private AppServices(AppView<? extends AppInfo> appView, Map<DexType, List<DexType>> services) {
+ private AppServices(AppView<? extends AppInfo> appView, Map<DexType, Set<DexType>> services) {
this.appView = appView;
this.services = services;
}
@@ -47,25 +45,25 @@
return services.keySet();
}
- public List<DexType> serviceImplementationsFor(DexType serviceType) {
+ public Set<DexType> serviceImplementationsFor(DexType serviceType) {
assert verifyRewrittenWithLens();
assert services.containsKey(serviceType);
- List<DexType> serviceImplementationTypes = services.get(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 ImmutableList.of();
+ return ImmutableSet.of();
}
return serviceImplementationTypes;
}
public AppServices rewrittenWithLens(GraphLense graphLens) {
- ImmutableMap.Builder<DexType, List<DexType>> rewrittenServices = ImmutableMap.builder();
- for (Entry<DexType, List<DexType>> entry : services.entrySet()) {
+ ImmutableMap.Builder<DexType, Set<DexType>> rewrittenServices = ImmutableMap.builder();
+ for (Entry<DexType, Set<DexType>> entry : services.entrySet()) {
DexType rewrittenServiceType = graphLens.lookupType(entry.getKey());
- ImmutableList.Builder<DexType> rewrittenServiceImplementationTypes = ImmutableList.builder();
+ ImmutableSet.Builder<DexType> rewrittenServiceImplementationTypes = ImmutableSet.builder();
for (DexType serviceImplementationType : entry.getValue()) {
rewrittenServiceImplementationTypes.add(graphLens.lookupType(serviceImplementationType));
}
@@ -75,7 +73,7 @@
}
private boolean verifyRewrittenWithLens() {
- for (Entry<DexType, List<DexType>> entry : services.entrySet()) {
+ 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);
@@ -91,7 +89,7 @@
public static class Builder {
private final AppView<? extends AppInfo> appView;
- private final Map<DexType, List<DexType>> services = new IdentityHashMap<>();
+ private final Map<DexType, Set<DexType>> services = new IdentityHashMap<>();
private Builder(AppView<? extends AppInfo> appView) {
this.appView = appView;
@@ -139,9 +137,8 @@
}
}
- private List<DexType> readServiceImplementationsForService(String contents, Origin origin) {
+ private Set<DexType> readServiceImplementationsForService(String contents, Origin origin) {
if (contents != null) {
- Set<DexType> seenTypes = new HashSet<>();
return Arrays.stream(contents.split(System.lineSeparator()))
.map(String::trim)
.map(this::prefixUntilCommentChar)
@@ -164,11 +161,11 @@
origin));
return false;
}
- return seenTypes.add(serviceImplementationType);
+ return true;
})
- .collect(Collectors.toList());
+ .collect(Collectors.toSet());
}
- return ImmutableList.of();
+ return ImmutableSet.of();
}
private String prefixUntilCommentChar(String line) {
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 4934f4a..61f40c8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -191,7 +191,6 @@
public final DexString npeDescriptor = createString("Ljava/lang/NullPointerException;");
public final DexString proxyDescriptor = createString("Ljava/lang/reflect/Proxy;");
public final DexString serviceLoaderDescriptor = createString("Ljava/util/ServiceLoader;");
- public final DexString listDescriptor = createString("Ljava/util/List;");
public final DexString intFieldUpdaterDescriptor =
createString("Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater;");
@@ -256,7 +255,6 @@
public final DexType npeType = createType(npeDescriptor);
public final DexType proxyType = createType(proxyDescriptor);
public final DexType serviceLoaderType = createType(serviceLoaderDescriptor);
- public final DexType listType = createType(listDescriptor);
public final StringBuildingMethods stringBuilderMethods =
new StringBuildingMethods(stringBuilderType);
@@ -266,7 +264,6 @@
public final ObjectMethods objectMethods = new ObjectMethods();
public final StringMethods stringMethods = new StringMethods();
public final LongMethods longMethods = new LongMethods();
- public final JavaUtilArraysMethods utilArraysMethods = new JavaUtilArraysMethods();
public final ThrowableMethods throwableMethods = new ThrowableMethods();
public final ClassMethods classMethods = new ClassMethods();
public final ConstructorMethods constructorMethods = new ConstructorMethods();
@@ -279,6 +276,7 @@
public final Kotlin kotlin;
public final PolymorphicMethods polymorphicMethods = new PolymorphicMethods();
public final ProxyMethods proxyMethods = new ProxyMethods();
+ public final ServiceLoaderMethods serviceLoaderMethods = new ServiceLoaderMethods();
public final DexString twrCloseResourceMethodName = createString("$closeResource");
public final DexProto twrCloseResourceMethodProto =
@@ -322,8 +320,6 @@
public final DexType externalizableType = createType("Ljava/io/Externalizable;");
public final DexType comparableType = createType("Ljava/lang/Comparable;");
- public final ServiceLoaderMethods serviceLoaderMethods = new ServiceLoaderMethods();
-
public final DexMethod metafactoryMethod =
createMethod(
metafactoryType,
@@ -440,20 +436,6 @@
}
}
- public class JavaUtilArraysMethods {
-
- public final DexMethod asList;
-
- private JavaUtilArraysMethods() {
- asList =
- createMethod(
- createString("Ljava/util/Arrays;"),
- createString("asList"),
- listDescriptor,
- new DexString[] {objectArrayDescriptor});
- }
- }
-
public class ThrowableMethods {
public final DexMethod addSuppressed;
@@ -905,7 +887,6 @@
public final DexMethod load;
public final DexMethod loadWithClassLoader;
public final DexMethod loadInstalled;
- public final DexMethod iterator;
private ServiceLoaderMethods() {
DexString loadName = createString("load");
@@ -920,8 +901,6 @@
serviceLoaderType,
createProto(serviceLoaderType, classType),
createString("loadInstalled"));
- iterator =
- createMethod(serviceLoaderType, createProto(iteratorType), createString("iterator"));
}
public boolean isLoadMethod(DexMethod method) {
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 2e72732..57562d8 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
@@ -59,7 +59,6 @@
import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
import com.android.tools.r8.ir.optimize.RedundantFieldLoadElimination;
import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
-import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization;
import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
@@ -136,7 +135,6 @@
private final UninstantiatedTypeOptimization uninstantiatedTypeOptimization;
private final TypeChecker typeChecker;
private final IdempotentFunctionCallCanonicalizer idempotentFunctionCallCanonicalizer;
- private final ServiceLoaderRewriter serviceLoaderRewriter;
final DeadCodeRemover deadCodeRemover;
@@ -217,8 +215,6 @@
? new UninstantiatedTypeOptimization(appViewWithLiveness)
: null;
this.typeChecker = new TypeChecker(appView.withLiveness());
- this.serviceLoaderRewriter =
- options.enableServiceLoaderRewriting ? new ServiceLoaderRewriter() : null;
} else {
this.classInliner = null;
this.classStaticizer = null;
@@ -230,7 +226,6 @@
this.devirtualizer = null;
this.uninstantiatedTypeOptimization = null;
this.typeChecker = null;
- this.serviceLoaderRewriter = null;
}
this.deadCodeRemover = new DeadCodeRemover(appView, codeRewriter);
this.idempotentFunctionCallCanonicalizer =
@@ -869,11 +864,6 @@
// we will return with finalizeEmptyThrowingCode() above.
assert code.verifyTypes(appView);
- if (serviceLoaderRewriter != null) {
- assert appView.appInfo().hasLiveness();
- serviceLoaderRewriter.rewrite(code, appView.withLiveness());
- }
-
if (classStaticizer != null) {
classStaticizer.fixupMethodCode(method, code);
assert code.isConsistentSSA();
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
deleted file mode 100644
index b19894e..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ /dev/null
@@ -1,286 +0,0 @@
-// 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.ir.optimize;
-
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.code.ArrayPut;
-import com.android.tools.r8.ir.code.ConstClass;
-import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeInterface;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.NewArrayEmpty;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.ImmutableList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * ServiceLoaderRewriter will attempt to rewrite calls on the form of: ServiceLoader.load(X.class,
- * X.class.getClassLoader()).iterator() ... to Arrays.asList(new X[] { new Y(), ..., new Z()
- * }).iterator() for classes Y..Z specified in the META-INF/services/X.
- *
- * <p>The reason for this optimization is to not have the ServiceLoader.load on the distributed R8
- * in AGP, since this can potentially conflict with debug versions added to a build.gradle file as:
- * classpath 'com.android.tools:r8:a.b.c' Additionally, it might also result in improved performance
- * because ServiceLoader.load is really slow on Android because it has to do a reflective lookup.
- *
- * <p>A call to ServiceLoader.load(X.class) is implicitly the same as ServiceLoader.load(X.class,
- * Thread.getContextClassLoader()) which can have different behavior in Android if a process host
- * multiple applications:
- *
- * <pre>
- * See <a href="https://stackoverflow.com/questions/13407006/android-class-loader-may-fail-for-
- * processes-that-host-multiple-applications">https://stackoverflow.com/questions/13407006/
- * android-class-loader-may-fail-for-processes-that-host-multiple-applications</a>
- * </pre>
- *
- * We therefore only conservatively rewrite if the invoke is on is on the form
- * ServiceLoader.load(X.class, X.class.getClassLoader()) or ServiceLoader.load(X.class, null).
- */
-public class ServiceLoaderRewriter {
-
- public static void rewrite(IRCode code, AppView<? extends AppInfoWithLiveness> appView) {
- DexItemFactory factory = appView.dexItemFactory();
- InstructionIterator instructionIterator = code.instructionIterator();
- while (instructionIterator.hasNext()) {
- Instruction instruction = instructionIterator.next();
-
- // Check if instruction is an invoke static on the desired form of ServiceLoader.load.
- if (!instruction.isInvokeStatic()
- || instruction.asInvokeStatic().getInvokedMethod()
- != factory.serviceLoaderMethods.loadWithClassLoader) {
- continue;
- }
-
- InvokeStatic serviceLoaderLoad = instruction.asInvokeStatic();
- Value serviceLoaderLoadOut = serviceLoaderLoad.outValue();
- if (serviceLoaderLoadOut.numberOfAllUsers() != 1
- || serviceLoaderLoadOut.numberOfPhiUsers() != 0) {
- continue;
- }
-
- // Check that the only user is a call to iterator().
- if (serviceLoaderLoadOut.singleUniqueUser().isInvokeVirtual()
- || serviceLoaderLoadOut.singleUniqueUser().asInvokeVirtual().getInvokedMethod()
- != factory.serviceLoaderMethods.iterator) {
- continue;
- }
-
- // Check that the first argument is a const class.
- Value argument = serviceLoaderLoad.inValues().get(0).getAliasedValue();
- if (argument.isPhi() || !argument.definition.isConstClass()) {
- continue;
- }
-
- ConstClass constClass =
- serviceLoaderLoad
- .inValues()
- .get(0)
- .getAliasedValue()
- .getConstInstruction()
- .asConstClass();
-
- // Check that the service is not kept.
- if (appView.appInfo().isPinned(constClass.getValue())) {
- continue;
- }
-
- // Check that the service is configured in the META-INF/services.
- if (!appView.appServices().allServiceTypes().contains(constClass.getValue())) {
- // Error already reported in the Enqueuer.
- continue;
- }
-
- // Check that ClassLoader used is the ClassLoader defined for the the service configuration
- // that we are instantiating or NULL.
- InvokeVirtual classLoaderInvoke =
- serviceLoaderLoad.inValues().get(1).definition.asInvokeVirtual();
- boolean isGetClassLoaderOnConstClassOrNull =
- serviceLoaderLoad.inValues().get(1).getTypeLattice().isNullType()
- || (classLoaderInvoke != null
- && classLoaderInvoke.inValues().size() == 1
- && classLoaderInvoke.getReceiver().getAliasedValue().isConstClass()
- && classLoaderInvoke
- .getReceiver()
- .getAliasedValue()
- .getConstInstruction()
- .asConstClass()
- .getValue()
- == constClass.getValue());
- if (!isGetClassLoaderOnConstClassOrNull) {
- continue;
- }
-
- // We can perform the rewrite of the ServiceLoader.load call.
- new Rewriter(appView, code, instructionIterator, serviceLoaderLoad)
- .perform(classLoaderInvoke, constClass.getValue());
- }
- }
-
- /**
- * Rewriter will look assume that code is on the form:
- *
- * <pre>
- * ConstClass v1 <- X
- * ConstClass v2 <- X
- * Invoke-Virtual v3 <- v2; method: java.lang.ClassLoader java.lang.Class.getClassLoader()
- * Invoke-Static v4 <- v1, v3; method: java.util.ServiceLoader java.util.ServiceLoader
- * .load(java.lang.Class, java.lang.ClassLoader)
- * Invoke-Virtual v5 <- v4; method: java.util.Iterator java.util.ServiceLoader.iterator()
- * </pre>
- *
- * and rewrites it to for classes impl(X) defined in META-INF/services/X:
- *
- * <pre>
- * ConstClass v1 <- X
- * ConstClass v2 <- X
- * ConstNumber va <- impl(X).size() (INT)
- * NewArrayEmpty vb <- va X[]
- * for i = 0 to C - 1:
- * ConstNumber vc(i) <- i (INT)
- * NewInstance vd <- impl(X).get(i)
- * Invoke-Direct vd; method: void impl(X).get(i).<init>()
- * ArrayPut vb, vc(i), vd
- * end for
- * Invoke-Static ve <- vb; method: java.util.List java.util.Arrays.asList(java.lang.Object[])
- * Invoke-Interface v5 <- ve; method: java.util.Iterator java.util.List.iterator()
- * </pre>
- *
- * We rely on the DeadCodeRemover to remove the ConstClasses and any aliased values no longer
- * used.
- */
- private static class Rewriter {
-
- private final AppView appView;
- private final DexItemFactory factory;
- private final IRCode code;
- private final InvokeStatic serviceLoaderLoad;
-
- private InstructionIterator iterator;
- private MemberType memberType;
- private Value valueArray;
- private int index = 0;
-
- public Rewriter(
- AppView appView,
- IRCode code,
- InstructionIterator iterator,
- InvokeStatic serviceLoaderLoad) {
- this.appView = appView;
- this.factory = appView.dexItemFactory();
- this.iterator = iterator;
- this.code = code;
- this.serviceLoaderLoad = serviceLoaderLoad;
- }
-
- public void perform(InvokeVirtual classLoaderInvoke, DexType dexType) {
- assert valueArray == null;
- assert memberType == null;
-
- // Remove the ClassLoader call since this can throw and will not be removed otherwise.
- if (classLoaderInvoke != null) {
- clearGetClassLoader(classLoaderInvoke);
- iterator.nextUntil(i -> i == serviceLoaderLoad);
- }
-
- // Remove the ServiceLoader.load call.
- InvokeVirtual serviceLoaderIterator =
- serviceLoaderLoad.outValue().singleUniqueUser().asInvokeVirtual();
- iterator.replaceCurrentInstruction(code.createConstNull());
-
- List<DexType> dexTypes = appView.appServices().serviceImplementationsFor(dexType);
-
- // Build the array for the "loaded" classes.
- ConstNumber arrayLength = code.createIntConstant(dexTypes.size());
- arrayLength.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(arrayLength);
-
- DexType arrayType = factory.createArrayType(1, dexType);
- TypeLatticeElement arrayLatticeElement =
- TypeLatticeElement.fromDexType(arrayType, definitelyNotNull(), appView);
- valueArray = code.createValue(arrayLatticeElement);
- NewArrayEmpty newArrayEmpty =
- new NewArrayEmpty(valueArray, arrayLength.outValue(), arrayType);
- newArrayEmpty.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(newArrayEmpty);
-
- this.memberType = MemberType.fromDexType(dexType);
-
- // Add all new instances to the array.
- dexTypes.forEach(this::addNewServiceAndPutInArray);
-
- // Build the Arrays.asList(...) instruction.
- Value vArrayAsList =
- code.createValue(
- TypeLatticeElement.fromDexType(factory.listType, definitelyNotNull(), appView));
- InvokeStatic arraysAsList =
- new InvokeStatic(
- factory.utilArraysMethods.asList, vArrayAsList, ImmutableList.of(valueArray));
- arraysAsList.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(arraysAsList);
-
- // Find the iterator instruction and replace it.
- iterator.nextUntil(x -> x == serviceLoaderIterator);
-
- DexMethod method =
- factory.createMethod(
- factory.listType, factory.createProto(factory.iteratorType), "iterator");
- InvokeInterface arrayIterator =
- new InvokeInterface(
- method, serviceLoaderIterator.outValue(), ImmutableList.of(vArrayAsList));
- iterator.replaceCurrentInstruction(arrayIterator);
- }
-
- private void addNewServiceAndPutInArray(DexType clazz) {
- ConstNumber indexInArray = code.createIntConstant(index++);
- indexInArray.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(indexInArray);
-
- TypeLatticeElement clazzLatticeElement =
- TypeLatticeElement.fromDexType(clazz, definitelyNotNull(), appView);
- Value vInstance = code.createValue(clazzLatticeElement);
- NewInstance newInstance = new NewInstance(clazz, vInstance);
- newInstance.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(newInstance);
-
- DexClass clazzDefinition = appView.definitionFor(clazz);
- DexMethod method = clazzDefinition.getDefaultInitializer().method;
- assert method.getArity() == 0;
- InvokeDirect invokeDirect =
- new InvokeDirect(method, null, Collections.singletonList(vInstance));
- invokeDirect.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(invokeDirect);
-
- ArrayPut put = new ArrayPut(memberType, valueArray, indexInArray.outValue(), vInstance);
- put.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(put);
- }
-
- private void clearGetClassLoader(InvokeVirtual classLoaderInvoke) {
- while (iterator.hasPrevious()) {
- Instruction instruction = iterator.previous();
- if (instruction == classLoaderInvoke) {
- iterator.replaceCurrentInstruction(code.createConstNull());
- break;
- }
- }
- }
- }
-}
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 98cd095..b1e074a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2175,7 +2175,7 @@
private void handleServiceInstantiation(DexType serviceType, KeepReason reason) {
instantiatedAppServices.add(serviceType);
- List<DexType> serviceImplementationTypes =
+ Set<DexType> serviceImplementationTypes =
appView.appServices().serviceImplementationsFor(serviceType);
for (DexType serviceImplementationType : serviceImplementationTypes) {
if (!serviceImplementationType.isClassType()) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index d07f025..bca5d8f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -142,7 +142,6 @@
public boolean enableClassStaticizer = true;
public boolean enableInitializedClassesAnalysis = true;
public boolean enableSideEffectAnalysis = true;
- public boolean enableServiceLoaderRewriting = true;
// TODO(b/120138731): Enable this when it is worthwhile, e.g., combined with Class#forName.
public boolean enableNameReflectionOptimization = false;
public int classInliningInstructionLimit = 50;
diff --git a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
deleted file mode 100644
index 6daef42..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
+++ /dev/null
@@ -1,280 +0,0 @@
-// 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.rewrite;
-
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
-import static junit.framework.TestCase.assertTrue;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.DataEntryResource;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestRunResult;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.origin.Origin;
-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.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.ServiceLoader;
-import java.util.concurrent.ExecutionException;
-import java.util.zip.ZipFile;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class ServiceLoaderRewritingTest extends TestBase {
-
- private final TestParameters parameters;
- private final String EXPECTED_OUTPUT =
- StringUtils.lines("Hello World!", "Hello World!", "Hello World!");
-
- public interface Service {
-
- void print();
- }
-
- public static class ServiceImpl implements Service {
-
- @Override
- public void print() {
- System.out.println("Hello World!");
- }
- }
-
- public static class MainRunner {
-
- public static void main(String[] args) {
- ServiceLoader.load(Service.class, Service.class.getClassLoader()).iterator().next().print();
- ServiceLoader.load(Service.class, null).iterator().next().print();
- for (Service x : ServiceLoader.load(Service.class, Service.class.getClassLoader())) {
- x.print();
- }
- // TODO(b/120436373) The stream API for ServiceLoader was added in Java 9. When we can model
- // streams correctly, uncomment the lines below and adjust EXPECTED_OUTPUT.
- // ServiceLoader.load(Service.class, Service.class.getClassLoader())
- // .stream().forEach(x -> x.get().print());
- }
- }
-
- public static class OtherRunner {
-
- public static void main(String[] args) {
- ServiceLoader.load(Service.class).iterator().next().print();
- ServiceLoader.load(Service.class, Thread.currentThread().getContextClassLoader())
- .iterator()
- .next()
- .print();
- ServiceLoader.load(Service.class, OtherRunner.class.getClassLoader())
- .iterator()
- .next()
- .print();
- }
- }
-
- public static class EscapingRunner {
-
- public ServiceLoader<Service> serviceImplementations;
-
- @NeverInline
- public ServiceLoader<Service> getServices() {
- return ServiceLoader.load(Service.class, Thread.currentThread().getContextClassLoader());
- }
-
- @NeverInline
- public void printServices() {
- print(ServiceLoader.load(Service.class, Thread.currentThread().getContextClassLoader()));
- }
-
- @NeverInline
- public void print(ServiceLoader<Service> loader) {
- loader.iterator().next().print();
- }
-
- @NeverInline
- public void assignServicesField() {
- serviceImplementations =
- ServiceLoader.load(Service.class, Thread.currentThread().getContextClassLoader());
- }
-
- public static void main(String[] args) {
- EscapingRunner escapingRunner = new EscapingRunner();
- escapingRunner.getServices().iterator().next().print();
- escapingRunner.printServices();
- escapingRunner.assignServicesField();
- escapingRunner.print(escapingRunner.serviceImplementations);
- }
- }
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
- }
-
- public ServiceLoaderRewritingTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testRewritings() throws IOException, CompilationFailedException, ExecutionException {
- Path path = temp.newFile("out.zip").toPath();
- R8TestRunResult inspect =
- testForR8(parameters.getBackend())
- .addInnerClasses(ServiceLoaderRewritingTest.class)
- .addKeepMainRule(MainRunner.class)
- .setMinApi(parameters.getRuntime())
- .addDataEntryResources(
- DataEntryResource.fromBytes(
- StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
- "META-INF/services/" + Service.class.getTypeName(),
- Origin.unknown()))
- .compile()
- .writeToZip(path)
- .run(parameters.getRuntime(), MainRunner.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT)
- .inspect(
- inspector -> {
- // Check that we have actually rewritten the calls to ServiceLoader.load.
- ClassSubject clazz = inspector.clazz(MainRunner.class);
- assertTrue(clazz.isPresent());
- MethodSubject main = clazz.uniqueMethodWithName("main");
- assertTrue(main.isPresent());
- assertTrue(
- main.streamInstructions()
- .noneMatch(ServiceLoaderRewritingTest::isServiceLoaderLoad));
- });
-
- // Check that we have removed the service configuration from META-INF/services.
- ZipFile zip = new ZipFile(path.toFile());
- assertNull(zip.getEntry("META-INF/services"));
- }
-
- @Test
- public void testDoNoRewrite() throws IOException, CompilationFailedException, ExecutionException {
- Path path = temp.newFile("out.zip").toPath();
- CodeInspector inspector =
- testForR8(parameters.getBackend())
- .addInnerClasses(ServiceLoaderRewritingTest.class)
- .addKeepMainRule(OtherRunner.class)
- .setMinApi(parameters.getRuntime())
- .addDataEntryResources(
- DataEntryResource.fromBytes(
- StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
- "META-INF/services/" + Service.class.getTypeName(),
- Origin.unknown()))
- .compile()
- .writeToZip(path)
- .run(parameters.getRuntime(), OtherRunner.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT)
- .inspector();
-
- // Check that we have not rewritten the calls to ServiceLoader.load.
- ClassSubject clazz = inspector.clazz(OtherRunner.class);
- assertTrue(clazz.isPresent());
- MethodSubject main = clazz.uniqueMethodWithName("main");
- assertTrue(main.isPresent());
- assertEquals(
- 3,
- main.streamInstructions().filter(ServiceLoaderRewritingTest::isServiceLoaderLoad).count());
-
- // Check that we have not removed the service configuration from META-INF/services.
- ZipFile zip = new ZipFile(path.toFile());
- ClassSubject serviceImpl = inspector.clazz(ServiceImpl.class);
- assertTrue(serviceImpl.isPresent());
- assertNotNull(zip.getEntry("META-INF/services/" + serviceImpl.getFinalName()));
- }
-
- @Test
- public void testDoNoRewriteWhenEscaping()
- throws IOException, CompilationFailedException, ExecutionException {
- Path path = temp.newFile("out.zip").toPath();
- CodeInspector inspector =
- testForR8(parameters.getBackend())
- .addInnerClasses(ServiceLoaderRewritingTest.class)
- .addKeepMainRule(EscapingRunner.class)
- .enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
- .noMinification()
- .addDataEntryResources(
- DataEntryResource.fromBytes(
- StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
- "META-INF/services/" + Service.class.getTypeName(),
- Origin.unknown()))
- .compile()
- .writeToZip(path)
- .run(parameters.getRuntime(), EscapingRunner.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT)
- .inspector();
-
- // Check that we have not rewritten the calls to ServiceLoader.load.
- ClassSubject clazz = inspector.clazz(EscapingRunner.class);
- assertTrue(clazz.isPresent());
- int allServiceLoaders =
- clazz.allMethods().stream()
- .mapToInt(
- method ->
- (int)
- method
- .streamInstructions()
- .filter(ServiceLoaderRewritingTest::isServiceLoaderLoad)
- .count())
- .sum();
- assertEquals(3, allServiceLoaders);
- // Check that we have not removed the service configuration from META-INF/services.
- ZipFile zip = new ZipFile(path.toFile());
- ClassSubject serviceImpl = inspector.clazz(ServiceImpl.class);
- assertTrue(serviceImpl.isPresent());
- assertNotNull(zip.getEntry("META-INF/services/" + serviceImpl.getFinalName()));
- }
-
- @Test
- public void testKeepAsOriginal()
- throws IOException, CompilationFailedException, ExecutionException {
- Path path = temp.newFile("out.zip").toPath();
- CodeInspector inspector =
- testForR8(parameters.getBackend())
- .addInnerClasses(ServiceLoaderRewritingTest.class)
- .addKeepMainRule(MainRunner.class)
- .addKeepClassRules(Service.class)
- .setMinApi(parameters.getRuntime())
- .addDataEntryResources(
- DataEntryResource.fromBytes(
- StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
- "META-INF/services/" + Service.class.getTypeName(),
- Origin.unknown()))
- .compile()
- .writeToZip(path)
- .run(parameters.getRuntime(), MainRunner.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT)
- .inspector();
-
- // Check that we have not rewritten the calls to ServiceLoader.load.
- ClassSubject clazz = inspector.clazz(MainRunner.class);
- assertTrue(clazz.isPresent());
- MethodSubject main = clazz.uniqueMethodWithName("main");
- assertTrue(main.isPresent());
- assertEquals(
- 3,
- main.streamInstructions().filter(ServiceLoaderRewritingTest::isServiceLoaderLoad).count());
-
- // Check that we have not removed the service configuration from META-INF/services.
- ZipFile zip = new ZipFile(path.toFile());
- ClassSubject service = inspector.clazz(Service.class);
- assertTrue(service.isPresent());
- assertNotNull(zip.getEntry("META-INF/services/" + service.getFinalName()));
- }
-
- private static boolean isServiceLoaderLoad(InstructionSubject instruction) {
- return instruction.isInvokeStatic()
- && instruction.getMethod().qualifiedName().contains("ServiceLoader.load");
- }
-}