Don't remove single line debug information in R8.
This CL also fixes the issue of invalid inline frames in companion class
methods with preambles. That fix required an update to when debug info
is emitted and various other various cases that did not consider the
lack of position information.
Bug: b/259667158
Change-Id: Iab2c519c49ed281769b4ace1efdfca509c2d5b6f
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 535a891..cb150e6 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -911,7 +911,13 @@
oldPosition.getPosition().withOutermostCallerPosition(callerPosition)));
} else {
if (!instruction.isLabel() && !seenPosition) {
- newInstructions.add(new CfPosition(firstLabel, callerPosition));
+ Position preamblePosition =
+ SyntheticPosition.builder()
+ .setMethod(callee)
+ .setCallerPosition(callerPosition)
+ .setLine(0)
+ .build();
+ newInstructions.add(new CfPosition(firstLabel, preamblePosition));
seenPosition = true;
}
newInstructions.add(instruction);
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 09db76d..048e01d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.dex.code.DexReturnVoid;
import com.android.tools.r8.dex.code.DexSwitchPayload;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
import com.android.tools.r8.graph.DexDebugEvent.SetPositionFrame;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
@@ -22,6 +23,8 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.PositionBuilder;
+import com.android.tools.r8.ir.code.Position.SourcePosition;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.ir.conversion.DexSourceCode;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -30,6 +33,7 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions.ThrowingMethodConversionOptions;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.structural.Equatable;
@@ -290,27 +294,34 @@
DexMethod caller, DexMethod callee, DexItemFactory factory) {
Position callerPosition = SyntheticPosition.builder().setLine(0).setMethod(caller).build();
EventBasedDebugInfo eventBasedInfo = DexDebugInfo.convertToEventBased(this, factory);
- Position inlinePosition =
- SyntheticPosition.builder()
- .setMethod(caller)
- .setCallerPosition(callerPosition)
- .disableLineCheck()
- .build();
if (eventBasedInfo == null) {
// If the method has no debug info we generate a preamble position to denote the inlining.
// This is consistent with the building IR for inlining which will always ensure the method
// has a position.
+ Position preamblePosition =
+ SyntheticPosition.builder()
+ .setMethod(callee)
+ .setCallerPosition(callerPosition)
+ .setLine(0)
+ .build();
return new EventBasedDebugInfo(
0,
new DexString[callee.getArity()],
- new DexDebugEvent[] {
- new SetPositionFrame(inlinePosition), factory.zeroChangeDefaultEvent
- });
+ new DexDebugEvent[] {new SetPositionFrame(preamblePosition)});
}
+ // The inline position should match the first actual callee position, so either its actual line
+ // at first instruction or it is a synthetic preamble.
+ int lineAtPcZero = findLineAtPcZero(callee, eventBasedInfo);
+ PositionBuilder<?, ?> frameBuilder =
+ lineAtPcZero == -1
+ ? SyntheticPosition.builder().setLine(0)
+ : SourcePosition.builder().setLine(lineAtPcZero);
DexDebugEvent[] oldEvents = eventBasedInfo.events;
DexDebugEvent[] newEvents = new DexDebugEvent[oldEvents.length + 1];
int i = 0;
- newEvents[i++] = new SetPositionFrame(inlinePosition);
+ newEvents[i++] =
+ new SetPositionFrame(
+ frameBuilder.setMethod(callee).setCallerPosition(callerPosition).build());
for (DexDebugEvent event : oldEvents) {
if (event instanceof SetPositionFrame) {
SetPositionFrame oldFrame = (SetPositionFrame) event;
@@ -325,6 +336,27 @@
return new EventBasedDebugInfo(eventBasedInfo.startLine, eventBasedInfo.parameters, newEvents);
}
+ private static int findLineAtPcZero(DexMethod method, EventBasedDebugInfo debugInfo) {
+ IntBox lineAtPcZero = new IntBox(-1);
+ DexDebugPositionState visitor =
+ new DexDebugPositionState(debugInfo.startLine, method) {
+ @Override
+ public void visit(Default defaultEvent) {
+ super.visit(defaultEvent);
+ if (getCurrentPc() == 0) {
+ lineAtPcZero.set(getCurrentLine());
+ }
+ }
+ };
+ for (DexDebugEvent event : debugInfo.events) {
+ event.accept(visitor);
+ if (visitor.getCurrentPc() > 0) {
+ break;
+ }
+ }
+ return lineAtPcZero.get();
+ }
+
public static int getLargestPrefix(DexItemFactory factory, DexString name) {
if (name != null && name.endsWith(factory.thisName)) {
String string = name.toString();
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java
deleted file mode 100644
index 8d41561..0000000
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForSingleLineMethod.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.graph;
-
-import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
-
-public class DexDebugInfoForSingleLineMethod extends EventBasedDebugInfo {
-
- private static final DexDebugInfoForSingleLineMethod INSTANCE =
- new DexDebugInfoForSingleLineMethod(0, DexString.EMPTY_ARRAY, DexDebugEvent.EMPTY_ARRAY);
-
- private DexDebugInfoForSingleLineMethod(
- int startLine, DexString[] parameters, DexDebugEvent[] events) {
- super(startLine, parameters, events);
- }
-
- public static DexDebugInfoForSingleLineMethod getInstance() {
- return INSTANCE;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
index d83d7e0..6e540b8 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
@@ -14,12 +14,12 @@
import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
import com.android.tools.r8.graph.DexDebugEvent.SetFile;
+import com.android.tools.r8.graph.DexDebugEvent.SetPositionFrame;
import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
import com.android.tools.r8.graph.DexDebugEventBuilder;
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
-import com.android.tools.r8.graph.DexDebugInfoForSingleLineMethod;
import com.android.tools.r8.graph.DexDebugPositionState;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -55,6 +55,10 @@
this.processedEvents = processedEvents;
}
+ public boolean didEmitLineEvents() {
+ return startLine != -1;
+ }
+
private void emitAdvancePc(int pc) {
processedEvents.add(new AdvancePC(pc - previousPc));
previousPc = pc;
@@ -183,24 +187,25 @@
}
};
- for (DexDebugEvent event : debugInfo.events) {
- event.accept(visitor);
- }
-
- // If we only have one line event we can always retrace back uniquely.
- if (mappedPositions.size() <= 1
- && !hasOverloads
- && !appView.options().debug
- && appView.options().lineNumberOptimization != LineNumberOptimization.OFF
- && appView.options().allowDiscardingResidualDebugInfo()
- && (mappedPositions.isEmpty() || !mappedPositions.get(0).isOutlineCaller())) {
- dexCode.setDebugInfo(DexDebugInfoForSingleLineMethod.getInstance());
- return mappedPositions;
+ DexDebugEvent[] events = debugInfo.events;
+ if (events.length > 0) {
+ SetPositionFrame preambleFrame = getAsPreambleFrame(events[0]);
+ if (preambleFrame != null) {
+ // The preamble is specially identified here as it is active at method entry and thus not
+ // part of the instruction stream events.
+ Position position = preambleFrame.getPosition();
+ Position newPosition =
+ PositionUtils.remapAndAdd(position, positionRemapper, mappedPositions);
+ processedEvents.add(appView.dexItemFactory().createPositionFrame(newPosition));
+ }
+ for (int i = (preambleFrame == null) ? 0 : 1; i < events.length; i++) {
+ events[i].accept(visitor);
+ }
}
EventBasedDebugInfo optimizedDebugInfo =
new EventBasedDebugInfo(
- positionEventEmitter.getStartLine(),
+ positionEventEmitter.didEmitLineEvents() ? positionEventEmitter.getStartLine() : 0,
debugInfo.parameters,
processedEvents.toArray(DexDebugEvent.EMPTY_ARRAY));
@@ -212,6 +217,16 @@
return mappedPositions;
}
+ private SetPositionFrame getAsPreambleFrame(DexDebugEvent event) {
+ SetPositionFrame positionFrame = event.asSetPositionFrame();
+ if (positionFrame != null
+ && positionFrame.getPosition().isSyntheticPosition()
+ && positionFrame.getPosition().getLine() == 0) {
+ return positionFrame;
+ }
+ return null;
+ }
+
// This conversion *always* creates an event based debug info encoding as any non-info will
// be created as an implicit PC encoding.
private static EventBasedDebugInfo getEventBasedDebugInfo(
diff --git a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
index f29f9cb..890cb80 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.utils.positions;
import com.android.tools.r8.debuginfo.DebugRepresentation;
-import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugEvent;
@@ -13,7 +12,6 @@
import com.android.tools.r8.graph.DexDebugEventVisitor;
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
-import com.android.tools.r8.graph.DexDebugInfoForSingleLineMethod;
import com.android.tools.r8.graph.DexDebugPositionState;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.ProgramMethod;
@@ -23,7 +21,6 @@
import com.android.tools.r8.ir.code.Position.OutlinePosition;
import com.android.tools.r8.ir.code.Position.PositionBuilder;
import com.android.tools.r8.ir.code.Position.SourcePosition;
-import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.positions.PositionToMappedRangeMapper.PcBasedDebugInfoRecorder;
@@ -49,7 +46,6 @@
EventBasedDebugInfo debugInfo =
getEventBasedDebugInfo(method.getDefinition(), dexCode, appView);
IntBox firstDefaultEventPc = new IntBox(-1);
- BooleanBox singleOriginalLine = new BooleanBox(true);
Pair<Integer, Position> lastPosition = new Pair<>();
DexDebugEventVisitor visitor =
new DexDebugPositionState(
@@ -64,10 +60,6 @@
}
Position currentPosition = getPositionFromPositionState(this);
if (lastPosition.getSecond() != null) {
- if (singleOriginalLine.isTrue()
- && !currentPosition.equals(lastPosition.getSecond())) {
- singleOriginalLine.set(false);
- }
remapAndAddForPc(
pcBasedDebugInfo,
lastPosition.getFirst(),
@@ -86,20 +78,6 @@
event.accept(visitor);
}
- // If the method has a single non-preamble line, check that the preamble is not active on any
- // throwing instruction before the single line becomes active.
- if (singleOriginalLine.isTrue() && firstDefaultEventPc.get() > 0) {
- for (DexInstruction instruction : dexCode.instructions) {
- if (instruction.getOffset() < firstDefaultEventPc.get()) {
- if (instruction.canThrow()) {
- singleOriginalLine.set(false);
- }
- } else {
- break;
- }
- }
- }
-
int lastInstructionPc = DebugRepresentation.getLastExecutableInstruction(dexCode).getOffset();
if (lastPosition.getSecond() != null) {
remapAndAddForPc(
@@ -112,14 +90,7 @@
}
assert !mappedPositions.isEmpty() || dexCode.instructions.length == 1;
- if (singleOriginalLine.isTrue()
- && lastPosition.getSecond() != null
- && (mappedPositions.isEmpty() || !mappedPositions.get(0).isOutlineCaller())) {
- dexCode.setDebugInfo(DexDebugInfoForSingleLineMethod.getInstance());
- pcBasedDebugInfo.recordSingleLineFor(method, pcEncodingCutoff);
- } else {
- pcBasedDebugInfo.recordPcMappingFor(method, pcEncodingCutoff);
- }
+ pcBasedDebugInfo.recordPcMappingFor(method, pcEncodingCutoff);
return mappedPositions;
}
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 ae0eb0e..13f013e 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
@@ -9,7 +9,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexDebugInfoForSingleLineMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
@@ -291,17 +290,10 @@
lastPosition = currentPosition;
}
}
- Range obfuscatedRange;
- if (definition.getCode().isDexCode()
- && definition.getCode().asDexCode().getDebugInfo()
- == DexDebugInfoForSingleLineMethod.getInstance()) {
- assert firstPosition.getOriginalLine() == lastPosition.getOriginalLine();
- obfuscatedRange = nonCardinalRangeCache.get(0, MAX_LINE_NUMBER);
- } else {
- obfuscatedRange =
- nonCardinalRangeCache.get(
- firstPosition.getObfuscatedLine(), lastPosition.getObfuscatedLine());
- }
+ Range obfuscatedRange =
+ nonCardinalRangeCache.get(
+ firstPosition.getObfuscatedLine(), lastPosition.getObfuscatedLine());
+
MappedRange lastMappedRange =
getMappedRangesForPosition(
appView,
diff --git a/src/main/java/com/android/tools/r8/utils/positions/PositionToMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/PositionToMappedRangeMapper.java
index e4cb801..914b174 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/PositionToMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/PositionToMappedRangeMapper.java
@@ -8,8 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugInfo;
-import com.android.tools.r8.graph.DexDebugInfoForSingleLineMethod;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.ProgramMethod;
import java.util.ArrayList;
import java.util.HashMap;
@@ -45,7 +43,7 @@
pcBasedDebugInfoRecorder =
appView.options().canUseNativeDexPcInsteadOfDebugInfo()
? new NativePcSupport()
- : new Pc2PcMappingSupport(appView.options().allowDiscardingResidualDebugInfo());
+ : new Pc2PcMappingSupport();
noPcMapper = new DexPositionToNoPcMappedRangeMapper(appView);
pcMapper = new DexPositionToPcMappedRangeMapper(appView, pcBasedDebugInfoRecorder);
}
@@ -57,17 +55,9 @@
boolean hasOverloads,
boolean canUseDexPc,
int pcEncodingCutoff) {
- List<MappedPosition> mappedPositions =
- canUseDexPc
- ? pcMapper.optimizeDexCodePositionsForPc(method, positionRemapper, pcEncodingCutoff)
- : noPcMapper.optimizeDexCodePositions(method, positionRemapper, hasOverloads);
- DexEncodedMethod definition = method.getDefinition();
- if (definition.getCode().isDexCode()
- && definition.getCode().asDexCode().getDebugInfo()
- == DexDebugInfoForSingleLineMethod.getInstance()) {
- pcBasedDebugInfoRecorder.recordSingleLineFor(method, pcEncodingCutoff);
- }
- return mappedPositions;
+ return canUseDexPc
+ ? pcMapper.optimizeDexCodePositionsForPc(method, positionRemapper, pcEncodingCutoff)
+ : noPcMapper.optimizeDexCodePositions(method, positionRemapper, hasOverloads);
}
@Override
@@ -80,9 +70,6 @@
/** Callback to record a code object with a given max instruction PC and parameter count. */
void recordPcMappingFor(ProgramMethod method, int maxEncodingPc);
- /** Callback to record a code object with only a single "line". */
- void recordSingleLineFor(ProgramMethod method, int maxEncodingPc);
-
/**
* Install the correct debug info objects.
*
@@ -124,29 +111,12 @@
private final List<UpdateInfo> codesToUpdate = new ArrayList<>();
- // We can only drop single-line debug info if it is OK to lose the source-file info.
- // This list is null if we must retain single-line entries.
- private final List<DexCode> singleLineCodesToClear;
-
- public Pc2PcMappingSupport(boolean allowDiscardingSourceFile) {
- singleLineCodesToClear = allowDiscardingSourceFile ? new ArrayList<>() : null;
- }
-
@Override
public int getPcEncoding(int pc) {
assert pc >= 0;
return pc + 1;
}
- private boolean cantAddToClearSet(ProgramMethod method) {
- assert method.getDefinition().getCode().isDexCode();
- if (singleLineCodesToClear == null) {
- return true;
- }
- singleLineCodesToClear.add(method.getDefinition().getCode().asDexCode());
- return false;
- }
-
@Override
public void recordPcMappingFor(ProgramMethod method, int maxEncodingPc) {
assert method.getDefinition().getCode().isDexCode();
@@ -157,13 +127,6 @@
}
@Override
- public void recordSingleLineFor(ProgramMethod method, int maxEncodingPc) {
- if (cantAddToClearSet(method)) {
- recordPcMappingFor(method, maxEncodingPc);
- }
- }
-
- @Override
public void updateDebugInfoInCodeObjects() {
Map<UpdateInfo, DexDebugInfo> debugInfos = new HashMap<>();
codesToUpdate.forEach(
@@ -175,9 +138,6 @@
assert debugInfo.asPcBasedInfo().getMaxPc() == entry.maxEncodingPc;
entry.code.setDebugInfo(debugInfo);
});
- if (singleLineCodesToClear != null) {
- singleLineCodesToClear.forEach(c -> c.setDebugInfo(null));
- }
}
private static DexDebugInfo buildPc2PcDebugInfo(UpdateInfo info) {
@@ -204,11 +164,6 @@
}
@Override
- public void recordSingleLineFor(ProgramMethod method, int maxEncodingPc) {
- clearDebugInfo(method);
- }
-
- @Override
public void updateDebugInfoInCodeObjects() {
// Already null out the info so nothing to do.
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java b/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
index 053f4b3..455bfe4 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
@@ -3,24 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debuginfo;
-import static org.junit.Assert.assertNull;
import com.android.tools.r8.AssumeMayHaveSideEffects;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.DexParser;
import com.android.tools.r8.dex.DexSection;
-import com.android.tools.r8.graph.DexDebugInfo;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.io.IOException;
import java.nio.file.Path;
-import java.nio.file.Paths;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,7 +48,7 @@
Class<?> clazzA = ClassA.class;
Class<?> clazzB = ClassB.class;
- R8TestCompileResult result =
+ Path classesPath =
testForR8(Backend.DEX)
.setMinApi(AndroidApiLevel.B)
.addProgramClasses(clazzA, clazzB)
@@ -63,19 +57,11 @@
"-keep class ** { public void call(int); }")
.enableInliningAnnotations()
.enableSideEffectAnnotations()
- .compile();
- result.inspect(
- inspector -> {
- DexEncodedMethod method =
- inspector.clazz(ClassA.class).uniqueMethodWithOriginalName("call").getMethod();
- DexDebugInfo debugInfo = method.getCode().asDexCode().getDebugInfo();
- assertNull(debugInfo);
- });
- Path classesPath = temp.getRoot().toPath();
- result.app.write(classesPath, OutputMode.DexIndexed);
- int numberOfDebugInfos =
- getNumberOfDebugInfos(Paths.get(temp.getRoot().getCanonicalPath(), "classes.dex"));
- Assert.assertEquals(0, numberOfDebugInfos);
+ .compile()
+ .writeToDirectory();
+ int numberOfDebugInfos = getNumberOfDebugInfos(classesPath.resolve("classes.dex"));
+ // All methods have one parameter and use a single shared pc2pc item.
+ Assert.assertEquals(1, numberOfDebugInfos);
}
// Two classes which has debug info that looks exactly the same, except for SetInlineFrame.
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
index c577259..84db0e5 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoInlineRemoveTest.java
@@ -51,6 +51,11 @@
.map(StackTrace::extractFromJvm);
}
+ private boolean compileApiHasPcLineSupport() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport());
+ }
+
@Test
public void testDefaultSourceFile() throws Exception {
testForR8(parameters.getBackend())
@@ -70,7 +75,7 @@
assertThat(mainSubject.uniqueMethodWithOriginalName("inlinee"), not(isPresent()));
assertThat(
mainSubject.uniqueMethodWithOriginalName("shouldRemoveLineNumberForInline"),
- notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+ notIf(hasLineNumberTable(), compileApiHasPcLineSupport()));
});
}
@@ -94,7 +99,7 @@
assertThat(mainSubject.uniqueMethodWithOriginalName("inlinee"), not(isPresent()));
assertThat(
mainSubject.uniqueMethodWithOriginalName("shouldRemoveLineNumberForInline"),
- notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+ notIf(hasLineNumberTable(), compileApiHasPcLineSupport()));
});
}
@@ -118,7 +123,7 @@
assertThat(mainSubject.uniqueMethodWithOriginalName("inlinee"), not(isPresent()));
assertThat(
mainSubject.uniqueMethodWithOriginalName("shouldRemoveLineNumberForInline"),
- notIf(hasLineNumberTable(), parameters.isDexRuntime()));
+ notIf(hasLineNumberTable(), compileApiHasPcLineSupport()));
});
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleCallsRemoveTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleCallsRemoveTest.java
index 542cfb6..e1075c6 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleCallsRemoveTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoMultipleCallsRemoveTest.java
@@ -5,9 +5,6 @@
package com.android.tools.r8.debuginfo;
import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static com.android.tools.r8.utils.codeinspector.Matchers.hasLineNumberTable;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -17,7 +14,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.naming.retrace.StackTrace;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,20 +59,7 @@
.enableInliningAnnotations()
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatThrows(NullPointerException.class)
- .inspectStackTrace(
- (stackTrace, inspector) -> {
- assertThat(stackTrace, isSame(expectedStackTrace));
- assertThat(inspector.clazz(Builder.class), isPresent());
- ClassSubject mainSubject = inspector.clazz(Main.class);
- assertThat(mainSubject, isPresent());
- assertThat(
- mainSubject.uniqueMethodWithOriginalName(
- "shouldRemoveLineNumberForMultipleInvokes"),
- notIf(hasLineNumberTable(), parameters.isDexRuntime()));
- assertThat(
- mainSubject.uniqueMethodWithOriginalName("main"),
- notIf(hasLineNumberTable(), parameters.isDexRuntime()));
- });
+ .inspectStackTrace(stackTrace -> assertThat(stackTrace, isSame(expectedStackTrace)));
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoRemoveTest.java b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoRemoveTest.java
index 8076acf..46b9e22 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoRemoveTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/SingleLineInfoRemoveTest.java
@@ -98,7 +98,9 @@
}
private boolean canSingleLineDebugInfoBeDiscarded() {
- return parameters.isDexRuntime() && !customSourceFile;
+ return parameters.isDexRuntime()
+ && !customSourceFile
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport());
}
public static class Main {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/pc2pc/DifferentParameterCountSingleLineCodeTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/pc2pc/DifferentParameterCountSingleLineCodeTestRunner.java
index 398ecb4..c036aaf 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/pc2pc/DifferentParameterCountSingleLineCodeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/pc2pc/DifferentParameterCountSingleLineCodeTestRunner.java
@@ -5,7 +5,6 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
@@ -58,15 +57,13 @@
// For a custom source file, all debug info must be present.
assertEquals("X", line.fileName);
assertTrue("Expected line number in: " + line, line.hasLineNumber());
- } else if (vmHasPcSupport()) {
- // Single line debug info is stripped. If running with PC support the PC is
- // printed.
+ } else if (compileApiHasPcSupport()) {
assertEquals("Unknown Source", line.fileName);
assertTrue("Expected PC in: " + line, line.hasLineNumber());
} else {
- // Otherwise, just the bare source file is printed.
+ // Otherwise, the bare source file is printed and a line.
assertEquals("SourceFile", line.fileName);
- assertFalse("Expected no line number in: " + line, line.hasLineNumber());
+ assertTrue("Expected line number in: " + line, line.hasLineNumber());
}
}
assertEquals("Expected 4 stack frames in:\n" + s, 4, s.getStackTraceLines().size());
@@ -84,11 +81,8 @@
.build())));
}
- private boolean vmHasPcSupport() {
- return parameters
- .asDexRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport());
+ private boolean compileApiHasPcSupport() {
+ return parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport());
}
private StackTraceLine makeLine(String methodName, int lineNumber) {
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
index 31f87d9..9c29a9b 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
@@ -108,7 +108,21 @@
.getStdOut();
assertTrue(
StringUtils.splitLines(stdout).stream()
- .allMatch(s -> s.contains(isAndroidOOrLater ? "NULL" : "SourceFile")));
+ .allMatch(
+ s -> {
+ if (parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport())) {
+ return s.contains("(NULL)");
+ } else if (isAndroidOOrLater) {
+ // On VMs with native support, no line info results in no source file printing.
+ // TODO(b/260384637): Create debug info for such methods to avoid this.
+ return s.equals("main(NULL)")
+ || (!s.startsWith("main") && s.contains("(SourceFile)"));
+ } else {
+ return s.contains("(SourceFile)");
+ }
+ }));
}
static class TestRunner {
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
index 10fec23..5475557 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
@@ -76,10 +76,6 @@
.assertFailure();
// Extract actual stack trace and retraced stack trace from failed run result.
- // TODO(122940268): Remove test code when fixed.
- System.out.println("<--- TEST RESULT START --->");
- System.out.println(result);
- System.out.println("<--- TEST RESULT END --->");
StackTrace actualStackTrace = StackTrace.extractFromArt(result.getStdErr());
StackTrace retracedStackTrace =
actualStackTrace.retrace(result.proguardMap(), temp.newFolder().toPath());
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
index 5403673..7e6306d 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
@@ -129,26 +129,6 @@
compileResult -> setSyntheticMethod(compileResult, syntheticMethod));
}
- @Test
- public void testNoLineNumberTable() throws Exception {
- assumeTrue(compat);
- assumeTrue(parameters.isDexRuntime());
- Box<MethodSubject> syntheticMethod = new Box<>();
- runTest(
- ImmutableList.of(),
- (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
- StackTrace reprocessedStackTrace =
- retracedStackTrace.filter(
- stackTraceLine -> filterSynthesizedMethod(stackTraceLine, syntheticMethod.get()));
- assertThat(
- reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
- isSameExceptForFileNameAndLineNumber(
- expectedStackTrace.filter(this::isNotDalvikNativeStartMethod)));
- assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
- },
- compileResult -> setSyntheticMethod(compileResult, syntheticMethod));
- }
-
private void setSyntheticMethod(
R8TestCompileResult compileResult, Box<MethodSubject> syntheticMethod) throws IOException {
compileResult.inspect(
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCompanionWithPreambleTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceCompanionWithPreambleTest.java
new file mode 100644
index 0000000..12eff52
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCompanionWithPreambleTest.java
@@ -0,0 +1,153 @@
+// 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.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.transformers.ClassFileTransformer.LineTranslation;
+import com.android.tools.r8.transformers.MethodTransformer.MethodContext;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
+import java.util.List;
+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 RetraceCompanionWithPreambleTest extends TestBase {
+
+ public enum Preamble {
+ NONE,
+ ONLY,
+ BOTH
+ }
+
+ @Parameters(name = "{0}, preamble:{1}")
+ public static List<Object[]> parameters() {
+ return buildParameters(
+ getTestParameters()
+ .withDefaultRuntimes()
+ .withApiLevel(AndroidApiLevel.B)
+ .enableApiLevelsForCf()
+ .build(),
+ Preamble.values());
+ }
+
+ private final TestParameters parameters;
+ private final Preamble preamble;
+
+ public RetraceCompanionWithPreambleTest(TestParameters parameters, Preamble preamble) {
+ this.parameters = parameters;
+ this.preamble = preamble;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClasses(Main.class, A.class)
+ .addProgramClassFileData(getI())
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkRunResult)
+ .inspectStackTrace(RetraceCompanionWithPreambleTest::checkExpectedStackTrace);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .internalEnableMappingOutput()
+ .addProgramClasses(Main.class, A.class)
+ .addProgramClassFileData(getI())
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkRunResult)
+ .inspectStackTrace(RetraceCompanionWithPreambleTest::checkExpectedStackTrace);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, A.class)
+ .addProgramClassFileData(getI())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSourceFile()
+ .addKeepAttributeLineNumberTable()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkRunResult)
+ .inspectStackTrace(RetraceCompanionWithPreambleTest::checkExpectedStackTrace);
+ }
+
+ private void checkRunResult(SingleTestRunResult<?> runResult) {
+ runResult.assertFailureWithErrorThatMatches(containsString("Throw It!"));
+ }
+
+ private static void checkExpectedStackTrace(StackTrace stackTrace) {
+ assertThat(
+ stackTrace,
+ isSameExceptForFileNameAndLineNumber(
+ StackTrace.builder()
+ .addWithoutFileNameAndLineNumber(Main.class, "doThrow")
+ .addWithoutFileNameAndLineNumber(I.class, "foo")
+ .addWithoutFileNameAndLineNumber(Main.class, "main")
+ .build()));
+ }
+
+ private byte[] getI() throws IOException {
+ return transformer(I.class)
+ .setPredictiveLineNumbering(
+ new LineTranslation() {
+ boolean firstLine = true;
+
+ @Override
+ public int translate(MethodContext context, int line) {
+ if (!context.getReference().getMethodName().equals("foo")) {
+ return line;
+ }
+ if (preamble == Preamble.NONE) {
+ return line;
+ }
+ if (preamble == Preamble.ONLY) {
+ return -1;
+ }
+ if (firstLine) {
+ firstLine = false;
+ return -1;
+ }
+ return line;
+ }
+ })
+ .transform();
+ }
+
+ interface I {
+
+ default void foo() {
+ Main.doThrow();
+ System.out.println("more stuff");
+ }
+ }
+
+ static class A implements I {}
+
+ public static class Main {
+
+ public static void doThrow() {
+ throw new RuntimeException("Throw It!");
+ }
+
+ public static void main(String[] args) {
+ new A().foo();
+ }
+ }
+}