@@ -44,6 +44,15 @@ extern "C" {
44
44
#define TIMED_WAITING 4
45
45
#define TERMINATED 5
46
46
47
+ #if JAVA_SPEC_VERSION >= 20
48
+ typedef struct J9ScopedValueBindingsWalkClasses {
49
+ J9Class *threadClass;
50
+ J9Class *virtualThreadClass;
51
+ J9Class *scopedValueCarrierClass;
52
+ J9Class *scopedValueSnapshotClass;
53
+ } J9ScopedValueBindingsWalkClasses;
54
+ #endif /* JAVA_SPEC_VERSION >= 20 */
55
+
47
56
jint
48
57
getJclThreadState (UDATA vmstate, jboolean started)
49
58
{
@@ -603,28 +612,131 @@ Java_java_lang_Thread_setExtentLocalCache(JNIEnv *env, jclass unusedClass, jobje
603
612
#endif /* JAVA_SPEC_VERSION >= 19 */
604
613
605
614
#if JAVA_SPEC_VERSION >= 20
615
+ /* *
616
+ * Frame walk iterator to find the most recent scoped value bindings on the stack.
617
+ *
618
+ * @param currentThread the current thread
619
+ * @param walkState the stack walk state
620
+ * @return J9_STACKWALK_KEEP_ITERATING or J9_STACKWALK_STOP_ITERATING
621
+ */
622
+ static UDATA
623
+ findScopedValueBindingsWalkFunction (J9VMThread *currentThread, J9StackWalkState *walkState)
624
+ {
625
+ UDATA rc = J9_STACKWALK_KEEP_ITERATING;
626
+
627
+ if (NULL != walkState->userData1 ) {
628
+ J9Method *method = walkState->method ;
629
+ J9Class *methodClass = J9_CLASS_FROM_METHOD (method);
630
+ J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD (method);
631
+ J9UTF8 *methodName = J9ROMMETHOD_NAME (romMethod);
632
+ J9ScopedValueBindingsWalkClasses *classes = (J9ScopedValueBindingsWalkClasses *)walkState->userData3 ;
633
+
634
+ if (((classes->threadClass == methodClass)
635
+ || (classes->virtualThreadClass == methodClass)
636
+ || (classes->scopedValueCarrierClass == methodClass))
637
+ && J9UTF8_LITERAL_EQUALS (J9UTF8_DATA (methodName), J9UTF8_LENGTH (methodName), " runWith" )
638
+ ) {
639
+ UDATA count = (UDATA)walkState->userData2 ;
640
+ rc = J9_STACKWALK_STOP_ITERATING;
641
+ /* runWith method should only have one instance of java.lang.ScopedValue$Snapshot. */
642
+ Assert_JCL_true ((NULL != walkState->userData1 ) && (1 == count));
643
+ } else if (0 == walkState->inlineDepth ) {
644
+ /* Reset userData1 and userData2 after iterating all O-slots of a method. */
645
+ walkState->userData1 = NULL ;
646
+ walkState->userData2 = (void *)0 ;
647
+ }
648
+ }
649
+
650
+ return rc;
651
+ }
652
+
653
+ /* *
654
+ * O-slot iterator to find the most recent scoped value bindings in the object slots.
655
+ *
656
+ * @param currentThread the current thread
657
+ * @param walkState the stack walk state
658
+ * @param slot pointer to slot containing an object pointer
659
+ * @param stackLocation pointer to the slot in the stack containing possibly compressed object pointer
660
+ */
661
+ static void
662
+ findScopedValueBindingsOSlotWalkFunction (J9VMThread *currentThread, J9StackWalkState *walkState, j9object_t *slot, const void *stackLocation)
663
+ {
664
+ j9object_t slotObject = *slot;
665
+ J9Class *clazz = J9OBJECT_CLAZZ (currentThread, slotObject);
666
+ J9ScopedValueBindingsWalkClasses *classes = (J9ScopedValueBindingsWalkClasses *)walkState->userData3 ;
667
+ J9Class *snapshotClass = (J9Class *)classes->scopedValueSnapshotClass ;
668
+
669
+ if (0 != isSameOrSuperClassOf (snapshotClass, clazz)) {
670
+ UDATA count = (UDATA)walkState->userData2 ;
671
+ walkState->userData1 = (void *)slotObject;
672
+ walkState->userData2 = (void *)(count + 1 );
673
+ }
674
+ }
675
+
606
676
/* *
607
677
* static native Object findScopedValueBindings();
608
678
*
609
- * Find the most recent scoped value bindings in the stack.
610
- * TODO: Complete the function description.
679
+ * Find the most recent scoped value bindings on the stack.
611
680
*
612
681
* @param env instance of JNIEnv
613
682
* @param unusedClass
614
- * @return jobject
683
+ * @return jobject the found scoped value bindings, or null if there are no
684
+ * scoped value bindings on the stack
615
685
*/
616
686
jobject JNICALL
617
687
Java_java_lang_Thread_findScopedValueBindings (JNIEnv *env, jclass unusedClass)
618
688
{
619
- /* Currently, this method is unused since there is no API or test that
620
- * invokes this method in OpenJ9. If the assertion ever triggers, the
621
- * draft implementation below should be completed and merged.
689
+ J9VMThread *currentThread = (J9VMThread *)env;
690
+ J9JavaVM *vm = currentThread->javaVM ;
691
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions ;
692
+ J9StackWalkState walkState = {0 };
693
+ J9ScopedValueBindingsWalkClasses classes = {0 };
694
+ jobject bindings = NULL ;
695
+
696
+ /* The goal is to find the first argument of the runWith method, which is an
697
+ * instance of java.lang.ScopedValue$Snapshot. The first argument cannot be
698
+ * reliably retrieved for compiled methods. There is supposed to be only one
699
+ * argument, which is an instance of java.lang.ScopedValue$Snapshot.
622
700
*
623
- * Issue: https://github.com/eclipse-openj9/openj9/issues/16677.
624
- * Implementation: https://github.com/eclipse-openj9/openj9/pull/17402.
701
+ * The O-slot iterator is invoked before the frame iterator. So, all O-slots
702
+ * are looked at until the runWith method is reached. For the runWith method,
703
+ * there is an assertion which checks that only a single instance of
704
+ * java.lang.ScopedValue$Snapshot is found. If the assertion triggers, it
705
+ * will indicate that the Java implementation of the runWith method has been
706
+ * modified.
625
707
*/
626
- Assert_JCL_unimplemented ();
627
- return NULL ;
708
+ walkState.walkThread = currentThread;
709
+ walkState.skipCount = 0 ;
710
+ walkState.userData1 = NULL ;
711
+ /* Counter to keep track of the java.lang.ScopedValue$Snapshot instances
712
+ * encountered for a method.
713
+ */
714
+ walkState.userData2 = (void *)0 ;
715
+ walkState.frameWalkFunction = findScopedValueBindingsWalkFunction;
716
+ walkState.objectSlotWalkFunction = findScopedValueBindingsOSlotWalkFunction;
717
+ walkState.flags = J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_MAINTAIN_REGISTER_MAP
718
+ | J9_STACKWALK_ITERATE_O_SLOTS | J9_STACKWALK_VISIBLE_ONLY;
719
+
720
+ vmFuncs->internalEnterVMFromJNI (currentThread);
721
+
722
+ /* Preload classes once, before walking the stack. Better for performance in
723
+ * comparison to loading them multiple times within the stackwalk callbacks.
724
+ */
725
+ classes.threadClass = J9VMJAVALANGTHREAD (vm);
726
+ classes.virtualThreadClass = J9VMJAVALANGVIRTUALTHREAD (vm);
727
+ classes.scopedValueCarrierClass = J9VMJAVALANGSCOPEDVALUECARRIER (vm);
728
+ classes.scopedValueSnapshotClass = J9VMJAVALANGSCOPEDVALUESNAPSHOT (vm);
729
+
730
+ walkState.userData3 = (void *)&classes;
731
+
732
+ vm->walkStackFrames (currentThread, &walkState);
733
+
734
+ if (NULL != walkState.userData1 ) {
735
+ bindings = VM_VMHelpers::createLocalRef (env, (j9object_t )walkState.userData1 );
736
+ }
737
+ vmFuncs->internalExitVMToJNI (currentThread);
738
+
739
+ return bindings;
628
740
}
629
741
630
742
/* *
0 commit comments