7070import com .oracle .graal .python .runtime .object .PythonObjectFactory ;
7171import com .oracle .graal .python .util .PythonUtils ;
7272import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
73+ import com .oracle .truffle .api .bytecode .BytecodeNode ;
7374import com .oracle .truffle .api .bytecode .Instruction ;
74- import com .oracle .truffle .api .bytecode .SourceInformation ;
75+ import com .oracle .truffle .api .bytecode .SourceInformationTree ;
7576import com .oracle .truffle .api .dsl .Bind ;
7677import com .oracle .truffle .api .dsl .Cached ;
7778import 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