Insert bridges for library targets for memberrebinding
Bug: b/254510678
Change-Id: Ia7b7700a48322352764e8d8f5ff4ccf6d63a730b
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 71898e2..14cac77 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -700,17 +700,8 @@
appView.setGraphLens(memberRebindingIdentityLens);
// Remove redundant bridges that have been inserted for member rebinding.
- // This can only be done if we have AppInfoWithLiveness.
- if (appView.appInfo().hasLiveness()) {
- new RedundantBridgeRemover(appView.withLiveness())
- .run(memberRebindingIdentityLens, executorService);
- } else {
- // If we don't have AppInfoWithLiveness here, it must be because we are not shrinking. When
- // we are not shrinking, we can't move visibility bridges. In principle, though, it would be
- // possible to remove visibility bridges that have been synthesized by R8, but we currently
- // do not have this information.
- assert !options.isShrinking();
- }
+ new RedundantBridgeRemover(appView.withLiveness())
+ .run(memberRebindingIdentityLens, executorService);
if (appView.appInfo().hasLiveness()) {
SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService, timing);
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index d0417b4..675d2df 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -73,6 +73,10 @@
public abstract String toString(DexEncodedMethod method, RetracerForCodePrinting retracer);
+ public boolean passThroughDesugarAndIRConversion() {
+ return false;
+ }
+
public boolean isCfCode() {
return false;
}
@@ -125,6 +129,14 @@
return false;
}
+ public boolean isMemberRebindingBridgeCode() {
+ return false;
+ }
+
+ public MemberRebindingBridgeCode asMemberRebindingBridgeCode() {
+ return null;
+ }
+
public ThrowNullCode asThrowNullCode() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
index 165ff81..dc2949c 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -67,6 +67,11 @@
return INSTANCE;
}
+ @Override
+ public boolean passThroughDesugarAndIRConversion() {
+ return true;
+ }
+
public static boolean canonicalizeCodeIfPossible(AppView<?> appView, ProgramMethod method) {
if (hasDefaultInstanceInitializerCode(method, appView)) {
method.setCode(get(), appView);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 411e5ee..2e2755d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -517,6 +517,12 @@
public final DexType retentionType =
createStaticallyKnownType("Ljava/lang/annotation/Retention;");
+ public final DexType recentlyNonNull =
+ createStaticallyKnownType("Landroidx/annotation/RecentlyNonNull;");
+ public final DexType recentlyNullable =
+ createStaticallyKnownType("Landroidx/annotation/RecentlyNullable;");
+ public final DexType nullable = createStaticallyKnownType("Landroidx/annotation/Nullable;");
+ public final DexType nonNull = createStaticallyKnownType("Landroidx/annotation/NonNull;");
public final DexType runtimeExceptionType = createStaticallyKnownType(runtimeExceptionDescriptor);
public final DexType assertionErrorType = createStaticallyKnownType(assertionErrorDescriptor);
public final DexType throwableType = createStaticallyKnownType(throwableDescriptor);
diff --git a/src/main/java/com/android/tools/r8/graph/MemberRebindingBridgeCode.java b/src/main/java/com/android/tools/r8/graph/MemberRebindingBridgeCode.java
new file mode 100644
index 0000000..9c136e8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MemberRebindingBridgeCode.java
@@ -0,0 +1,202 @@
+// Copyright (c) 2022, 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.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeSuper;
+import com.android.tools.r8.ir.code.NumberGenerator;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.SyntheticStraightLineSourceCode;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.RetracerForCodePrinting;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class MemberRebindingBridgeCode extends Code {
+
+ private final DexMethod target;
+ private final boolean isInterface;
+
+ private MemberRebindingBridgeCode(DexMethod target, boolean isInterface) {
+ this.target = target;
+ this.isInterface = isInterface;
+ }
+
+ @Override
+ public boolean isMemberRebindingBridgeCode() {
+ return true;
+ }
+
+ public DexMethod getTarget() {
+ return target;
+ }
+
+ public boolean getInterface() {
+ return isInterface;
+ }
+
+ @Override
+ public boolean passThroughDesugarAndIRConversion() {
+ return true;
+ }
+
+ @Override
+ public MemberRebindingBridgeCode asMemberRebindingBridgeCode() {
+ return this;
+ }
+
+ @Override
+ public IRCode buildIR(
+ ProgramMethod method,
+ AppView<?> appView,
+ Origin origin,
+ MutableMethodConversionOptions conversionOptions) {
+ DexMethod originalMethod =
+ appView.graphLens().getOriginalMethodSignature(method.getReference());
+ MemberRebindingBridgeSourceCode source =
+ new MemberRebindingBridgeSourceCode(originalMethod, this.target, isInterface);
+ return IRBuilder.create(method, appView, source, origin).build(method, conversionOptions);
+ }
+
+ @Override
+ public IRCode buildInliningIR(
+ ProgramMethod context,
+ ProgramMethod method,
+ AppView<?> appView,
+ GraphLens codeLens,
+ NumberGenerator valueNumberGenerator,
+ Position callerPosition,
+ Origin origin,
+ RewrittenPrototypeDescription protoChanges) {
+ DexMethod originalMethod =
+ appView.graphLens().getOriginalMethodSignature(method.getReference());
+ MemberRebindingBridgeSourceCode source =
+ new MemberRebindingBridgeSourceCode(
+ originalMethod, this.target, isInterface, callerPosition);
+ return IRBuilder.createForInlining(
+ method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges)
+ .build(context, new ThrowingMethodConversionOptions(appView.options()));
+ }
+
+ @Override
+ public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+ registry.registerInvokeSuper(this.target);
+ }
+
+ @Override
+ public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+ registry.registerInvokeSuper(this.target);
+ }
+
+ @Override
+ public String toString() {
+ return "<member-rebinding-bridge-code>";
+ }
+
+ @Override
+ public String toString(DexEncodedMethod method, RetracerForCodePrinting retracer) {
+ StringBuilder builder = new StringBuilder();
+ if (method != null) {
+ builder.append(method.toSourceString()).append("\n");
+ }
+ return builder.append(this).toString();
+ }
+
+ @Override
+ public int estimatedDexCodeSizeUpperBoundInBytes() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean isEmptyVoidMethod() {
+ return false;
+ }
+
+ @Override
+ protected int computeHashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ return this == other;
+ }
+
+ @Override
+ public boolean isSharedCodeObject() {
+ return true;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ static class MemberRebindingBridgeSourceCode extends SyntheticStraightLineSourceCode {
+
+ MemberRebindingBridgeSourceCode(DexMethod context, DexMethod method, boolean isInterface) {
+ this(context, method, isInterface, null);
+ }
+
+ MemberRebindingBridgeSourceCode(
+ DexMethod context, DexMethod method, boolean isInterface, Position callerPosition) {
+ super(
+ getInstructionBuilders(method, isInterface),
+ SyntheticPosition.builder()
+ .setLine(0)
+ .setMethod(context)
+ .setCallerPosition(callerPosition)
+ .build());
+ }
+
+ private static List<Consumer<IRBuilder>> getInstructionBuilders(
+ DexMethod method, boolean isInterface) {
+ return ImmutableList.of(
+ builder -> {
+ InvokeSuper invokeSuper =
+ InvokeSuper.builder()
+ .setMethod(method)
+ .setArguments(
+ ListUtils.newArrayList(
+ builder.getReceiverValue(), builder.getArgumentValues()))
+ .setInterface(isInterface)
+ .applyIf(
+ !method.getReturnType().isVoidType(),
+ b -> b.setFreshOutValue(builder.appView, builder))
+ .build();
+ builder.add(invokeSuper);
+ builder.addReturn(new Return(invokeSuper.outValue()));
+ });
+ }
+ }
+
+ public static class Builder {
+
+ private DexMethod target;
+ private boolean isInterface;
+
+ public Builder setTarget(DexMethod target) {
+ this.target = target;
+ return this;
+ }
+
+ public Builder setInterface(boolean isInterface) {
+ this.isInterface = isInterface;
+ return this;
+ }
+
+ public MemberRebindingBridgeCode build() {
+ assert target != null;
+ return new MemberRebindingBridgeCode(target, isInterface);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index 12da839..085d96d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -7,6 +7,7 @@
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
public class MethodAccessFlags extends AccessFlags<MethodAccessFlags> {
@@ -264,17 +265,17 @@
public Builder set(int flag) {
flags.set(flag);
- return this;
+ return self();
}
public Builder setBridge() {
flags.setBridge();
- return this;
+ return self();
}
public Builder setConstructor() {
flags.setConstructor();
- return this;
+ return self();
}
public Builder setStrict(boolean value) {
@@ -283,7 +284,7 @@
} else {
flags.unsetStrict();
}
- return this;
+ return self();
}
public Builder setSynchronized(boolean value) {
@@ -292,7 +293,23 @@
} else {
flags.unsetSynchronized();
}
- return this;
+ return self();
+ }
+
+ public Builder setAbstract(boolean value) {
+ if (value) {
+ flags.setAbstract();
+ } else {
+ flags.unsetAbstract();
+ }
+ return self();
+ }
+
+ public Builder applyIf(boolean condition, Consumer<Builder> consumer) {
+ if (condition) {
+ consumer.accept(self());
+ }
+ return self();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index c99ee11..2cef6fe 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -47,6 +47,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -1685,6 +1686,18 @@
return self();
}
+ public B apply(Consumer<B> consumer) {
+ consumer.accept(self());
+ return self();
+ }
+
+ public B applyIf(boolean condition, Consumer<B> consumer) {
+ if (condition) {
+ consumer.accept(self());
+ }
+ return self();
+ }
+
public B setPosition(Instruction other) {
return setPosition(other.getPosition());
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index ae47eb6..0fd2dc9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -140,4 +140,28 @@
void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
registry.registerInvokeSuper(getInvokedMethod());
}
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder extends InvokeMethod.Builder<InvokeSuper.Builder, InvokeSuper> {
+
+ private boolean isInterface;
+
+ @Override
+ public InvokeSuper build() {
+ return amend(new InvokeSuper(method, outValue, arguments, isInterface));
+ }
+
+ public Builder setInterface(boolean isInterface) {
+ this.isInterface = isInterface;
+ return self();
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 4a29cb6..cb45923 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -114,6 +114,7 @@
import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.Ushr;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueFactory;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.ValueTypeConstraint;
import com.android.tools.r8.ir.code.Xor;
@@ -148,11 +149,11 @@
/**
* Builder object for constructing high-level IR from dex bytecode.
*
- * <p>The generated IR is in SSA form. The SSA construction is based on the paper
- * "Simple and Efficient Construction of Static Single Assignment Form" available at
+ * <p>The generated IR is in SSA form. The SSA construction is based on the paper "Simple and
+ * Efficient Construction of Static Single Assignment Form" available at
* http://compilers.cs.uni-saarland.de/papers/bbhlmz13cc.pdf
*/
-public class IRBuilder {
+public class IRBuilder implements ValueFactory {
public static final int INITIAL_BLOCK_OFFSET = -1;
@@ -181,6 +182,11 @@
}
}
+ @Override
+ public Value createValue(TypeElement type, DebugLocalInfo localInfo) {
+ return new Value(valueNumberGenerator.next(), type, localInfo);
+ }
+
// SSA construction uses a worklist of basic blocks reachable from the entry and their
// instruction offsets.
private static class WorklistItem {
@@ -1830,7 +1836,7 @@
addReturn(new Return());
}
- private void addReturn(Return ret) {
+ public void addReturn(Return ret) {
// Attach the live locals to the return instruction to avoid a local change on monitor exit.
attachLocalValues(ret);
source.buildPostlude(this);
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 f85f8d5..73c4b37 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
@@ -1566,7 +1566,7 @@
}
Code code = method.getDefinition().getCode();
assert !code.isThrowNullCode();
- return code.isDefaultInstanceInitializerCode();
+ return code.passThroughDesugarAndIRConversion();
}
// Compute optimization info summary for the current method unless it is pinned
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index 0da73cf..4c47989 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -195,7 +195,9 @@
instructions);
code.asCfCode().setInstructions(newInstructions);
} else {
- assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
+ assert code.isDefaultInstanceInitializerCode()
+ || code.isThrowNullCode()
+ || code.isMemberRebindingBridgeCode();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 2ac83b2..79bbfe6 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.AndroidApiLevelUtils.isApiSafeForMemberRebinding;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndField;
@@ -18,6 +19,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.LibraryMethod;
+import com.android.tools.r8.graph.MemberRebindingBridgeCode;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
@@ -53,6 +56,7 @@
private final InternalOptions options;
private final MemberRebindingLens.Builder lensBuilder;
+ private final Map<DexMethod, DexClassAndMethod> insertedLibraryBridges = new IdentityHashMap<>();
public MemberRebindingAnalysis(AppView<AppInfoWithLiveness> appView) {
assert appView.graphLens().isContextFreeForMethods();
@@ -74,69 +78,66 @@
return original;
}
- if (invokeType.isSuper() && options.canHaveSuperInvokeBug()) {
- // To preserve semantics we should find the first library method on the boundary.
- DexType firstLibraryTarget =
- firstLibraryClassOrFirstInterfaceTarget(
- resolutionResult.getResolvedHolder(),
- appView,
- resolvedMethod.getReference(),
- original.getHolderType(),
- DexClass::lookupMethod);
- if (firstLibraryTarget == null) {
- return original;
+ if (!invokeType.isSuper() || !options.canHaveSuperInvokeBug()) {
+ LibraryMethod eligibleLibraryMethod = null;
+ SingleResolutionResult<?> currentResolutionResult = resolutionResult;
+ while (currentResolutionResult != null) {
+ DexClassAndMethod currentResolvedMethod = currentResolutionResult.getResolutionPair();
+ if (canRebindDirectlyToLibraryMethod(
+ currentResolvedMethod,
+ currentResolutionResult.withInitialResolutionHolder(
+ currentResolutionResult.getResolvedHolder()),
+ contexts,
+ invokeType,
+ original)) {
+ eligibleLibraryMethod = currentResolvedMethod.asLibraryMethod();
+ }
+ if (appView.getAssumeInfoCollection().contains(currentResolvedMethod)) {
+ break;
+ }
+ DexClass currentResolvedHolder = currentResolvedMethod.getHolder();
+ if (resolvedMethod.getDefinition().belongsToVirtualPool()
+ && !currentResolvedHolder.isInterface()
+ && currentResolvedHolder.getSuperType() != null) {
+ currentResolutionResult =
+ appView
+ .appInfo()
+ .resolveMethodOnClassLegacy(currentResolvedHolder.getSuperType(), original)
+ .asSingleResolution();
+ } else {
+ break;
+ }
}
- DexClass libraryHolder = appView.definitionFor(firstLibraryTarget);
- if (libraryHolder == null) {
- return original;
+ if (eligibleLibraryMethod != null) {
+ return eligibleLibraryMethod.getReference();
}
- if (libraryHolder == resolvedMethod.getHolder()) {
- return resolvedMethod.getReference();
- }
- return resolvedMethod.getReference().withHolder(libraryHolder, appView.dexItemFactory());
}
- LibraryMethod eligibleLibraryMethod = null;
- SingleResolutionResult<?> currentResolutionResult = resolutionResult;
- while (currentResolutionResult != null) {
- DexClassAndMethod currentResolvedMethod = currentResolutionResult.getResolutionPair();
- if (canRebindDirectlyToLibraryMethod(
- currentResolvedMethod,
- currentResolutionResult.withInitialResolutionHolder(
- currentResolutionResult.getResolvedHolder()),
- contexts,
- invokeType,
- original)) {
- eligibleLibraryMethod = currentResolvedMethod.asLibraryMethod();
- }
- if (appView.getAssumeInfoCollection().contains(currentResolvedMethod)) {
- break;
- }
- DexClass currentResolvedHolder = currentResolvedMethod.getHolder();
- if (resolvedMethod.getDefinition().belongsToVirtualPool()
- && !currentResolvedHolder.isInterface()
- && currentResolvedHolder.getSuperType() != null) {
- currentResolutionResult =
- appView
- .appInfo()
- .resolveMethodOnClassLegacy(currentResolvedHolder.getSuperType(), original)
- .asSingleResolution();
- } else {
- break;
- }
- }
- if (eligibleLibraryMethod != null) {
- return eligibleLibraryMethod.getReference();
+ if (resolvedMethod.getDefinition().isStatic()) {
+ return original;
}
- DexType newHolder =
- firstLibraryClassOrFirstInterfaceTarget(
- resolvedMethod.getHolder(),
- appView,
- resolvedMethod.getReference(),
- original.getHolderType(),
- DexClass::lookupMethod);
- return newHolder != null ? original.withHolder(newHolder, appView.dexItemFactory()) : original;
+ // If we could not find a valid library method to rebind to then create a bridge on the top most
+ // program class before crossing into library.
+ DexClass newHolder =
+ resolvedMethod.getHolder().isInterface()
+ ? firstProgramClassForTarget(
+ appView, resolvedMethod.getReference(), original.getHolderType())
+ : firstProgramClass(appView, original.getHolderType());
+ if (newHolder == null || newHolder.isNotProgramClass()) {
+ return original;
+ }
+ // We cannot insert default methods on interfaces after desugaring, so we return the resolved
+ // method.
+ if (newHolder.isInterface() && !options.canUseDefaultAndStaticInterfaceMethods()) {
+ return resolvedMethod.getReference();
+ }
+ if (!appView.getAssumeInfoCollection().get(resolvedMethod).isEmpty()) {
+ return original;
+ }
+ DexMethod bridge = original.withHolder(newHolder, appView.dexItemFactory());
+ insertedLibraryBridges.put(bridge, resolvedMethod);
+ return bridge;
}
private boolean canRebindDirectlyToLibraryMethod(
@@ -146,8 +147,8 @@
Type invokeType,
DexMethod original) {
// TODO(b/194422791): It could potentially be that `original.holder` is not a subtype of
- // `original.holder` on all API levels, in which case it is not OK to rebind to the resolved
- // method.
+ // `resolvedMethod.holder` on all API levels, in which case it is not OK to rebind to the
+ // resolved method.
return resolvedMethod.isLibraryMethod()
&& isAccessibleInAllContexts(resolvedMethod, resolutionResult, contexts)
&& !isInvokeSuperToInterfaceMethod(resolvedMethod, invokeType)
@@ -238,6 +239,34 @@
return null;
}
+ private static DexClass firstProgramClassForTarget(
+ DexDefinitionSupplier definitions, DexMethod target, DexType current) {
+ DexClass clazz = definitions.contextIndependentDefinitionFor(current);
+ if (clazz == null) {
+ return null;
+ }
+ DexEncodedMethod potential = clazz.lookupMethod(target);
+ if (potential != null) {
+ // Found, return type.
+ return clazz;
+ }
+ if (clazz.superType != null) {
+ DexClass matchingSuper = firstProgramClassForTarget(definitions, target, clazz.superType);
+ if (matchingSuper != null) {
+ // Found in supertype, return first program class.
+ return matchingSuper.isNotProgramClass() ? clazz : matchingSuper;
+ }
+ }
+ for (DexType iface : clazz.getInterfaces()) {
+ DexClass matchingIface = firstProgramClassForTarget(definitions, target, iface);
+ if (matchingIface != null) {
+ // Found in interface, return first program class.
+ return matchingIface.isNotProgramClass() ? clazz : matchingIface;
+ }
+ }
+ return null;
+ }
+
private static DexType firstLibraryClass(DexDefinitionSupplier definitions, DexType bottom) {
DexClass searchClass = definitions.contextIndependentDefinitionFor(bottom);
while (searchClass != null && searchClass.isProgramClass()) {
@@ -247,6 +276,21 @@
return searchClass != null ? searchClass.getType() : null;
}
+ private static DexProgramClass firstProgramClass(
+ DexDefinitionSupplier definitions, DexType bottom) {
+ DexProgramClass searchClass =
+ DexProgramClass.asProgramClassOrNull(definitions.contextIndependentDefinitionFor(bottom));
+ while (searchClass != null && searchClass.isProgramClass()) {
+ DexClass superClass =
+ definitions.definitionFor(searchClass.getSuperType(), searchClass.asProgramClass());
+ if (superClass.isNotProgramClass()) {
+ return searchClass;
+ }
+ searchClass = superClass.asProgramClass();
+ }
+ return null;
+ }
+
private MethodResolutionResult resolveMethodOnClass(DexMethod method) {
return appView.appInfo().resolveMethodOnClassLegacy(method.holder, method);
}
@@ -371,7 +415,7 @@
builder -> {
if (!targetDefinition.isAbstract()
&& targetDefinition.getApiLevelForCode().isNotSetApiLevel()) {
- assert target.isLibraryMethod();
+ assert !target.isProgramMethod();
builder.setApiLevelForCode(
appView
.apiLevelCompute()
@@ -380,7 +424,8 @@
appView.computedMinApiLevel()));
}
builder.setIsLibraryMethodOverrideIf(
- target.isLibraryMethod(), OptionalBool.TRUE);
+ !target.isProgramMethod() && !targetDefinition.isStatic(),
+ OptionalBool.TRUE);
});
bridgeHolder.addMethod(bridgeMethodDefinition);
}
@@ -512,9 +557,39 @@
computeMethodRebinding(appInfo.getMethodAccessInfoCollection());
recordNonReboundFieldAccesses(executorService);
appInfo.getFieldAccessInfoCollection().flattenAccessContexts();
+ insertLibraryBridges();
return lensBuilder.build();
}
+ private void insertLibraryBridges() {
+ insertedLibraryBridges.forEach(
+ (method, resolvedMethod) -> {
+ assert !resolvedMethod.isProgramMethod();
+ DexProgramClass holder =
+ DexProgramClass.asProgramClassOrNull(appView.definitionFor(method.getHolderType()));
+ assert holder != null;
+ assert !holder.isInterface() || options.canUseDefaultAndStaticInterfaceMethods();
+ DexEncodedMethod resolvedDefinition = resolvedMethod.getDefinition();
+ holder.addMethod(
+ DexEncodedMethod.syntheticBuilder()
+ .setMethod(method)
+ .setApiLevelForDefinition(resolvedDefinition.getApiLevelForDefinition())
+ // Since we could not member rebind to this definition it is at least higher than
+ // min-api.
+ .setApiLevelForCode(ComputedApiLevel.unknown())
+ .setCode(
+ MemberRebindingBridgeCode.builder()
+ .setTarget(resolvedMethod.getDefinition().getReference())
+ .setInterface(resolvedMethod.getHolder().isInterface())
+ .build())
+ .setAccessFlags(MethodAccessFlags.builder().setPublic().setSynthetic().build())
+ .setClassFileVersion(resolvedDefinition.getClassFileVersion())
+ .setDeprecated(resolvedDefinition.deprecated)
+ .setIsLibraryMethodOverride(OptionalBool.TRUE)
+ .build());
+ });
+ }
+
private boolean verifyFieldAccessCollectionContainsAllNonReboundFieldReferences(
ExecutorService executorService) throws ExecutionException {
Set<DexField> nonReboundFieldReferences = computeNonReboundFieldReferences(executorService);
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
index 5cbb1b6..f3fa401 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.utils.ThreadUtils.processItems;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -19,7 +20,6 @@
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.Map;
@@ -29,20 +29,23 @@
public class RedundantBridgeRemover {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
- public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
+ public RedundantBridgeRemover(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
+ assert appView.hasLiveness();
DexEncodedMethod definition = method.getDefinition();
BridgeInfo bridgeInfo = definition.getOptimizationInfo().getBridgeInfo();
boolean isBridge = definition.isBridge() || bridgeInfo != null;
+ // TODO(b/258176116): We can only remove bridges if they are marked as bridge or abstract.
if (!isBridge || definition.isAbstract()) {
return null;
}
- InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor(appView, method);
+ InvokeSingleTargetExtractor targetExtractor =
+ new InvokeSingleTargetExtractor(appView.withLiveness(), method);
method.registerCodeReferences(targetExtractor);
DexMethod target = targetExtractor.getTarget();
// javac-generated visibility forward bridge method has same descriptor (name, signature and
@@ -134,12 +137,27 @@
RedundantBridgeRemovalLens.Builder lensBuilder, ExecutorService executorService)
throws ExecutionException {
Map<DexProgramClass, ProgramMethodSet> bridgesToRemove = new ConcurrentHashMap<>();
+ boolean hasLiveness = appView.hasLiveness();
+ // If we don't have AppInfoWithLiveness here, it must be because we are not shrinking. When we
+ // are not shrinking, we can't remove visibility bridges. In principle, though, it would be
+ // possible to remove visibility bridges that have been synthesized by R8, but we currently do
+ // not have this information.
+ assert hasLiveness || !appView.options().isShrinking();
processItems(
appView.appInfo().classes(),
clazz -> {
ProgramMethodSet bridgesToRemoveForClass = ProgramMethodSet.create();
clazz.forEachProgramMethod(
method -> {
+ DexEncodedMethod definition = method.getDefinition();
+ if (definition.getCode() != null
+ && definition.getCode().isMemberRebindingBridgeCode()) {
+ bridgesToRemoveForClass.add(method);
+ return;
+ }
+ if (!hasLiveness) {
+ return;
+ }
KeepMethodInfo keepInfo = appView.getKeepInfo(method);
if (!keepInfo.isShrinkingAllowed(appView.options())
|| !keepInfo.isOptimizationAllowed(appView.options())) {
@@ -161,7 +179,7 @@
// the target is not public, but still accessible to call sites.
boolean isEligibleForRetargeting =
appView.testing().enableRetargetingConstructorBridgeCalls
- || !method.getDefinition().isInstanceInitializer();
+ || !definition.isInstanceInitializer();
if (isEligibleForRetargeting
&& target.getAccessFlags().isPublic()
&& target.getHolder().isPublic()) {
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 b479808..33d8f24 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -52,6 +52,7 @@
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
+import com.android.tools.r8.graph.MemberRebindingBridgeCode;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
@@ -802,6 +803,20 @@
graphLens.getOriginalMethodSignature(implementationMethod);
assert originalMethod == originalImplementationMethod;
assert implementationMethod == renamedMethod;
+ } else if (encodedMethod.hasCode()
+ && encodedMethod.getCode().isMemberRebindingBridgeCode()
+ && renamedMethod != originalMethod) {
+ MemberRebindingBridgeCode memberRebindingBridgeCode =
+ encodedMethod.getCode().asMemberRebindingBridgeCode();
+ DexEncodedMethod resolvedMethod =
+ appView
+ .appInfo()
+ .resolveMethodOn(
+ originalMethod.getHolderType(),
+ originalMethod,
+ memberRebindingBridgeCode.getInterface())
+ .getResolvedMethod();
+ assert resolvedMethod.getReference() == memberRebindingBridgeCode.getTarget();
} else {
assert method == renamedMethod;
}
@@ -857,8 +872,8 @@
.resolveMethodOnInterfaceLegacy(method.getHolderType(), method.getReference())
.lookupVirtualDispatchTargets(target, appInfo)
.asLookupResultSuccess();
- assert lookupResult != null;
if (lookupResult == null) {
+ assert false;
return true;
}
if (lookupResult.contains(method)) {
@@ -1545,19 +1560,29 @@
assert invocationTarget.isStatic()
|| invocationTarget.isNonPrivateVirtualMethod()
|| invocationTarget.isNonStaticPrivateMethod();
- SynthesizedBridgeCode code =
- new SynthesizedBridgeCode(
- newMethod,
- appView.graphLens().getOriginalMethodSignature(method.getReference()),
- invocationTarget.getReference(),
- invocationTarget.isStatic()
- ? STATIC
- : (invocationTarget.isNonPrivateVirtualMethod() ? VIRTUAL : DIRECT),
- target.isInterface());
- // Add the bridge to the list of synthesized bridges such that the method signatures will
- // be updated by the end of vertical class merging.
- synthesizedBridges.add(code);
+ Code code;
+ if (invocationTarget.getCode().isMemberRebindingBridgeCode()) {
+ // Duplicate the code object, it should never be emitted anyway. The bridge is also
+ // inserted for signatures defined in the library and these are not subject to be rewritten
+ // after.
+ code = invocationTarget.getCode();
+ } else {
+ SynthesizedBridgeCode bridgeCode =
+ new SynthesizedBridgeCode(
+ newMethod,
+ appView.graphLens().getOriginalMethodSignature(method.getReference()),
+ invocationTarget.getReference(),
+ invocationTarget.isStatic()
+ ? STATIC
+ : (invocationTarget.isNonPrivateVirtualMethod() ? VIRTUAL : DIRECT),
+ target.isInterface());
+
+ // Add the bridge to the list of synthesized bridges such that the method signatures will
+ // be updated by the end of vertical class merging.
+ synthesizedBridges.add(bridgeCode);
+ code = bridgeCode;
+ }
CfVersion classFileVersion =
method.hasClassFileVersion() ? method.getClassFileVersion() : null;
@@ -1998,6 +2023,10 @@
return AbortReason.UNSAFE_INLINING;
}
+ public Collection<DexType> getRemovedClasses() {
+ return Collections.unmodifiableCollection(mergedClasses.keySet());
+ }
+
public class SingleTypeMapperGraphLens extends NonIdentityGraphLens {
private final DexType source;
@@ -2412,8 +2441,4 @@
};
}
}
-
- public Collection<DexType> getRemovedClasses() {
- return Collections.unmodifiableCollection(mergedClasses.keySet());
- }
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 406d482..33e4a3d 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -197,6 +197,16 @@
return list;
}
+ public static <T> ArrayList<T> newArrayList(T element, List<T> otherOrNull) {
+ int size = otherOrNull == null ? 1 : otherOrNull.size() + 1;
+ ArrayList<T> list = new ArrayList<>(size);
+ list.add(element);
+ if (otherOrNull != null) {
+ list.addAll(otherOrNull);
+ }
+ return list;
+ }
+
public static <T> ArrayList<T> newArrayList(ForEachable<T> forEachable) {
ArrayList<T> list = new ArrayList<>();
forEachable.forEach(list::add);
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java
index 3c2923d..179eab8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java
@@ -138,14 +138,7 @@
if (isGreaterOrEqualToClassMethodMockLevel()) {
runResult.assertSuccessWithOutputLines("LibraryClass::foo");
} else if (isGreaterOrEqualToIfaceMockLevel()) {
- if (isR8) {
- runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
- } else {
- runResult.assertFailureWithErrorThatThrows(AbstractMethodError.class);
- }
- } else if (isR8 && parameters.isCfRuntime()) {
- // TODO(b/254510678): R8 should not rebind to the library method.
- runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+ runResult.assertFailureWithErrorThatThrows(AbstractMethodError.class);
} else {
runResult.assertSuccessWithOutputLines("Hello World");
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java
index f6a0a49..8f122d6 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java
@@ -125,9 +125,6 @@
private void checkOutput(SingleTestRunResult<?> runResult, boolean isR8) {
if (isGreaterOrEqualToMockLevel()) {
runResult.assertSuccessWithOutputLines("LibraryClass::foo");
- } else if (isR8 && parameters.isCfRuntime()) {
- // TODO(b/254510678): R8 should not rebind to the library method.
- runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
} else {
runResult.assertSuccessWithOutputLines("Hello World");
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineSubTypeStaticReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineSubTypeStaticReferenceTest.java
index 19971c3..28a9abb 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineSubTypeStaticReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineSubTypeStaticReferenceTest.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.lang.reflect.Method;
import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -127,16 +126,11 @@
}
private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
- Method otherMethod = Sub.class.getMethod("otherMethod");
- Method libraryMethod = LibraryClass.class.getMethod("foo");
- // TODO(b/254510678): R8 should not member-rebind to a potential non-existing method.
verifyThat(
inspector,
parameters,
- isR8
- ? Reference.methodFromMethod(libraryMethod)
- : Reference.method(
- Reference.classFromClass(Sub.class), "foo", Collections.emptyList(), null))
+ Reference.method(
+ Reference.classFromClass(Sub.class), "foo", Collections.emptyList(), null))
.isOutlinedFromUntil(Sub.class.getDeclaredMethod("otherMethod"), libraryApiLevel);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index d6ede8b..994d9cf 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -152,10 +152,12 @@
String holder =
libraryDesugaringSpecification.hasTimeDesugaring(parameters)
? "j$.time.temporal.TemporalAccessor"
- : "java.time.temporal.TemporalAccessor";
+ : "com.android.tools.r8.desugar.desugaredlibrary.JavaTimeTest$TemporalAccessorImpl";
+ ClassSubject temporalAccessor = inspector.clazz(holder);
+ assertThat(temporalAccessor, isPresent());
assertThat(
inspector.clazz(TemporalAccessorImplSub.class).uniqueMethodWithFinalName("query"),
- CodeMatchers.invokesMethod(null, holder, "query", null));
+ CodeMatchers.invokesMethod(null, temporalAccessor.getFinalName(), "query", null));
} else {
if (!parameters
.getApiLevel()
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingFrontierTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingFrontierTest.java
index 2584946..98bf6f6 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingFrontierTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingFrontierTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.memberrebinding;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithHolderAndName;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
@@ -14,7 +16,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,7 +44,7 @@
.compile()
.apply(this::setupRunclasspath)
.run(parameters.getRuntime(), Main.class)
- .apply(result -> checkOutput(result, false));
+ .apply(this::checkOutput);
}
@Test
@@ -61,15 +62,13 @@
.apply(this::setupRunclasspath)
.inspect(
inspector -> {
- // TODO(b/254510678): We should not rebind to I.foo
ClassSubject mainClass = inspector.clazz(Main.class);
assertThat(mainClass, isPresent());
MethodSubject foo = mainClass.mainMethod();
- assertThat(
- foo, CodeMatchers.invokesMethodWithHolderAndName(typeName(I.class), "foo"));
+ assertThat(foo, not(invokesMethodWithHolderAndName(typeName(I.class), "foo")));
})
.run(parameters.getRuntime(), Main.class)
- .apply(result -> checkOutput(result, true));
+ .apply(this::checkOutput);
}
private byte[] removeFooMethod(Class<?> clazz) throws Exception {
@@ -92,17 +91,12 @@
.addRunClasspathClassFileData(removeFooMethod(I.class)));
}
- private void checkOutput(SingleTestRunResult<?> runResult, boolean r8) {
+ private void checkOutput(SingleTestRunResult<?> runResult) {
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
runResult.assertSuccessWithOutputLines("I::foo");
return;
}
- // TODO(b/254510678): We should not rebind to I.foo
- if (r8) {
- runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
- } else {
- runResult.assertSuccessWithOutputLines("Base::foo");
- }
+ runResult.assertSuccessWithOutputLines("Base::foo");
}
private interface I {
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index 605ddf7..eb9a8dc 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -165,10 +165,13 @@
assertTrue(iterator.next().holder().is("java.util.AbstractList"));
assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClassInTheMiddle"));
assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClassInTheMiddle"));
- // For the next three - test that we re-bind to the lowest library class.
- assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
- assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
- assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+ // For the next three - test that we do not rebind but keep the static holder.
+ assertTrue(
+ iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+ assertTrue(
+ iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+ assertTrue(
+ iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
// The next one is already precise.
assertTrue(
iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
@@ -177,8 +180,10 @@
assertTrue(iterator.next().holder().is("memberrebindinglib.AnIndependentInterface"));
// Some dispatches on classes.
assertTrue(iterator.next().holder().is("java.lang.System"));
- assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
- assertTrue(iterator.next().holder().is("memberrebindinglib.ImplementedInProgramClass"));
+ assertTrue(
+ iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+ assertTrue(
+ iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
assertFalse(iterator.hasNext());
}
diff --git a/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java b/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
index d01d917..8c4d936 100644
--- a/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
@@ -62,7 +61,7 @@
.addKeepMainRule(MAIN)
.addClasspathClasses(LIBRARY)
.setMinApi(parameters.getApiLevel())
- .addKeepRules("-dontwarn")
+ .addDontWarn(MissingLibraryClass.class)
.compile()
.addRunClasspathFiles(runtimeClasspath())
.run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 8108730..b72d822 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1139,6 +1139,25 @@
});
}
+ public ClassFileTransformer removeCode(MethodPredicate predicate) {
+ return addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public void visitCode() {
+ if (!MethodPredicate.testContext(predicate, getContext())) {
+ super.visitCode();
+ }
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ if (!MethodPredicate.testContext(predicate, getContext())) {
+ super.visitInsn(opcode);
+ }
+ }
+ });
+ }
+
public ClassFileTransformer transformInvokeDynamicInsnInMethod(
String methodName, InvokeDynamicInsnTransform transform) {
return addMethodTransformer(