Reland "Interface method desugaring instruction desugaring cf to cf"
This reverts commit e088ba0e0686c0dd6485db27fcc833e3a891dbf2.
Bug: 166397278
Change-Id: I5ed91a5129bb03c0edcdd7e84e7d63d14db6c8cf
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 7b6c2e7..625430b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -144,7 +144,9 @@
}
}
- private Invoke.Type getInvokeType(DexClassAndMethod context) {
+ // We should avoid interpreting a CF invoke using DEX semantics.
+ @Deprecated
+ public Invoke.Type getInvokeType(DexClassAndMethod context) {
switch (opcode) {
case Opcodes.INVOKEINTERFACE:
return Type.INTERFACE;
@@ -171,10 +173,12 @@
return getMethod().isInstanceInitializer(dexItemFactory);
}
+ // We should avoid interpreting a CF invoke using DEX semantics.
+ @Deprecated
public boolean isInvokeSuper(DexType clazz) {
- return opcode == Opcodes.INVOKESPECIAL &&
- method.holder != clazz &&
- !method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME);
+ return opcode == Opcodes.INVOKESPECIAL
+ && method.holder != clazz
+ && !method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME);
}
@Override
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 785fb64..0243104 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
@@ -1475,7 +1475,8 @@
previous = printMethod(code, "IR after class inlining (SSA)", previous);
- if (interfaceMethodRewriter != null) {
+ // TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
+ if (interfaceMethodRewriter != null && appView.enableWholeProgramOptimizations()) {
timing.begin("Rewrite interface methods");
interfaceMethodRewriter.rewriteMethodReferences(
code, methodProcessor, methodProcessingContext);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index cc28692..44c3df3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -87,8 +87,11 @@
@Override
public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
- return instruction.isInvoke()
- && getMethodProviderOrNull(instruction.asInvoke().getMethod()) != null;
+ return instruction.isInvoke() && methodIsBackport(instruction.asInvoke().getMethod());
+ }
+
+ public boolean methodIsBackport(DexMethod method) {
+ return getMethodProviderOrNull(method) != null;
}
public static List<DexMethod> generateListOfBackportedMethods(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
index 11c18d0..9d7b629 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -36,4 +36,14 @@
* <p>This should return true if-and-only-if {@link #desugarInstruction} returns non-null.
*/
boolean needsDesugaring(CfInstruction instruction, ProgramMethod context);
+
+ /**
+ * Returns true if and only if needsDesugaring() answering true implies a desugaring is needed.
+ * Some optimizations may have some heuristics, so that needsDesugaring() answers true in rare
+ * case even if no desugaring is needed.
+ */
+ // TODO(b/187913003): Fixing interface desugaring should eliminate the need for this.
+ default boolean hasPreciseNeedsDesugaring() {
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index a0c4c64..6951b39 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.desugar.backports.BackportedMethodDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
import com.android.tools.r8.ir.desugar.lambda.LambdaDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
@@ -40,7 +41,8 @@
LambdaDesugaringEventConsumer,
NestBasedAccessDesugaringEventConsumer,
RecordDesugaringEventConsumer,
- TwrCloseResourceDesugaringEventConsumer {
+ TwrCloseResourceDesugaringEventConsumer,
+ InterfaceMethodDesugaringEventConsumer {
public static D8CfInstructionDesugaringEventConsumer createForD8(
D8MethodProcessor methodProcessor) {
@@ -59,6 +61,17 @@
return new CfInstructionDesugaringEventConsumer() {
@Override
+ public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
+ public void acceptInvokeStaticInterfaceOutliningMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false;
+ }
+
+ @Override
public void acceptRecordClass(DexProgramClass recordClass) {
assert false;
}
@@ -168,6 +181,17 @@
methodProcessor.scheduleDesugaredMethodForProcessing(closeMethod);
}
+ @Override
+ public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ }
+
+ @Override
+ public void acceptInvokeStaticInterfaceOutliningMethod(
+ ProgramMethod method, ProgramMethod context) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ }
+
public List<ProgramMethod> finalizeDesugaring(
AppView<?> appView, ClassConverterResult.Builder classConverterResultBuilder) {
List<ProgramMethod> needsProcessing = new ArrayList<>();
@@ -257,6 +281,17 @@
}
@Override
+ public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
+ assert false : "TODO(b/183998768): To be implemented";
+ }
+
+ @Override
+ public void acceptInvokeStaticInterfaceOutliningMethod(
+ ProgramMethod method, ProgramMethod context) {
+ assert false : "TODO(b/183998768): To be implemented";
+ }
+
+ @Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
// Intentionally empty. The backported method will be hit by the tracing in R8 as if it was
// present in the input code, and thus nothing needs to be done.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 26a2c5c..0d4a470 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -15,13 +15,13 @@
import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.EmptyCfClassDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.NonEmptyCfClassDesugaringCollection;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceInstructionDesugaring;
import com.android.tools.r8.utils.IntBox;
-import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -43,19 +43,26 @@
NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
this.appView = appView;
this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
+ BackportedMethodRewriter backportedMethodRewriter = null;
+ if (appView.options().enableBackportedMethodRewriting()) {
+ backportedMethodRewriter = new BackportedMethodRewriter(appView);
+ }
+ // Place TWR before Interface desugaring to eliminate potential $closeResource interface calls.
+ if (appView.options().enableTryWithResourcesDesugaring()) {
+ desugarings.add(new TwrCloseResourceInstructionDesugaring(appView));
+ }
+ // TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
+ if (appView.options().isInterfaceMethodDesugaringEnabled()
+ && !appView.enableWholeProgramOptimizations()) {
+ desugarings.add(new InterfaceMethodRewriter(appView, backportedMethodRewriter));
+ }
desugarings.add(new LambdaInstructionDesugaring(appView));
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
desugarings.add(new InvokeToPrivateRewriter());
desugarings.add(new StringConcatInstructionDesugaring(appView));
desugarings.add(new BufferCovariantReturnTypeRewriter(appView));
- if (appView.options().enableBackportedMethodRewriting()) {
- BackportedMethodRewriter backportedMethodRewriter = new BackportedMethodRewriter(appView);
- if (backportedMethodRewriter.hasBackports()) {
- desugarings.add(backportedMethodRewriter);
- }
- }
- if (appView.options().enableTryWithResourcesDesugaring()) {
- desugarings.add(new TwrCloseResourceInstructionDesugaring(appView));
+ if (backportedMethodRewriter != null && backportedMethodRewriter.hasBackports()) {
+ desugarings.add(backportedMethodRewriter);
}
if (nestBasedAccessDesugaring != null) {
desugarings.add(nestBasedAccessDesugaring);
@@ -152,10 +159,32 @@
cfCode.setMaxLocals(maxLocalsForCode.get());
cfCode.setMaxStack(maxStackForCode.get());
} else {
- assert false : "Expected code to be desugared";
+ assert noDesugaringBecauseOfImpreciseDesugaring(method);
}
}
+ private boolean noDesugaringBecauseOfImpreciseDesugaring(ProgramMethod method) {
+ assert desugarings.stream().anyMatch(desugaring -> !desugaring.hasPreciseNeedsDesugaring())
+ : "Expected code to be desugared";
+ assert needsDesugaring(method);
+ boolean foundFalsePositive = false;
+ for (CfInstruction instruction :
+ method.getDefinition().getCode().asCfCode().getInstructions()) {
+ for (CfInstructionDesugaring impreciseDesugaring :
+ Iterables.filter(desugarings, desugaring -> !desugaring.hasPreciseNeedsDesugaring())) {
+ if (impreciseDesugaring.needsDesugaring(instruction, method)) {
+ foundFalsePositive = true;
+ }
+ }
+ for (CfInstructionDesugaring preciseDesugaring :
+ Iterables.filter(desugarings, desugaring -> desugaring.hasPreciseNeedsDesugaring())) {
+ assert !preciseDesugaring.needsDesugaring(instruction, method);
+ }
+ }
+ assert foundFalsePositive;
+ return true;
+ }
+
@Override
public CfClassDesugaringCollection createClassDesugaringCollection() {
if (recordRewriter == null) {
@@ -185,8 +214,8 @@
methodProcessingContext,
appView.dexItemFactory());
if (replacement != null) {
- assert verifyNoOtherDesugaringNeeded(
- instruction, context, methodProcessingContext, iterator);
+ assert desugaring.needsDesugaring(instruction, context);
+ assert verifyNoOtherDesugaringNeeded(instruction, context, iterator, desugaring);
return replacement;
}
}
@@ -220,26 +249,26 @@
private boolean verifyNoOtherDesugaringNeeded(
CfInstruction instruction,
ProgramMethod context,
- MethodProcessingContext methodProcessingContext,
- Iterator<CfInstructionDesugaring> iterator) {
- assert IteratorUtils.nextUntil(
- iterator,
- desugaring ->
- desugaring.desugarInstruction(
- instruction,
- requiredRegisters -> {
- assert false;
- return 0;
- },
- localStackHeight -> {
- assert false;
- },
- CfInstructionDesugaringEventConsumer.createForDesugaredCode(),
- context,
- methodProcessingContext,
- appView.dexItemFactory())
- != null)
- == null;
+ Iterator<CfInstructionDesugaring> iterator,
+ CfInstructionDesugaring appliedDesugaring) {
+ iterator.forEachRemaining(
+ desugaring -> {
+ boolean alsoApplicable = desugaring.needsDesugaring(instruction, context);
+ // TODO(b/187913003): As part of precise interface desugaring, make sure the
+ // identification is explicitly non-overlapping and remove the exceptions below.
+ assert !alsoApplicable
+ || (appliedDesugaring instanceof InterfaceMethodRewriter
+ && (desugaring instanceof InvokeToPrivateRewriter
+ || desugaring instanceof D8NestBasedAccessDesugaring))
+ || (appliedDesugaring instanceof TwrCloseResourceInstructionDesugaring
+ && desugaring instanceof InterfaceMethodRewriter)
+ : "Desugaring of "
+ + instruction
+ + " has multiple matches: "
+ + appliedDesugaring.getClass().getName()
+ + " and "
+ + desugaring.getClass().getName();
+ });
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
index 1b5e6aa..2e71826 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
@@ -305,15 +305,8 @@
@Override
public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
assert !instruction.isInitClass();
- // TODO(b/179146128): This is a temporary work-around to test desugaring of records
- // without rewriting the record invoke-custom. This should be removed when the record support
- // is complete.
- if (instruction.isInvokeDynamic()
- && context.getHolder().superType == factory.recordType
- && (context.getName() == factory.toStringMethodName
- || context.getName() == factory.hashCodeMethodName
- || context.getName() == factory.equalsMethodName)) {
- return true;
+ if (instruction.isInvokeDynamic()) {
+ return needsDesugaring(instruction.asInvokeDynamic(), context);
}
if (instruction.isInvoke()) {
CfInvoke cfInvoke = instruction.asInvoke();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
new file mode 100644
index 0000000..031e142
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2021, 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.itf;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface InterfaceMethodDesugaringEventConsumer {
+
+ void acceptThrowMethod(ProgramMethod method, ProgramMethod context);
+
+ void acceptInvokeStaticInterfaceOutliningMethod(ProgramMethod method, ProgramMethod context);
+
+ // TODO(b/183998768): Add acceptCompanionClass and acceptEmulatedInterface.
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index b0a4ffa..782ff63 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -9,23 +9,24 @@
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
import static com.android.tools.r8.ir.code.Invoke.Type.SUPER;
import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_CUSTOM;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
import static com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter.getRetargetPackageAndClassPrefixDescriptor;
import static com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX;
import static com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer.VIVIFIED_TYPE_WRAPPER_SUFFIX;
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
@@ -38,6 +39,7 @@
import com.android.tools.r8.graph.DexProgramClass;
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.DexValue;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
@@ -48,17 +50,20 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.InvokeCustom;
-import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.itf.DefaultMethodsHelper.Collection;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
@@ -78,6 +83,9 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -86,6 +94,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
//
@@ -114,7 +124,7 @@
// set of default interface methods missing and add them, the created methods
// forward the call to an appropriate method in interface companion class.
//
-public final class InterfaceMethodRewriter {
+public final class InterfaceMethodRewriter implements CfInstructionDesugaring {
// Public for testing.
public static final String EMULATE_LIBRARY_CLASS_NAME_SUFFIX = "$-EL";
@@ -137,6 +147,10 @@
private final Map<DexType, DefaultMethodsHelper.Collection> cache = new ConcurrentHashMap<>();
private final Predicate<DexType> shouldIgnoreFromReportsPredicate;
+
+ // This is used to filter out double desugaring on backported methods.
+ private final BackportedMethodRewriter backportedMethodRewriter;
+
/** Defines a minor variation in desugaring. */
public enum Flavor {
/** Process all application resources. */
@@ -145,10 +159,24 @@
ExcludeDexResources
}
+ // Constructor for cf to cf desugaring.
+ public InterfaceMethodRewriter(AppView<?> appView, BackportedMethodRewriter rewriter) {
+ this.appView = appView;
+ this.converter = null;
+ this.backportedMethodRewriter = rewriter;
+ this.options = appView.options();
+ this.factory = appView.dexItemFactory();
+ this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
+ initializeEmulatedInterfaceVariables();
+ }
+
+ // Constructor for IR desugaring.
public InterfaceMethodRewriter(AppView<?> appView, IRConverter converter) {
assert converter != null;
this.appView = appView;
this.converter = converter;
+ this.backportedMethodRewriter = null;
this.options = appView.options();
this.factory = appView.dexItemFactory();
this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
@@ -232,6 +260,8 @@
}
private boolean invokeNeedsRewriting(DexMethod method, Type invokeType) {
+ // TODO(b/187913003): Refactor the implementation of needsDesugaring and desugarInstruction so
+ // that the identification is shared and thus guaranteed to be equivalent.
if (invokeType == SUPER || invokeType == STATIC || invokeType == DIRECT) {
DexClass clazz = appView.appInfo().definitionFor(method.getHolderType());
if (clazz != null && clazz.isInterface()) {
@@ -240,11 +270,257 @@
return emulatedMethods.contains(method.getName());
}
if (invokeType == VIRTUAL || invokeType == INTERFACE) {
- return defaultMethodForEmulatedDispatchOrNull(method, invokeType) != null;
+ // A virtual dispatch can target a private method, on self or on a nest mate.
+ AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring();
+ SingleResolutionResult resolution =
+ appInfoForDesugaring.resolveMethod(method, invokeType == INTERFACE).asSingleResolution();
+ if (resolution != null && resolution.getResolvedMethod().isPrivateMethod()) {
+ return true;
+ }
+ return defaultMethodForEmulatedDispatchOrNull(method, invokeType == INTERFACE) != null;
}
return true;
}
+ @Override
+ public boolean hasPreciseNeedsDesugaring() {
+ return false;
+ }
+
+ /**
+ * If the method is not required to be desugared, scanning is used to upgrade when required the
+ * class file version, as well as reporting missing type.
+ */
+ @Override
+ public void scan(ProgramMethod context, CfInstructionDesugaringEventConsumer eventConsumer) {
+ if (isSyntheticMethodThatShouldNotBeDoubleProcessed(context)) {
+ leavingStaticInvokeToInterface(context);
+ return;
+ }
+ CfCode code = context.getDefinition().getCode().asCfCode();
+ for (CfInstruction instruction : code.getInstructions()) {
+ if (instruction.isInvokeDynamic()
+ && !LambdaInstructionDesugaring.isLambdaInvoke(instruction, context, appView)
+ && !StringConcatInstructionDesugaring.isStringConcatInvoke(
+ instruction, appView.dexItemFactory())) {
+ reportInterfaceMethodHandleCallSite(instruction.asInvokeDynamic().getCallSite(), context);
+ continue;
+ }
+ if (instruction.isInvoke()) {
+ CfInvoke cfInvoke = instruction.asInvoke();
+ if (backportedMethodRewriter.methodIsBackport(cfInvoke.getMethod())) {
+ continue;
+ }
+ if (cfInvoke.isInvokeStatic()) {
+ scanInvokeStatic(cfInvoke, context);
+ } else if (cfInvoke.isInvokeSpecial()) {
+ scanInvokeDirectOrSuper(cfInvoke, context);
+ }
+ }
+ }
+ }
+
+ private void scanInvokeDirectOrSuper(CfInvoke cfInvoke, ProgramMethod context) {
+ if (cfInvoke.isInvokeConstructor(factory)) {
+ return;
+ }
+ DexMethod invokedMethod = cfInvoke.getMethod();
+ DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
+ if (clazz == null) {
+ // NOTE: For invoke-super, this leaves unchanged those calls to undefined targets.
+ // This may lead to runtime exception but we can not report it as error since it can also be
+ // the intended behavior.
+ // For invoke-direct, this reports the missing class since we don't know if it is an
+ // interface.
+ warnMissingType(context, invokedMethod.holder);
+ }
+ }
+
+ private void scanInvokeStatic(CfInvoke cfInvoke, ProgramMethod context) {
+ DexMethod invokedMethod = cfInvoke.getMethod();
+ DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
+ if (clazz == null) {
+ // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+ // exception but we can not report it as error since it can also be the intended
+ // behavior.
+ if (cfInvoke.isInterface()) {
+ leavingStaticInvokeToInterface(context);
+ }
+ warnMissingType(context, invokedMethod.holder);
+ return;
+ }
+
+ if (!clazz.isInterface()) {
+ if (cfInvoke.isInterface()) {
+ leavingStaticInvokeToInterface(context);
+ }
+ return;
+ }
+
+ if (isNonDesugaredLibraryClass(clazz)) {
+ // NOTE: we intentionally don't desugar static calls into static interface
+ // methods coming from android.jar since it is only possible in case v24+
+ // version of android.jar is provided.
+ //
+ // We assume such calls are properly guarded by if-checks like
+ // 'if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.XYZ) { ... }'
+ //
+ // WARNING: This may result in incorrect code on older platforms!
+ // Retarget call to an appropriate method of companion class.
+
+ if (options.canLeaveStaticInterfaceMethodInvokes()) {
+ // When leaving static interface method invokes upgrade the class file version.
+ leavingStaticInvokeToInterface(context);
+ }
+ }
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ if (instruction.isInvoke()) {
+ CfInvoke cfInvoke = instruction.asInvoke();
+ return needsRewriting(cfInvoke.getMethod(), cfInvoke.getInvokeType(context), context);
+ }
+ return false;
+ }
+
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
+ if (!instruction.isInvoke() || isSyntheticMethodThatShouldNotBeDoubleProcessed(context)) {
+ return null;
+ }
+ CfInvoke invoke = instruction.asInvoke();
+ if (backportedMethodRewriter.methodIsBackport(invoke.getMethod())) {
+ return null;
+ }
+
+ Function<DexMethod, Collection<CfInstruction>> rewriteInvoke =
+ (newTarget) ->
+ Collections.singletonList(
+ new CfInvoke(org.objectweb.asm.Opcodes.INVOKESTATIC, newTarget, false));
+
+ Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow =
+ (resolutionResult) ->
+ rewriteInvokeToThrowCf(
+ invoke, resolutionResult, eventConsumer, context, methodProcessingContext);
+
+ if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
+ AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring();
+ SingleResolutionResult resolution =
+ appInfoForDesugaring
+ .resolveMethod(invoke.getMethod(), invoke.isInterface())
+ .asSingleResolution();
+ if (resolution != null
+ && resolution.getResolvedMethod().isPrivateMethod()
+ && resolution.isAccessibleFrom(context, appInfoForDesugaring).isTrue()) {
+ return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke);
+ }
+ return rewriteInvokeInterfaceOrInvokeVirtual(
+ invoke.getMethod(), invoke.isInterface(), rewriteInvoke);
+ }
+ if (invoke.isInvokeStatic()) {
+ Consumer<ProgramMethod> staticOutliningMethodConsumer =
+ staticOutliningMethod -> {
+ synthesizedMethods.add(staticOutliningMethod);
+ eventConsumer.acceptInvokeStaticInterfaceOutliningMethod(
+ staticOutliningMethod, context);
+ };
+ return rewriteInvokeStatic(
+ invoke.getMethod(),
+ invoke.isInterface(),
+ methodProcessingContext,
+ context,
+ staticOutliningMethodConsumer,
+ rewriteInvoke,
+ rewriteToThrow);
+ }
+ assert invoke.isInvokeSpecial();
+ if (invoke.isInvokeSuper(context.getHolderType())) {
+ return rewriteInvokeSuper(invoke.getMethod(), context, rewriteInvoke, rewriteToThrow);
+ }
+ return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke);
+ }
+
+ private Collection<CfInstruction> rewriteInvokeToThrowCf(
+ CfInvoke invoke,
+ SingleResolutionResult resolutionResult,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
+ if (backportedMethodRewriter != null
+ && backportedMethodRewriter.methodIsBackport(invoke.getMethod())) {
+ // In Cf to Cf it is not allowed to desugar twice the same instruction, if the backported
+ // method rewriter already desugars the instruction, it takes precedence and nothing has
+ // to be done here.
+ return null;
+ }
+
+ MethodSynthesizerConsumer methodSynthesizerConsumer;
+ if (resolutionResult == null) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowNoSuchMethodErrorMethod;
+ } else if (resolutionResult.getResolvedMethod().isStatic() != invoke.isInvokeStatic()) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowIncompatibleClassChangeErrorMethod;
+ } else {
+ assert false;
+ return null;
+ }
+
+ assert needsDesugaring(invoke, context);
+
+ // Replace the entire effect of the invoke by by call to the throwing helper:
+ // ...
+ // invoke <method> [receiver] args*
+ // =>
+ // ...
+ // (pop arg)*
+ // [pop receiver]
+ // invoke <throwing-method>
+ // pop exception result
+ // [push fake result for <method>]
+ UtilityMethodForCodeOptimizations throwMethod =
+ methodSynthesizerConsumer.synthesizeMethod(appView, methodProcessingContext);
+ ProgramMethod throwProgramMethod = throwMethod.uncheckedGetMethod();
+ eventConsumer.acceptThrowMethod(throwProgramMethod, context);
+
+ ArrayList<CfInstruction> replacement = new ArrayList<>();
+ DexTypeList parameters = invoke.getMethod().getParameters();
+ for (int i = parameters.values.length - 1; i >= 0; i--) {
+ replacement.add(
+ new CfStackInstruction(
+ parameters.get(i).isWideType()
+ ? CfStackInstruction.Opcode.Pop2
+ : CfStackInstruction.Opcode.Pop));
+ }
+ if (!invoke.isInvokeStatic()) {
+ replacement.add(new CfStackInstruction(CfStackInstruction.Opcode.Pop));
+ }
+
+ CfInvoke throwInvoke =
+ new CfInvoke(
+ org.objectweb.asm.Opcodes.INVOKESTATIC, throwProgramMethod.getReference(), false);
+ assert throwInvoke.getMethod().getReturnType().isClassType();
+ replacement.add(throwInvoke);
+ replacement.add(new CfStackInstruction(CfStackInstruction.Opcode.Pop));
+
+ DexType returnType = invoke.getMethod().getReturnType();
+ if (returnType != factory.voidType) {
+ replacement.add(
+ returnType.isPrimitiveType()
+ ? new CfConstNumber(0, ValueType.fromDexType(returnType))
+ : new CfConstNull());
+ }
+ return replacement;
+ }
+
DexType getEmulatedInterface(DexType itf) {
return emulatedInterfaces.get(itf);
}
@@ -290,44 +566,16 @@
InstructionListIterator instructions = block.listIterator(code);
while (instructions.hasNext()) {
Instruction instruction = instructions.next();
- switch (instruction.opcode()) {
- case INVOKE_CUSTOM:
- rewriteInvokeCustom(instruction.asInvokeCustom(), context);
- break;
- case INVOKE_DIRECT:
- rewriteInvokeDirect(instruction.asInvokeDirect(), instructions, context);
- break;
- case INVOKE_STATIC:
- rewriteInvokeStatic(
- instruction.asInvokeStatic(),
- code,
- blocks,
- instructions,
- affectedValues,
- blocksToRemove,
- methodProcessor,
- methodProcessingContext);
- break;
- case INVOKE_SUPER:
- rewriteInvokeSuper(
- instruction.asInvokeSuper(),
- code,
- blocks,
- instructions,
- affectedValues,
- blocksToRemove,
- methodProcessor,
- methodProcessingContext);
- break;
- case INVOKE_INTERFACE:
- case INVOKE_VIRTUAL:
- rewriteInvokeInterfaceOrInvokeVirtual(
- instruction.asInvokeMethodWithReceiver(), instructions);
- break;
- default:
- // Intentionally empty.
- break;
- }
+ rewriteMethodReferences(
+ code,
+ methodProcessor,
+ methodProcessingContext,
+ context,
+ affectedValues,
+ blocksToRemove,
+ blocks,
+ instructions,
+ instruction);
}
}
@@ -340,14 +588,71 @@
assert code.isConsistentSSA();
}
+ private void rewriteMethodReferences(
+ IRCode code,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext,
+ ProgramMethod context,
+ Set<Value> affectedValues,
+ Set<BasicBlock> blocksToRemove,
+ ListIterator<BasicBlock> blocks,
+ InstructionListIterator instructions,
+ Instruction instruction) {
+ if (instruction.isInvokeCustom()) {
+ reportInterfaceMethodHandleCallSite(instruction.asInvokeCustom().getCallSite(), context);
+ return;
+ }
+ if (!instruction.isInvokeMethod()) {
+ return;
+ }
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ Function<DexMethod, Collection<CfInstruction>> rewriteInvoke =
+ (newTarget) -> {
+ instructions.replaceCurrentInstruction(
+ new InvokeStatic(newTarget, invoke.outValue(), invoke.arguments()));
+ return null;
+ };
+ if (instruction.isInvokeDirect()) {
+ rewriteInvokeDirect(invoke.getInvokedMethod(), context, rewriteInvoke);
+ } else if (instruction.isInvokeVirtual() || instruction.isInvokeInterface()) {
+ rewriteInvokeInterfaceOrInvokeVirtual(
+ invoke.getInvokedMethod(), invoke.getInterfaceBit(), rewriteInvoke);
+ } else {
+ Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow =
+ (resolutionResult) ->
+ rewriteInvokeToThrowIR(
+ invoke,
+ resolutionResult,
+ code,
+ blocks,
+ instructions,
+ affectedValues,
+ blocksToRemove,
+ methodProcessor,
+ methodProcessingContext);
+ if (instruction.isInvokeStatic()) {
+ rewriteInvokeStatic(
+ invoke.getInvokedMethod(),
+ invoke.getInterfaceBit(),
+ methodProcessingContext,
+ context,
+ synthesizedMethods::add,
+ rewriteInvoke,
+ rewriteToThrow);
+ } else {
+ assert instruction.isInvokeSuper();
+ rewriteInvokeSuper(invoke.getInvokedMethod(), context, rewriteInvoke, rewriteToThrow);
+ }
+ }
+ }
+
private boolean isSyntheticMethodThatShouldNotBeDoubleProcessed(ProgramMethod method) {
return appView.getSyntheticItems().isSyntheticMethodThatShouldNotBeDoubleProcessed(method);
}
- private void rewriteInvokeCustom(InvokeCustom invoke, ProgramMethod context) {
+ private void reportInterfaceMethodHandleCallSite(DexCallSite callSite, ProgramMethod context) {
// Check that static interface methods are not referenced from invoke-custom instructions via
// method handles.
- DexCallSite callSite = invoke.getCallSite();
reportStaticInterfaceMethodHandle(context, callSite.bootstrapMethod);
for (DexValue arg : callSite.bootstrapArgs) {
if (arg.isDexValueMethodHandle()) {
@@ -356,22 +661,23 @@
}
}
- private void rewriteInvokeDirect(
- InvokeDirect invoke, InstructionListIterator instructions, ProgramMethod context) {
- DexMethod method = invoke.getInvokedMethod();
- if (factory.isConstructor(method)) {
- return;
+ private Collection<CfInstruction> rewriteInvokeDirect(
+ DexMethod invokedMethod,
+ ProgramMethod context,
+ Function<DexMethod, Collection<CfInstruction>> rewriteInvoke) {
+ if (factory.isConstructor(invokedMethod)) {
+ return null;
}
- DexClass clazz = appView.definitionForHolder(method, context);
+ DexClass clazz = appView.definitionForHolder(invokedMethod, context);
if (clazz == null) {
// Report missing class since we don't know if it is an interface.
- warnMissingType(context, method.holder);
- return;
+ warnMissingType(context, invokedMethod.holder);
+ return null;
}
if (!clazz.isInterface()) {
- return;
+ return null;
}
if (clazz.isLibraryClass()) {
@@ -382,72 +688,63 @@
getMethodOrigin(context.getReference()));
}
- DexClassAndMethod directTarget = clazz.lookupClassMethod(method);
+ DexClassAndMethod directTarget = clazz.lookupClassMethod(invokedMethod);
if (directTarget != null) {
// This can be a private instance method call. Note that the referenced
// method is expected to be in the current class since it is private, but desugaring
// may move some methods or their code into other classes.
- assert invokeNeedsRewriting(method, DIRECT);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- directTarget.getDefinition().isPrivateMethod()
- ? privateAsMethodOfCompanionClass(directTarget)
- : defaultAsMethodOfCompanionClass(directTarget),
- invoke.outValue(),
- invoke.arguments()));
+ assert invokeNeedsRewriting(invokedMethod, DIRECT);
+ return rewriteInvoke.apply(
+ directTarget.getDefinition().isPrivateMethod()
+ ? privateAsMethodOfCompanionClass(directTarget)
+ : defaultAsMethodOfCompanionClass(directTarget));
} else {
// The method can be a default method in the interface hierarchy.
DexClassAndMethod virtualTarget =
- appView.appInfoForDesugaring().lookupMaximallySpecificMethod(clazz, method);
+ appView.appInfoForDesugaring().lookupMaximallySpecificMethod(clazz, invokedMethod);
if (virtualTarget != null) {
// This is a invoke-direct call to a virtual method.
- assert invokeNeedsRewriting(method, DIRECT);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- defaultAsMethodOfCompanionClass(virtualTarget),
- invoke.outValue(),
- invoke.arguments()));
+ assert invokeNeedsRewriting(invokedMethod, DIRECT);
+ return rewriteInvoke.apply(defaultAsMethodOfCompanionClass(virtualTarget));
} else {
// The below assert is here because a well-type program should have a target, but we
// cannot throw a compilation error, since we have no knowledge about the input.
assert false;
}
}
+ return null;
}
- private void rewriteInvokeStatic(
- InvokeStatic invoke,
- IRCode code,
- ListIterator<BasicBlock> blockIterator,
- InstructionListIterator instructions,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove,
- MethodProcessor methodProcessor,
- MethodProcessingContext methodProcessingContext) {
- DexMethod invokedMethod = invoke.getInvokedMethod();
+ private Collection<CfInstruction> rewriteInvokeStatic(
+ DexMethod invokedMethod,
+ boolean interfaceBit,
+ MethodProcessingContext methodProcessingContext,
+ ProgramMethod context,
+ Consumer<ProgramMethod> staticOutliningMethodConsumer,
+ Function<DexMethod, Collection<CfInstruction>> rewriteInvoke,
+ Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow) {
if (appView.getSyntheticItems().isPendingSynthetic(invokedMethod.holder)) {
// We did not create this code yet, but it will not require rewriting.
- return;
+ return null;
}
- ProgramMethod context = code.context();
DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
if (clazz == null) {
// NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
// exception but we can not report it as error since it can also be the intended
// behavior.
- if (invoke.getInterfaceBit()) {
+ if (interfaceBit) {
leavingStaticInvokeToInterface(context);
}
warnMissingType(context, invokedMethod.holder);
- return;
+ return null;
}
if (!clazz.isInterface()) {
- if (invoke.getInterfaceBit()) {
+ if (interfaceBit) {
leavingStaticInvokeToInterface(context);
}
- return;
+ return null;
}
if (isNonDesugaredLibraryClass(clazz)) {
@@ -467,6 +764,19 @@
// so the user class is not rejected because it make this call directly.
// TODO(b/166247515): If this an incorrect invoke-static without the interface bit
// we end up "fixing" the code and remove and ICCE error.
+ if (synthesizedMethods.contains(context)) {
+ // When reprocessing the method generated below, the desugaring asserts this method
+ // does not need any new desugaring, while the interface method rewriter tries
+ // to outline again the invoke-static. Just do nothing instead.
+ return null;
+ }
+ if (backportedMethodRewriter != null
+ && backportedMethodRewriter.methodIsBackport(invokedMethod)) {
+ // In Cf to Cf it is not allowed to desugar twice the same instruction, if the backported
+ // method rewriter already desugars the instruction, it takes precedence and nothing has
+ // to be done here.
+ return null;
+ }
ProgramMethod newProgramMethod =
appView
.getSyntheticItems()
@@ -484,19 +794,17 @@
.setStaticTarget(invokedMethod, true)
.setStaticSource(m)
.build()));
+ staticOutliningMethodConsumer.accept(newProgramMethod);
assert invokeNeedsRewriting(invokedMethod, STATIC);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- newProgramMethod.getReference(), invoke.outValue(), invoke.arguments()));
// The synthetic dispatch class has static interface method invokes, so set
// the class file version accordingly.
- newProgramMethod.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
- synthesizedMethods.add(newProgramMethod);
+ leavingStaticInvokeToInterface(newProgramMethod);
+ return rewriteInvoke.apply(newProgramMethod.getReference());
} else {
// When leaving static interface method invokes upgrade the class file version.
- context.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
+ leavingStaticInvokeToInterface(context);
}
- return;
+ return null;
}
SingleResolutionResult resolutionResult =
@@ -504,67 +812,38 @@
.appInfoForDesugaring()
.resolveMethodOnInterface(clazz, invokedMethod)
.asSingleResolution();
- if (clazz.isInterface()
- && rewriteInvokeToThrow(
- invoke,
- resolutionResult,
- code,
- blockIterator,
- instructions,
- affectedValues,
- blocksToRemove,
- methodProcessor,
- methodProcessingContext)) {
- assert invokeNeedsRewriting(invoke.getInvokedMethod(), STATIC);
- return;
+ if (clazz.isInterface() && shouldRewriteToInvokeToThrow(resolutionResult, true)) {
+ assert invokeNeedsRewriting(invokedMethod, STATIC);
+ return rewriteToThrow.apply(resolutionResult);
}
assert resolutionResult != null;
assert resolutionResult.getResolvedMethod().isStatic();
assert invokeNeedsRewriting(invokedMethod, STATIC);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- staticAsMethodOfCompanionClass(resolutionResult.getResolutionPair()),
- invoke.outValue(),
- invoke.arguments()));
+ return rewriteInvoke.apply(
+ staticAsMethodOfCompanionClass(resolutionResult.getResolutionPair()));
}
- private void rewriteInvokeSuper(
- InvokeSuper invoke,
- IRCode code,
- ListIterator<BasicBlock> blockIterator,
- InstructionListIterator instructions,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove,
- MethodProcessor methodProcessor,
- MethodProcessingContext methodProcessingContext) {
- ProgramMethod context = code.context();
- DexMethod invokedMethod = invoke.getInvokedMethod();
+ private Collection<CfInstruction> rewriteInvokeSuper(
+ DexMethod invokedMethod,
+ ProgramMethod context,
+ Function<DexMethod, Collection<CfInstruction>> rewriteInvoke,
+ Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow) {
DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
if (clazz == null) {
// NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
// exception but we can not report it as error since it can also be the intended
// behavior.
warnMissingType(context, invokedMethod.holder);
- return;
+ return null;
}
SingleResolutionResult resolutionResult =
appView.appInfoForDesugaring().resolveMethodOn(clazz, invokedMethod).asSingleResolution();
- if (clazz.isInterface()
- && rewriteInvokeToThrow(
- invoke,
- resolutionResult,
- code,
- blockIterator,
- instructions,
- affectedValues,
- blocksToRemove,
- methodProcessor,
- methodProcessingContext)) {
- assert invokeNeedsRewriting(invoke.getInvokedMethod(), SUPER);
- return;
+ if (clazz.isInterface() && shouldRewriteToInvokeToThrow(resolutionResult, false)) {
+ assert invokeNeedsRewriting(invokedMethod, SUPER);
+ return rewriteToThrow.apply(resolutionResult);
}
if (clazz.isInterface() && !clazz.isLibraryClass()) {
@@ -578,81 +857,83 @@
// WARNING: This may result in incorrect code on older platforms!
// Retarget call to an appropriate method of companion class.
assert invokeNeedsRewriting(invokedMethod, SUPER);
- DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- defaultAsMethodOfCompanionClass(amendedMethod, appView.dexItemFactory()),
- invoke.outValue(),
- invoke.arguments()));
- } else {
- DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
- if (emulatedItf == null) {
- if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
- DexClassAndMethod target =
- appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
- if (target != null && target.getDefinition().isDefaultMethod()) {
- DexClass holder = target.getHolder();
- if (holder.isLibraryClass() && holder.isInterface()) {
- assert invokeNeedsRewriting(invokedMethod, SUPER);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- defaultAsMethodOfCompanionClass(target),
- invoke.outValue(),
- invoke.arguments()));
- }
- }
+ if (resolutionResult.getResolvedMethod().isPrivateMethod()) {
+ if (resolutionResult.isAccessibleFrom(context, appView.appInfoForDesugaring()).isFalse()) {
+ // TODO(b/145775365): This should throw IAE.
+ return rewriteToThrow.apply(null);
}
+ return rewriteInvoke.apply(
+ privateAsMethodOfCompanionClass(resolutionResult.getResolutionPair()));
} else {
- // That invoke super may not resolve since the super method may not be present
- // since it's in the emulated interface. We need to force resolution. If it resolves
- // to a library method, then it needs to be rewritten.
- // If it resolves to a program overrides, the invoke-super can remain.
- DexClassAndMethod superTarget =
- appView.appInfoForDesugaring().lookupSuperTarget(invoke.getInvokedMethod(), context);
- if (superTarget != null && superTarget.isLibraryMethod()) {
- // Rewriting is required because the super invoke resolves into a missing
- // method (method is on desugared library). Find out if it needs to be
- // retarget or if it just calls a companion class method and rewrite.
- DexMethod retargetMethod =
- options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
- if (retargetMethod == null) {
- DexMethod originalCompanionMethod = defaultAsMethodOfCompanionClass(superTarget);
- DexMethod companionMethod =
- factory.createMethod(
- getCompanionClassType(emulatedItf),
- factory.protoWithDifferentFirstParameter(
- originalCompanionMethod.proto, emulatedItf),
- originalCompanionMethod.name);
+ DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
+ return rewriteInvoke.apply(
+ defaultAsMethodOfCompanionClass(amendedMethod, appView.dexItemFactory()));
+ }
+ }
+
+ DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
+ if (emulatedItf == null) {
+ if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
+ DexClassAndMethod target =
+ appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+ if (target != null && target.getDefinition().isDefaultMethod()) {
+ DexClass holder = target.getHolder();
+ if (holder.isLibraryClass() && holder.isInterface()) {
assert invokeNeedsRewriting(invokedMethod, SUPER);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(companionMethod, invoke.outValue(), invoke.arguments()));
- } else {
- assert invokeNeedsRewriting(invokedMethod, SUPER);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
+ return rewriteInvoke.apply(defaultAsMethodOfCompanionClass(target));
}
}
}
+ return null;
}
+ // That invoke super may not resolve since the super method may not be present
+ // since it's in the emulated interface. We need to force resolution. If it resolves
+ // to a library method, then it needs to be rewritten.
+ // If it resolves to a program overrides, the invoke-super can remain.
+ DexClassAndMethod superTarget =
+ appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+ if (superTarget != null && superTarget.isLibraryMethod()) {
+ // Rewriting is required because the super invoke resolves into a missing
+ // method (method is on desugared library). Find out if it needs to be
+ // retarget or if it just calls a companion class method and rewrite.
+ DexMethod retargetMethod =
+ options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
+ if (retargetMethod == null) {
+ DexMethod originalCompanionMethod = defaultAsMethodOfCompanionClass(superTarget);
+ DexMethod companionMethod =
+ factory.createMethod(
+ getCompanionClassType(emulatedItf),
+ factory.protoWithDifferentFirstParameter(
+ originalCompanionMethod.proto, emulatedItf),
+ originalCompanionMethod.name);
+ assert invokeNeedsRewriting(invokedMethod, SUPER);
+ return rewriteInvoke.apply(companionMethod);
+ } else {
+ assert invokeNeedsRewriting(invokedMethod, SUPER);
+ return rewriteInvoke.apply(retargetMethod);
+ }
+ }
+ return null;
}
private DexClassAndMethod defaultMethodForEmulatedDispatchOrNull(
- DexMethod method, Type invokeType) {
- assert invokeType == VIRTUAL || invokeType == INTERFACE;
- boolean interfaceBit = invokeType.isInterface();
- DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(method);
+ DexMethod invokedMethod, boolean interfaceBit) {
+ DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
if (emulatedItf == null) {
return null;
}
// The call potentially ends up in a library class, in which case we need to rewrite, since the
// code may be in the desugared library.
SingleResolutionResult resolution =
- appView.appInfoForDesugaring().resolveMethod(method, interfaceBit).asSingleResolution();
+ appView
+ .appInfoForDesugaring()
+ .resolveMethod(invokedMethod, interfaceBit)
+ .asSingleResolution();
if (resolution != null
&& (resolution.getResolvedHolder().isLibraryClass()
|| appView.options().isDesugaredLibraryCompilation())) {
DexClassAndMethod defaultMethod =
- appView.definitionFor(emulatedItf).lookupClassMethod(method);
+ appView.definitionFor(emulatedItf).lookupClassMethod(invokedMethod);
if (defaultMethod != null && !dontRewrite(defaultMethod)) {
assert !defaultMethod.getAccessFlags().isAbstract();
return defaultMethod;
@@ -661,20 +942,25 @@
return null;
}
- private void rewriteInvokeInterfaceOrInvokeVirtual(
- InvokeMethodWithReceiver invoke, InstructionListIterator instructions) {
+ private Collection<CfInstruction> rewriteInvokeInterfaceOrInvokeVirtual(
+ DexMethod invokedMethod,
+ boolean interfaceBit,
+ Function<DexMethod, Collection<CfInstruction>> rewriteInvoke) {
DexClassAndMethod defaultMethod =
- defaultMethodForEmulatedDispatchOrNull(invoke.getInvokedMethod(), invoke.getType());
+ defaultMethodForEmulatedDispatchOrNull(invokedMethod, interfaceBit);
if (defaultMethod != null) {
- instructions.replaceCurrentInstruction(
- new InvokeStatic(
- emulateInterfaceLibraryMethod(defaultMethod, factory),
- invoke.outValue(),
- invoke.arguments()));
+ return rewriteInvoke.apply(emulateInterfaceLibraryMethod(defaultMethod, factory));
}
+ return null;
}
- private boolean rewriteInvokeToThrow(
+ private boolean shouldRewriteToInvokeToThrow(
+ SingleResolutionResult resolutionResult, boolean isInvokeStatic) {
+ return resolutionResult == null
+ || resolutionResult.getResolvedMethod().isStatic() != isInvokeStatic;
+ }
+
+ private Collection<CfInstruction> rewriteInvokeToThrowIR(
InvokeMethod invoke,
SingleResolutionResult resolutionResult,
IRCode code,
@@ -692,7 +978,8 @@
methodSynthesizerConsumer =
UtilityMethodsForCodeOptimizations::synthesizeThrowIncompatibleClassChangeErrorMethod;
} else {
- return false;
+ assert false;
+ return null;
}
// Replace by throw new SomeException.
@@ -725,7 +1012,7 @@
throwBlockIterator.next();
throwBlockIterator.replaceCurrentInstructionWithThrow(
appView, code, blockIterator, throwInvoke.outValue(), blocksToRemove, affectedValues);
- return true;
+ return null;
}
private DexType maximallySpecificEmulatedInterfaceOrNull(DexMethod invokedMethod) {
@@ -1100,7 +1387,7 @@
return collection;
}
collection = createInterfaceInfo(classToDesugar, implementing, iface);
- Collection existing = cache.putIfAbsent(iface, collection);
+ DefaultMethodsHelper.Collection existing = cache.putIfAbsent(iface, collection);
return existing != null ? existing : collection;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index ad1cacb..71010c3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -158,6 +158,11 @@
@Override
public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ return isLambdaInvoke(instruction, context, appView);
+ }
+
+ public static boolean isLambdaInvoke(
+ CfInstruction instruction, ProgramMethod context, AppView<?> appView) {
return instruction.isInvokeDynamic()
&& LambdaDescriptor.tryInfer(
instruction.asInvokeDynamic().getCallSite(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
index 4f4d963..607b088 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/stringconcat/StringConcatInstructionDesugaring.java
@@ -216,13 +216,17 @@
@Override
public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
- return instruction.isInvokeDynamic()
- && needsDesugaring(instruction.asInvokeDynamic().getCallSite());
+ return isStringConcatInvoke(instruction, factory);
}
- private boolean needsDesugaring(DexCallSite callSite) {
+ public static boolean isStringConcatInvoke(CfInstruction instruction, DexItemFactory factory) {
+ CfInvokeDynamic invoke = instruction.asInvokeDynamic();
+ if (invoke == null) {
+ return false;
+ }
// We are interested in bootstrap methods StringConcatFactory::makeConcat
// and StringConcatFactory::makeConcatWithConstants, both are static.
+ DexCallSite callSite = invoke.getCallSite();
if (callSite.bootstrapMethod.type.isInvokeStatic()) {
DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
return bootstrapMethod == factory.stringConcatFactoryMembers.makeConcat
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index 953a78c..2b15739 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -177,6 +177,10 @@
return method;
}
+ public ProgramMethod uncheckedGetMethod() {
+ return method;
+ }
+
public void optimize(MethodProcessor methodProcessor) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
optimized = true;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index ce1647b..20f0ab4 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -207,6 +207,11 @@
createDescriptor(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context.getBinaryName(), id));
}
+ public static boolean isInternalStaticInterfaceCall(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(
+ reference, Phase.INTERNAL, SyntheticKind.STATIC_INTERFACE_CALL);
+ }
+
static boolean isSynthetic(ClassReference clazz, Phase phase, SyntheticKind kind) {
String typeName = clazz.getTypeName();
if (kind.isFixedSuffixSynthetic) {
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java
index b54a7d5..35e52b4 100644
--- a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java
@@ -15,12 +15,12 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collection;
@@ -149,8 +149,8 @@
return box.get();
}
- private Set<MethodReference> getSyntheticMethods(CodeInspector inspector) {
- Set<MethodReference> methods = new HashSet<>();
+ private Set<FoundMethodSubject> getSyntheticMethods(CodeInspector inspector) {
+ Set<FoundMethodSubject> methods = new HashSet<>();
assert inspector.allClasses().stream()
.allMatch(
c ->
@@ -159,10 +159,7 @@
c.getFinalReference()));
inspector.allClasses().stream()
.filter(c -> SyntheticItemsTestUtils.isExternalStaticInterfaceCall(c.getFinalReference()))
- .forEach(
- c ->
- c.allMethods(m -> !m.isInstanceInitializer())
- .forEach(m -> methods.add(m.asMethodReference())));
+ .forEach(c -> methods.addAll(c.allMethods(m -> !m.isInstanceInitializer())));
return methods;
}