Reland "Extend kotlin inline function retracing with callee positions"
This reverts commit ae88d453bfe4e9101fa8e837e8e11641d132a0ea.
Bug: 185358363
Change-Id: I60e67381a920a09de2153de7e506e5fa91564893
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
index 256ee1e..065ff92 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
@@ -25,6 +25,7 @@
private static final String SMAP_IDENTIFIER = "SMAP";
private static final String SMAP_SECTION_START = "*S";
private static final String SMAP_SECTION_KOTLIN_START = SMAP_SECTION_START + " Kotlin";
+ private static final String SMAP_SECTION_KOTLIN_DEBUG_START = SMAP_SECTION_START + " KotlinDebug";
private static final String SMAP_FILES_IDENTIFIER = "*F";
private static final String SMAP_LINES_IDENTIFIER = "*L";
private static final String SMAP_END_IDENTIFIER = "*E";
@@ -84,13 +85,13 @@
readUntil(terminator::equals, linesInBlock, callback);
}
- void readUntil(
+ String readUntil(
Predicate<String> terminator,
int linesInBlock,
ThrowingConsumer<List<String>, KotlinSourceDebugExtensionParserException> callback)
throws IOException, KotlinSourceDebugExtensionParserException {
if (terminator.test(readLine)) {
- return;
+ return readLine;
}
List<String> readStrings = new ArrayList<>();
readStrings.add(readNextLine());
@@ -106,10 +107,11 @@
}
readStrings.add(readNextLine());
}
- if (readStrings.size() > 0 && !terminator.test(readStrings.get(0))) {
+ if (!readStrings.isEmpty() && !terminator.test(readStrings.get(0))) {
throw new KotlinSourceDebugExtensionParserException(
"Block size does not match linesInBlock = " + linesInBlock);
}
+ return readStrings.isEmpty() ? null : readStrings.get(0);
}
@Override
@@ -132,14 +134,17 @@
// + <file_id_i> <file_name_i>
// <path_i>
// *L
- // <from>#<file>,<to>:<debug-line-position>
+ // <from>#<file>,<range>:<debug-line-position>
// <from>#<file>:<debug-line-position>
// *E <-- This is an error in versions prior to kotlin 1.5 and is not present in kotlin 1.5.
// *S KotlinDebug
- // ***
+ // + <file_id_i> <file_name_i>
+ // <path_i>
+ // *L
+ // <from>#<file>,<range>:<debug-line-position>
+ // <from>#<file>:<debug-line-position>
// *E
try (BufferedStringReader reader = new BufferedStringReader(annotationData)) {
- ResultBuilder builder = new ResultBuilder();
// Check for SMAP
if (!reader.readExpectedLine(SMAP_IDENTIFIER)) {
return null;
@@ -147,42 +152,67 @@
if (reader.readUntil(SMAP_SECTION_KOTLIN_START).isEOF()) {
return null;
}
- // At this point we should be parsing a kotlin source debug extension, so we will throw if we
- // read an unexpected line.
- reader.readExpectedLineOrThrow(SMAP_FILES_IDENTIFIER);
- // Iterate over the files section with the format:
- // + <file_number_i> <file_name_i>
- // <file_path_i>
- reader.readUntil(
- SMAP_LINES_IDENTIFIER, 2, block -> addFileToBuilder(block.get(0), block.get(1), builder));
- // Ensure that we read *L.
- if (reader.isEOF()) {
- throw new KotlinSourceDebugExtensionParserException(
- "Unexpected EOF - no debug line positions");
+ StratumBuilder inlineePositions = new StratumBuilder();
+ StratumBuilder calleePositions = new StratumBuilder();
+
+ String terminatedLine = parseStratumContents(reader, inlineePositions);
+
+ // Read callee positions
+ if (terminatedLine.equals(SMAP_END_IDENTIFIER)) {
+ String nextLine = reader.readNextLine();
+ // If we read to the end then there is not KotlinDebug section which translates to no need
+ // for mapping the resulting positions obtained from the inlineePositions.
+ if (reader.isEOF()) {
+ assert nextLine == null;
+ return new Result(inlineePositions.segmentTree, calleePositions.segmentTree);
+ }
+ if (!nextLine.equals(SMAP_SECTION_KOTLIN_DEBUG_START)) {
+ return null;
+ }
+ } else if (!terminatedLine.equals(SMAP_SECTION_KOTLIN_DEBUG_START)) {
+ return null;
}
- // Iterate over the debug line number positions:
- // <from>#<file>,<range>:<debug-line-position>
- // or
- // <from>#<file>:<debug-line-position>
- reader.readUntil(
- line -> line.equals(SMAP_END_IDENTIFIER) || line.startsWith(SMAP_SECTION_START),
- 1,
- block -> addDebugEntryToBuilder(block.get(0), builder));
+ parseStratumContents(reader, calleePositions);
// Ensure that we read the end section and it is terminated.
if (reader.isEOF() && !reader.readLine.equals(SMAP_END_IDENTIFIER)) {
throw new KotlinSourceDebugExtensionParserException(
"Unexpected EOF when parsing SMAP debug entries");
}
-
- return builder.build();
+ return new Result(inlineePositions.segmentTree, calleePositions.segmentTree);
} catch (IOException | KotlinSourceDebugExtensionParserException e) {
return null;
}
}
- private static void addFileToBuilder(String entryLine, String filePath, ResultBuilder builder)
+ private static String parseStratumContents(BufferedStringReader reader, StratumBuilder builder)
+ throws KotlinSourceDebugExtensionParserException, IOException {
+ // At this point we should be parsing a kotlin source debug extension, so we will throw if we
+ // read an unexpected line.
+ reader.readExpectedLineOrThrow(SMAP_FILES_IDENTIFIER);
+ // Iterate over the files section with the format:
+ // + <file_number_i> <file_name_i>
+ // <file_path_i>
+ reader.readUntil(
+ SMAP_LINES_IDENTIFIER, 2, block -> addFileToBuilder(block.get(0), block.get(1), builder));
+
+ // Ensure that we read *L.
+ if (reader.isEOF()) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Unexpected EOF - no debug line positions");
+ }
+ // Iterate over the debug line number positions:
+ // <from>#<file>,<range>:<debug-line-position>
+ // or
+ // <from>#<file>:<debug-line-position>
+ return reader.readUntil(
+ line -> line.equals(SMAP_END_IDENTIFIER) || line.startsWith(SMAP_SECTION_START),
+ 1,
+ block -> addDebugEntryToBuilder(block.get(0), builder));
+ }
+
+ private static void addFileToBuilder(String entryLine, String filePath, StratumBuilder builder)
throws KotlinSourceDebugExtensionParserException {
// + <file_number_i> <file_name_i>
// <file_path_i>
@@ -221,22 +251,30 @@
return number;
}
- private static void addDebugEntryToBuilder(String debugEntry, ResultBuilder builder)
+ private static void addDebugEntryToBuilder(String debugEntry, StratumBuilder builder)
throws KotlinSourceDebugExtensionParserException {
- // <from>#<file>,<size>:<debug-line-position>
+ // <from>#<file>,<size>:<debug-line-position>,<size>
// or
// <from>#<file>:<debug-line-position>
// All positions should define intervals for mappings.
try {
+ int size = 1;
int targetSplit = debugEntry.indexOf(':');
- int target = Integer.parseInt(debugEntry.substring(targetSplit + 1));
+ int targetRangeStart = targetSplit + 1;
+ int targetRangeSeparator = debugEntry.indexOf(',', targetSplit);
+ int target;
+ if (targetRangeSeparator > -1) {
+ target = Integer.parseInt(debugEntry.substring(targetRangeStart, targetRangeSeparator));
+ size = Integer.parseInt(debugEntry.substring(targetRangeSeparator + 1));
+ } else {
+ target = Integer.parseInt(debugEntry.substring(targetRangeStart));
+ }
String original = debugEntry.substring(0, targetSplit);
int fileIndexSplit = original.indexOf('#');
int originalStart = Integer.parseInt(original.substring(0, fileIndexSplit));
// The range may have a different end than start.
String fileAndEndRange = original.substring(fileIndexSplit + 1);
int endRangeCharPosition = fileAndEndRange.indexOf(',');
- int size = 1;
if (endRangeCharPosition > -1) {
// The file should be at least one number wide.
assert endRangeCharPosition > 0;
@@ -260,29 +298,31 @@
public static class Result {
- private final SegmentTree<Position> segmentTree;
+ private final SegmentTree<Position> inlineePositions;
+ private final SegmentTree<Position> calleePositions;
- public Result(SegmentTree<Position> segmentTree) {
- this.segmentTree = segmentTree;
+ public Result(SegmentTree<Position> inlineePositions, SegmentTree<Position> calleePositions) {
+ this.inlineePositions = inlineePositions;
+ this.calleePositions = calleePositions;
}
- public Map.Entry<Integer, Position> lookup(int point) {
- return segmentTree.findEntry(point);
+ public Map.Entry<Integer, Position> lookupInlinedPosition(int point) {
+ return inlineePositions.findEntry(point);
}
- public int size() {
- return segmentTree.size();
+ public Map.Entry<Integer, Position> lookupCalleePosition(int point) {
+ return calleePositions.findEntry(point);
+ }
+
+ public int inlinePositionsCount() {
+ return inlineePositions.size();
}
}
- public static class ResultBuilder {
+ public static class StratumBuilder {
SegmentTree<Position> segmentTree = new SegmentTree<>(false);
Map<Integer, Source> files = new HashMap<>();
-
- public Result build() {
- return new Result(segmentTree);
- }
}
public static class Source {
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index da825d1..ec1b7d0 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -60,6 +60,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -143,17 +144,17 @@
if (parsedData == null) {
return baseRemapper.createRemappedPosition(position);
}
- Map.Entry<Integer, KotlinSourceDebugExtensionParser.Position> currentPosition =
- parsedData.lookup(line);
- if (currentPosition == null) {
+ Map.Entry<Integer, KotlinSourceDebugExtensionParser.Position> inlinedPosition =
+ parsedData.lookupInlinedPosition(line);
+ if (inlinedPosition == null) {
return baseRemapper.createRemappedPosition(position);
}
- int delta = line - currentPosition.getKey();
- int originalPosition = currentPosition.getValue().getRange().from + delta;
+ int inlineeLineDelta = line - inlinedPosition.getKey();
+ int originalInlineeLine = inlinedPosition.getValue().getRange().from + inlineeLineDelta;
try {
- String binaryName = currentPosition.getValue().getSource().getPath();
+ String binaryName = inlinedPosition.getValue().getSource().getPath();
String nameAndDescriptor =
- lineToMethodMapper.lookupNameAndDescriptor(binaryName, originalPosition);
+ lineToMethodMapper.lookupNameAndDescriptor(binaryName, originalInlineeLine);
if (nameAndDescriptor == null) {
return baseRemapper.createRemappedPosition(position);
}
@@ -173,8 +174,20 @@
factory.createString(returnTypeDescriptor),
argumentDexStringDescriptors);
if (!inlinee.equals(position.method)) {
+ // We have an inline from a different method than the current position.
+ Entry<Integer, KotlinSourceDebugExtensionParser.Position> calleePosition =
+ parsedData.lookupCalleePosition(line);
+ if (calleePosition != null) {
+ // Take the first line as the callee position
+ position =
+ new Position(
+ calleePosition.getValue().getRange().from,
+ position.file,
+ position.method,
+ position.callerPosition);
+ }
return baseRemapper.createRemappedPosition(
- new Position(originalPosition, null, inlinee, position));
+ new Position(originalInlineeLine, null, inlinee, position));
}
// This is the same position, so we should really not mark this as an inline position. Fall
// through to the default case.
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
index 336e9a8..6ba334d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
@@ -53,9 +53,9 @@
"*E");
Result result = KotlinSourceDebugExtensionParser.parse(annotationData);
assertNotNull(result);
- assertEquals(1, result.size());
- assertEquals(1, (int) result.lookup(1).getKey());
- Position position = result.lookup(1).getValue();
+ assertEquals(1, result.inlinePositionsCount());
+ assertEquals(1, (int) result.lookupInlinedPosition(1).getKey());
+ Position position = result.lookupInlinedPosition(1).getValue();
assertEquals("EnumSwitch.kt", position.getSource().getFileName());
assertEquals("enumswitch/EnumSwitchKt", position.getSource().getPath());
assertEquals(1, position.getRange().from);
@@ -94,21 +94,21 @@
"*E");
Result result = KotlinSourceDebugExtensionParser.parse(annotationData);
assertNotNull(result);
- assertEquals(3, result.size());
- assertEquals(1, (int) result.lookup(1).getKey());
- assertEquals(23, (int) result.lookup(23).getKey());
- assertEquals(24, (int) result.lookup(24).getKey());
+ assertEquals(3, result.inlinePositionsCount());
+ assertEquals(1, (int) result.lookupInlinedPosition(1).getKey());
+ assertEquals(23, (int) result.lookupInlinedPosition(23).getKey());
+ assertEquals(24, (int) result.lookupInlinedPosition(24).getKey());
// Check that files are correctly parsed.
- Position pos1 = result.lookup(1).getValue();
+ Position pos1 = result.lookupInlinedPosition(1).getValue();
assertEquals("Main.kt", pos1.getSource().getFileName());
assertEquals("retrace/MainKt", pos1.getSource().getPath());
- Position pos2 = result.lookup(23).getValue();
+ Position pos2 = result.lookupInlinedPosition(23).getValue();
assertEquals("InlineFunction.kt", pos2.getSource().getFileName());
assertEquals("retrace/InlineFunctionKt", pos2.getSource().getPath());
- Position pos3 = result.lookup(24).getValue();
+ Position pos3 = result.lookupInlinedPosition(24).getValue();
assertEquals("InlineFunction.kt", pos3.getSource().getFileName());
assertEquals("retrace/InlineFunction", pos3.getSource().getPath());
@@ -315,8 +315,8 @@
"*E");
Result parsedResult = KotlinSourceDebugExtensionParser.parse(annotationData);
assertNotNull(parsedResult);
- assertEquals(24, (int) parsedResult.lookup(25).getKey());
- Position value = parsedResult.lookup(25).getValue();
+ assertEquals(24, (int) parsedResult.lookupInlinedPosition(25).getKey());
+ Position value = parsedResult.lookupInlinedPosition(25).getValue();
assertEquals(12, value.getRange().from);
assertEquals(13, value.getRange().to);
}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 24fad30..e0eb6ca 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -74,7 +74,6 @@
}
private int getObfuscatedLinePosition() {
- // TODO(b/185358363): This should go away when we correctly retrace.
return kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72) ? 43 : 32;
}
@@ -122,10 +121,7 @@
8,
FILENAME_INLINE),
LinePosition.create(
- mainSubject.asFoundMethodSubject(),
- 1,
- getObfuscatedLinePosition(),
- FILENAME_INLINE));
+ mainSubject.asFoundMethodSubject(), 1, 21, FILENAME_INLINE));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index ed842eb..032a370 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -122,7 +122,7 @@
LinePosition.stack(
LinePosition.create(
inlineExceptionStatic(kotlinInspector), 2, 8, FILENAME_INLINE_STATIC),
- LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 15, mainFileName));
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 9, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -155,7 +155,7 @@
2,
15,
FILENAME_INLINE_INSTANCE),
- LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 13, mainFileName));
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 7, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -187,7 +187,7 @@
inlineExceptionStatic(kotlinInspector), 3, 8, FILENAME_INLINE_STATIC),
// TODO(b/146399675): There should be a nested frame on
// retrace.NestedInlineFunctionKt.nestedInline(line 10).
- LinePosition.create(mainSubject.asFoundMethodSubject(), 3, 19, mainFileName));
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 3, 10, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -218,8 +218,8 @@
LinePosition.create(
inlineExceptionStatic(kotlinInspector), 2, 8, FILENAME_INLINE_STATIC),
// TODO(b/146399675): There should be a nested frame on
- // retrace.NestedInlineFunctionKt.nestedInlineOnFirstLine(line 15).
- LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 20, mainFileName));
+ // retrace.NestedInlineFunctionKt.nestedInline(line 10).
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 10, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}