Merge "Account for minification of names in <clinit> default values."
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 494a62b..0bad20c 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -407,7 +407,7 @@
Set<DexCallSite> desugaredCallSites;
CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
try {
- IRConverter converter = new IRConverter(appView, options, timing, printer);
+ IRConverter converter = new IRConverter(appView, options, timing, printer, rootSet);
application = converter.optimize(application, executorService);
desugaredCallSites = converter.getDesugaredCallSites();
} finally {
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 18e67bb..1541f80 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
@@ -68,6 +68,7 @@
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -106,6 +107,7 @@
public final AppInfo appInfo;
public final AppView<? extends AppInfoWithSubtyping> appView;
+ public final RootSet rootSet;
private final Timing timing;
private final Outliner outliner;
@@ -150,13 +152,15 @@
InternalOptions options,
Timing timing,
CfgPrinter printer,
- AppView<? extends AppInfoWithSubtyping> appView) {
+ AppView<? extends AppInfoWithSubtyping> appView,
+ RootSet rootSet) {
assert appInfo != null;
assert options != null;
assert options.programConsumer != null;
this.timing = timing != null ? timing : new Timing("internal");
this.appInfo = appInfo;
this.appView = appView;
+ this.rootSet = rootSet;
this.options = options;
this.printer = printer;
this.codeRewriter = new CodeRewriter(this, libraryMethodsReturningReceiver(), options);
@@ -180,6 +184,7 @@
assert appInfo.hasLiveness();
AppInfoWithLiveness appInfoWithLiveness = appInfo.withLiveness();
AppView<? extends AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ assert rootSet != null;
this.nonNullTracker =
new NonNullTracker(appInfo, libraryMethodsReturningNonNull(appInfo.dexItemFactory));
this.inliner = new Inliner(appViewWithLiveness, this, options);
@@ -241,14 +246,14 @@
* Create an IR converter for processing methods with full program optimization disabled.
*/
public IRConverter(AppInfo appInfo, InternalOptions options) {
- this(appInfo, options, null, null, null);
+ this(appInfo, options, null, null, null, null);
}
/**
* Create an IR converter for processing methods with full program optimization disabled.
*/
public IRConverter(AppInfo appInfo, InternalOptions options, Timing timing, CfgPrinter printer) {
- this(appInfo, options, timing, printer, null);
+ this(appInfo, options, timing, printer, null, null);
}
/**
@@ -258,8 +263,9 @@
AppView<AppInfoWithSubtyping> appView,
InternalOptions options,
Timing timing,
- CfgPrinter printer) {
- this(appView.appInfo(), options, timing, printer, appView);
+ CfgPrinter printer,
+ RootSet rootSet) {
+ this(appView.appInfo(), options, timing, printer, appView, rootSet);
}
private boolean enableInterfaceMethodDesugaring() {
@@ -927,7 +933,7 @@
stringOptimizer.computeTrivialOperationsOnConstString(code, appInfo.dexItemFactory);
// Reflection optimization 2. get*Name() with const-class -> const-string
if (options.enableNameReflectionOptimization) {
- stringOptimizer.rewriteClassGetName(code, appInfo);
+ stringOptimizer.rewriteClassGetName(code, appInfo, rootSet);
}
// Reflection optimization 3. String#valueOf(const-string) -> no op.
stringOptimizer.removeTrivialConversions(code, appInfo);
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 0b54a4d..a875679 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
@@ -90,6 +90,7 @@
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.ReflectionOptimizer.ClassNameComputationInfo;
import com.android.tools.r8.ir.optimize.SwitchUtils.EnumSwitchInfo;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -1815,25 +1816,48 @@
assert false;
}
} else {
- // TODO(b/120280603): Consider minification!
InvokeVirtual invoke = inValue.definition.asInvokeVirtual();
DexMethod invokedMethod = invoke.getInvokedMethod();
DexType holderType = method.method.getHolder();
DexClass holder = appInfo.definitionFor(holderType);
assert holder != null;
String descriptor = holderType.toDescriptorString();
+ DexItemBasedValueString deferred = null;
String name = null;
if (invokedMethod == appInfo.dexItemFactory.classMethods.getName) {
- name = computeClassName(descriptor, holder, NAME, 0);
+ if (code.options.enableMinification
+ && !converter.rootSet.noObfuscation.contains(holder)) {
+ deferred = new DexItemBasedValueString(
+ holderType, new ClassNameComputationInfo(NAME));
+ } else {
+ name = computeClassName(descriptor, holder, NAME);
+ }
} else if (invokedMethod == appInfo.dexItemFactory.classMethods.getTypeName) {
// TODO(b/119426668): desugar Type#getTypeName
} else if (invokedMethod == appInfo.dexItemFactory.classMethods.getCanonicalName) {
- name = computeClassName(descriptor, holder, CANONICAL_NAME, 0);
+ if (code.options.enableMinification
+ && !converter.rootSet.noObfuscation.contains(holder)) {
+ deferred = new DexItemBasedValueString(
+ holderType, new ClassNameComputationInfo(CANONICAL_NAME));
+ } else {
+ name = computeClassName(descriptor, holder, CANONICAL_NAME);
+ }
} else if (invokedMethod == appInfo.dexItemFactory.classMethods.getSimpleName) {
- name = computeClassName(descriptor, holder, SIMPLE_NAME, 0);
+ if (code.options.enableMinification
+ && !converter.rootSet.noObfuscation.contains(holder)) {
+ deferred = new DexItemBasedValueString(
+ holderType, new ClassNameComputationInfo(SIMPLE_NAME));
+ } else {
+ name = computeClassName(descriptor, holder, SIMPLE_NAME);
+ }
}
- assert name != null;
- encodedField.setStaticValue(new DexValueString(dexItemFactory.createString(name)));
+ assert name != null || deferred != null;
+ if (name != null) {
+ encodedField.setStaticValue(new DexValueString(dexItemFactory.createString(name)));
+ } else {
+ assert deferred != null;
+ encodedField.setStaticValue(deferred);
+ }
}
} else if (field.type.isClassType() || field.type.isArrayType()) {
if (inValue.isZero()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 496e113..511b077 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -44,11 +44,15 @@
}
private static final ClassNameComputationInfo DEFAULT_INSTANCE =
- new ClassNameComputationInfo(ClassNameComputationOption.NONE, 0);
+ new ClassNameComputationInfo(ClassNameComputationOption.NONE);
final ClassNameComputationOption classNameComputationOption;
final int arrayDepth;
+ public ClassNameComputationInfo(ClassNameComputationOption classNameComputationOption) {
+ this(classNameComputationOption, 0);
+ }
+
public ClassNameComputationInfo(
ClassNameComputationOption classNameComputationOption, int arrayDepth) {
this.classNameComputationOption = classNameComputationOption;
@@ -141,6 +145,13 @@
public static String computeClassName(
String descriptor,
DexClass holder,
+ ClassNameComputationOption classNameComputationOption) {
+ return computeClassName(descriptor, holder, classNameComputationOption, 0);
+ }
+
+ public static String computeClassName(
+ String descriptor,
+ DexClass holder,
ClassNameComputationOption classNameComputationOption,
int arrayDepth) {
String name;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 2e19475..fe8b3f8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -115,7 +116,7 @@
}
// Find Class#get*Name() with a constant-class and replace it with a const-string if possible.
- public void rewriteClassGetName(IRCode code, AppInfo appInfo) {
+ public void rewriteClassGetName(IRCode code, AppInfo appInfo, RootSet rootSet) {
// Conflict with {@link CodeRewriter#collectClassInitializerDefaults}.
if (code.method.isClassInitializer()) {
return;
@@ -169,7 +170,7 @@
DexItemBasedConstString deferred = null;
String name = null;
if (invokedMethod == appInfo.dexItemFactory.classMethods.getName) {
- if (code.options.enableMinification) {
+ if (code.options.enableMinification && !rootSet.noObfuscation.contains(holder)) {
deferred = new DexItemBasedConstString(
invoke.outValue(), baseType, new ClassNameComputationInfo(NAME, arrayDepth));
} else {
@@ -189,7 +190,7 @@
if (!assumeTopLevel) {
continue;
}
- if (code.options.enableMinification) {
+ if (code.options.enableMinification && !rootSet.noObfuscation.contains(holder)) {
deferred =
new DexItemBasedConstString(
invoke.outValue(),
@@ -209,7 +210,7 @@
if (!assumeTopLevel) {
continue;
}
- if (code.options.enableMinification) {
+ if (code.options.enableMinification && !rootSet.noObfuscation.contains(holder)) {
deferred = new DexItemBasedConstString(
invoke.outValue(), baseType, new ClassNameComputationInfo(SIMPLE_NAME, arrayDepth));
} else {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameInClassInitializerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameInClassInitializerTest.java
new file mode 100644
index 0000000..8f172bd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameInClassInitializerTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2018, 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.reflection;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+
+class GetNameClinitClass {
+ static String name;
+ static {
+ name = GetNameClinitClass.class.getName();
+ }
+
+ @NeverInline
+ static String getName() {
+ return name;
+ }
+}
+
+class GetNameClinitRunner {
+ public static void main(String[] args) {
+ System.out.print(GetNameClinitClass.getName());
+ }
+}
+
+public class GetNameInClassInitializerTest extends GetNameTestBase {
+ private Collection<Path> classPaths;
+ private static final String JAVA_OUTPUT = GetNameClinitClass.class.getName();
+ private static final Class<?> MAIN = GetNameClinitRunner.class;
+
+ public GetNameInClassInitializerTest(Backend backend, boolean enableMinification)
+ throws Exception {
+ super(backend, enableMinification);
+
+ ImmutableList.Builder<Path> builder = ImmutableList.builder();
+ builder.addAll(ToolHelper.getClassFilesForTestDirectory(
+ ToolHelper.getPackageDirectoryForTestPackage(MAIN.getPackage()),
+ path -> path.getFileName().toString().startsWith("GetNameClinit")));
+ builder.add(ToolHelper.getClassFileForTestClass(NeverInline.class));
+ classPaths = builder.build();
+ }
+
+ @Test
+ public void testJVMoutput() throws Exception {
+ assumeTrue("Only run JVM reference once (for CF backend)",
+ backend == Backend.CF && !enableMinification);
+ testForJvm().addTestClasspath().run(MAIN).assertSuccessWithOutput(JAVA_OUTPUT);
+ }
+
+ @Test
+ public void testR8_pinning() throws Exception {
+ // Pinning the test class.
+ R8TestBuilder builder = testForR8(backend)
+ .addProgramFiles(classPaths)
+ .enableInliningAnnotations()
+ .addKeepMainRule(MAIN)
+ .addKeepRules("-keep class **.GetNameClinit*")
+ .addKeepRules("-printmapping");
+ if (!enableMinification) {
+ builder.addKeepRules("-dontobfuscate");
+ }
+ builder
+ .addOptionsModification(this::configure)
+ .run(MAIN)
+ .assertSuccessWithOutput(JAVA_OUTPUT);
+ }
+
+ @Test
+ public void testR8_shallow_pinning() throws Exception {
+ // Pinning the test class.
+ R8TestBuilder builder = testForR8(backend)
+ .addProgramFiles(classPaths)
+ .enableInliningAnnotations()
+ .addKeepMainRule(MAIN)
+ .addKeepRules("-keep,allowobfuscation class **.GetNameClinit*")
+ .addKeepRules("-printmapping");
+ if (!enableMinification) {
+ builder.addKeepRules("-dontobfuscate");
+ }
+
+ TestRunResult result =
+ builder
+ .addOptionsModification(this::configure)
+ .run(MAIN);
+ result.assertSuccessWithOutput(
+ result.inspector().clazz(GetNameClinitClass.class).getFinalName());
+ }
+}