Proposal: java.lang.runtime.Carrier

Dan Heidinga heidinga at redhat.com
Wed Mar 9 13:42:36 UTC 2022


On Tue, Mar 8, 2022 at 4:29 PM Brian Goetz <brian.goetz at oracle.com> wrote:
>
> The minimal constraint is that the return type of the constructor MH is the same type as the argument type of the component MHs.

Agreed.  The types should match but they shouldn't be considered part
of the api.  I don't think (correct me if I'm wrong) that we want them
to "escape" and be baked into classfiles.  The implementation of the
anonymous class holding the fields ("holder object") should remain as
a hidden implementation detail.  One way to do that is to enforce that
the holder object is always hidden behind other public types like
Object.

> It would seem to me that preserving stronger types here dynamically gives MH combinators more room to optimize?

Only the outer edge of the MH chain would need to return (constructor)
/ take (component) Object.  The implementation of the MHs can use a
sharper type.  I don't think we gain any optimization abilities here
by exposing the sharper type - at worst there's an asType operation to
check the type but that shouldn't be visible in the performance
profile.

--Dan

>
> > On Mar 8, 2022, at 4:25 PM, Dan Heidinga <heidinga at redhat.com> wrote:
> >
> > Hi Jim,
> >
> > Will Carrier::constructor(MethodType) require that the MT's return
> > type is Object.class and thus return a MH that returns an Object?  Or
> > can other Classes / Interfaces be used as the return type?  Likewise,
> > will Carrier::component(MethodType, int) only accept Object as the
> > input argument?  The return type of the ::constructor generated MH
> > will need to be the same as the input arg of the ::component
> > accessors.
> >
> > And do you see this api primarily being used in invokedynamic
> > bootstrap methods?  I'm wondering how easy it will be to statically
> > determine the set of MethodHandles required by uses of this API.
> > Primarily for when we need to implement this in qbicc but also for
> > other native image style projects.
> >
> > --Dan
> >
> > On Thu, Mar 3, 2022 at 8:58 AM Jim Laskey <james.laskey at oracle.com> wrote:
> >>
> >> We propose to provide a runtime anonymous carrier class object generator; java.lang.runtime.Carrier. This generator class is designed to share anonymous classes when shapes are similar. For example, if several clients require objects containing two integer fields, then Carrier will ensure that each client generates carrier objects using the same underlying anonymous class.
> >>
> >> Providing this mechanism decouples the strategy for carrier class generation from the client facility. One could implement one class per shape; one class for all shapes (with an Object[]), or something in the middle; having this decision behind a bootstrap means that it can be evolved at runtime, and optimized differently for different situations.
> >>
> >> Motivation
> >>
> >> The String Templates JEP draft proposes the introduction of a TemplatedString object for the primary purpose of carrying the template and associated values derived from a template literal. To avoid value boxing, early prototypes described these carrierobjects using per-callsite anonymous classes shaped by value types, The use of distinct anonymous classes here is overkill, especially considering that many of these classes are similar; containing one or two object fields and/or one or two integral fields. Pattern matching has a similar issue when carrying the values for the holes of a pattern. With potentially hundreds (thousands?) of template literals or patterns per application, we need to find an alternate approach for these value carriers.
> >>
> >> Description
> >>
> >> In general terms, the Carrier class simply caches anonymous classes keyed on shape. To further increase similarity in shape, the ordering of value types is handled by the API and not in the underlying anonymous class. If one client requires an object with one object value and one integer value and a second client requires an object with one integer value and one object value, then both clients will use the same underlying anonymous class. Further, types are folded as either integer (byte, short, int, boolean, char, float), long (long, double) or object. [We've seen that performance hit by folding the long group into the integer group is significant, hence the separate group.]
> >>
> >> The Carrier API uses MethodType parameter types to describe the shape of a carrier. This incorporates with the primary use case where bootstrap methods need to capture indy non-static arguments. The API has three static methods;
> >>
> >> // Return a constructor MethodHandle for a carrier with components
> >> // aligning with the parameter types of the supplied methodType.
> >> static MethodHandle constructor(MethodType methodType)
> >>
> >> // Return a component getter MethodHandle for component i.
> >> static MethodHandle component(MethodType methodType, int i)
> >>
> >> // Return component getter MethodHandles for all the carrier's components.
> >> static MethodHandle[] components(MethodType methodType)
> >>
> >> Examples
> >>
> >> import java.lang.runtime.Carrier;
> >> ...
> >>
> >> // Define the carrier description.
> >> MethodType methodType =
> >>    MethodType.methodType(Object.class, byte.class, short.class,
> >>            char.class, int.class, long.class,
> >>            float.class, double.class,
> >>            boolean.class, String.class);
> >>
> >> // Fetch the carrier constructor.
> >> MethodHandle constructor = Carrier.constructor(methodType);
> >>
> >> // Create a carrier object.
> >> Object object = (Object)constructor.invokeExact((byte)0xFF, (short)0xFFFF,
> >>        'C', 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFFL,
> >>        1.0f / 3.0f, 1.0 / 3.0,
> >>        true, "abcde");
> >>
> >> // Get an array of accessors for the carrier object.
> >> MethodHandle[] components = Carrier.components(methodType);
> >>
> >> // Access fields.
> >> byte b = (byte)components[0].invokeExact(object);
> >> short s = (short)components[1].invokeExact(object);
> >> char c =(char)components[2].invokeExact(object);
> >> int i = (int)components[3].invokeExact(object);
> >> long l = (long)components[4].invokeExact(object);
> >> float f =(float)components[5].invokeExact(object);
> >> double d = (double)components[6].invokeExact(object);
> >> boolean tf (boolean)components[7].invokeExact(object);
> >> String s = (String)components[8].invokeExact(object));
> >>
> >> // Access a specific field.
> >> MethodHandle component = Carrier.component(methodType, 3);
> >> int ii = (int)component.invokeExact(object);
> >>
> >>
> >
>



More information about the amber-spec-experts mailing list