[jep-334] Asymmetric method types of MHD vs MH

Michael van Acken michael.van.acken at gmail.com
Sat Dec 1 08:57:30 UTC 2018


Brian Goetz <brian.goetz at oracle.com> schrieb am Fr., 30. Nov. 2018 um
21:03 Uhr:

> Pushed; give it a try.
>

Thanks.  I've ported my code over.  Short summary: I like it!

Some observations (in no particular order):

1) Friction between ASM and j.l.c.* decreased even further.  For example,
going from DMHDesc to ASM Handle is now (pardon the Clojure)

  (let [k (.kind mhd)]
    (Handle. (.-refKind k) (td/internal-name (.owner mhd)) (.methodName mhd)
             (.lookupDescriptor mhd) (.-isInterface k)))

I don't read class files, but I expect the reverse direction of Handle ->
DMHDesc
is now similarly trivial.

2) One thing that ASM needs that j.l.c.* does not expose yet as a concept is
"internal name" (see above for an example).  This is the only mapping step
remaining, where I have to do low-level string manipulation to call into
ASM.

3) At one place I was instance testing on TypeDescriptor$OfMethod to decide
whether to do a visitMethodInsn() or a visitFieldInsn().  For now I replaced
that with a test on the first char of the descriptor string.  Having a
predicate
kind.isField() would have helped here.

4) There was a single place where the retired methodType() was useful to
me, as an accident of how I map Clojure function arities to methods.   A
predicate of Kind that indicates whether invocationType() has the curried
on parameter (across all of method, setter, getter) would help me here.

I do not miss the old methodType().  With just a single incidental
exception,
all of its uses were either a lookupDescriptor() or an invocationType() in
disguise.

-- Michael van Acken




> On 11/30/2018 10:46 AM, Brian Goetz wrote:
> > This suggests to me a much better stacking than my first proposal:
> >
> >  - We already have ofField() and ofConstructor, complete the set by
> > adding ofMethod(), for which the lookup descriptor is now completely
> > compatible with what is used in Lookup / reflection / CP;
> >  - Eliminate the total of() variant that takes a MTDesc;
> >  - Make the total of() variant that takes a String accept method
> > descriptor strings for method/ctor and field descriptor strings for
> > fields;
> >  - Provide a lookupDescriptor() accessor that will yield the
> > descriptor accepted by of()
> >
> >
> >
> > On 11/30/2018 3:27 AM, Michael van Acken wrote:
> >> Brian Goetz <brian.goetz at oracle.com <mailto:brian.goetz at oracle.com>>
> >> schrieb am Do., 29. Nov. 2018 um 17:27 Uhr:
> >>
> >>     MemberName calls them type and invocationType; unfortunately, what
> >>     MethodHandle calls type() is the invocation type, and this seems
> >>     pretty
> >>     natural for MethodHandle (what else are you going to do with a MH,
> >>     except invoke it?)  Which suggests either:
> >>
> >>       - MHDesc has a method called type(), and it is also be the
> >>     invocation
> >>     type;
> >>       - MHDesc has no method called type(), but instead has
> >>     invocationType()
> >>     and lookupType(), or some other better name.
> >>
> >>
> >> I believe this would match the usage of invocationType() in
> >> DynamicCallSiteDesc as well.
> >>
> >> Is the distinction "invocationType() is a thing of the operand stack"
> >> while
> >> "lookupType() is a thing of the constant pool" a useful mental model
> >> here?
> >>
> >>
> >>     I think the latter is probably safer.
> >>
> >>     Now, what should the lookupType() be?  There's again two ways to go:
> >>       - Return a ClassDesc for field ops, and a MethodTypeDesc for
> >>     method ops;
> >>       - Return a MethodTypeDesc for both
> >>
> >>
> >> My first association for "lookupType()" was the table of
> >> lookup.findXXX()
> >> methods with its clear separation MethodType / Class here:
> >>
> https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html
> >>
> >>
> >>
> >>     The latter is nicer from an API design perspective (there's a single
> >>     type), but less nice for those who want to use the API.
> >>
> >>     Michael has already given one example of where you'd want to use
> >>     this --
> >>     when calling ASM.  Here, there's lots of gymanstics needed to turn
> >>     the
> >>     method type into the right thing.
> >>
> >>     Another is if you want to feed a MHDesc back into the lookup
> >> methods:
> >>
> >>          MHDesc.of(mhd.kind(), mhd.owner(), mhd.name
> >>     <http://mhd.name>(), mhd.lookupType())
> >>
> >>
> >> Looks to me like this could be made to work with lookupType() returning
> >> either of String, TypeDescriptor, or MethodTypeDesc.
> >>
> >> Trying to understand the implications, I have two questions:
> >>
> >> What should the descriptorString overload of MHDesc.of() take e.g.
> >> for an
> >> instance getter?  In MHDesc.of(GETTER, clazz, name, X) the X could be
> >> one of "(R)T", "()T", or "T".  From my experience with ASM, I assume
> >> that
> >> the field descriptor variant "T" is closest to both constant pool
> >> representation
> >> and lookup.findGetter(), but there seem to be reasons for all three
> >> of them.
> >>
> >> Related to this: What is it of benefit in looking at a *field*
> >> DMHDesc through
> >> the lens of a method descriptor/MethodTypeDesc, outside of the point of
> >> view offered by invocationType()?
> >>
> >> -- Michael van Acken
> >>
> >>
> >>
> >>
> >>
> >>     On 11/23/2018 5:05 PM, John Rose wrote:
> >>     > I think MemberName calls them type and invocationType. That
> >>     seems like a good division of labor to me, but then I wrote it. :-)
> >>     >
> >>     >> On Nov 20, 2018, at 3:26 PM, Maurizio Cimadamore
> >>     <maurizio.cimadamore at oracle.com
> >>     <mailto:maurizio.cimadamore at oracle.com>> wrote:
> >>     >>
> >>     >> I agree - there are two types: the one based on real classfile
> >>     descriptor and the unified, curried one exposed by
> >>     MethodHandle::type(). The former is used for lookup, the latter
> >>     for invocation. Maybe lookupType() and invocationType() ?
> >>     >>
> >>     >> Maurizio
> >>     >>
> >>     >>> On 20/11/2018 18:40, Brian Goetz wrote:
> >>     >>> There are actually two types here, and they both should be
> >>     exposed.  One is a property of MHDesc (which is correctly called
> >>     type()) which describes the invocation type of the MH; the other
> >>     is a propertly of DirectMHDesc (which needs a new name) and which
> >>     is the type that one would provide to the lookup / place in the
> >>     NameAndType. We're currently in an inconsistent situation.
> >>     >>>
> >>     >>>> On 11/20/2018 10:20 AM, Remi Forax wrote:
> >>     >>>> Hi Brian,
> >>     >>>> while i agree that the API is primarily a mirror the JVMS
> >>     view and not the java.lang.invoke view,
> >>     >>>> one way to avoid that ambiguity is to rename methodType() to
> >>     type() (as in NameAndType).
> >>     >>>>
> >>     >>>> Rémi
> >>     >>>>
> >>     >>>> ----- Mail original -----
> >>     >>>>> De: "Brian Goetz" <brian.goetz at oracle.com
> >>     <mailto:brian.goetz at oracle.com>>
> >>     >>>>> À: "Michael van Acken" <michael.van.acken at gmail.com
> >>     <mailto:michael.van.acken at gmail.com>>, "amber-dev"
> >>     <amber-dev at openjdk.java.net <mailto:amber-dev at openjdk.java.net>>
> >>     >>>>> Envoyé: Mardi 20 Novembre 2018 14:28:06
> >>     >>>>> Objet: Re: [jep-334] Asymmetric method types of MHD vs MH
> >>     >>>>> Thanks for poking into these new APIs. This is a good
> >> question.
> >>     >>>>>
> >>     >>>>> Your mental model is a good first-order approximation, but
> >>     it has some
> >>     >>>>> risk; in the event of conflict, XxDesc's classes loyalty
> >>     will be to
> >>     >>>>> Constant_Xxx_info (the classfile format), rather than its
> >>     live-type
> >>     >>>>> counterpart.
> >>     >>>>>
> >>     >>>>> MethodHandle embeds the invocation mode (static, virtual,
> >>     interface,
> >>     >>>>> special) in the MH itself, so that you can treat the
> >>     receiver as just
> >>     >>>>> another parameter.  This is tremendously useful as the
> >>     primary purpose
> >>     >>>>> of method handles is to be invoked; it collapse four
> >>     invocation modes
> >>     >>>>> into one.
> >>     >>>>>
> >>     >>>>> DirectMethodHandleDesc models the classfile structure of an
> >>     invocation;
> >>     >>>>> the MethodRef_info/NameAndType/invocation bytecode -- as
> >>     shown in your
> >>     >>>>> example:
> >>     >>>>>
> >>     >>>>>       jshell> var mhd =
> >>     (MethodHandleDesc)mh.describeConstable().get()
> >>     >>>>>       mhd ==>
> >>     MethodHandleDesc[VIRTUAL/PrintStream::println()void]
> >>     >>>>>
> >>     >>>>> I think what you're saying is: there is enough information
> >>     here to correctly
> >>     >>>>> compose the type() -- for virtual/special/interface, curry
> >>     the owner type on --
> >>     >>>>> and that this would provide alignment with the live
> >>     MethodHandle behavior.
> >>     >>>>>
> >>     >>>>> But, it would separate from the classfile
> >>     Constant_MethodHandle_info behavior;
> >>     >>>>> what goes in the NameAndType here (for instance methods) is
> >>     the receiver-less
> >>     >>>>> version.  Hence the conflict.
> >>     >>>>>
> >>     >>>>> I think what is really going on here is that there are two
> >>     types -- the MH type,
> >>     >>>>> and the lookup type; the latter is a property of the
> >>     DMHDesc, while the former
> >>     >>>>> is a property of the MHDesc?
> >>     >>>>>
> >>     >>>>>
> >>     >>>>>> On 11/20/2018 7:06 AM, Michael van Acken wrote:
> >>     >>>>>> While tracking down unexpected behavior in my compiler, I
> >>     noticed that
> >>     >>>>>> MethodHandleDesc and MethodHandle differ in what they list
> >>     in their
> >>     >>>>>> method type.  I was expecting that the MHD matches the MH,
> >>     in that
> >>     >>>>>> the receiver of a virtual method is included in the
> >>     parameter list.
> >>     >>>>>> Instead,
> >>     >>>>>> it is omitted.  (See below for a jshell example.)
> >>     >>>>>>
> >>     >>>>>> As a compiler writer, I find the MH's unified view on
> >>     static and virtual
> >>     >>>>>> method types very helpful.  The current implementation's
> >>     MHD class
> >>     >>>>>> does not mirror this view, which means I would have to
> >>     special case the
> >>     >>>>>> method types for some operations.
> >>     >>>>>>
> >>     >>>>>> My mental model of the descriptor classes is, that they are
> >>     one-to-one
> >>     >>>>>> symbolic representations of their handle counterparts.  I
> >>     am very confused
> >>     >>>>>> by the difference in structure, with the receiver dropping
> >>     out of the
> >>     >>>>>> method type.
> >>     >>>>>>
> >>     >>>>>>
> >>     >>>>>> |  Welcome to JShell -- Version 12-internal
> >>     >>>>>> |  For an introduction type: /help intro
> >>     >>>>>>
> >>     >>>>>> jshell> import java.lang.invoke.*
> >>     >>>>>>
> >>     >>>>>> jshell> import java.lang.constant.*
> >>     >>>>>>
> >>     >>>>>> jshell> var l = MethodHandles.lookup()
> >>     >>>>>> l ==>
> >>     >>>>>>
> >>     >>>>>> jshell> var mt = MethodType.methodType(Void.TYPE)
> >>     >>>>>> mt ==> ()void
> >>     >>>>>>
> >>     >>>>>> jshell> var mh = l.findVirtual(java.io
> >>     <http://java.io>.PrintStream.class, "println", mt)
> >>     >>>>>> mh ==> MethodHandle(PrintStream)void
> >>     >>>>>>
> >>     >>>>>> jshell> mh.type()
> >>     >>>>>> $6 ==> (PrintStream)void
> >>     >>>>>>
> >>     >>>>>> jshell> mh.type().parameterCount()
> >>     >>>>>> $7 ==> 1
> >>     >>>>>>
> >>     >>>>>> jshell> var mhd =
> >>     (MethodHandleDesc)mh.describeConstable().get()
> >>     >>>>>> mhd ==> MethodHandleDesc[VIRTUAL/PrintStream::println()void]
> >>     >>>>>>
> >>     >>>>>> jshell> mhd.methodType()
> >>     >>>>>> $9 ==> MethodTypeDesc[()void]
> >>     >>>>>>
> >>     >>>>>> jshell> mhd.methodType().parameterCount()
> >>     >>>>>> $10 ==> 0
> >>     >>>>>>
> >>     >>>>>>
> >>     >>>>>> Regards,
> >>     >>>>>> Michael van Acken
> >>
> >
>
>


More information about the amber-dev mailing list