Move vertically merged method from parent by inlining positions
Change-Id: Ic61e81df691d0b38343671295ed50e6eb24817bd
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9c3131f..f80b0d8 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
@@ -943,10 +944,7 @@
return;
}
if (code.isCfCode() || code.isDexCode()) {
- code.forEachPositionOrInlineFrame(
- position -> {
- assert position.getMethod() == originalMethod;
- });
+ assert verifyOriginalMethodInPosition(code, originalMethod, method);
} else {
assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
}
@@ -955,6 +953,27 @@
return true;
}
+ private static boolean verifyOriginalMethodInPosition(
+ Code code, DexMethod originalMethod, ProgramMethod context) {
+ code.forEachPosition(
+ position -> {
+ if (position.isOutlineCaller()) {
+ // Check the outlined positions for the original method
+ position
+ .getOutlinePositions()
+ .forEach(
+ (ignored, outlinePosition) -> {
+ assert outlinePosition.hasMethodInChain(originalMethod);
+ });
+ } else if (context.getDefinition().isD8R8Synthesized()) {
+ assert position.hasMethodInChain(originalMethod);
+ } else {
+ assert position.getOutermostCaller().getMethod() == originalMethod;
+ }
+ });
+ return true;
+ }
+
private AppView<AppInfoWithLiveness> runEnqueuer(
AnnotationRemover.Builder annotationRemoverBuilder,
ExecutorService executorService,
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index 1d05d43..f360949 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -62,7 +62,7 @@
// Record original method signatures.
for (DexEncodedMethod encodedMethod : clazz.methods()) {
DexMethod method = encodedMethod.getReference();
- DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
+ DexMethod original = appView.graphLens().getOriginalMethodSignatureForMapping(method);
DexMethod existing = originalMethodSignatures.inverse().get(original);
if (existing == null) {
originalMethodSignatures.put(method, original);
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index acad41b..fff7a6e 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -975,7 +975,7 @@
}
@Override
- public void forEachPositionOrInlineFrame(Consumer<Position> positionConsumer) {
+ public void forEachPosition(Consumer<Position> positionConsumer) {
for (CfInstruction instruction : getInstructions()) {
if (instruction.isPosition()) {
positionConsumer.accept(instruction.asPosition().getPosition());
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 11a95cd..bc0c72b 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -191,7 +191,7 @@
Position callerPosition, Position oldPosition, boolean isCalleeD8R8Synthesized) {
Position outermostCaller = oldPosition.getOutermostCaller();
if (!isCalleeD8R8Synthesized) {
- return oldPosition.withOutermostCallerPosition(callerPosition);
+ return removeSameMethodAndLineZero(oldPosition, callerPosition);
}
// We can replace the position since the callee was synthesized by the compiler, however, if
// the position carries special information we need to copy it.
@@ -210,7 +210,22 @@
return oldPosition.replacePosition(outermostCaller, positionBuilder.build());
}
- public void forEachPositionOrInlineFrame(Consumer<Position> positionConsumer) {
+ @Deprecated()
+ // TODO(b/261971803): When having complete control over the positions we should not need this.
+ private static Position removeSameMethodAndLineZero(
+ Position calleePosition, Position callerPosition) {
+ Position outermostCaller = calleePosition.getOutermostCaller();
+ if (outermostCaller.getLine() == 0) {
+ while (callerPosition != null
+ && outermostCaller.getMethod() == callerPosition.getMethod()
+ && callerPosition.getLine() == 0) {
+ callerPosition = callerPosition.getCallerPosition();
+ }
+ }
+ return calleePosition.withOutermostCallerPosition(callerPosition);
+ }
+
+ public void forEachPosition(Consumer<Position> positionConsumer) {
// Intentionally empty. Override where we have fully build CF or DEX code.
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 8e65622..60ce8b5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -777,7 +777,7 @@
}
@Override
- public void forEachPositionOrInlineFrame(Consumer<Position> positionConsumer) {
+ public void forEachPosition(Consumer<Position> positionConsumer) {
if (getDebugInfo() == null || getDebugInfo().isPcBasedInfo()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 46d91ef..43558a8 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -329,6 +329,18 @@
return original;
}
+ public final DexMethod getOriginalMethodSignatureForMapping(DexMethod method) {
+ GraphLens current = this;
+ DexMethod original = method;
+ while (current.isNonIdentityLens()) {
+ NonIdentityGraphLens nonIdentityLens = current.asNonIdentityLens();
+ original = nonIdentityLens.getPreviousMethodSignatureForMapping(original);
+ current = nonIdentityLens.getPrevious();
+ }
+ assert current.isIdentityLens();
+ return original;
+ }
+
public final DexField getRenamedFieldSignature(DexField originalField) {
return getRenamedFieldSignature(originalField, null);
}
@@ -971,6 +983,15 @@
public abstract DexMethod getPreviousMethodSignature(DexMethod method);
+ /***
+ * The previous mapping for a method often coincides with the previous method signature, but it
+ * may not, for example for bridges inserted in vertically merged classes where the original
+ * signature is used for computing invoke-super but should not be used for mapping output.
+ */
+ public DexMethod getPreviousMethodSignatureForMapping(DexMethod method) {
+ return getPreviousMethodSignature(method);
+ }
+
public abstract DexMethod getNextMethodSignature(DexMethod method);
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 80bb77b..a5dcdae 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -94,11 +94,6 @@
}
public static Type fromInvokeSpecial(
- DexMethod invokedMethod, DexClassAndMethod context, AppView<?> appView) {
- return fromInvokeSpecial(invokedMethod, context, appView, appView.codeLens());
- }
-
- public static Type fromInvokeSpecial(
DexMethod invokedMethod,
DexClassAndMethod context,
AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index c3a705e..4c809dc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -64,6 +64,10 @@
return false;
}
+ public boolean isOutlineCaller() {
+ return false;
+ }
+
public DexMethod getOutlineCallee() {
return null;
}
@@ -131,8 +135,17 @@
assert position.isNone();
position = SourcePosition.builder().setMethod(context.getReference()).build();
}
- assert position.getOutermostCaller().method
- == appView.graphLens().getOriginalMethodSignature(context.getReference());
+ if (context.getDefinition().isD8R8Synthesized()) {
+ // Some rewritings map a synthetic method back to an original in the program. To ensure we
+ // have correct line information we have to rewrite the positions as inline position
+ // therefore we only check if the original method is present.
+ DexMethod originalMethodSignature =
+ appView.graphLens().getOriginalMethodSignature(context.getReference());
+ assert position.hasMethodInChain(originalMethodSignature);
+ } else {
+ assert position.getOutermostCaller().method
+ == appView.graphLens().getOriginalMethodSignature(context.getReference());
+ }
return position;
}
@@ -178,6 +191,21 @@
return null;
}
+ public boolean hasPositionMatching(Predicate<Position> positionPredicate) {
+ Position lastPosition = this;
+ while (lastPosition != null) {
+ if (positionPredicate.test(lastPosition)) {
+ return true;
+ }
+ lastPosition = lastPosition.getCallerPosition();
+ }
+ return false;
+ }
+
+ public boolean hasMethodInChain(DexMethod method) {
+ return hasPositionMatching(position -> position.getMethod() == method);
+ }
+
public Position withOutermostCallerPosition(Position newOutermostCallerPosition) {
return builderWithCopy()
.setCallerPosition(
@@ -566,6 +594,11 @@
}
@Override
+ public boolean isOutlineCaller() {
+ return true;
+ }
+
+ @Override
public DexMethod getOutlineCallee() {
return outlineCallee;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index ea0f655..7adbf67 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -258,10 +258,10 @@
}
private Position getCanonicalPositionAppendCaller(DexDebugEntry entry) {
- // If this instruction has already been inlined then this.method must be the outermost caller.
+ // If this instruction has already been inlined then the original method must be in the caller
+ // chain.
Position position = entry.getPosition();
- assert !position.hasCallerPosition()
- || position.getOutermostCaller().getMethod() == originalMethod;
+ assert position.hasMethodInChain(originalMethod);
return canonicalPositions.getCanonical(
position
.builderWithCopy()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 657845b..c621494 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -753,7 +753,11 @@
.setGenericSignature(encodedMethod.getGenericSignature())
.setAnnotations(encodedMethod.annotations())
.setParameterAnnotations(encodedMethod.parameterAnnotationsList)
- .setCode(encodedMethod.getCode())
+ .setCode(
+ encodedMethod
+ .getCode()
+ .getCodeAsInlining(
+ callTarget, encodedMethod, appView.dexItemFactory()))
.setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
.setApiLevelForCode(encodedMethod.getApiLevelForCode())
.build();
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 8e0d7d4..f9c13e9 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -70,6 +70,7 @@
.setLine(0)
.setMethod(originalMethod)
.setCallerPosition(callerPosition)
+ .setIsD8R8Synthesized(true)
.build();
}
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..32878da 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -65,6 +65,7 @@
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
@@ -2389,7 +2390,16 @@
.setTarget(invocationTarget)
.setInvokeType(type)
.setIsInterface(isInterface);
- return forwardSourceCodeBuilder::build;
+ return (context, callerPosition) -> {
+ SyntheticPosition caller =
+ SyntheticPosition.builder()
+ .setLine(0)
+ .setMethod(method)
+ .setIsD8R8Synthesized(true)
+ .setCallerPosition(callerPosition)
+ .build();
+ return forwardSourceCodeBuilder.build(context, caller);
+ };
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 05837f0..d0f940b 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -153,6 +153,12 @@
}
@Override
+ public DexMethod getPreviousMethodSignatureForMapping(DexMethod method) {
+ DexMethod orDefault = newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
+ return super.getPreviousMethodSignature(orDefault);
+ }
+
+ @Override
protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
return mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
}
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 189367d..c0a65b6 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
@@ -192,7 +192,7 @@
boolean canUseDexPc) {
DexEncodedMethod definition = method.getDefinition();
DexMethod originalMethod =
- appView.graphLens().getOriginalMethodSignature(method.getReference());
+ appView.graphLens().getOriginalMethodSignatureForMapping(method.getReference());
MethodSignature originalSignature =
MethodSignature.fromDexMethod(originalMethod, originalMethod.holder != originalType);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
index 9e361e6..5b4dcb2 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
@@ -66,15 +66,6 @@
return retracedStackTraceLine.lineNumber > 0;
}
- private boolean filterSynthesizedBridgeMethod(StackTraceLine retracedStackTraceLine) {
- if (!haveSeenLines.add(retracedStackTraceLine)) {
- return false;
- }
- return !retracedStackTraceLine.className.contains("ResourceWrapper")
- || !retracedStackTraceLine.methodName.contains("foo")
- || retracedStackTraceLine.lineNumber != 0;
- }
-
@Test
public void testSourceFileAndLineNumberTable() throws Exception {
runTest(
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
deleted file mode 100644
index 7e6306d..0000000
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.naming.retraceproguard;
-
-import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileName;
-import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileNameAndLineNumber;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.util.Collection;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class VerticalClassMergingRetraceTest extends RetraceTestBase {
-
- @Parameters(name = "{0}, mode: {1}, compat: {2}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters()
- .withCfRuntimes()
- // Runtimes prior to 8 will emit stack trace lines as:
- // Exception in thread "main" java.lang.NullPointerException: throw with null exception
- // at com.android.tools.r8.naming.retraceproguard.a.b(SourceFile)
- // PG do not support retracing if no line number is specified.
- .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
- .withAllApiLevels()
- .build(),
- CompilationMode.values(),
- BooleanUtils.values());
- }
-
- public VerticalClassMergingRetraceTest(
- TestParameters parameters, CompilationMode mode, boolean compat) {
- super(parameters, mode, compat);
- }
-
- @Override
- public void configure(R8TestBuilder builder) {
- builder.enableInliningAnnotations();
- }
-
- @Override
- public Collection<Class<?>> getClasses() {
- return ImmutableList.of(getMainClass(), ResourceWrapper.class, TintResources.class);
- }
-
- @Override
- public Class<?> getMainClass() {
- return MainApp.class;
- }
-
- private int expectedActualStackTraceHeight() {
- // In RELEASE mode, a synthetic bridge will be added by vertical class merger.
- int height = mode == CompilationMode.RELEASE ? 3 : 2;
- if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) {
- // Dalvik places a stack trace line in the bottom.
- height += 1;
- }
- return height;
- }
-
- private boolean filterSynthesizedMethod(
- StackTraceLine retracedStackTraceLine, MethodSubject syntheticMethod) {
- if (syntheticMethod.isPresent()) {
- String qualifiedMethodName =
- retracedStackTraceLine.className + "." + retracedStackTraceLine.methodName;
- return !qualifiedMethodName.equals(syntheticMethod.getOriginalName())
- || retracedStackTraceLine.lineNumber > 0;
- }
- return true;
- }
-
- @Test
- public void testSourceFileAndLineNumberTable() throws Exception {
- Box<MethodSubject> syntheticMethod = new Box<>();
- runTest(
- ImmutableList.of("-keepattributes SourceFile,LineNumberTable"),
- (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
- // Even when SourceFile is present retrace replaces the file name in the stack trace.
- StackTrace reprocessedStackTrace =
- retracedStackTrace.filter(
- stackTraceLine -> filterSynthesizedMethod(stackTraceLine, syntheticMethod.get()));
- assertThat(
- reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
- isSameExceptForFileName(
- expectedStackTrace.filter(this::isNotDalvikNativeStartMethod)));
- assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
- },
- compileResult -> setSyntheticMethod(compileResult, syntheticMethod));
- }
-
- @Test
- public void testLineNumberTableOnly() throws Exception {
- assumeTrue(compat);
- assumeTrue(parameters.isDexRuntime());
- Box<MethodSubject> syntheticMethod = new Box<>();
- runTest(
- ImmutableList.of("-keepattributes LineNumberTable"),
- (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
- StackTrace reprocessedStackTrace =
- retracedStackTrace.filter(
- stackTraceLine -> filterSynthesizedMethod(stackTraceLine, syntheticMethod.get()));
- assertThat(
- reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
- isSameExceptForFileName(
- expectedStackTrace.filter(this::isNotDalvikNativeStartMethod)));
- assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
- },
- compileResult -> setSyntheticMethod(compileResult, syntheticMethod));
- }
-
- private void setSyntheticMethod(
- R8TestCompileResult compileResult, Box<MethodSubject> syntheticMethod) throws IOException {
- compileResult.inspect(
- inspector -> {
- ClassSubject tintResourcesClassSubject = inspector.clazz(TintResources.class);
- MethodSubject uniqueSyntheticMethod =
- tintResourcesClassSubject.uniqueMethodThatMatches(
- method -> method.getAccessFlags().isSynthetic());
- assertThat(uniqueSyntheticMethod, onlyIf(mode == CompilationMode.RELEASE, isPresent()));
- syntheticMethod.set(uniqueSyntheticMethod);
- });
- }
-}
-
-class ResourceWrapper {
- // Will be merged down, and represented as:
- // java.lang.String ...ResourceWrapper.foo() -> a
- @NeverInline
- String foo(boolean doThrow) {
- if (doThrow) {
- throw null;
- }
- return System.currentTimeMillis() > 0 ? "arg" : null;
- }
-}
-
-class TintResources extends ResourceWrapper {}
-
-class MainApp {
- public static void main(String[] args) {
- TintResources t = new TintResources();
- boolean doThrow = System.currentTimeMillis() > 0;
- System.out.println(t.foo(doThrow));
- }
-}