Merge "Follow up with changes on Enqueuer"
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index e235e67..8731d2a 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -12,6 +12,7 @@
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;
@@ -181,6 +182,12 @@
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)
+ throws IOException, ResourceException {
OpenOption[] options =
new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
try (Closer closer = Closer.create()) {
@@ -191,6 +198,11 @@
byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.STORED);
}
+ for (DataEntryResource dataResource : dataResources) {
+ String entryName = dataResource.getName();
+ byte[] bytes = ByteStreams.toByteArray(closer.register(dataResource.getByteStream()));
+ ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.STORED);
+ }
}
}
}
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 5333125..8f923b0 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -91,6 +92,8 @@
private final AppInfo appInfo;
private final Map<String, String> packageRenaming;
private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+ // This set is only used for asserting no duplicated names.
+ private final Map<DexString, DexType> renamedTypesForVerification;
private MinifiedRenaming(
ClassRenaming classRenaming,
@@ -103,6 +106,10 @@
renaming.putAll(methodRenaming.renaming);
renaming.putAll(methodRenaming.callSiteRenaming);
renaming.putAll(fieldRenaming.renaming);
+ renamedTypesForVerification = new HashMap<>();
+ for (Map.Entry<DexType, DexString> entry : classRenaming.classRenaming.entrySet()) {
+ renamedTypesForVerification.put(entry.getValue(), entry.getKey());
+ }
}
@Override
@@ -112,7 +119,18 @@
@Override
public DexString lookupDescriptor(DexType type) {
- return renaming.getOrDefault(type, type.descriptor);
+ DexString dexString = renaming.get(type);
+ if (dexString != null) {
+ return dexString;
+ }
+ assert type.isPrimitiveType()
+ || type.isVoidType()
+ || !renamedTypesForVerification.containsKey(type.descriptor)
+ : "Duplicate minified type '"
+ + type.descriptor
+ + "' already mapped for: "
+ + renamedTypesForVerification.get(type.descriptor);
+ return type.descriptor;
}
@Override
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 28d1628..369367e 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DataResource;
import com.android.tools.r8.DataResourceProvider;
+import com.android.tools.r8.DataResourceProvider.Visitor;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DirectoryClassFileProvider;
@@ -213,6 +214,29 @@
}
}
+ public List<DataEntryResource> getDataEntryResourcesForTesting() throws ResourceException {
+ List<DataEntryResource> out = new ArrayList<>();
+ for (ProgramResourceProvider programResourceProvider : getProgramResourceProviders()) {
+ DataResourceProvider dataResourceProvider = programResourceProvider.getDataResourceProvider();
+ if (dataResourceProvider != null) {
+ dataResourceProvider.accept(
+ new Visitor() {
+
+ @Override
+ public void visit(DataDirectoryResource directory) {
+ // Ignore.
+ }
+
+ @Override
+ public void visit(DataEntryResource file) {
+ out.add(file);
+ }
+ });
+ }
+ }
+ return out;
+ }
+
/** Get program resource providers. */
public List<ProgramResourceProvider> getProgramResourceProviders() {
return programResourceProviders;
@@ -320,14 +344,12 @@
}
}
- /**
- * Write the dex program resources to @code{archive} and the proguard resource as its sibling.
- */
+ /** Write the dex program resources to @code{archive}. */
public void writeToZip(Path archive, OutputMode outputMode) throws IOException {
try {
if (outputMode == OutputMode.DexIndexed) {
- List<ProgramResource> resources = getDexProgramResourcesForTesting();
- DexIndexedConsumer.ArchiveConsumer.writeResources(archive, resources);
+ DexIndexedConsumer.ArchiveConsumer.writeResources(
+ archive, getDexProgramResourcesForTesting(), getDataEntryResourcesForTesting());
} else if (outputMode == OutputMode.DexFilePerClassFile) {
List<ProgramResource> resources = getDexProgramResourcesForTesting();
DexFilePerClassFileConsumer.ArchiveConsumer.writeResources(
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 4371edd..c7078ec 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -6,6 +6,9 @@
import com.android.tools.r8.BaseCompilerCommand;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.DataDirectoryResource;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceConsumer;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DexIndexedConsumer.ForwardingConsumer;
@@ -105,6 +108,29 @@
}
}
+ @Override
+ public DataResourceConsumer getDataResourceConsumer() {
+ assert consumer.getDataResourceConsumer() == null;
+ return new DataResourceConsumer() {
+
+ @Override
+ public void accept(
+ DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler) {
+ // Ignore.
+ }
+
+ @Override
+ public void accept(DataEntryResource file, DiagnosticsHandler diagnosticsHandler) {
+ builder.addDataResource(file);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ // Ignore.
+ }
+ };
+ }
+
synchronized void addDexFile(int fileIndex, byte[] data, Set<String> descriptors) {
files.put(fileIndex, new DescriptorsWithContents(descriptors, data));
}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 244639e..c91c288 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -141,7 +141,7 @@
return builder.toString();
}
- public static String lines(String... lines) {
+ public static String lines(List<String> lines) {
StringBuilder builder = new StringBuilder();
for (String line : lines) {
builder.append(line).append(LINE_SEPARATOR);
@@ -149,6 +149,10 @@
return builder.toString();
}
+ public static String lines(String... lines) {
+ return lines(Arrays.asList(lines));
+ }
+
public static String withNativeLineSeparator(String s) {
s = s.replace("\r\n", "\n");
if (LINE_SEPARATOR.equals("\r\n")) {
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 9dfb30d..245a35e 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -131,6 +131,11 @@
}
@Override
+ public ExternalR8TestBuilder addDataEntryResources(DataEntryResource... resources) {
+ throw new Unimplemented("No support for adding data entry resources");
+ }
+
+ @Override
public ExternalR8TestBuilder addProgramClasses(Collection<Class<?>> classes) {
// Adding a collection of classes will build a jar of exactly those classes so that no other
// classes are made available via a too broad classpath directory.
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 0fd0354..3b4647f 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -181,6 +181,11 @@
}
@Override
+ public ProguardTestBuilder addDataEntryResources(DataEntryResource... resources) {
+ throw new Unimplemented("No support for adding data entry resources");
+ }
+
+ @Override
public ProguardTestBuilder addProgramClasses(Collection<Class<?>> classes) {
// Adding a collection of classes will build a jar of exactly those classes so that no other
// classes are made available via a too broad classpath directory.
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index daf5568..e8555d7 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -97,6 +97,11 @@
}
@Override
+ public R8TestBuilder addDataEntryResources(DataEntryResource... resources) {
+ return addDataResources(Arrays.asList(resources));
+ }
+
+ @Override
public R8TestBuilder addKeepRuleFiles(List<Path> files) {
builder.addProguardConfigurationFiles(files);
return self();
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 9d8e03c..a17ab09 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -46,6 +46,8 @@
return minification(false);
}
+ public abstract T addDataEntryResources(DataEntryResource... resources);
+
public abstract T addKeepRuleFiles(List<Path> files);
public T addKeepRuleFiles(Path... files) throws IOException {
@@ -62,6 +64,10 @@
return addKeepRules("-keep class ** { *; }");
}
+ public T addKeepAllInterfacesRule() {
+ return addKeepRules("-keep interface ** { *; }");
+ }
+
public T addKeepClassRules(Class<?>... classes) {
for (Class<?> clazz : classes) {
addKeepRules("-keep class " + clazz.getTypeName());
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierTest.java b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
index 473ca53..adf74ce 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
@@ -3,13 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
import com.android.tools.r8.graph.DexField;
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.naming.Minifier.MinifiedRenaming;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Timing;
import java.nio.file.Paths;
@@ -40,6 +43,25 @@
inspection.accept(dexItemFactory, naming);
}
+ @Test
+ public void ensureClassesAddedToRenamingOrNoClashTest() throws Exception {
+ MinifiedRenaming naming =
+ (MinifiedRenaming) runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
+ // Create a type that exists.
+ String existingType = "La/c;";
+ DexType d = dexItemFactory.createType(existingType);
+ try {
+ naming.lookupDescriptor(d);
+ } catch (AssertionError ae) {
+ assertTrue(
+ ae.getMessage()
+ .startsWith(
+ "Duplicate minified type '" + existingType + "' already mapped for: naming001."));
+ return;
+ }
+ fail("Should have thrown an error.");
+ }
+
@Parameters(name = "test: {0} keep: {1}")
public static Collection<Object[]> data() {
List<String> tests = Arrays.asList("naming001");
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
new file mode 100644
index 0000000..e005a2c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -0,0 +1,96 @@
+// 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.shaking;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.TestBase;
+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.CodeInspector;
+import com.google.common.collect.Lists;
+import java.util.List;
+import java.util.ServiceLoader;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ServiceLoaderTest extends TestBase {
+
+ private final boolean includeWorldGreeter;
+
+ @Parameters(name = "Include WorldGreeter: {0}")
+ public static Boolean[] data() {
+ return BooleanUtils.values();
+ }
+
+ public ServiceLoaderTest(boolean includeWorldGreeter) {
+ this.includeWorldGreeter = includeWorldGreeter;
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = includeWorldGreeter ? "Hello world!" : "Hello";
+
+ List<String> serviceImplementations = Lists.newArrayList(HelloGreeter.class.getTypeName());
+ if (includeWorldGreeter) {
+ serviceImplementations.add(WorldGreeter.class.getTypeName());
+ }
+
+ CodeInspector inspector =
+ testForR8(Backend.DEX)
+ .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()))
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput)
+ .inspector();
+
+ // TODO(b/124181030): Verify that Greeter is merged into HelloGreeter when `includeWorldGreeter`
+ // is false.
+
+ // TODO(b/124181030): Verify that META-INF/services/...WorldGreeter is removed when
+ // `includeWorldGreeter` is false.
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ for (Greeter greeter : ServiceLoader.load(Greeter.class)) {
+ System.out.print(greeter.greeting());
+ }
+ }
+ }
+
+ public interface Greeter {
+
+ String greeting();
+ }
+
+ public static class HelloGreeter implements Greeter {
+
+ @Override
+ public String greeting() {
+ return "Hello";
+ }
+ }
+
+ public static class WorldGreeter implements Greeter {
+
+ @Override
+ public String greeting() {
+ return " world!";
+ }
+ }
+}
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 53ddbe7..d6dd647 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -730,10 +730,11 @@
# Make a copy of r8.jar and r8lib.jar such that they stay the same for
# the entire execution of this script.
- if 'r8-nolib' in options.shrinker:
+ all_shrinkers_enabled = (options.shrinker == None)
+ if all_shrinkers_enabled or 'r8-nolib' in options.shrinker:
assert os.path.isfile(utils.R8_JAR), 'Cannot build without r8.jar'
shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
- if 'r8' in options.shrinker:
+ if all_shrinkers_enabled or 'r8' in options.shrinker:
assert os.path.isfile(utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))