6363
6464import com .oracle .truffle .api .CallTarget ;
6565import com .oracle .truffle .api .CompilerDirectives ;
66+ import com .oracle .truffle .api .Truffle ;
6667import com .oracle .truffle .api .TruffleStackTrace ;
6768import com .oracle .truffle .api .TruffleStackTraceElement ;
6869import com .oracle .truffle .api .bytecode .BytecodeConfig ;
70+ import com .oracle .truffle .api .bytecode .BytecodeLocal ;
6971import com .oracle .truffle .api .bytecode .BytecodeNode ;
7072import com .oracle .truffle .api .bytecode .BytecodeParser ;
7173import com .oracle .truffle .api .bytecode .BytecodeRootNode ;
7274import com .oracle .truffle .api .bytecode .BytecodeRootNodes ;
7375import com .oracle .truffle .api .bytecode .ConstantOperand ;
76+ import com .oracle .truffle .api .bytecode .ContinuationResult ;
7477import com .oracle .truffle .api .bytecode .GenerateBytecode ;
7578import com .oracle .truffle .api .bytecode .GenerateBytecodeTestVariants ;
7679import com .oracle .truffle .api .bytecode .GenerateBytecodeTestVariants .Variant ;
80+ import com .oracle .truffle .api .bytecode .Instruction ;
7781import com .oracle .truffle .api .bytecode .Operation ;
7882import com .oracle .truffle .api .dsl .Bind ;
7983import com .oracle .truffle .api .dsl .Specialization ;
8084import com .oracle .truffle .api .exception .AbstractTruffleException ;
8185import com .oracle .truffle .api .frame .FrameDescriptor ;
86+ import com .oracle .truffle .api .frame .FrameInstance ;
87+ import com .oracle .truffle .api .frame .FrameInstanceVisitor ;
8288import com .oracle .truffle .api .interop .ArityException ;
8389import com .oracle .truffle .api .interop .InteropLibrary ;
8490import com .oracle .truffle .api .interop .TruffleObject ;
@@ -166,10 +172,7 @@ public void testThrow() {
166172 Assert .fail ();
167173 } catch (TestException e ) {
168174 List <TruffleStackTraceElement > elements = TruffleStackTrace .getStackTrace (e );
169- assertEquals (nodes .length , elements .size ());
170- for (int i = 0 ; i < nodes .length ; i ++) {
171- assertStackElement (elements .get (i ), nodes [i ], false );
172- }
175+ assertStackElements (nodes , elements , "c.ThrowError" , "c.Call" , false );
173176 }
174177 }
175178 }
@@ -192,10 +195,7 @@ public void testThrowBehindInterop() {
192195 Assert .fail ();
193196 } catch (TestException e ) {
194197 List <TruffleStackTraceElement > elements = TruffleStackTrace .getStackTrace (e );
195- assertEquals (nodes .length , elements .size ());
196- for (int i = 0 ; i < nodes .length ; i ++) {
197- assertStackElement (elements .get (i ), nodes [i ], false );
198- }
198+ assertStackElements (nodes , elements , "c.ThrowErrorBehindInterop" , "c.Call" , false );
199199 }
200200 }
201201 }
@@ -216,10 +216,7 @@ public void testCapture() {
216216
217217 for (int repeat = 0 ; repeat < REPEATS ; repeat ++) {
218218 List <TruffleStackTraceElement > elements = (List <TruffleStackTraceElement >) outer .getCallTarget ().call ();
219- assertEquals (nodes .length , elements .size ());
220- for (int i = 0 ; i < nodes .length ; i ++) {
221- assertStackElement (elements .get (i ), nodes [i ], false );
222- }
219+ assertStackElements (nodes , elements , "c.CaptureStack" , "c.Call" , false );
223220 }
224221 }
225222
@@ -244,15 +241,112 @@ public void testCaptureWithSources() {
244241
245242 for (int repeat = 0 ; repeat < REPEATS ; repeat ++) {
246243 List <TruffleStackTraceElement > elements = (List <TruffleStackTraceElement >) outer .getCallTarget ().call ();
247- assertEquals (nodes .length , elements .size ());
248- for (int i = 0 ; i < nodes .length ; i ++) {
249- assertStackElement (elements .get (i ), nodes [i ], true );
250- }
244+ assertStackElements (nodes , elements , "c.CaptureStack" , "c.Call" , true );
251245 }
252246 }
253247
254- private void assertStackElement (TruffleStackTraceElement element , StackTraceTestRootNode target , boolean checkSources ) {
255- assertSame (target .getCallTarget (), element .getTarget ());
248+ @ Test
249+ @ SuppressWarnings ("unchecked" )
250+ public void testValidateFrameInstances () {
251+ int depth = run .depth ;
252+ StackTraceTestRootNode [] nodesConstant = new StackTraceTestRootNode [run .depth ];
253+ StackTraceTestRootNode [] nodes = chainCalls (depth , b -> {
254+ b .beginRoot ();
255+ b .emitDummy ();
256+ b .emitValidateFrameInstances (run , nodesConstant , "c.Call" );
257+ b .endRoot ();
258+ }, true , false );
259+ StackTraceTestRootNode outer = nodes [nodes .length - 1 ];
260+ System .arraycopy (nodes , 0 , nodesConstant , 0 , depth );
261+
262+ for (int repeat = 0 ; repeat < REPEATS ; repeat ++) {
263+ outer .getCallTarget ().call ();
264+ }
265+ }
266+
267+ @ Test
268+ @ SuppressWarnings ("unchecked" )
269+ public void testCaptureResumed () {
270+ int dept = run .depth ;
271+ StackTraceTestRootNode [] nodes = chainResumes (dept , b -> {
272+ b .beginRoot ();
273+ b .beginYield ();
274+ b .emitLoadNull ();
275+ b .endYield ();
276+ b .beginReturn ();
277+ b .emitCaptureStack ();
278+ b .endReturn ();
279+ b .endRoot ();
280+ }, false );
281+ StackTraceTestRootNode outer = nodes [nodes .length - 1 ];
282+
283+ for (int repeat = 0 ; repeat < REPEATS ; repeat ++) {
284+ ContinuationResult outerCont = (ContinuationResult ) outer .getCallTarget ().call ();
285+ List <TruffleStackTraceElement > elements = (List <TruffleStackTraceElement >) outerCont .continueWith (null );
286+ assertStackElements (nodes , elements , "c.CaptureStack" , "c.Resume" , false );
287+ }
288+ }
289+
290+ @ Test
291+ @ SuppressWarnings ("unchecked" )
292+ public void testCaptureResumedWithSources () {
293+ int dept = run .depth ;
294+ Source s = Source .newBuilder (BytecodeDSLTestLanguage .ID , "root0" , "root0.txt" ).build ();
295+ StackTraceTestRootNode [] nodes = chainResumes (dept , b -> {
296+ b .beginSource (s );
297+ b .beginSourceSection (0 , "root0" .length ());
298+ b .beginRoot ();
299+ b .beginYield ();
300+ b .emitLoadNull ();
301+ b .endYield ();
302+ b .beginReturn ();
303+ b .emitCaptureStack ();
304+ b .endReturn ();
305+ b .endRoot ();
306+ b .endSourceSection ();
307+ b .endSource ();
308+ }, true );
309+ StackTraceTestRootNode outer = nodes [nodes .length - 1 ];
310+
311+ for (int repeat = 0 ; repeat < REPEATS ; repeat ++) {
312+ ContinuationResult outerCont = (ContinuationResult ) outer .getCallTarget ().call ();
313+ List <TruffleStackTraceElement > elements = (List <TruffleStackTraceElement >) outerCont .continueWith (null );
314+ assertStackElements (nodes , elements , "c.CaptureStack" , "c.Resume" , true );
315+ }
316+ }
317+
318+ @ Test
319+ @ SuppressWarnings ("unchecked" )
320+ public void testValidateResumedFrameInstances () {
321+ int depth = run .depth ;
322+ StackTraceTestRootNode [] nodesConstant = new StackTraceTestRootNode [run .depth ];
323+ StackTraceTestRootNode [] nodes = chainResumes (depth , b -> {
324+ b .beginRoot ();
325+ b .beginYield ();
326+ b .emitLoadNull ();
327+ b .endYield ();
328+ b .emitValidateFrameInstances (run , nodesConstant , "c.Resume" );
329+ b .endRoot ();
330+ }, false );
331+ StackTraceTestRootNode outer = nodes [nodes .length - 1 ];
332+ System .arraycopy (nodes , 0 , nodesConstant , 0 , depth );
333+
334+ for (int repeat = 0 ; repeat < REPEATS ; repeat ++) {
335+ ContinuationResult outerCont = (ContinuationResult ) outer .getCallTarget ().call ();
336+ outerCont .continueWith (null );
337+ }
338+ }
339+
340+ private void assertStackElements (StackTraceTestRootNode [] nodes , List <TruffleStackTraceElement > elements , String firstElementInstruction , String chainedElementInstruction , boolean checkSources ) {
341+ assertEquals (nodes .length , elements .size ());
342+ assertStackElement (elements .get (0 ), nodes [0 ], firstElementInstruction , checkSources );
343+ for (int i = 1 ; i < nodes .length ; i ++) {
344+ assertStackElement (elements .get (i ), nodes [i ], chainedElementInstruction , checkSources );
345+ }
346+ }
347+
348+ private void assertStackElement (TruffleStackTraceElement element , StackTraceTestRootNode target , String instruction , boolean checkSources ) {
349+ assertEquals (target , run .interpreter .variant .cast (element .getTarget ()));
256350 assertNotNull (element .getLocation ());
257351 BytecodeNode bytecode = target .getBytecodeNode ();
258352 if (run .interpreter .cached ) {
@@ -261,7 +355,7 @@ private void assertStackElement(TruffleStackTraceElement element, StackTraceTest
261355 } else {
262356 assertSame (bytecode , element .getLocation ());
263357 }
264- assertEquals (bytecode . getInstructionsAsList (). get ( 1 ). getBytecodeIndex ( ), element .getBytecodeIndex ());
358+ assertEquals (getBytecodeIndexOfFirstInstruction ( bytecode , instruction ), element .getBytecodeIndex ());
265359
266360 Object interopObject = element .getGuestObject ();
267361 InteropLibrary lib = InteropLibrary .getFactory ().create (interopObject );
@@ -276,7 +370,16 @@ private void assertStackElement(TruffleStackTraceElement element, StackTraceTest
276370 } catch (UnsupportedMessageException ex ) {
277371 fail ("Interop object could not receive message: " + ex );
278372 }
373+ }
279374
375+ private static int getBytecodeIndexOfFirstInstruction (BytecodeNode bytecode , String instructionName ) {
376+ for (Instruction i : bytecode .getInstructions ()) {
377+ if (instructionName .equals (i .getName ())) {
378+ return i .getBytecodeIndex ();
379+ }
380+ }
381+ fail ("No instruction found matching instruction name " + instructionName );
382+ return -1 ;
280383 }
281384
282385 @ Test
@@ -345,15 +448,64 @@ private StackTraceTestRootNode[] chainCalls(int depth, BytecodeParser<StackTrace
345448 return nodes ;
346449 }
347450
451+ private StackTraceTestRootNode [] chainResumes (int depth , BytecodeParser <StackTraceTestRootNodeBuilder > innerParser , boolean includeSources ) {
452+ // Constructs a chain of methods that call the previous method, then yield, and then resume
453+ // the previous method.
454+ StackTraceTestRootNode [] nodes = new StackTraceTestRootNode [depth ];
455+ nodes [0 ] = parse (innerParser );
456+ nodes [0 ].setName ("root0" );
457+ for (int i = 1 ; i < depth ; i ++) {
458+ int index = i ;
459+ String name = "root" + i ;
460+ Source s = includeSources ? Source .newBuilder (BytecodeDSLTestLanguage .ID , name , name + ".txt" ).build () : null ;
461+ nodes [i ] = parse (b -> {
462+ // x = prev_root()
463+ // yield
464+ // resume(x)
465+ if (includeSources ) {
466+ b .beginSource (s );
467+ b .beginSourceSection (0 , name .length ());
468+ }
469+ b .beginRoot ();
470+ BytecodeLocal calleeContinuation = b .createLocal ();
471+ b .beginStoreLocal (calleeContinuation );
472+ b .emitCall (nodes [index - 1 ].getCallTarget ());
473+ b .endStoreLocal ();
474+
475+ b .beginYield ();
476+ b .emitLoadConstant (index );
477+ b .endYield ();
478+
479+ b .beginReturn ();
480+ b .beginResume ();
481+ b .emitLoadLocal (calleeContinuation );
482+ b .emitLoadNull ();
483+ b .endResume ();
484+ b .endReturn ();
485+ b .endRoot ().depth = index ;
486+ if (includeSources ) {
487+ b .endSourceSection ();
488+ b .endSource ();
489+ }
490+ });
491+ nodes [i ].setName (name );
492+ }
493+ return nodes ;
494+ }
495+
348496 @ GenerateBytecodeTestVariants ({
349497 @ Variant (suffix = "CachedDefault" , configuration = //
350- @ GenerateBytecode (languageClass = BytecodeDSLTestLanguage .class , storeBytecodeIndexInFrame = false , enableUncachedInterpreter = false , boxingEliminationTypes = {int .class })),
498+ @ GenerateBytecode (languageClass = BytecodeDSLTestLanguage .class , storeBytecodeIndexInFrame = false , enableUncachedInterpreter = false , enableYield = true , boxingEliminationTypes = {
499+ int .class })),
351500 @ Variant (suffix = "UncachedDefault" , configuration = //
352- @ GenerateBytecode (languageClass = BytecodeDSLTestLanguage .class , storeBytecodeIndexInFrame = false , enableUncachedInterpreter = true , boxingEliminationTypes = {int .class })),
501+ @ GenerateBytecode (languageClass = BytecodeDSLTestLanguage .class , storeBytecodeIndexInFrame = false , enableUncachedInterpreter = true , enableYield = true , boxingEliminationTypes = {
502+ int .class })),
353503 @ Variant (suffix = "CachedBciInFrame" , configuration = //
354- @ GenerateBytecode (languageClass = BytecodeDSLTestLanguage .class , storeBytecodeIndexInFrame = true , enableUncachedInterpreter = false , boxingEliminationTypes = {int .class })),
504+ @ GenerateBytecode (languageClass = BytecodeDSLTestLanguage .class , storeBytecodeIndexInFrame = true , enableUncachedInterpreter = false , enableYield = true , boxingEliminationTypes = {
505+ int .class })),
355506 @ Variant (suffix = "UncachedBciInFrame" , configuration = //
356- @ GenerateBytecode (languageClass = BytecodeDSLTestLanguage .class , storeBytecodeIndexInFrame = true , enableUncachedInterpreter = true , boxingEliminationTypes = {int .class }))
507+ @ GenerateBytecode (languageClass = BytecodeDSLTestLanguage .class , storeBytecodeIndexInFrame = true , enableUncachedInterpreter = true , enableYield = true , boxingEliminationTypes = {
508+ int .class }))
357509 })
358510 public abstract static class StackTraceTestRootNode extends DebugBytecodeRootNode implements BytecodeRootNode {
359511
@@ -405,6 +557,14 @@ static Object doDefault(CallTarget target) {
405557 }
406558 }
407559
560+ @ Operation (storeBytecodeIndex = true )
561+ static final class Resume {
562+ @ Specialization
563+ static Object doDefault (ContinuationResult cont , Object resumeValue , @ Bind Node node ) {
564+ return cont .getContinuationCallTarget ().call (node , cont .getFrame (), resumeValue );
565+ }
566+ }
567+
408568 @ Operation (storeBytecodeIndex = true )
409569 static final class ThrowErrorBehindInterop {
410570
@@ -445,6 +605,64 @@ static Object doDefault(@Bind Node node) {
445605 return TruffleStackTrace .getStackTrace (ex );
446606 }
447607 }
608+
609+ @ SuppressWarnings ("truffle-interpreted-performance" )
610+ @ Operation (storeBytecodeIndex = true )
611+ // The parser should populate this array after parsing. This is a hack to expose the roots.
612+ @ ConstantOperand (type = Run .class , name = "run" )
613+ @ ConstantOperand (type = StackTraceTestRootNode [].class , name = "rootNodes" )
614+ @ ConstantOperand (type = String .class , name = "chainedInstruction" )
615+ static final class ValidateFrameInstances {
616+
617+ @ Specialization
618+ static void doDefault (Run run , StackTraceTestRootNode [] rootNodes , String chainedInstruction ) {
619+ FrameInstanceValidator visitor = new FrameInstanceValidator (run , rootNodes , chainedInstruction );
620+ Truffle .getRuntime ().iterateFrames (visitor );
621+ visitor .checkFinished ();
622+ }
623+ }
624+
625+ static final class FrameInstanceValidator implements FrameInstanceVisitor <Void > {
626+ public final Run run ;
627+ public final StackTraceTestRootNode [] targets ;
628+ public final String chainedFrameInstruction ;
629+ public int index = 0 ;
630+
631+ FrameInstanceValidator (Run run , StackTraceTestRootNode [] targets , String chainedFrameInstruction ) {
632+ this .run = run ;
633+ this .targets = targets ;
634+ this .chainedFrameInstruction = chainedFrameInstruction ;
635+ }
636+
637+ public Void visitFrame (FrameInstance frameInstance ) {
638+ if (index >= targets .length ) {
639+ fail ("Visited too many frames." );
640+ }
641+ StackTraceTestRootNode target = targets [index ];
642+
643+ assertEquals (target , run .interpreter .variant .cast (frameInstance .getCallTarget ()));
644+ BytecodeNode bytecode = target .getBytecodeNode ();
645+ if (index != 0 ) {
646+ assertNotNull (frameInstance .getCallNode ());
647+ if (run .interpreter .cached ) {
648+ assertSame (bytecode , BytecodeNode .get (frameInstance .getCallNode ()));
649+ assertSame (bytecode , BytecodeNode .get (frameInstance ));
650+ } else {
651+ assertSame (bytecode , frameInstance .getCallNode ());
652+ }
653+ assertEquals (getBytecodeIndexOfFirstInstruction (bytecode , chainedFrameInstruction ), frameInstance .getBytecodeIndex ());
654+ }
655+ index ++;
656+ return null ;
657+ }
658+
659+ public void checkFinished () {
660+ if (index != targets .length ) {
661+ fail ("Did not visit every frame. Index is " + index + " and there are " + targets .length + " expected frames." );
662+ }
663+ }
664+
665+ }
448666 }
449667
450668 @ ExportLibrary (InteropLibrary .class )
0 commit comments