Merge "Do not output <init> for static constructors"
diff --git a/build.gradle b/build.gradle
index 5631d94..9e84b01 100644
--- a/build.gradle
+++ b/build.gradle
@@ -276,6 +276,8 @@
"android_jar/lib-v24",
"android_jar/lib-v25",
"android_jar/lib-v26",
+ "android_jar/lib-v27",
+ "android_jar/lib-v28",
"core-lambda-stubs",
"dart-sdk",
"ddmlib",
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 48c67fd..7b15c6e 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -44,7 +44,8 @@
DexApplication application =
new ApplicationReader(app, options, timing).read(executor).toDirect();
AppView<? extends AppInfoWithSubtyping> appView =
- new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
+ new AppView<>(
+ new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
RootSet mainDexRootSet =
new RootSetBuilder(appView, application, options.mainDexKeepRules, options).run(executor);
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 7bef553..475ca70 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -84,7 +84,8 @@
DexApplication application =
new ApplicationReader(command.getInputApp(), options, timing).read(executor).toDirect();
AppView<? extends AppInfoWithSubtyping> appView =
- new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
+ new AppView<>(
+ new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
RootSet rootSet =
new RootSetBuilder(
appView, application, options.getProguardConfiguration().getRules(), options)
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5b94a6f..70c0f40 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -250,7 +250,8 @@
inputApp.closeInternalArchiveProviders();
AppView<AppInfoWithSubtyping> appView =
- new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
+ new AppView<>(
+ new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
RootSet rootSet;
String proguardSeedsData = null;
timing.begin("Strip unused code");
@@ -289,7 +290,9 @@
options.getProguardConfiguration().getDontWarnPatterns(),
executorService,
timing));
- // assert rootSet.verifyKeptMethodsAreTargetedAndLive(appView.appInfo().withLiveness());
+ assert rootSet.verifyKeptFieldsAreAccessedAndLive(appView.appInfo().withLiveness());
+ assert rootSet.verifyKeptMethodsAreTargetedAndLive(appView.appInfo().withLiveness());
+ assert rootSet.verifyKeptTypesAreLive(appView.appInfo().withLiveness());
if (options.getProguardConfiguration().isPrintSeeds()) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
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 2354918..f9a9dde 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -6,18 +6,21 @@
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.VerticalClassMerger.VerticallyMergedClasses;
+import com.android.tools.r8.utils.InternalOptions;
public class AppView<T extends AppInfo> {
private T appInfo;
private final DexItemFactory dexItemFactory;
private GraphLense graphLense;
+ private final InternalOptions options;
private VerticallyMergedClasses verticallyMergedClasses;
- public AppView(T appInfo, GraphLense graphLense) {
+ public AppView(T appInfo, GraphLense graphLense, InternalOptions options) {
this.appInfo = appInfo;
this.dexItemFactory = appInfo != null ? appInfo.dexItemFactory : null;
this.graphLense = graphLense;
+ this.options = options;
}
public T appInfo() {
@@ -46,6 +49,10 @@
this.graphLense = graphLense;
}
+ public InternalOptions options() {
+ return options;
+ }
+
// Get the result of vertical class merging. Returns null if vertical class merging has not been
// run.
public VerticallyMergedClasses verticallyMergedClasses() {
@@ -63,7 +70,7 @@
private class AppViewWithLiveness extends AppView<AppInfoWithLiveness> {
private AppViewWithLiveness() {
- super(null, null);
+ super(null, null, null);
}
@Override
@@ -94,6 +101,11 @@
}
@Override
+ public InternalOptions options() {
+ return AppView.this.options();
+ }
+
+ @Override
public AppView<AppInfoWithLiveness> withLiveness() {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 9d25704..04e6516 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Iterator;
@@ -226,12 +227,23 @@
public ConstraintWithTarget computeInliningConstraint(
DexEncodedMethod encodedMethod,
- AppInfoWithLiveness appInfo,
+ AppView<? extends AppInfoWithLiveness> appView,
GraphLense graphLense,
DexType invocationContext) {
InliningConstraintVisitor visitor =
new InliningConstraintVisitor(
- application, appInfo, graphLense, encodedMethod, invocationContext);
+ application, appView.appInfo(), graphLense, encodedMethod, invocationContext);
+
+ if (appView.options().enableDesugaring
+ && appView.options().interfaceMethodDesugaring == OffOrAuto.Auto
+ && !appView.options().canUseDefaultAndStaticInterfaceMethods()) {
+ // TODO(b/120130831): Conservatively need to say "no" at this point if there are invocations
+ // to static interface methods. This should be fixed by making sure that the desugared
+ // versions of default and static interface methods are present in the application during
+ // IR processing.
+ visitor.disallowStaticInterfaceMethodCalls();
+ }
+
AbstractInsnNode insn = node.instructions.getFirst();
while (insn != null) {
insn.accept(visitor);
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 6ee0f6e..0434f66 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
@@ -41,7 +41,6 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.Java8MethodRewriter;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.desugar.StringConcatRewriter;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
@@ -120,7 +119,6 @@
private final LambdaRewriter lambdaRewriter;
private final InterfaceMethodRewriter interfaceMethodRewriter;
private final TwrCloseResourceRewriter twrCloseResourceRewriter;
- private final Java8MethodRewriter java8MethodRewriter;
private final LambdaMerger lambdaMerger;
private final ClassInliner classInliner;
private final ClassStaticizer classStaticizer;
@@ -185,8 +183,6 @@
this.twrCloseResourceRewriter =
(options.enableDesugaring && enableTwrCloseResourceDesugaring())
? new TwrCloseResourceRewriter(this) : null;
- this.java8MethodRewriter =
- options.enableDesugaring ? new Java8MethodRewriter(this) : null;
this.lambdaMerger =
options.enableLambdaMerging ? new LambdaMerger(appInfo, options.reporter) : null;
this.covariantReturnTypeAnnotationTransformer =
@@ -371,12 +367,6 @@
}
}
- private void synthesizeJava8UtilityClass(Builder<?> builder) {
- if (java8MethodRewriter != null) {
- java8MethodRewriter.synthesizeUtilityClass(builder, options);
- }
- }
-
private void processCovariantReturnTypeAnnotations(Builder<?> builder) {
if (covariantReturnTypeAnnotationTransformer != null) {
covariantReturnTypeAnnotationTransformer.process(builder);
@@ -397,7 +387,6 @@
synthesizeLambdaClasses(builder, executor);
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
synthesizeTwrCloseResourceUtilityClass(builder);
- synthesizeJava8UtilityClass(builder);
processCovariantReturnTypeAnnotations(builder);
handleSynthesizedClassMapping(builder);
@@ -584,10 +573,11 @@
printPhase("Interface method desugaring");
desugarInterfaceMethods(builder, IncludeAllResources, executorService);
+
printPhase("Twr close resource utility class synthesis");
synthesizeTwrCloseResourceUtilityClass(builder);
- synthesizeJava8UtilityClass(builder);
handleSynthesizedClassMapping(builder);
+
printPhase("Lambda merging finalization");
finalizeLambdaMerging(application, feedback, builder, executorService);
@@ -1020,9 +1010,6 @@
if (options.enableDesugaring && enableTryWithResourcesDesugaring()) {
codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
}
- if (java8MethodRewriter != null) {
- java8MethodRewriter.desugar(code);
- }
stringConcatRewriter.desugarStringConcats(method.method, code);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
deleted file mode 100644
index 3cb741a..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/Java8MethodRewriter.java
+++ /dev/null
@@ -1,262 +0,0 @@
-// 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.desugar;
-
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication.Builder;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-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.InvokeStatic;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.desugar.Java8MethodRewriter.RewritableMethods.MethodGenerator;
-import com.android.tools.r8.ir.synthetic.TemplateMethodCode;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.BiFunction;
-
-public final class Java8MethodRewriter {
- private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L$r8$java8methods$utility";
- private final Set<DexProgramClass> referencingClasses = Sets.newConcurrentHashSet();
-
- private final IRConverter converter;
- private final DexItemFactory factory;
- private final RewritableMethods rewritableMethods;
-
- private Map<DexMethod, MethodGenerator> methodGenerators = new ConcurrentHashMap<>();
-
- public Java8MethodRewriter(IRConverter converter) {
- this.converter = converter;
- this.factory = converter.appInfo.dexItemFactory;
- this.rewritableMethods = new RewritableMethods(factory);
- }
-
- public void desugar(IRCode code) {
- InstructionIterator iterator = code.instructionIterator();
- while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (!instruction.isInvokeStatic()) {
- continue;
- }
- InvokeStatic invoke = instruction.asInvokeStatic();
-
- MethodGenerator generator = getMethodGeneratorOrNull(converter, invoke.getInvokedMethod());
- if (generator == null) {
- continue;
- }
- iterator.replaceCurrentInstruction(
- new InvokeStatic(generator.generateMethod(factory),
- invoke.outValue(), invoke.inValues()));
- methodGenerators.putIfAbsent(generator.generateMethod(factory), generator);
- referencingClasses.add(
- converter.appInfo.definitionFor(code.method.method.holder).asProgramClass());
- }
- }
-
- public void synthesizeUtilityClass(Builder<?> builder, InternalOptions options) {
- if (referencingClasses.isEmpty()) {
- return;
- }
-
- MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
- ClassAccessFlags classAccessFlags =
- ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
-
- for (MethodGenerator generator : methodGenerators.values()) {
- DexMethod method = generator.generateMethod(factory);
- TemplateMethodCode code = generator.generateTemplateMethod(options, method);
- DexEncodedMethod dexEncodedMethod= new DexEncodedMethod(method,
- flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
- DexProgramClass utilityClass =
- new DexProgramClass(
- method.holder,
- null,
- new SynthesizedOrigin("java8 methods utility class", getClass()),
- classAccessFlags,
- factory.objectType,
- DexTypeList.empty(),
- null,
- null,
- Collections.emptyList(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- new DexEncodedMethod[]{dexEncodedMethod},
- DexEncodedMethod.EMPTY_ARRAY,
- factory.getSkipNameValidationForTesting(),
- referencingClasses);
- code.setUpContext(utilityClass);
- boolean addToMainDexList = referencingClasses.stream()
- .anyMatch(clazz -> converter.appInfo.isInMainDexList(clazz.type));
- converter.optimizeSynthesizedClass(utilityClass);
- builder.addSynthesizedClass(utilityClass, addToMainDexList);
- }
- }
-
-
- private MethodGenerator getMethodGeneratorOrNull(IRConverter converter, DexMethod method) {
- DexMethod original = converter.graphLense().getOriginalMethodSignature(method);
- assert original != null;
- return rewritableMethods.getGenerator(
- original.holder.descriptor, original.name, original.proto);
- }
-
-
- private static final class IntegerMethods extends TemplateMethodCode {
- IntegerMethods(InternalOptions options, DexMethod method, String methodName, String desc) {
- super(options, method, methodName, desc);
- }
-
- public static IntegerMethods hashCodeCode(InternalOptions options, DexMethod method) {
- return new IntegerMethods(options, method, "hashCodeImpl", "(I)I");
- }
-
- public static IntegerMethods maxCode(InternalOptions options, DexMethod method) {
- return new IntegerMethods(options, method, "maxImpl", "(II)I");
- }
-
- public static int hashCodeImpl(int value) {
- return Integer.valueOf(value).hashCode();
- }
-
- public static int maxImpl(int a, int b) {
- return java.lang.Math.max(a, b);
- }
- }
-
- private static final class DoubleMethods extends TemplateMethodCode {
- DoubleMethods(InternalOptions options, DexMethod method, String methodName, String desc) {
- super(options, method, methodName, desc);
- }
-
- public static DoubleMethods hashCodeCode(InternalOptions options, DexMethod method) {
- return new DoubleMethods(options, method, "hashCodeImpl", "(D)I");
- }
-
- public static DoubleMethods maxCode(InternalOptions options, DexMethod method) {
- return new DoubleMethods(options, method, "maxImpl", "(DD)D");
- }
-
- public static int hashCodeImpl(double value) {
- return Double.valueOf(value).hashCode();
- }
-
- public static double maxImpl(double a, double b) {
- return java.lang.Math.max(a, b);
- }
- }
-
- public static final class RewritableMethods {
- // Map class, method, proto to a generator for creating the code and method.
- private final Map<DexString, Map<DexString, Map<DexProto, MethodGenerator>>> rewritable;
-
-
- public RewritableMethods(DexItemFactory factory) {
- rewritable = new HashMap<>();
- // Integer
- DexString clazz = factory.boxedIntDescriptor;
- // int Integer.hashCode(int i)
-
- DexString method = factory.createString("hashCode");
- DexProto proto = factory.createProto(factory.intType, factory.intType);
- addOrGetMethod(clazz, method)
- .put(proto, new MethodGenerator(IntegerMethods::hashCodeCode, clazz, method, proto));
-
- // int Integer.max(int a, int b)
- method = factory.createString("max");
- proto = factory.createProto(factory.intType, factory.intType, factory.intType);
- addOrGetMethod(clazz, method)
- .put(proto, new MethodGenerator(IntegerMethods::maxCode, clazz, method, proto));
-
- // Double
- clazz = factory.boxedDoubleDescriptor;
- // int Double.hashCode(double d)
- method = factory.createString("hashCode");
- proto = factory.createProto(factory.intType, factory.doubleType);
- addOrGetMethod(clazz, method)
- .put(proto, new MethodGenerator(DoubleMethods::hashCodeCode, clazz, method, proto));
-
- // double Double.max(double a, double b)
- method = factory.createString("max");
- proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
- addOrGetMethod(clazz, method)
- .put(proto, new MethodGenerator(DoubleMethods::maxCode, clazz, method, proto));
-
- }
-
- private Map<DexString, Map<DexProto, MethodGenerator>> addOrGetClass(DexString clazz) {
- return rewritable.computeIfAbsent(clazz, k -> new HashMap<>());
- }
-
- private Map<DexProto, MethodGenerator> addOrGetMethod(
- DexString clazz, DexString method) {
- return addOrGetClass(clazz).computeIfAbsent(method, k -> new HashMap<>());
- }
-
- public MethodGenerator getGenerator(DexString clazz, DexString method, DexProto proto) {
- Map<DexString, Map<DexProto, MethodGenerator>> classMap = rewritable.get(clazz);
- if (classMap != null) {
- Map<DexProto, MethodGenerator> methodMap = classMap.get(method);
- if (methodMap != null) {
- return methodMap.get(proto);
- }
- }
- return null;
- }
-
- public static class MethodGenerator {
- private final BiFunction<InternalOptions, DexMethod, TemplateMethodCode> generator;
- private final DexString clazz;
- private final DexString method;
- private final DexProto proto;
- private DexMethod dexMethod;
-
- public MethodGenerator(
- BiFunction<InternalOptions, DexMethod, TemplateMethodCode> generator,
- DexString clazz, DexString method, DexProto proto) {
- this.generator = generator;
- this.clazz = clazz;
- this.method = method;
- this.proto = proto;
- }
-
- public DexMethod generateMethod(DexItemFactory factory) {
- if (dexMethod != null) {
- return dexMethod;
- }
- String clazzDescriptor = DescriptorUtils.getSimpleClassNameFromDescriptor(clazz.toString());
- String postFix = "$" + clazzDescriptor + "$" + method + "$" + proto.shorty.toString();
- DexType clazz = factory.createType(UTILITY_CLASS_DESCRIPTOR_PREFIX + postFix + ";");
- dexMethod = factory.createMethod(clazz, proto, method);
- return dexMethod;
- }
-
- public TemplateMethodCode generateTemplateMethod(InternalOptions options, DexMethod method) {
- return generator.apply(options, method);
- }
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 4e3f9be..3292704 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -24,6 +24,8 @@
private AppInfoWithLiveness appInfo;
+ private boolean allowStaticInterfaceMethodCalls = true;
+
// Currently used only by the vertical class merger (in all other cases this is the identity).
//
// When merging a type A into its subtype B we need to inline A.<init>() into B.<init>().
@@ -46,6 +48,10 @@
this.graphLense = graphLense;
}
+ public void disallowStaticInterfaceMethodCalls() {
+ allowStaticInterfaceMethodCalls = false;
+ }
+
public ConstraintWithTarget forAlwaysMaterializingUser() {
return ConstraintWithTarget.ALWAYS;
}
@@ -280,6 +286,11 @@
DexType methodHolder = graphLense.lookupType(target.method.holder);
DexClass methodClass = appInfo.definitionFor(methodHolder);
if (methodClass != null) {
+ if (!allowStaticInterfaceMethodCalls && methodClass.isInterface() && target.hasCode()) {
+ // See b/120121170.
+ return ConstraintWithTarget.NEVER;
+ }
+
ConstraintWithTarget methodConstraintWithTarget =
ConstraintWithTarget.deriveConstraint(
invocationContext, methodHolder, target.accessFlags, appInfo);
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
index c707968..18998c8 100644
--- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.conversion.JarSourceCode;
@@ -62,6 +63,10 @@
? inliningConstraints.forMonitor() : ConstraintWithTarget.ALWAYS;
}
+ public void disallowStaticInterfaceMethodCalls() {
+ inliningConstraints.disallowStaticInterfaceMethodCalls();
+ }
+
public ConstraintWithTarget getConstraint() {
return constraint;
}
@@ -160,12 +165,15 @@
}
break;
- case Opcodes.INVOKESTATIC:
- type = Invoke.Type.STATIC;
- assert noNeedToUseGraphLense(target, type);
+ case Opcodes.INVOKESTATIC: {
+ // Static invokes may have changed as a result of horizontal class merging.
+ GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, Invoke.Type.STATIC);
+ target = lookup.getMethod();
+ type = lookup.getType();
break;
+ }
- case Opcodes.INVOKEVIRTUAL:
+ case Opcodes.INVOKEVIRTUAL: {
type = Invoke.Type.VIRTUAL;
// Instructions that target a private method in the same class translates to invoke-direct.
if (target.holder == method.method.holder) {
@@ -174,8 +182,13 @@
type = Invoke.Type.DIRECT;
}
}
- assert noNeedToUseGraphLense(target, type);
+
+ // Virtual invokes may have changed to interface invokes as a result of member rebinding.
+ GraphLenseLookupResult lookup = graphLense.lookupMethod(target, null, type);
+ target = lookup.getMethod();
+ type = lookup.getType();
break;
+ }
default:
throw new Unreachable("Unexpected opcode " + opcode);
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 c579500..169f8a3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1153,9 +1153,9 @@
private void markDirectStaticOrConstructorMethodAsLive(
DexEncodedMethod encodedMethod, KeepReason reason) {
assert encodedMethod != null;
+ markMethodAsTargeted(encodedMethod, reason);
if (!liveMethods.contains(encodedMethod)) {
markTypeAsLive(encodedMethod.method.holder);
- markMethodAsTargeted(encodedMethod, reason);
if (Log.ENABLED) {
Log.verbose(getClass(), "Method `%s` has become live due to direct invoke",
encodedMethod.method);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 5bf6b4c..7dde660 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -1021,6 +1021,24 @@
.unmodifiableMap(dependentNoShrinking.getOrDefault(item, Collections.emptyMap()));
}
+ public boolean verifyKeptFieldsAreAccessedAndLive(AppInfoWithLiveness appInfo) {
+ for (DexDefinition definition : noShrinking.keySet()) {
+ if (definition.isDexEncodedField()) {
+ DexEncodedField field = definition.asDexEncodedField();
+ if (field.isStatic() || isKeptDirectlyOrIndirectly(field.field.clazz, appInfo)) {
+ // TODO(b/121354886): Enable asserts for reads and writes.
+ /*assert appInfo.fieldsRead.contains(field.field)
+ : "Expected kept field `" + field.field.toSourceString() + "` to be read";
+ assert appInfo.fieldsWritten.contains(field.field)
+ : "Expected kept field `" + field.field.toSourceString() + "` to be written";*/
+ assert appInfo.liveFields.contains(field.field)
+ : "Expected kept field `" + field.field.toSourceString() + "` to be live";
+ }
+ }
+ }
+ return true;
+ }
+
public boolean verifyKeptMethodsAreTargetedAndLive(AppInfoWithLiveness appInfo) {
for (DexDefinition definition : noShrinking.keySet()) {
if (definition.isDexEncodedMethod()) {
@@ -1040,6 +1058,17 @@
return true;
}
+ public boolean verifyKeptTypesAreLive(AppInfoWithLiveness appInfo) {
+ for (DexDefinition definition : noShrinking.keySet()) {
+ if (definition.isDexClass()) {
+ DexClass clazz = definition.asDexClass();
+ assert appInfo.liveTypes.contains(clazz.type)
+ : "Expected kept type `" + clazz.type.toSourceString() + "` to be live";
+ }
+ }
+ return true;
+ }
+
private boolean isKeptDirectlyOrIndirectly(DexType type, AppInfoWithLiveness appInfo) {
DexClass clazz = appInfo.definitionFor(type);
if (clazz == null) {
@@ -1090,11 +1119,21 @@
for (DexProgramClass clazz : application.classes()) {
Set<DexDefinition> requiredDefinitions =
requiredDefinitionsPerType.getOrDefault(clazz.type, ImmutableSet.of());
+
+ Set<DexField> fields = null;
+ Set<DexMethod> methods = null;
+
for (DexDefinition requiredDefinition : requiredDefinitions) {
if (requiredDefinition.isDexEncodedField()) {
DexEncodedField requiredField = requiredDefinition.asDexEncodedField();
- assert Streams.stream(clazz.fields())
- .anyMatch(field -> field.field == requiredField.field);
+ if (fields == null) {
+ // Create a Set of the fields to avoid quadratic behavior.
+ fields =
+ Streams.stream(clazz.fields())
+ .map(DexEncodedField::getKey)
+ .collect(Collectors.toSet());
+ }
+ assert fields.contains(requiredField.field);
} else if (requiredDefinition.isDexEncodedMethod()) {
DexEncodedMethod requiredMethod = requiredDefinition.asDexEncodedMethod();
if (isInterfaceMethodDesugaringEnabled) {
@@ -1104,8 +1143,14 @@
continue;
}
}
- assert Streams.stream(clazz.methods())
- .anyMatch(method -> method.method == requiredMethod.method);
+ if (methods == null) {
+ // Create a Set of the methods to avoid quadratic behavior.
+ methods =
+ Streams.stream(clazz.methods())
+ .map(DexEncodedMethod::getKey)
+ .collect(Collectors.toSet());
+ }
+ assert methods.contains(requiredMethod.method);
} else {
assert false;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 4abd3e3..0d69178 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1619,7 +1619,7 @@
ConstraintWithTarget constraint =
jarCode.computeInliningConstraint(
method,
- appInfo,
+ appView,
new SingleTypeMapperGraphLense(method.method.holder, invocationContext),
invocationContext);
return constraint == ConstraintWithTarget.NEVER;
diff --git a/src/test/java/com/android/tools/r8/classmerging/ForceInliningWithStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/ForceInliningWithStaticInterfaceMethodTest.java
new file mode 100644
index 0000000..7c86d1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/ForceInliningWithStaticInterfaceMethodTest.java
@@ -0,0 +1,62 @@
+// 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.classmerging;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+
+/** Regression test for b/120121170. */
+public class ForceInliningWithStaticInterfaceMethodTest extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("A.<init>()", "I.m()", "B.<init>()");
+ testForR8(Backend.DEX)
+ .addInnerClasses(ForceInliningWithStaticInterfaceMethodTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(AndroidApiLevel.M)
+ .compile()
+ .run(TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new B();
+ }
+ }
+
+ static class A {
+
+ public A() {
+ System.out.println("A.<init>()");
+
+ // By the time the vertical class merger runs, I.m() still exists, so the inlining oracle
+ // concludes that A.<init>() is eligible for inlining. However, by the time A.<init>() will
+ // be force-inlined into B.<init>(), I.m() has been rewritten as a result of interface method
+ // desugaring, but the target method is not yet added to the application. Hence the inlining
+ // oracle concludes that A.<init>() is not eligible for inlining, which leads to an error
+ // "FORCE inlining on non-inlinable".
+ I.m();
+ }
+ }
+
+ static class B extends A {
+
+ public B() {
+ System.out.println("B.<init>()");
+ }
+ }
+
+ interface I {
+
+ static void m() {
+ System.out.println("I.m()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java b/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
deleted file mode 100644
index c4632d7..0000000
--- a/src/test/java/com/android/tools/r8/desugar/Java8MethodsTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.desugar;
-
-import com.android.tools.r8.TestBase;
-import org.junit.Before;
-import org.junit.Test;
-
-public class Java8MethodsTest extends TestBase {
- static String expectedOutput = "";
-
- @Before
- public void testJvm() throws Exception {
- expectedOutput = testForJvm()
- .addTestClasspath()
- .run(Java8Methods.class).getStdOut();
- }
-
- @Test
- public void testD8() throws Exception {
- testForD8()
- .addProgramClasses(Java8Methods.class)
- .run(Java8Methods.class)
- .assertSuccessWithOutput(expectedOutput);
- }
-
- static class Java8Methods {
- public static void main(String[] args) {
- System.out.println(Integer.hashCode(42));
- System.out.println(Integer.max(42, 3));
- System.out.println(Double.hashCode(42.0));
- System.out.println(Double.max(42.0, 43.0));
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
index 855e2ad..58c4237 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
@@ -32,7 +32,8 @@
DexApplication dexApplication =
new ApplicationReader(app, TEST_OPTIONS, timing).read().toDirect();
AppView<AppInfoWithSubtyping> appView =
- new AppView<>(new AppInfoWithSubtyping(dexApplication), GraphLense.getIdentityLense());
+ new AppView<>(
+ new AppInfoWithSubtyping(dexApplication), GraphLense.getIdentityLense(), TEST_OPTIONS);
ExecutorService executorService = ThreadUtils.getExecutorService(TEST_OPTIONS);
RootSet rootSet =
new RootSetBuilder(
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index 301247d..4f4639a 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -44,7 +44,6 @@
private DexApplication program;
DexItemFactory dexItemFactory;
- private AppView<AppInfoWithSubtyping> appView;
NamingTestBase(
String test,
@@ -61,7 +60,6 @@
public void readApp() throws IOException, ExecutionException {
program = ToolHelper.buildApplication(ImmutableList.of(appFileName));
dexItemFactory = program.dexItemFactory;
- appView = new AppView<>(new AppInfoWithSubtyping(program), GraphLense.getIdentityLense());
}
NamingLens runMinifier(List<Path> configPaths) throws ExecutionException {
@@ -71,6 +69,8 @@
ExecutorService executor = ThreadUtils.getExecutorService(1);
+ AppView<AppInfoWithSubtyping> appView =
+ new AppView<>(new AppInfoWithSubtyping(program), GraphLense.getIdentityLense(), options);
RootSet rootSet =
new RootSetBuilder(appView, program, configuration.getRules(), options).run(executor);
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index a84f585..7ec1613 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -105,7 +105,8 @@
AndroidApp app = readClassesAndAsmDump(CLASSES, ASM_CLASSES);
DexApplication application = new ApplicationReader(app, options, timing).read().toDirect();
AppView<? extends AppInfoWithSubtyping> appView =
- new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
+ new AppView<>(
+ new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
ExecutorService executor = Executors.newSingleThreadExecutor();
RootSet rootSet =