Add pending inline frame on methods that are abstract or have canonicalized code
Bug: b/302509457
Bug: b/325913397
Change-Id: I5233f82ed8ac4cf9c7904ad480434727e60bc338
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 f00b07b..b4913b1 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -245,4 +245,8 @@
DexMethod method, boolean isD8R8Synthesized, Consumer<Position> positionConsumer) {
// Intentionally empty. Override where we have fully build CF or DEX code.
}
+
+ public boolean supportsPendingInlineFrame() {
+ return false;
+ }
}
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 2ce1db6..229e8c0 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -7,7 +7,9 @@
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfReturnVoid;
import com.android.tools.r8.dex.CodeToKeep;
import com.android.tools.r8.dex.IndexedItemCollection;
@@ -33,6 +35,7 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.ir.conversion.SyntheticStraightLineSourceCode;
+import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirEncodingStrategy;
import com.android.tools.r8.lightir.LirStrategy;
@@ -42,7 +45,7 @@
import com.android.tools.r8.utils.structural.HashingVisitor;
import com.google.common.collect.ImmutableList;
import java.nio.ShortBuffer;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
@@ -90,25 +93,20 @@
AppView<?> appView, ProgramMethod method, DexType superType) {
DexEncodedMethod definition = method.getDefinition();
assert definition.getCode().isDefaultInstanceInitializerCode();
+ Position position = method.getDefinition().getAndClearPendingInlineFrameAsPosition();
+ Code newCode;
if (appView.testing().isSupportedLirPhase()) {
- method.setCode(get().toLirCode(appView, method, superType), appView);
+ newCode = get().toLirCode(appView, method, superType, position);
} else {
assert appView.testing().isPreLirPhase();
- method.setCode(get().toCfCode(method, appView.dexItemFactory(), superType), appView);
+ newCode = get().toCfCode(method, appView.dexItemFactory(), superType, position);
}
+ method.setCode(newCode, appView);
}
@Override
- public Code getCodeAsInlining(
- DexMethod caller,
- boolean isCallerD8R8Synthesized,
- DexMethod callee,
- boolean isCalleeD8R8Synthesized,
- DexItemFactory factory) {
- // TODO(b/261971803): It is odd this code does not have an original position for the <init>.
- // See OverrideParentCollisionTest for a case hitting this inlining where callee is a
- // non-synthetic non-default <init> (it has argument String).
- return this;
+ public boolean supportsPendingInlineFrame() {
+ return true;
}
private static boolean hasDefaultInstanceInitializerCode(
@@ -168,7 +166,9 @@
MutableMethodConversionOptions conversionOptions) {
DefaultInstanceInitializerSourceCode source =
new DefaultInstanceInitializerSourceCode(
- method.getReference(), method.getDefinition().isD8R8Synthesized());
+ method.getReference(),
+ method.getDefinition().isD8R8Synthesized(),
+ method.getDefinition().getPendingInlineFrameAsPosition());
return IRBuilder.create(method, appView, source).build(method, conversionOptions);
}
@@ -183,7 +183,11 @@
RewrittenPrototypeDescription protoChanges) {
DefaultInstanceInitializerSourceCode source =
new DefaultInstanceInitializerSourceCode(
- method.getReference(), method.getDefinition().isD8R8Synthesized(), callerPosition);
+ method.getReference(),
+ method.getDefinition().isD8R8Synthesized(),
+ method
+ .getDefinition()
+ .getPendingInlineFrameAsPositionWithCallerPosition(callerPosition));
return IRBuilder.createForInlining(
method, appView, codeLens, source, valueNumberGenerator, protoChanges)
.build(context, MethodConversionOptions.nonConverting());
@@ -364,21 +368,24 @@
// Intentionally empty. This piece of code does not have any call sites.
}
- public CfCode toCfCode(ProgramMethod method, DexItemFactory dexItemFactory) {
- return toCfCode(method, dexItemFactory, method.getHolder().getSuperType());
- }
-
- public CfCode toCfCode(ProgramMethod method, DexItemFactory dexItemFactory, DexType supertype) {
- List<CfInstruction> instructions =
- Arrays.asList(
- new CfLoad(ValueType.OBJECT, 0),
- new CfInvoke(
- Opcodes.INVOKESPECIAL, dexItemFactory.createInstanceInitializer(supertype), false),
- new CfReturnVoid());
+ public CfCode toCfCode(
+ ProgramMethod method, DexItemFactory dexItemFactory, DexType supertype, Position position) {
+ List<CfInstruction> instructions = new ArrayList<>(position != null ? 5 : 3);
+ if (position != null) {
+ CfLabel entryLabel = new CfLabel();
+ instructions.add(entryLabel);
+ instructions.add(new CfPosition(entryLabel, position));
+ }
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ instructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESPECIAL, dexItemFactory.createInstanceInitializer(supertype), false));
+ instructions.add(new CfReturnVoid());
return new CfCode(method.getHolderType(), getMaxStack(), getMaxLocals(method), instructions);
}
- public LirCode<?> toLirCode(AppView<?> appView, ProgramMethod method, DexType supertype) {
+ public LirCode<?> toLirCode(
+ AppView<?> appView, ProgramMethod method, DexType supertype, Position position) {
TypeElement receiverType =
method.getHolder().getType().toTypeElement(appView, Nullability.definitelyNotNull());
Value receiver = new Value(0, receiverType, null);
@@ -386,11 +393,16 @@
LirEncodingStrategy<Value, Integer> strategy =
LirStrategy.getDefaultStrategy().getEncodingStrategy();
strategy.defineValue(receiver, 0);
- return LirCode.builder(
+ LirBuilder<Value, Integer> builder =
+ LirCode.builder(
method.getReference(),
method.getDefinition().isD8R8Synthesized(),
strategy,
- appView.options())
+ appView.options());
+ if (position != null) {
+ builder.setCurrentPosition(position);
+ }
+ return builder
.addArgument(0, false)
.addInvokeDirect(invokedMethod, ImmutableList.of(receiver), false)
.addReturnVoid()
@@ -457,10 +469,6 @@
static class DefaultInstanceInitializerSourceCode extends SyntheticStraightLineSourceCode {
- DefaultInstanceInitializerSourceCode(DexMethod method, boolean isD8R8Synthesized) {
- this(method, isD8R8Synthesized, null);
- }
-
DefaultInstanceInitializerSourceCode(
DexMethod method, boolean isD8R8Synthesized, Position callerPosition) {
super(getInstructionBuilders(), getPosition(method, isD8R8Synthesized, callerPosition));
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index e71bb97..7c95315 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -38,6 +38,7 @@
import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -57,8 +58,10 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import com.android.tools.r8.utils.structural.CompareToVisitor;
@@ -129,6 +132,7 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
null,
+ null,
false,
ComputedApiLevel.notSet(),
ComputedApiLevel.notSet(),
@@ -142,6 +146,7 @@
public final boolean deprecated;
public ParameterAnnotationsList parameterAnnotationsList;
private Code code;
+ private DexMethod pendingInlineFrame;
// TODO(b/128967328): towards finer-grained inlining constraints,
// we need to maintain a set of states with (potentially different) contexts.
private CompilationState compilationState = CompilationState.NOT_PROCESSED;
@@ -205,20 +210,6 @@
return compilationState;
}
- /**
- * Flags this method as no longer being obsolete.
- *
- * <p>Example use case: The vertical class merger optimistically merges two classes before it is
- * guaranteed that the two classes can be merged. In this process, methods are moved from the
- * source class to the target class using {@link #toTypeSubstitutedMethodAsInlining(DexMethod,
- * DexItemFactory)}, which causes the original methods of the source class to become obsolete. If
- * vertical class merging is aborted, the original methods of the source class needs to be marked
- * as not being obsolete.
- */
- public void unsetObsolete() {
- obsolete = false;
- }
-
private DexEncodedMethod(
DexMethod method,
MethodAccessFlags accessFlags,
@@ -226,6 +217,7 @@
DexAnnotationSet annotations,
ParameterAnnotationsList parameterAnnotationsList,
Code code,
+ DexMethod pendingInlineFrame,
boolean d8R8Synthesized,
ComputedApiLevel apiLevelForDefinition,
ComputedApiLevel apiLevelForCode,
@@ -238,11 +230,13 @@
this.genericSignature = genericSignature;
this.parameterAnnotationsList = parameterAnnotationsList;
this.code = code;
+ this.pendingInlineFrame = pendingInlineFrame;
this.classFileVersion = classFileVersion;
this.apiLevelForCode = apiLevelForCode;
this.optimizationInfo = requireNonNull(optimizationInfo);
assert accessFlags != null;
assert code == null || !shouldNotHaveCode();
+ assert supportsPendingInlineFrame() || !hasPendingInlineFrame();
assert parameterAnnotationsList != null;
assert apiLevelForDefinition != null;
assert apiLevelForCode != null;
@@ -275,6 +269,7 @@
.withItem(m -> m.parameterAnnotationsList)
.withNullableItem(m -> m.classFileVersion)
.withBool(DexEncodedMember::isD8R8Synthesized)
+ .withNullableItem(m -> m.pendingInlineFrame)
// TODO(b/171867022): Make signatures structural and include it in the definition.
.withAssert(m -> m.genericSignature.hasNoSignature())
.withCustomItem(
@@ -747,10 +742,38 @@
public void setCode(Code code, Int2ReferenceMap<DebugLocalInfo> parameterInfo) {
checkIfObsolete();
+ if (code != null && !code.supportsPendingInlineFrame() && hasPendingInlineFrame()) {
+ // If the method is set abstract, or the code supports a pending inline frame, then it simply
+ // remains in place. Note that if we start supporting pending frames on instruction-code
+ // objects this implicit keeping of the pending frame would not be correct.
+ assert verifyPendingInlinePositionFoundInCode(code);
+ pendingInlineFrame = null;
+ }
this.code = code;
this.parameterInfo = parameterInfo;
}
+ private boolean verifyPendingInlinePositionFoundInCode(Code code) {
+ BooleanBox foundInlineFrame = new BooleanBox(false);
+ code.forEachPosition(
+ getReference(),
+ isDexEncodedMethod(),
+ position -> {
+ if (foundInlineFrame.isTrue()) {
+ return;
+ }
+ do {
+ if (!position.isD8R8Synthesized() && position.getMethod().equals(pendingInlineFrame)) {
+ foundInlineFrame.set(true);
+ break;
+ }
+ position = position.getCallerPosition();
+ } while (position != null);
+ });
+ assert foundInlineFrame.isTrue();
+ return true;
+ }
+
public void unsetCode() {
checkIfObsolete();
code = null;
@@ -787,6 +810,50 @@
return accessFlags.isAbstract() || accessFlags.isNative();
}
+ public boolean supportsPendingInlineFrame() {
+ return code == null || code.supportsPendingInlineFrame();
+ }
+
+ public boolean hasPendingInlineFrame() {
+ return pendingInlineFrame != null;
+ }
+
+ public DexMethod getPendingInlineFrame() {
+ return pendingInlineFrame;
+ }
+
+ public Position getAndClearPendingInlineFrameAsPosition() {
+ Position position = getPendingInlineFrameAsPosition();
+ pendingInlineFrame = null;
+ return position;
+ }
+
+ public Position getPendingInlineFrameAsPosition() {
+ if (!hasPendingInlineFrame()) {
+ return null;
+ }
+ // This method is the caller method and must be synthetic.
+ assert isD8R8Synthesized();
+ return getPendingInlineFrameAsPositionWithCallerPosition(
+ Position.SyntheticPosition.builder()
+ .setMethod(getReference())
+ .setIsD8R8Synthesized(isD8R8Synthesized())
+ .setLine(0)
+ .build());
+ }
+
+ public Position getPendingInlineFrameAsPositionWithCallerPosition(Position callerPosition) {
+ assert callerPosition != null;
+ if (!hasPendingInlineFrame()) {
+ return callerPosition;
+ }
+ return Position.SyntheticPosition.builder()
+ .setLine(0)
+ .setMethod(getPendingInlineFrame())
+ .setCallerPosition(callerPosition)
+ .build();
+ }
+
public boolean hasCode() {
return code != null;
}
@@ -1102,7 +1169,10 @@
method,
isCallerD8R8Synthesized,
builder -> {
- if (code != null) {
+ if (code == null || code.supportsPendingInlineFrame()) {
+ builder.setInlineFrame(getReference(), isD8R8Synthesized());
+ } else {
+ // TODO(b/302509457): Use prepend inline frame here too and delay code rewriting.
builder.setCode(
getCode()
.getCodeAsInlining(
@@ -1113,7 +1183,9 @@
factory));
}
if (consumer != null) {
+ Code oldCode = builder.code;
consumer.accept(builder);
+ assert ObjectUtils.identical(oldCode, builder.code);
}
});
}
@@ -1389,6 +1461,7 @@
private ComputedApiLevel apiLevelForCode = ComputedApiLevel.notSet();
private final boolean d8R8Synthesized;
private boolean deprecated = false;
+ private DexMethod pendingInlineFrame = null;
// Checks to impose on the built method. They should always be active to start with and be
// lowered on the use site.
@@ -1409,6 +1482,7 @@
genericSignature = from.getGenericSignature();
annotations = from.annotations();
code = from.getCode();
+ pendingInlineFrame = from.getPendingInlineFrame();
apiLevelForDefinition = from.getApiLevelForDefinition();
apiLevelForCode = from.getApiLevelForCode();
optimizationInfo =
@@ -1608,6 +1682,18 @@
});
}
+ public Builder setInlineFrame(DexMethod callee, boolean calleeIsD8R8Synthesized) {
+ assert code == null || code.supportsPendingInlineFrame();
+ if (calleeIsD8R8Synthesized) {
+ // No need to record a compiler synthesized inline frame.
+ return this;
+ }
+ // Two pending non-synthetic callee frames should never happen.
+ assert pendingInlineFrame == null;
+ pendingInlineFrame = callee;
+ return this;
+ }
+
public Builder setCode(Code code) {
this.code = code;
return this;
@@ -1672,6 +1758,7 @@
|| parameterAnnotations.size() == method.proto.parameters.size();
assert !checkAndroidApiLevels || apiLevelForDefinition != null;
assert !checkAndroidApiLevels || apiLevelForCode != null;
+ assert code == null || code.supportsPendingInlineFrame() || pendingInlineFrame == null;
DexEncodedMethod result =
new DexEncodedMethod(
method,
@@ -1680,6 +1767,7 @@
annotations,
parameterAnnotations,
code,
+ pendingInlineFrame,
d8R8Synthesized,
apiLevelForDefinition,
apiLevelForCode,
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
index 903796b..45bdf68 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
@@ -49,6 +49,11 @@
}
@Override
+ public boolean supportsPendingInlineFrame() {
+ return true;
+ }
+
+ @Override
public IRCode buildIR(
ProgramMethod method,
AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
index af96367..8bdd8b6 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -54,15 +54,8 @@
}
@Override
- public Code getCodeAsInlining(
- DexMethod caller,
- boolean isCallerD8R8Synthesized,
- DexMethod callee,
- boolean isCalleeD8R8Synthesized,
- DexItemFactory factory) {
- // We don't maintain a position on the throwing stub. We may want to reconsider this as it
- // would allow retracing to recover inlinings of this stub.
- return this;
+ public boolean supportsPendingInlineFrame() {
+ return true;
}
@Override
@@ -70,7 +63,8 @@
ProgramMethod method,
AppView<?> appView,
MutableMethodConversionOptions conversionOptions) {
- ThrowNullSourceCode source = new ThrowNullSourceCode(method);
+ ThrowNullSourceCode source =
+ new ThrowNullSourceCode(method, method.getDefinition().getPendingInlineFrameAsPosition());
return IRBuilder.create(method, appView, source).build(method, conversionOptions);
}
@@ -83,7 +77,12 @@
NumberGenerator valueNumberGenerator,
Position callerPosition,
RewrittenPrototypeDescription protoChanges) {
- ThrowNullSourceCode source = new ThrowNullSourceCode(method, callerPosition);
+ ThrowNullSourceCode source =
+ new ThrowNullSourceCode(
+ method,
+ method
+ .getDefinition()
+ .getPendingInlineFrameAsPositionWithCallerPosition(callerPosition));
return IRBuilder.createForInlining(
method, appView, codeLens, source, valueNumberGenerator, protoChanges)
.build(context, MethodConversionOptions.nonConverting());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index e14f91d..ebf89d8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -951,25 +951,27 @@
LocalEnumUnboxingUtilityClass localUtilityClass,
Predicate<DexMethod> availableMethodSignatures) {
assert method.getAccessFlags().isAbstract();
- DexMethod newMethod =
- createFreshMethodSignature(
- method, localUtilityClass, availableMethodSignatures, method.getReference());
+ // Replace the code by a throwing stub and then rewrite the method as "inlining".
+ Position position = method.getDefinition().getAndClearPendingInlineFrameAsPosition();
+ method.setCode(
+ new ThrowCfCodeProvider(
+ appView, localUtilityClass.getType(), factory.abstractMethodErrorType, position)
+ .generateCfCode(),
+ appView);
DexEncodedMethod dexEncodedMethod =
method
.getDefinition()
.toTypeSubstitutedMethodAsInlining(
- newMethod,
+ createFreshMethodSignature(
+ method, localUtilityClass, availableMethodSignatures, method.getReference()),
factory,
builder ->
transformMethodForLocalUtility(builder, method)
.modifyAccessFlags(MethodAccessFlags::unsetAbstract)
- .setCode(
- new ThrowCfCodeProvider(
- appView, newMethod, factory.abstractMethodErrorType)
- .generateCfCode())
.setClassFileVersion(CfVersion.V1_8)
.setApiLevelForDefinition(appView.computedMinApiLevel())
.setApiLevelForCode(appView.computedMinApiLevel()));
+
methodsToProcess.add(new ProgramMethod(localUtilityClass.getDefinition(), dexEncodedMethod));
return dexEncodedMethod;
}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ThrowCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/ThrowCfCodeProvider.java
index ff9609d..d6f9242 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ThrowCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ThrowCfCodeProvider.java
@@ -7,7 +7,9 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
import com.android.tools.r8.cf.code.CfThrow;
@@ -15,25 +17,34 @@
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Position;
import java.util.ArrayList;
import java.util.List;
/**
- * Generates a method that just throws a exception with empty <init></init> with *any* signature
- * passed, so the method can be inserted in a hierarchy and be called with normal virtual dispatch.
+ * Generates a method that just throws an exception with empty <init> with *any* signature passed,
+ * so the method can be inserted in a hierarchy and be called with normal virtual dispatch.
*/
public class ThrowCfCodeProvider extends SyntheticCfCodeProvider {
private final DexType exceptionType;
+ private final Position position;
- public ThrowCfCodeProvider(AppView<?> appView, DexMethod method, DexType exceptionType) {
- super(appView, method.getHolderType());
+ public ThrowCfCodeProvider(
+ AppView<?> appView, DexType holder, DexType exceptionType, Position position) {
+ super(appView, holder);
this.exceptionType = exceptionType;
+ this.position = position;
}
@Override
public CfCode generateCfCode() {
List<CfInstruction> instructions = new ArrayList<>();
+ if (position != null) {
+ CfLabel entryLabel = new CfLabel();
+ instructions.add(entryLabel);
+ instructions.add(new CfPosition(entryLabel, position));
+ }
instructions.add(new CfNew(exceptionType));
instructions.add(new CfStackInstruction(Opcode.Dup));
DexMethod init = appView.dexItemFactory().createInstanceInitializer(exceptionType);
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 80e22b5..3da079c 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -225,31 +225,58 @@
PositionRemapper positionRemapper,
boolean canUseDexPc) {
DexEncodedMethod definition = method.getDefinition();
-
- OneShotCollectionConsumer<MappingInformation> methodSpecificMappingInformation =
- OneShotCollectionConsumer.wrap(new ArrayList<>());
- // We only do global synthetic classes when using names from the library. For such classes it
- // is important that we do not filter out stack frames since they could appear from concrete
- // classes in the library. Additionally, this is one place where it is helpful for developers
- // to also get reported synthesized frames since stubbing can change control-flow and
- // exceptions.
- boolean canStripOuterFrame = canStripOuterFrame(method, mappedPositions);
- boolean residualIsD8R8Synthesized =
- method.getDefinition().isD8R8Synthesized()
- && !appView.getSyntheticItems().isGlobalSyntheticClass(method.getHolder())
- // TODO(b/302509457): Currently we can only represent moves on methods that have code
- // and thus positions. For methods with no code, use the lens to find the original.
- && method.getDefinition().hasCode();
- if (residualIsD8R8Synthesized && !canStripOuterFrame) {
- methodSpecificMappingInformation.add(CompilerSynthesizedMappingInformation.getInstance());
- }
-
DexMethod residualMethod =
appView.getNamingLens().lookupMethod(method.getReference(), appView.dexItemFactory());
MethodSignature residualSignature = MethodSignature.fromDexMethod(residualMethod);
- DexMethod originalMethod =
+ // TODO(b/261971803): This original method via lens is just to assert compatibility with the
+ // previous implementation. Remove this as part of cleaning-up / reducing lens usage.
+ DexMethod lensOriginalMethod =
appView.graphLens().getOriginalMethodSignatureForMapping(method.getReference());
+
+ DexMethod originalMethod;
+ boolean canStripOuterFrame;
+ boolean residualIsD8R8Synthesized;
+ // TODO(b/325913397): Use residual methods. This requires some test updates to not use names.
+ boolean useResidualForSynthetics = false;
+ if (!definition.supportsPendingInlineFrame()) {
+ residualIsD8R8Synthesized = markAsCompilerSynthetic(method);
+ canStripOuterFrame = canStripOuterFrame(method, mappedPositions);
+ // TODO(b/261971803): Amend code generation of global synthetics to include original
+ // position as an inline frame and remove this special handling of globals.
+ originalMethod =
+ isGlobalSyntheticMethod(method)
+ ? lensOriginalMethod
+ : (useResidualForSynthetics && residualIsD8R8Synthesized
+ ? residualMethod
+ : method.getReference());
+ } else {
+ assert mappedPositions.isEmpty();
+ canStripOuterFrame = false;
+ if (definition.hasPendingInlineFrame()) {
+ originalMethod = definition.getPendingInlineFrame();
+ residualIsD8R8Synthesized = false;
+ } else {
+ residualIsD8R8Synthesized = markAsCompilerSynthetic(method);
+ // TODO(b/261971803): Amend code generation of global synthetics to include original
+ // position as an inline frame and remove this special handling of globals.
+ originalMethod =
+ isGlobalSyntheticMethod(method)
+ ? lensOriginalMethod
+ : (useResidualForSynthetics && residualIsD8R8Synthesized
+ ? residualMethod
+ : definition.getReference());
+ }
+ }
+ assert residualIsD8R8Synthesized || originalMethod.isIdenticalTo(lensOriginalMethod);
+
+ OneShotCollectionConsumer<MappingInformation> methodSpecificMappingInformation =
+ OneShotCollectionConsumer.wrap(new ArrayList<>());
+
+ if (residualIsD8R8Synthesized && !canStripOuterFrame) {
+ methodSpecificMappingInformation.add(CompilerSynthesizedMappingInformation.getInstance());
+ }
+
MethodSignature originalSignature =
MethodSignature.fromDexMethod(originalMethod, originalMethod.holder != originalType);
@@ -444,6 +471,20 @@
return this;
}
+ private boolean isGlobalSyntheticMethod(ProgramMethod method) {
+ return method.getDefinition().isD8R8Synthesized()
+ && appView.getSyntheticItems().isGlobalSyntheticClass(method.getHolder());
+ }
+
+ private boolean markAsCompilerSynthetic(ProgramMethod method) {
+ // We only do global synthetic classes when using names from the library. For such classes it
+ // is important that we do not filter out stack frames since they could appear from concrete
+ // classes in the library. Additionally, this is one place where it is helpful for developers
+ // to also get reported synthesized frames since stubbing can change control-flow and
+ // exceptions.
+ return method.getDefinition().isD8R8Synthesized() && !isGlobalSyntheticMethod(method);
+ }
+
private boolean canStripOuterFrame(ProgramMethod method, List<MappedPosition> mappedPositions) {
assert verifySyntheticPositions(method, mappedPositions);
if (!method.getDefinition().isD8R8Synthesized() || mappedPositions.isEmpty()) {
@@ -452,7 +493,7 @@
for (MappedPosition mappedPosition : mappedPositions) {
Position position = mappedPosition.getPosition();
if (!position.hasCallerPosition()) {
- // At least one position only has the synthetic method as its frame, so we can't strip it.
+ // At least one position has only the synthetic method as its frame, so we can't strip it.
return false;
}
if (position.isOutline() || position.isOutlineCaller()) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java
index f819541..1f593bf 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java
@@ -5,11 +5,13 @@
package com.android.tools.r8.classmerging.horizontal.dispatch;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNot.not;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
import org.junit.Test;
@@ -28,6 +30,8 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
+ .compile()
+ .apply(this::checkMappingOutput)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("foo", "bar", "foo", "parent")
.inspect(
@@ -37,6 +41,12 @@
});
}
+ private void checkMappingOutput(R8TestCompileResult result) {
+ // Merging of Parent, A and B should still emit a stack trace mapping back to the Parent frame.
+ // That frame will be encoded as a qualified method mapping.
+ assertThat(result.getProguardMap(), containsString(Parent.class.getTypeName() + ".<init>()"));
+ }
+
@NeverClassInline
public static class Parent {
@NeverInline