[External] : Re: premain: Crash in Infinispan server code caused by NPE under MethodHandleNative::linkDynamicConstant
Ashutosh Mehra
asmehra at redhat.com
Wed Sep 25 01:36:44 UTC 2024
>
> I disabled the AOTClassInitializer::is_forced_preinit_class() call and
> synced AOTClassInitializer::can_archive_preinitialized_mirror() with my
> upstream PR. This seems to fix the error in your test case.
>
I verified that this change fixes the error in the testcase. Thanks for
fixing this issue.
- Ashutosh Mehra
On Tue, Sep 24, 2024 at 1:01 PM <ioi.lam at oracle.com> wrote:
> Hi Ashutosh,
>
> I disabled the AOTClassInitializer::is_forced_preinit_class() call and
> synced AOTClassInitializer::can_archive_preinitialized_mirror() with my
> upstream PR. This seems to fix the error in your test case.
>
> Could you give it a try?
>
>
> https://github.com/iklam/jdk/commit/df72d0ba8dc799767521c939a531c4128e58c636
>
> Thanks
>
> - Ioi
>
>
>
> https://github.com/iklam/jdk/commit/df72d0ba8dc799767521c939a531c4128e58c636
> On 9/24/24 8:24 AM, Ashutosh Mehra wrote:
>
> Hi Ioi,
> Thanks for sharing the code changes.
> The wildfly-elytron testcase passes with these changes, but I still get
> the NPE in PrimitiveClassDescImpl.<clinit> when running with my own
> testcase.
> I have pushed my test case to a repo [2], so you can try it as well. I
> have added some instructions to the Readme file.
> Let me know if you face any issues in running the test case.
>
> [2] https://github.com/ashu-mehra/leyden-testcase/
> <https://urldefense.com/v3/__https://github.com/ashu-mehra/leyden-testcase/__;!!ACWV5N9M2RV99hQ!JiRlHQ7c-vdXVJoc61a64CilNOg5VTLitO8JELhohSZdibAXZUEjeA38WbtygjLvPsQIQKUYXJ2_kA$>
>
> Thanks,
> - Ashutosh Mehra
>
>
> On Mon, Sep 23, 2024 at 11:24 PM <ioi.lam at oracle.com> wrote:
>
>> Hi Ashutosh,
>>
>> Thank you again for the summary of the issues.
>> I looked at the call paths again and I think two calls need to be moved
>>
>> (1) MethodType::createArchivedObjects() needs to be called after we have
>> executed all other Java code. So I put it before the
>> StringTable::allocate_shared_strings_array() call.
>>
>> (2) SystemDictionary::get_all_method_handle_intrinsics() needs to be done
>> inside the safepoint, so it can be sure to find all the method handle
>> intrinsic methods (which can be generated as the side effect of Java code
>> execution).
>>
>> I also added the archiving of the classData field.
>>
>> See my temporary change at
>> https://github.com/iklam/jdk/commit/eafaa9731ae89b93f3e20702fc4d5a12cb070149
>> <https://urldefense.com/v3/__https://github.com/iklam/jdk/commit/eafaa9731ae89b93f3e20702fc4d5a12cb070149__;!!ACWV5N9M2RV99hQ!JiRlHQ7c-vdXVJoc61a64CilNOg5VTLitO8JELhohSZdibAXZUEjeA38WbtygjLvPsQIQKXvE8zr1g$>
>>
>> With this change, I am able to successfully run the test in
>> https://github.com/tristantarrant/elytron-leyden
>> <https://urldefense.com/v3/__https://github.com/tristantarrant/elytron-leyden__;!!ACWV5N9M2RV99hQ!JiRlHQ7c-vdXVJoc61a64CilNOg5VTLitO8JELhohSZdibAXZUEjeA38WbtygjLvPsQIQKW7YFmGjQ$>
>> . I also modified the LambdaWithUseImplMethodHandle.java test to add a
>> similar test scenario.
>>
>> I agree with you that the code in
>> AOTClassInitializer::is_forced_preinit_class() is too ad-hoc, and might
>> change the order of class initialization. In the upstream PR, I have
>> changed the code to only assert that the listed of classes has been
>> initialized:
>>
>>
>> https://github.com/iklam/jdk/blob/5cc31ed60cc9597d63b86f20b95c964d4d1a6b84/src/hotspot/share/cds/aotClassInitializer.cpp#L66-L84
>> <https://urldefense.com/v3/__https://github.com/iklam/jdk/blob/5cc31ed60cc9597d63b86f20b95c964d4d1a6b84/src/hotspot/share/cds/aotClassInitializer.cpp*L66-L84__;Iw!!ACWV5N9M2RV99hQ!JiRlHQ7c-vdXVJoc61a64CilNOg5VTLitO8JELhohSZdibAXZUEjeA38WbtygjLvPsQIQKUJSR9oEw$>
>>
>> I will try to merge this version of the code back to the Leyden repo.
>>
>> I think by doing this, we can avoid direct manipulation of the class-init
>> order of the core-library classes. Instead, we just make sure that we
>> execute enough "normal" code during the assembly phase to ensure that these
>> classes are initialized in their normal order.
>>
>> What do you think?
>>
>> Thanks
>>
>> - Ioi
>>
>>
>> On 9/19/24 8:05 PM, Ashutosh Mehra wrote:
>>
>> Upon further investigation, it seems a simple patch might fix the problem
>>> with classData.
>>>
>>> I tested with
>>> test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java by
>>> running jtreg with -javaoptions:-XX:+AOTClassLinking
>>> Without the patch, I'd get a NullPointerException. With the patch, the
>>> test passes.
>>>
>>> It looks like classData is not treated as a static field of the class,
>>> but rather an instance field in the mirror. So classData wasn't copied by
>>> the loop in HeapShared::copy_preinitialized_mirror().
>>>
>>> Ashutosh, could you try it this fixes the crash on your side?
>>>
>>> Thanks
>>>
>>> - Ioi
>>>
>>> diff --git a/src/hotspot/share/cds/heapShared.cpp
>>> b/src/hotspot/share/cds/heapShared.cpp
>>> index 0e70778f057..62cf142f67a 100644
>>> --- a/src/hotspot/share/cds/heapShared.cpp
>>> +++ b/src/hotspot/share/cds/heapShared.cpp
>>> @@ -634,6 +634,8 @@ void HeapShared::copy_preinitialized_mirror(Klass*
>>> orig_k, oop orig_mirror, oop
>>> }
>>> }
>>>
>>> + java_lang_Class::set_class_data(m,
>>> java_lang_Class::class_data(orig_mirror));
>>> +
>>> // Class::reflectData use SoftReference, which cannot be archived.
>>> Set it
>>> // to null and it will be recreated at runtime.
>>> java_lang_Class::set_reflection_data(m, nullptr);
>>>
>>
>> Hi Ioi,
>>
>> This patch effectively does the same thing as my initial patch for
>> storing classData in the scratch mirror object [1].
>>
>> At this point I think it would be useful to do a quick recap of the
>> issues mentioned in this thread. There are 3 issues I have encountered so
>> far.
>> I was initially thinking of a solution that would cover all these
>> problems, but looking at these again, I feel these are orthogonal to each
>> other:
>>
>> 1. The missing classData in scratch mirrors which can be fixed by either
>> of the patches shared in this thread. They achieve the same goal and I
>> don't have any strong preference for any of them.
>>
>> 2. After fixing the classData, I ran into WrongMethodTypeException issue
>> [2] which I fixed by exchanging the order of indy resolution with the call
>> to MethodType::createArchivedObjects [3].
>> This change makes sense because we want to make sure all the MethodType
>> objects that can be archived are in MethodType internTable before calling
>> MethodType::createArchivedObjects.
>> I am not sure why LambdaWithUseImplMethodHandle.java didn't throw the
>> WrongMethodTypeException, as the Infinispan code does.
>>
>> 3. After fixing the WrongMethodTypeException I hit NPE due to the class
>> initialization cycle between PrimitiveClassDescImpl and ConstantDescs [4]
>> in one of my own testcase.
>> We are looking for a solution to this problem. I am not sure if we can
>> break this cycle by refactoring the Java code.
>> Nevertheless, I think it is fair to assume that there is no Java code
>> that can initiate initialization of PrimitiveClassDescImpl before
>> ConstantDescs, otherwise we would have hit the NPE earlier.
>> So if ConstantDescs is always expected to be initialized before
>> PrimitiveClassDescImpl then I think the VM code should also maintain
>> this assumption.
>> Now this brings us back to the forceful initialization done by the VM
>> during the assembly phase based on the forced_preinit_classes list in
>> aotClassInitializer.cpp:
>>
>> // TODO:
>> // This is needed since JDK-8338532. Without this, when
>> // archived heap objects are used, the class init order is not
>> // expected by the jdk/internal/constant bootstrap code and we
>> // will get a null pointer exception.
>> //
>> // When bootstraping has intricated/fragile order, it's probably
>> // better to archive all related classes in an initialized state
>> // (i.e., take a snapshot). The existing approach in
>> // heapShared::resolve_or_init_classes_for_subgraph_of() won't work.
>> "jdk/internal/constant/PrimitiveClassDescImpl",
>> "jdk/internal/constant/ReferenceClassDescImpl",
>> "java/lang/constant/ConstantDescs",
>> "sun/invoke/util/Wrapper",
>>
>> I wonder why we need to explicitly add PrimitiveClassDescImpl and
>> ReferenceClassDescImpl in this list.
>> Initialization of ConstantDescs would anyway trigger the initialization
>> of PrimitiveClassDescImpl and ReferenceClassDescImpl.
>> So if we only keep the entry for ConstantDescs and remove
>> PrimitiveClassDescImpl and ReferenceClassDescImpl from this list,
>> we can be sure that the forceful initialization by the VM would maintain
>> the same order of initialization as guided by the Java code,
>> and we will not hit the NPE when initializing PrimitiveClassDescImpl during
>> the assembly phase.
>> Does this make sense?
>>
>>
>> [1]
>> https://mail.openjdk.org/pipermail/leyden-dev/2024-September/000994.html
>> [2]
>> https://mail.openjdk.org/pipermail/leyden-dev/2024-September/000997.html
>> [3]
>> https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e
>> <https://urldefense.com/v3/__https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e__;!!ACWV5N9M2RV99hQ!KvYLs_vG0a4GO3ltinXQkxBG1kMhCAvmmM6MwODoKzOhhVjzNL80S_0mBzYvYyhYPQnGmXhAXSw78Q$>
>> [4]
>> https://mail.openjdk.org/pipermail/leyden-dev/2024-September/001018.html
>>
>> Thanks,
>> - Ashutosh Mehra
>>
>>
>> On Thu, Sep 19, 2024 at 8:51 PM <ioi.lam at oracle.com> wrote:
>>
>>> Upon further investigation, it seems a simple patch might fix the
>>> problem with classData.
>>>
>>> I tested with
>>> test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java by
>>> running jtreg with -javaoptions:-XX:+AOTClassLinking
>>> Without the patch, I'd get a NullPointerException. With the patch, the
>>> test passes.
>>>
>>> It looks like classData is not treated as a static field of the class,
>>> but rather an instance field in the mirror. So classData wasn't copied by
>>> the loop in HeapShared::copy_preinitialized_mirror().
>>>
>>> Ashutosh, could you try it this fixes the crash on your side?
>>>
>>> Thanks
>>>
>>> - Ioi
>>>
>>> diff --git a/src/hotspot/share/cds/heapShared.cpp
>>> b/src/hotspot/share/cds/heapShared.cpp
>>> index 0e70778f057..62cf142f67a 100644
>>> --- a/src/hotspot/share/cds/heapShared.cpp
>>> +++ b/src/hotspot/share/cds/heapShared.cpp
>>> @@ -634,6 +634,8 @@ void HeapShared::copy_preinitialized_mirror(Klass*
>>> orig_k, oop orig_mirror, oop
>>> }
>>> }
>>>
>>> + java_lang_Class::set_class_data(m,
>>> java_lang_Class::class_data(orig_mirror));
>>> +
>>> // Class::reflectData use SoftReference, which cannot be archived.
>>> Set it
>>> // to null and it will be recreated at runtime.
>>> java_lang_Class::set_reflection_data(m, nullptr);
>>>
>>>
>>>
>>> On 9/19/24 12:51 PM, ioi.lam at oracle.com wrote:
>>>
>>>
>>> On 9/19/24 7:44 AM, Ashutosh Mehra wrote:
>>>
>>> As I am cleaning up the code for upstreaming to mainline, I am going add
>>>> an equivalent check in the C code to filter out these indy call sites, so
>>>> they won't be resolved at all during the assembly phase. Otherwise, I will
>>>> run into problems described in
>>>> https://bugs.openjdk.org/browse/JDK-8290417
>>>
>>>
>>> Thanks for the link to the bug. The scenario described in that bug is
>>> exactly the same as the Infinispan case.
>>> So if we filter out such cases during indy resolution then it should
>>> resolve the Infinispan issue as well.
>>>
>>> A basic question: why can't CDS handle the lambda proxy class generated
>>> in useImplMethodHandle mode?
>>>
>>> If I remember correctly, it has to do with the shape of dynamically
>>> generated bytecode:
>>>
>>> public void accept(java.lang.Object);
>>> Code:
>>> 0: ldc #28 // Dynamic #0:_:Ljava/lang/invoke/MethodHandle;
>>> 2: aload_0
>>> 3: getfield #15 // Field arg$1:LTester;
>>> 6: aload_1
>>> 7: checkcast #30 // class java/lang/String
>>> 10: invokevirtual #36 // Method
>>> java/lang/invoke/MethodHandle.invokeExact:(LTester;Ljava/lang/String;)V
>>> 13: return
>>>
>>> The result of the "ldc" was not symbolically encoded in the generated
>>> class (as the generated class has no permission to access that method). So
>>> the MethodHandle is stored as a binary object in the mirror of this
>>> generated class (with the java.lang.Class::classData field).
>>>
>>> Plain CDS doesn't archive class mirrors, so the classData will be lost.
>>>
>>> With JEP 483, we should be able to preserve the classData, so I am not
>>> sure why the useImplMethodHandle case is still failing. My plan is to
>>> filter these out for now, but I will get back to it later when I have more
>>> time.
>>>
>>> Thanks
>>>
>>> - Ioi
>>>
>>>
>>>
>>> Thanks,
>>> - Ashutosh Mehra
>>>
>>>
>>> On Thu, Sep 19, 2024 at 1:45 AM <ioi.lam at oracle.com> wrote:
>>>
>>>> Hi Ashutosh,
>>>>
>>>> I have some update:
>>>>
>>>> The original crash was caused by the "useImplMethodHandle" code in
>>>> InnerClassLambdaMetafactory.java:
>>>>
>>>> // If the target class invokes a protected method inherited
>>>> from a
>>>> // superclass in a different package, or does 'invokespecial',
>>>> the
>>>> // lambda class has no access to the resolved method, or does
>>>> // 'invokestatic' on a hidden class which cannot be resolved by
>>>> name.
>>>> // Instead, we need to pass the live implementation method
>>>> handle to
>>>> // the proxy class to invoke directly. (javac prefers to avoid
>>>> this
>>>> // situation by generating bridges in the target class)
>>>> useImplMethodHandle =
>>>> (Modifier.isProtected(implInfo.getModifiers()) &&
>>>> !VerifyAccess.isSamePackage(targetClass,
>>>> implInfo.getDeclaringClass())) ||
>>>> implKind ==
>>>> MethodHandleInfo.REF_invokeSpecial ||
>>>> implKind ==
>>>> MethodHandleInfo.REF_invokeStatic && implClass.isHidden();
>>>>
>>>> As I am cleaning up the code for upstreaming to mainline, I am going
>>>> add an equivalent check in the C code to filter out these indy call sites,
>>>> so they won't be resolved at all during the assembly phase. Otherwise, I
>>>> will run into problems described in
>>>> https://bugs.openjdk.org/browse/JDK-8290417
>>>>
>>>> Once I get the filtering code working, I will integrate it back to
>>>> premain.
>>>>
>>>> >I am wondering if we can workaround class circularity issues by
>>>> recording class initialization order
>>>> >during training run and use that to guide the initialization during
>>>> assembly phase.
>>>>
>>>> In the production run we take different paths than the training run
>>>>
>>>> (1) some classes are aot-initialized (especially the enums)
>>>> (2) some classes make special CDS calls
>>>>
>>>> so I am not sure if it's possible to get the same initialization order
>>>> as in the training run (or assembly phase).
>>>>
>>>> (more below)
>>>> On 9/18/24 9:10 AM, Ashutosh Mehra wrote:
>>>>
>>>> Hi Ioi,
>>>>
>>>> I was having a similar circularity issue (but in production time) and I
>>>>> just added enough classes to make the NPE go away.
>>>>>
>>>>
>>>> I am wondering if you have a test case that reproduces the NPE which
>>>> prompted you to add these classes:
>>>>
>>>>
>>>> https://github.com/openjdk/leyden/blob/7781109154bf2af89854c7e13aa3e160bb82608e/src/hotspot/share/cds/aotClassInitializer.cpp#L65-L78
>>>> <https://urldefense.com/v3/__https://github.com/openjdk/leyden/blob/7781109154bf2af89854c7e13aa3e160bb82608e/src/hotspot/share/cds/aotClassInitializer.cpp*L65-L78__;Iw!!ACWV5N9M2RV99hQ!NYvqLbb_Ib2kSdm2fcYVLGby-AiB2TG7KtC1HQHVKLiM-_kRb5mOt8krV5IZfReaYZ0MPadDfa08hQ$>
>>>>
>>>> I commented this code and ran the tests under premain but didn't
>>>> hit the NPE.
>>>>
>>>> I forgot what the problem was, but it happened for very simple cases.
>>>>
>>>> Thanks
>>>>
>>>> - Ioi
>>>>
>>>>
>>>> Thanks,
>>>> - Ashutosh Mehra
>>>>
>>>>
>>>> On Tue, Sep 17, 2024 at 1:11 AM <ioi.lam at oracle.com> wrote:
>>>>
>>>>> Hi Ashutosh,
>>>>>
>>>>> So this looks like a potential bug (or feature) in the core lib code.
>>>>> When CDS forcefully initializes a class in an unexpected (or untested)
>>>>> order, the "initialization soup" fails.
>>>>>
>>>>> Perhaps a work-around would be to make some harmless calls at the
>>>>> place where CDS was calling into MethodType.createArchivedObjects(). E.g.,
>>>>> do something like this:
>>>>>
>>>>> + if (CDSConfig::is_dumping_invokedynamic()) {
>>>>> + // call into Java: jdk.internal.misc::warmupInvokeDynamic();
>>>>> + }
>>>>> // Rewrite and link classes
>>>>> log_info(cds)("Rewriting and linking classes ...");
>>>>>
>>>>> Maybe you can add a new Lambda expressions, MethodHandle invocations,
>>>>> etc, that would hopefully cause PrimitiveClassDescImpl and friends to be
>>>>> initialized in their natural order.
>>>>>
>>>>> Or call class.forName("java.lang.constant.ConstantDescs") ??
>>>>>
>>>>> BTW, you can see my comments in
>>>>> AOTClassInitializer::is_forced_preinit_class():
>>>>>
>>>>> // TODO:
>>>>> // This is needed since JDK-8338532. Without this, when
>>>>> // archived heap objects are used, the class init order is not
>>>>> // expected by the jdk/internal/constant bootstrap code and we
>>>>> // will get a null pointer exception.
>>>>> //
>>>>> // When bootstraping has intricated/fragile order, it's probably
>>>>> // better to archive all related classes in an initialized state
>>>>> // (i.e., take a snapshot). The existing approach in
>>>>> // heapShared::resolve_or_init_classes_for_subgraph_of() won't
>>>>> work.
>>>>> "jdk/internal/constant/PrimitiveClassDescImpl",
>>>>> "jdk/internal/constant/ReferenceClassDescImpl",
>>>>> "java/lang/constant/ConstantDescs",
>>>>> "sun/invoke/util/Wrapper",
>>>>>
>>>>> I was having a similar circularity issue (but in production time) and
>>>>> I just added enough classes to make the NPE go away. For your test case, if
>>>>> you manage to fix in in the assembly run but run into NPE in production
>>>>> run, you might need to add more classes to this list. Yes, it's a hack :-(
>>>>>
>>>>>
>>>>> Thanks
>>>>>
>>>>> - Ioi
>>>>> On 9/13/24 7:05 PM, Ashutosh Mehra wrote:
>>>>>
>>>>> This is turning out to be a real example of class initialization soup!
>>>>> As mentioned during the meeting, I am getting NPE in the assembly
>>>>> phase when testing the patch [0] that I proposed in my earlier mail
>>>>> using a test case inspired by the Infinispan code.
>>>>> NPE occurs when running the class initializer for
>>>>> PrimitiveClassDescImpl
>>>>> Interestingly, PrimitiveClassDescImpl is "forcefully" initialized by
>>>>> MetaspaceShared::link_shared_classes().
>>>>>
>>>>> I couldn't get a stack trace so I relied on exception logs (using
>>>>> -Xlog:exceptions=trace) to find the cause which indicate following frames
>>>>> on the stack:
>>>>>
>>>>> [0]
>>>>> jdk/internal/constant/MethodTypeDescImpl::validateArgument(Ljava/lang/constant/ClassDesc;)Ljava/lang/constant/ClassDesc; @
>>>>> bci 1
>>>>> [1]
>>>>> jdk/internal/constant/MethodTypeDescImpl::ofTrusted(Ljava/lang/constant/ClassDesc;[Ljava/lang/constant/ClassDesc;)Ljdk/internal/constant/MethodTypeDescImpl; @
>>>>> bci 27
>>>>> [2]
>>>>> java/lang/constant/ConstantDescs::ofConstantBootstrap(Ljava/lang/constant/ClassDesc;Ljava/lang/String;Ljava/lang/constant/ClassDesc;[Ljava/lang/constant/ClassDesc;)Ljava/lang/constant/DirectMethodHandleDesc; @
>>>>> bci 47
>>>>> [3] java/lang/constant/ConstantDescs::<clinit> @ bci 664
>>>>> [4]
>>>>> jdk/internal/constant/PrimitiveClassDescImpl::<init>(Ljava/lang/String;)V @
>>>>> bci 1
>>>>> [5]
>>>>> jdk/internal/constant/PrimitiveClassDescImpl::<clinit>(Ljava/lang/String;)V @
>>>>> bci 6
>>>>>
>>>>> Notice that invocation of PrimitiveClassDescImpl::<clinit> results in
>>>>> initialization of ConstantDescs class (see frame 3).
>>>>> ConstantDescs::<clinit> @ 664 corresponds to following java code:
>>>>>
>>>>> public static final DirectMethodHandleDesc BSM_CLASS_DATA_AT
>>>>> = ofConstantBootstrap(CD_MethodHandles, "classDataAt",
>>>>> CD_Object, CD_int);
>>>>>
>>>>> The last parameter CD_int is initialized as:
>>>>>
>>>>> public static final ClassDesc CD_int =
>>>>> PrimitiveClassDescImpl.CD_int;
>>>>>
>>>>> So, its value is obtained from PrimitiveClassDescImpl.CD_int which
>>>>> hasn't been initialized properly yet. As a result ConstantDescs::CD_int
>>>>> is assigned null which results in MethodTypeDescImpl::validateArgument
>>>>> throwing NPE later.
>>>>> There is a clear class initialization circularity involving
>>>>> PrimitiveClassDescImpl and ConstantDescs, and the result depends on
>>>>> which class gets initialized first.
>>>>>
>>>>> Without my patch this issue is not seen because PrimitiveClassDescImpl
>>>>> has already been initialized by the time
>>>>> MetaspaceShared::link_shared_classes() is called.
>>>>> Its initialization is triggered by the call to
>>>>> MethodType::createArchivedObjects().
>>>>> It also explains why my patch introduced this issue because it
>>>>> effectively moved the call to MethodType::createArchivedObjects()
>>>>> after MetaspaceShared::link_shared_classes().
>>>>>
>>>>> [0]
>>>>> https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e
>>>>> <https://urldefense.com/v3/__https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e__;!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhZK3GwoiA$>
>>>>>
>>>>> Thanks,
>>>>> - Ashutosh Mehra
>>>>>
>>>>>
>>>>> On Wed, Sep 11, 2024 at 3:12 PM Ashutosh Mehra <asmehra at redhat.com>
>>>>> wrote:
>>>>>
>>>>>> Regarding the WrongMethodTypeException that I mentioned in my
>>>>>> previous email (see pt 3),
>>>>>> this exception happens when lambda proxy class attempts to invoke the
>>>>>> MethodHandle it obtained from the classData:
>>>>>>
>>>>>> public void accept(java.lang.Object);
>>>>>> descriptor: (Ljava/lang/Object;)V
>>>>>> flags: (0x0001) ACC_PUBLIC
>>>>>> Code:
>>>>>> stack=3, locals=2, args_size=2
>>>>>> 0: ldc #26 // Dynamic
>>>>>> #0:_:Ljava/lang/invoke/MethodHandle;
>>>>>> 2: aload_0
>>>>>> 3: getfield #13 // Field
>>>>>> arg$1:Lorg/wildfly/security/WildFlyElytronBaseProvider;
>>>>>> 6: aload_1
>>>>>> 7: checkcast #28 // class
>>>>>> java/security/Provider$Service
>>>>>> 10: invokevirtual #34 // Method
>>>>>> java/lang/invoke/MethodHandle.invokeExact:(Lorg/wildfly/security/WildFlyElytronBaseProvider;Ljava/security/Provider$Service;)V
>>>>>> 13: return
>>>>>>
>>>>>> The scenario is during the assembly phase as part of the indy
>>>>>> resolution the MethodHandle for which the exception is thrown gets created.
>>>>>> Normally MethodHandle's type gets added in MethodType::internTable
>>>>>> but by the time indy resolution happens, JVM has already taken
>>>>>> snapshot of the MethodType::internTable through an upcall to
>>>>>> MethodType::createArchivedObjects().
>>>>>> As a result the AOTCache ends up with the MethodType object which is
>>>>>> not in AOTHolder.archivedMethodTypes.
>>>>>>
>>>>>> During the production run, when the jvm invokes the MethodHandle, it
>>>>>> searches for the MethodType corresponding to the signature passed at the
>>>>>> callsite.
>>>>>> As expected, it fails to find it in the AOTHolder.archivedMethodTypes,
>>>>>> so it creates a new instance of the MethodType.
>>>>>> But Invokers.checkExactType() relies on the MethodHandle's type to
>>>>>> be the same object as the MethodType object passed as parameter.
>>>>>>
>>>>>> static void checkExactType(MethodHandle mhM, MethodType
>>>>>> expected) {
>>>>>> MethodType targetType = mh.type();
>>>>>> if (targetType != expected)
>>>>>> throw newWrongMethodTypeException(targetType, expected);
>>>>>> }
>>>>>>
>>>>>> Hence, it throws WrongMethodTypeException though the two MT objects
>>>>>> have the same signature.
>>>>>>
>>>>>> To handle this scenario, I changed the order of indy resolution and upcall
>>>>>> to MethodType::createArchivedObjects() as:
>>>>>>
>>>>>> diff --git a/src/hotspot/share/cds/metaspaceShared.cpp
>>>>>> b/src/hotspot/share/cds/metaspaceShared.cpp
>>>>>> index df4bcadefa3..457716cac5b 100644
>>>>>> --- a/src/hotspot/share/cds/metaspaceShared.cpp
>>>>>> +++ b/src/hotspot/share/cds/metaspaceShared.cpp
>>>>>> @@ -751,6 +751,20 @@ void MetaspaceShared::link_shared_classes(bool
>>>>>> jcmd_request, TRAPS) {
>>>>>> if (CDSConfig::is_dumping_final_static_archive()) {
>>>>>> FinalImageRecipes::apply_recipes(CHECK);
>>>>>> }
>>>>>> +
>>>>>> +#if INCLUDE_CDS_JAVA_HEAP
>>>>>> + if (CDSConfig::is_dumping_invokedynamic()) {
>>>>>> + // This makes sure that the MethodType and MethodTypeForm tables
>>>>>> won't be updated
>>>>>> + // concurrently when we are saving their contents into a side
>>>>>> table.
>>>>>> + assert(CDSConfig::allow_only_single_java_thread(), "Required");
>>>>>> +
>>>>>> + JavaValue result(T_VOID);
>>>>>> + JavaCalls::call_static(&result, vmClasses::MethodType_klass(),
>>>>>> + vmSymbols::createArchivedObjects(),
>>>>>> + vmSymbols::void_method_signature(),
>>>>>> + CHECK);
>>>>>> + }
>>>>>> +#endif
>>>>>> }
>>>>>>
>>>>>> Note that indy resolution happens as part of FinalImageRecipes::apply_recipes(CHECK)
>>>>>> which is now invoked before the upcall to createArchivedObjects().
>>>>>> With this change I am able to run the application without any
>>>>>> exceptions.
>>>>>> My complete patch can be seen here:
>>>>>> https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e
>>>>>> <https://urldefense.com/v3/__https://github.com/ashu-mehra/leyden/commit/d8f99cce67df1c7b0f7ef8562676df438633a66e__;!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhZK3GwoiA$>
>>>>>> I will do more testing with this patch.
>>>>>>
>>>>>> @Ioi Lam <ioi.lam at oracle.com> do you have any feedback on this patch.
>>>>>>
>>>>>> Thanks,
>>>>>> - Ashutosh Mehra
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Wed, Sep 11, 2024 at 10:14 AM Ashutosh Mehra <asmehra at redhat.com>
>>>>>> wrote:
>>>>>>
>>>>>>> Hi Andrew,
>>>>>>> Thanks for sharing the initial investigation.
>>>>>>> I have been looking into this and have a few of things to add to
>>>>>>> your analysis:
>>>>>>>
>>>>>>> 1. As you mentioned the classData for the lambda
>>>>>>> class WildFlyElytronBaseProvider$$Lambda is null.
>>>>>>> The classData is stored in the mirror object of the InstanceKlass
>>>>>>> when the class is defined through JVM_LookupDefineClass.
>>>>>>> However, when we create the scratch mirror object (which get stored
>>>>>>> in the AOT cache) the classData is not populated.
>>>>>>> See
>>>>>>> https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/hotspot/share/classfile/javaClasses.cpp#L1128-L1131
>>>>>>> <https://urldefense.com/v3/__https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/hotspot/share/classfile/javaClasses.cpp*L1128-L1131__;Iw!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhbKjzG3gw$>
>>>>>>>
>>>>>>> Handle classData; // set to null. Will be reinitialized at runtime
>>>>>>> Handle mirror;
>>>>>>> Handle comp_mirror;
>>>>>>> allocate_mirror(k, /*is_scratch=*/true, protection_domain,
>>>>>>> classData, mirror, comp_mirror, CHECK);
>>>>>>>
>>>>>>> So this explains why the call to classData(caller.lookupClass())
>>>>>>> returned null.
>>>>>>>
>>>>>>> 2. In the mainline there is a check
>>>>>>> in InnerClassLambdaMetafactory.java for the particular code pattern used by
>>>>>>> the application.
>>>>>>> If this code pattern is found then the lambda proxy class is not
>>>>>>> included in the CDS archive.
>>>>>>> See
>>>>>>> https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java#L163-L170
>>>>>>> <https://urldefense.com/v3/__https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java*L163-L170__;Iw!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhZl2S9tQg$>
>>>>>>> and
>>>>>>> https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java#L246
>>>>>>> <https://urldefense.com/v3/__https://github.com/openjdk/leyden/blob/d23b9f2d5e3523cc547337da59327ed86a6057a3/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java*L246__;Iw!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhYsI3Xx0g$>
>>>>>>>
>>>>>>> // If the target class invokes a protected method inherited
>>>>>>> from a
>>>>>>> // superclass in a different package, or does
>>>>>>> 'invokespecial', the
>>>>>>> // lambda class has no access to the resolved method, or does
>>>>>>> // 'invokestatic' on a hidden class which cannot be resolved
>>>>>>> by name.
>>>>>>> // Instead, we need to pass the live implementation method
>>>>>>> handle to
>>>>>>> // the proxy class to invoke directly. (javac prefers to
>>>>>>> avoid this
>>>>>>> // situation by generating bridges in the target class)
>>>>>>> useImplMethodHandle =
>>>>>>> (Modifier.isProtected(implInfo.getModifiers()) &&
>>>>>>>
>>>>>>> !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) ||
>>>>>>> implKind ==
>>>>>>> MethodHandleInfo.REF_invokeSpecial ||
>>>>>>> implKind ==
>>>>>>> MethodHandleInfo.REF_invokeStatic && implClass.isHidden();
>>>>>>>
>>>>>>> In premain lambda proxy classes get included in the AOT cache as a
>>>>>>> result of indy resolution and that mechanism doesn't have this kind of
>>>>>>> check.
>>>>>>>
>>>>>>> 3. For the null classData problem mentioned above, I tried to fix it
>>>>>>> by storing classData in the scratch mirror using the following patch:
>>>>>>>
>>>>>>> diff --git a/src/hotspot/share/classfile/javaClasses.cpp
>>>>>>> b/src/hotspot/share/classfile/javaClasses.cpp
>>>>>>> index bd8141adbcc..41766e98093 100644
>>>>>>> --- a/src/hotspot/share/classfile/javaClasses.cpp
>>>>>>> +++ b/src/hotspot/share/classfile/javaClasses.cpp
>>>>>>> @@ -1094,9 +1094,9 @@ void java_lang_Class::create_mirror(Klass* k,
>>>>>>> Handle class_loader,
>>>>>>> }
>>>>>>> if (CDSConfig::is_dumping_heap()) {
>>>>>>> if (CDSConfig::is_dumping_protection_domains()) {
>>>>>>> - create_scratch_mirror(k, protection_domain, CHECK);
>>>>>>> + create_scratch_mirror(k, protection_domain, classData,
>>>>>>> CHECK);
>>>>>>> } else {
>>>>>>> - create_scratch_mirror(k, Handle() /* null
>>>>>>> protection_domain*/, CHECK);
>>>>>>> + create_scratch_mirror(k, Handle() /* null
>>>>>>> protection_domain*/, classData, CHECK);
>>>>>>> }
>>>>>>> }
>>>>>>> } else {
>>>>>>> @@ -1117,7 +1117,7 @@ void java_lang_Class::create_mirror(Klass* k,
>>>>>>> Handle class_loader,
>>>>>>> // Note: we archive the "scratch mirror" instead of
>>>>>>> k->java_mirror(), because the
>>>>>>> // latter may contain dumptime-specific information that cannot be
>>>>>>> archived
>>>>>>> // (e.g., ClassLoaderData*, or static fields that are modified by
>>>>>>> Java code execution).
>>>>>>> -void java_lang_Class::create_scratch_mirror(Klass* k, Handle
>>>>>>> protection_domain, TRAPS) {
>>>>>>> +void java_lang_Class::create_scratch_mirror(Klass* k, Handle
>>>>>>> protection_domain, Handle classData, TRAPS) {
>>>>>>> if (k->class_loader() != nullptr &&
>>>>>>> k->class_loader() != SystemDictionary::java_platform_loader()
>>>>>>> &&
>>>>>>> k->class_loader() != SystemDictionary::java_system_loader()) {
>>>>>>> @@ -1125,9 +1125,11 @@ void
>>>>>>> java_lang_Class::create_scratch_mirror(Klass* k, Handle protection_domain,
>>>>>>> return;
>>>>>>> }
>>>>>>>
>>>>>>> - Handle classData; // set to null. Will be reinitialized at runtime
>>>>>>> + //Handle classData; // set to null. Will be reinitialized at
>>>>>>> runtime
>>>>>>> Handle mirror;
>>>>>>> Handle comp_mirror;
>>>>>>> allocate_mirror(k, /*is_scratch=*/true, protection_domain,
>>>>>>> classData, mirror, comp_mirror, CHECK);
>>>>>>>
>>>>>>> if (comp_mirror() != nullptr) {
>>>>>>> diff --git a/src/hotspot/share/classfile/javaClasses.hpp
>>>>>>> b/src/hotspot/share/classfile/javaClasses.hpp
>>>>>>> index bc49a0861a7..7ec2a2556dd 100644
>>>>>>> --- a/src/hotspot/share/classfile/javaClasses.hpp
>>>>>>> +++ b/src/hotspot/share/classfile/javaClasses.hpp
>>>>>>> @@ -263,7 +263,7 @@ class java_lang_Class : AllStatic {
>>>>>>>
>>>>>>> // Archiving
>>>>>>> static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
>>>>>>> - static void create_scratch_mirror(Klass* k, Handle
>>>>>>> protection_domain, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
>>>>>>> + static void create_scratch_mirror(Klass* k, Handle
>>>>>>> protection_domain, Handle classData, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
>>>>>>> static bool restore_archived_mirror(Klass *k, Handle
>>>>>>> class_loader, Handle module,
>>>>>>> Handle protection_domain,
>>>>>>> TRAPS)
>>>>>>> NOT_CDS_JAVA_HEAP_RETURN_(false);
>>>>>>>
>>>>>>> But this resulted in a different exception:
>>>>>>>
>>>>>>> Exception in thread "main" java.lang.ExceptionInInitializerError
>>>>>>> at com.redhat.leyden.Main.main(Main.java:7)
>>>>>>> Caused by: java.lang.invoke.WrongMethodTypeException: handle's
>>>>>>> method type (WildFlyElytronBaseProvider,Service)void but found
>>>>>>> (WildFlyElytronBaseProvider,Service)void
>>>>>>> at
>>>>>>> java.base/java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:521)
>>>>>>> at
>>>>>>> java.base/java.lang.invoke.Invokers.checkExactType(Invokers.java:530)
>>>>>>> at
>>>>>>> java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)
>>>>>>> at
>>>>>>> org.wildfly.security.WildFlyElytronBaseProvider$$Lambda/0x80000000c.accept(Unknown
>>>>>>> Source)
>>>>>>> at
>>>>>>> org.wildfly.security.WildFlyElytronBaseProvider.putMakedPasswordImplementations(WildFlyElytronBaseProvider.java:112)
>>>>>>> at
>>>>>>> org.wildfly.security.WildFlyElytronBaseProvider.putPasswordImplementations(WildFlyElytronBaseProvider.java:107)
>>>>>>> at
>>>>>>> org.wildfly.security.password.WildFlyElytronPasswordProvider.<init>(WildFlyElytronPasswordProvider.java:43)
>>>>>>> at
>>>>>>> org.wildfly.security.password.WildFlyElytronPasswordProvider.<clinit>(WildFlyElytronPasswordProvider.java:36)
>>>>>>> ... 1 more
>>>>>>>
>>>>>>> The exception message is strange because the handle's method type
>>>>>>> and the expected type are both symbolically the same.
>>>>>>> I am debugging this exception at the moment.
>>>>>>>
>>>>>>> Thanks,
>>>>>>> - Ashutosh Mehra
>>>>>>>
>>>>>>>
>>>>>>> On Wed, Sep 11, 2024 at 6:03 AM Andrew Dinn <adinn at redhat.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> Oops, sorry, I debugged this a few days ago! Correction to a few
>>>>>>>> details:
>>>>>>>>
>>>>>>>> On 11/09/2024 10:39, Andrew Dinn wrote:
>>>>>>>> > A crash due to an NPE was observed in the Infinispan (Data Grid)
>>>>>>>> server
>>>>>>>> > app when deployed using the Leyden EA. The crash still manifests
>>>>>>>> with
>>>>>>>> > the latest premain code. The crash happens below an application
>>>>>>>> call
>>>>>>>> > which employs a method reference as argument
>>>>>>>> >
>>>>>>>> > putMakedPasswordImplementations(this::putService, this);
>>>>>>>>
>>>>>>>> The called method in turn calls consumer.accept
>>>>>>>>
>>>>>>>> consumer.accept(new Service(provider,
>>>>>>>> PASSWORD_FACTORY_TYPE, algorithm,
>>>>>>>> "org.wildfly.security.password.impl.PasswordFactorySpiImpl",
>>>>>>>> emptyList,
>>>>>>>> emptyMap));
>>>>>>>>
>>>>>>>> which enters enters MethodHandleNative::linkDynamicConstant()
>>>>>>>>
>>>>>>>> > Debugging shows that the call to linkDynamicConstant() returns
>>>>>>>> null.
>>>>>>>> >
>>>>>>>> > A simple reproducer for the problem is available as a maven
>>>>>>>> project on
>>>>>>>> > github:
>>>>>>>> >
>>>>>>>> > https://github.com/tristantarrant/elytron-leyden
>>>>>>>> <https://urldefense.com/v3/__https://github.com/tristantarrant/elytron-leyden__;!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhZaZr1akQ$>
>>>>>>>> >
>>>>>>>> > The ReadMe provides an explanation of how to reproduce the
>>>>>>>> problem. I
>>>>>>>> > did so and the debugged to find out some of the details of what
>>>>>>>> is
>>>>>>>> > happening (see below) but did not fully clarify the problem. Help
>>>>>>>> from
>>>>>>>> > someone more conversant with the ins and outs of method handle
>>>>>>>> > bootstraps in premain would be welcome. Details follow.
>>>>>>>> >
>>>>>>>> > regards,
>>>>>>>> >
>>>>>>>> >
>>>>>>>> > Andrew Dinn
>>>>>>>> > -----------
>>>>>>>> >
>>>>>>>> > I downloaded the git repo and attached the Java sources using
>>>>>>>> Maven command
>>>>>>>> >
>>>>>>>> > $ mvn dependency:sources
>>>>>>>> >
>>>>>>>> > Having manifested the crash by following the instructions in the
>>>>>>>> README
>>>>>>>> > I reran the leyden JVM under gdb using the following commands to
>>>>>>>> enable
>>>>>>>> > Java debugging
>>>>>>>> >
>>>>>>>> > $ gdb ${LEYDEN_HOME}/bin/java
>>>>>>>> > (gdb) cd /path/to/mvn/project
>>>>>>>> > (gdb) run
>>>>>>>> >
>>>>>>>> -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005
>>>>>>>> > -classpath
>>>>>>>> >
>>>>>>>> /home/adinn/redhat/openjdk/infinispan/elytron-leyden/base/target/elytron-leyden-base-0.0.1-SNAPSHOT.jar:/home/adinn/.m2/repository/org/wildfly/security/wildfly-elytron-credential/
>>>>>>>> 2.5.1.
>>>>>>>> <https://urldefense.com/v3/__http://2.5.1.__;!!ACWV5N9M2RV99hQ!MnS3LSY2PXCCXbHvdyMfR6Nj57XH7Ey7aZuOLmQ__hhE_RhUrKnkzZ0uP8AwLZYcl8lAhhauJ-RjcQ$>Final/wildfly-elytron-credential-2.5.1.Final.jar:/home/adinn/.m2/repository/org/wildfly/security/wildfly-elytron-base/2.5.1.Final/wildfly-elytron-base-2.5.1.Final.jar
>>>>>>>> -XX:CacheDataStore=elytron.aot com.redhat.leyden.Main
>>>>>>>> >
>>>>>>>> > The problem manifests at WildflyElytronBaseProvider.java:112 in
>>>>>>>> method
>>>>>>>> > WildflyElytronBaseProvider::putMakedPasswordImplementations
>>>>>>>>
>>>>>>>> static void putMakedPasswordImplementations(Consumer<Service>
>>>>>>>> consumer, Provider provider) {
>>>>>>>> for (String algorithm : MASKED_ALGORITHMS) {
>>>>>>>> consumer.accept(new Service(provider,
>>>>>>>> PASSWORD_FACTORY_TYPE, algorithm,
>>>>>>>> "org.wildfly.security.password.impl.PasswordFactorySpiImpl",
>>>>>>>> emptyList,
>>>>>>>> emptyMap)); <== NPE under this call
>>>>>>>> }
>>>>>>>>
>>>>>>>>
>>>>>>>> > The source code for this method can be found in the following
>>>>>>>> source jar
>>>>>>>> >
>>>>>>>> >
>>>>>>>> >
>>>>>>>> ${M2_REPO}/org/wildfly/security/wildfly-elytron-base/2.5.1.Final/wildfly-elytron-base-2.5.1.Final-sources.jar
>>>>>>>> >
>>>>>>>> > (where M2_REPO will normally be ~/.m2/repository)
>>>>>>>> >
>>>>>>>> > Stepping into accept eventually enters
>>>>>>>> MethodHandleNative::linkDynamicConstant
>>>>>>>> > which in turn enters into ConstantBootstraps.makeConstant(). The
>>>>>>>> caller
>>>>>>>> > Class at this point is a lambda class which prints as
>>>>>>>> >
>>>>>>>> org.wildfly.security.WildflyElytronBaseProvider$$Lambda/0x800000000c
>>>>>>>> >
>>>>>>>> > Several steps further the code enters
>>>>>>>> BootstrapMethodInvoker::invoke
>>>>>>>> > (below the app method call but via 3 hidden frames) with
>>>>>>>> bootstrapMethod
>>>>>>>> > bound to a DirectMethodHandle. After several more steps this
>>>>>>>> enters
>>>>>>>> > DirectMethodHandle$Holder.invokeStatic which in turn calls
>>>>>>>> > MethodHandles::classData(Lookup,String,Class).
>>>>>>>> >
>>>>>>>> > At this point caller is a MethodHandleLookup for the lambda class
>>>>>>>> > Lambda/0x800000000c mentioned above. The following call
>>>>>>>> >
>>>>>>>> > Object classdata = classData(caller.lookupClass());
>>>>>>>> >
>>>>>>>> > returns null to DirectMethodHandle$Holder.invokeStatic which pops
>>>>>>>> the
>>>>>>>> > same result back out to BootstrapMethodInvoker::invoke at line 90
>>>>>>>> >
>>>>>>>> > if (type instanceof Class<?> c) {
>>>>>>>> > result = bootstrapMethod.invoke(caller,
>>>>>>>> name, c);
>>>>>>>> > <== null
>>>>>>>> >
>>>>>>>> > This null result pops back out as the value for the call to
>>>>>>>> > BootstrapMethodInvoker.invoke(),
>>>>>>>> ConstantBootstraps.makeConstant() and
>>>>>>>> > MethodHandleNative::linkDynamicConstant().
>>>>>>>> >
>>>>>>>>
>>>>>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/leyden-dev/attachments/20240924/cb233e80/attachment.htm>
More information about the leyden-dev
mailing list