Thanks for the good response Remi.
This is part of a larger FAQ, “why are MHs hard to use?”
Part of the answer is “they model bytecode behavior”.
(Perhaps they should have been called BytecodeBehaviorHandles.)
Lifting such raw VM-level behaviors up into Java source code is
necessary, but it will never be pleasant, at least not until Java has
enough capabilities in its syntax and type system to model such beasties
without extra layers of object wrapping.
That would include fully incorporating exception checking into the
generic type system, a hard problem. It would also involve making some
way to name a Java method (perhaps as `Foo::bar` or `myFoo::bar` or some
lambda) but get a MH out of the expression. Also some kinds of varargs
processing might be needed to “add suger” to varargs-related MH
transforms.
The amount of Java language engineering necessary for such things is so
large it will never be done, if MHs are the only use case. There are
far too many important improvements to make. (Thought experiment:
Should we drop some part of the pattern matching work in order to make
room for method handle exception checks? I thought not.)
Perhaps in the future there will come a time when exception checking is
tracked and/or `Foo::bar` syntax is made more generic, and that will
benefit MHs, but if it happens it will be for a list of weighty reasons,
apart from MHs.
For now, MH code has to be written in a low-level style in Java source
code. (But it works beautifully at the assembly code level, if you are
spinning bytecodes.) For example, Java source methods which exist to
process MHs should just declare `throws Throwable`. Then you catch the
non-existent throwables at the boundary.
You can make this a little easier on yourself, sometimes, if you write a
higher-order helper function that takes a Throwable-throwing lambda and
sanitizes it down to the exceptions you expect. Then there’s just one
clunky `catch`, and your low-level Throwable-throwing code goes inside
lambda bodies.
On 21 May 2023, at 6:47, Remi Forax wrote:
----- Original Message -----
From: "-" <liangchenb...@gmail.com>
To: "core-libs-dev" <core-libs-dev@openjdk.org>
Sent: Sunday, May 21, 2023 6:52:44 AM
Subject: Exposing checked exceptions that a MethodHandle can throw
Hello,
I am eliciting a discussion on the feasibility of tracking checked
exceptions thrown by a MethodHandle. It is already requested in
https://bugs.openjdk.org/browse/JDK-8268116 as it appears useful in
the development of Foreign Function Interface API.
At the bytecode level, checked exceptions are stored in an attribute
associated to a method.
https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-4.html#jvms-4.7.5
If you have a direct MethodHandle, you can already get the checked
exceptions using Lookup.revealDirect() + MethodHandleInfo.reflectAs().
Currently, explicit MethodHandle usages are hampered by the catch
block as the invoke methods are declared to throw any Throwable.
Could
it be possible that we specify the types of possible exceptions at
MethodHandle invocation, so that:
1. Javac accepts code that just call the handle without ugly
try-catch block
2. If the exceptions anticipated at invocation site are incompatible
with (i.e. more specific than) those declared by the invoked handle,
it can throw an exception like the existing
`WrongMethodTypeException`
eagerly.
The bug you reference seems to be about runtime information, but the
paragraph above is about type-checking information.
The question here is "is the Java type system good enough to track
checked exception in a backward compatible way ?"
Practically, I believe the answer is no, you can not compose function
with different type parameters representing exception, there is no
varargs of type parameters, there is no default type argument for a
type parameter, etc.
It's also significant departure of the way the method handle API is
created, the idea behind is that each combiner has the semantics of an
existing bytecode. But all invoke* bytecodes are oblivious to
exception. Said differently, the method handle API represent how the
JVM works, not how Java the language works.
Is such a plan feasible? Tracking of exceptions should be easy from
Direct MH and the MethodHandles combinators already, while the
invocation semantics update might be more complex, maybe in the form
of compiler-inserted stubs before the actual invoke calls. I wish
such
improvements can make MethodHandle more friendly to direct usages in
code, so users don't need to wrap MH invocations in try-catch blocks
everywhere.
see above.
Chen Liang
Rémi