Add ikvm.runtime.Util.getDelegateFromMethodHandle/getDelegateFromMethod - #738
Merged
Conversation
wasabii
marked this pull request as ready for review
August 11, 2026 12:25
Gives callers a supported way to obtain a .NET delegate for a Java method without reaching into IKVM.Runtime.ByteCodeHelper or the internal MH/MHV delegate family, neither of which is API. getDelegateFromMethodHandle adapts the handle to the requested delegate's signature with asType, which performs the boxing, primitive widening, ghost wrapping and receiver binding that a raw MethodBase from RuntimeJavaMethod.GetMethod() cannot express -- that method is one-to-many for remapped types and null for artificial ones. It then materializes the canonical invoke-exact delegate and binds its Invoke as the requested type. getDelegateFromMethod is a thin wrapper over Lookup.unreflect and unreflectConstructor. Access is checked as it is there, so callers must setAccessible an otherwise inaccessible member. Also factors the non-generic core out of MethodHandleUtil.GetDelegateForInvokeExact<T>. The generic form previously threw unconditionally when the handle already had a cached delegate, because the type test sat inside the null check; it now returns the cached delegate when the type matches. Net behaviour is unchanged, since the sole caller in ByteCodeHelper performs that same test before calling in.
wasabii
force-pushed
the
feature/delegate-from-methodhandle
branch
from
August 11, 2026 12:59
c254878 to
e6d6aa2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a supported way to obtain a .NET delegate for a Java method, so callers don't have to reach into
IKVM.Runtime.ByteCodeHelperor the internalMH/MHVdelegate family. Neither of those is API —ByteCodeHelperis public only because emitted IL calls it — but between them they are currently the only route.Why not expose a
MethodInfoThe obvious shape,
Method→MethodInfo, has no total answer.RuntimeJavaMethod.GetMethod()is one-to-many for remapped types (mb/mbHelper/mbNonvirtualHelper, seeRemappedJavaMethod), is null for artificial methods such as enumwrap(), and the correct choice depends on the call-site kind rather than the method. What it returns is also a compiler-internal entry point: a trailing syntheticCallerIDparameter whenHasCallerID, ghost-typed parameters as the ghost struct, and no null check on the receiver, so calling it directly yieldsNullReferenceExceptionin place ofNullPointerException.A delegate does have a total answer, and
MethodHandlealready resolves every one of those cases.API
getDelegateFromMethodHandlevalidates the type is a delegate with no by-ref or pointer parameters, adapts the handle to that delegate's own signature withasType— which performs the boxing, primitive widening, ghost wrapping and receiver binding — then materialises the canonical invoke-exact delegate and binds itsInvokeas the requested type. An unadaptable handle surfaces asWrongMethodTypeException.Taking the handle rather than only the
ExecutablemeansbindTo,insertArguments,unreflectSpecial(the super-call caseGetMethod()structurally cannot express),unreflectConstructorandunreflectGetter/Setterall compose through the same entry point.getDelegateFromMethodis a thin wrapper overunreflect/unreflectConstructor. Access is checked as it is there, so an inaccessible member needssetAccessiblefirst; this does not escalate on the caller's behalf.Also
Factors the non-generic core out of
MethodHandleUtil.GetDelegateForInvokeExact<T>. The generic form previously threw unconditionally when the handle already had a cached delegate, because theas Ttest sat inside the null check. It now returns the cached delegate when the type matches. Net behaviour is unchanged, since the sole caller inByteCodeHelperperforms that same test before calling in.Verification status — please do not merge on my say-so
Both managed builds compile clean (
IKVM.RuntimeandIKVM.Java, 0 errors). The 10 added tests have never executed. The local native build fails with pre-existing clang errors in stock OpenJDK C (libiava,libkrb5,libfontmanager,libawt_lwawt—-Wincompatible-pointer-typesunderikvm.clang.sdk 2.0.4), and a standalone harness needs the full IKVM home that same build produces. This branch touches four files, none underext/orsrc/lib*.Left unproven, and what CI should be read for:
UnsatisfiedLinkErrorat runtime, not at ikvmc timejava.lang.Object.hashCode), which is the case the design exists to handleGetDelegateMethodType→CreateMethodHandleDelegateTyperound-trip producing a signatureDelegate.CreateDelegateaccepts. Reasoning says identity for reference and primitive types, mismatching only for ghost parameters — which would be unusual in a user-authored delegate and fails loudly — but that is argument, not evidence.Draft until CI is green.