Skip to content

Commit 09af687

Browse files
committed
Implement co_lines properly using SourceInformationTree
1 parent 1122e3b commit 09af687

File tree

1 file changed

+75
-5
lines changed
  • graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code

1 file changed

+75
-5
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@
7070
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
7171
import com.oracle.graal.python.util.PythonUtils;
7272
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
73+
import com.oracle.truffle.api.bytecode.BytecodeNode;
7374
import com.oracle.truffle.api.bytecode.Instruction;
74-
import com.oracle.truffle.api.bytecode.SourceInformation;
75+
import com.oracle.truffle.api.bytecode.SourceInformationTree;
7576
import com.oracle.truffle.api.dsl.Bind;
7677
import com.oracle.truffle.api.dsl.Cached;
7778
import com.oracle.truffle.api.dsl.Fallback;
@@ -308,10 +309,7 @@ static Object lines(PCode self) {
308309
if (co != null) {
309310
if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) {
310311
PBytecodeDSLRootNode rootNode = (PBytecodeDSLRootNode) self.getRootNodeForExtraction();
311-
List<PTuple> lines = new ArrayList<>();
312-
for (SourceInformation sourceInfo : rootNode.getBytecodeNode().getSourceInformation()) {
313-
lines.add(factory.createTuple(new int[]{sourceInfo.getStartIndex(), sourceInfo.getEndIndex(), sourceInfo.getSourceSection().getStartLine()}));
314-
}
312+
List<PTuple> lines = computeLinesForBytecodeDSLInterpreter(rootNode, factory);
315313
tuple = factory.createTuple(lines.toArray());
316314
} else {
317315
BytecodeCodeUnit bytecodeCo = (BytecodeCodeUnit) co;
@@ -336,6 +334,78 @@ static Object lines(PCode self) {
336334
}
337335
return PyObjectGetIter.executeUncached(tuple);
338336
}
337+
338+
private static List<PTuple> computeLinesForBytecodeDSLInterpreter(PBytecodeDSLRootNode root, PythonObjectFactory factory) {
339+
BytecodeNode bytecodeNode = root.getBytecodeNode();
340+
List<int[]> triples = new ArrayList<>();
341+
traverseSourceInformationTree(bytecodeNode.getSourceInformationTree(), triples);
342+
return convertTripleBcisToInstructionIndices(bytecodeNode, factory, triples);
343+
}
344+
345+
/**
346+
* This function traverses the source information tree recursively to compute a list of
347+
* consecutive bytecode ranges with their corresponding line numbers.
348+
* <p>
349+
* Each node in the tree covers a bytecode range. Each child covers some sub-range. The
350+
* bytecodes covered by a particular node are the bytecodes within its range that are *not*
351+
* covered by the node's children.
352+
* <p>
353+
* For example, consider a node covering [0, 20] with children covering [4, 9] and [15, 18].
354+
* The node itself covers the ranges [0, 4], [9, 15], and [18, 20]. These ranges are
355+
* assigned the line number of the node.
356+
*/
357+
private static void traverseSourceInformationTree(SourceInformationTree tree, List<int[]> triples) {
358+
int startIndex = tree.getStartIndex();
359+
int startLine = tree.getSourceSection().getStartLine();
360+
for (SourceInformationTree child : tree.getChildren()) {
361+
if (startIndex < child.getStartIndex()) {
362+
// range before child.start is uncovered
363+
triples.add(new int[]{startIndex, child.getStartIndex(), startLine});
364+
}
365+
// recursively handle [child.start, child.end]
366+
traverseSourceInformationTree(child, triples);
367+
startIndex = child.getEndIndex();
368+
}
369+
370+
if (startIndex < tree.getEndIndex()) {
371+
// range after last_child.end is uncovered
372+
triples.add(new int[]{startIndex, tree.getEndIndex(), startLine});
373+
}
374+
}
375+
376+
/**
377+
* The bci ranges in the triples are not stable and can change when the bytecode is
378+
* instrumented. We create new triples with stable instruction indices by walking the
379+
* instructions.
380+
*/
381+
private static List<PTuple> convertTripleBcisToInstructionIndices(BytecodeNode bytecodeNode, PythonObjectFactory factory, List<int[]> triples) {
382+
List<PTuple> result = new ArrayList<>(triples.size());
383+
int tripleIndex = 0;
384+
int[] triple = triples.get(0);
385+
assert triple[0] == 0 : "the first bytecode range should start from 0";
386+
387+
int startInstructionIndex = 0;
388+
int instructionIndex = 0;
389+
for (Instruction instruction : bytecodeNode.getInstructions()) {
390+
if (instruction.getBytecodeIndex() == triple[1] /* end bci */) {
391+
result.add(factory.createTuple(new int[]{startInstructionIndex, instructionIndex, triple[2]}));
392+
startInstructionIndex = instructionIndex;
393+
triple = triples.get(++tripleIndex);
394+
assert triple[0] == instruction.getBytecodeIndex() : "bytecode ranges should be consecutive";
395+
}
396+
397+
if (!instruction.isInstrumentation()) {
398+
// Emulate CPython's fixed 2-word instructions.
399+
instructionIndex += 2;
400+
}
401+
}
402+
403+
result.add(factory.createTuple(new int[]{startInstructionIndex, instructionIndex, triple[2]}));
404+
assert tripleIndex == triples.size() : "every bytecode range should have been converted to an instruction range";
405+
406+
return result;
407+
}
408+
339409
}
340410

341411
@Builtin(name = "co_positions", minNumOfPositionalArgs = 1)

0 commit comments

Comments
 (0)