Proposal: java.lang.runtime.Carrier
Remi Forax
forax at univ-mlv.fr
Thu Mar 3 18:20:05 UTC 2022
For the pattern matching,
we also need a 'with' method, that return a method handle that takes a carrier and a value and return a new carrier with the component value updated.
static MethodHandle withComponent(MethodType methodType, int i)
// returns a mh (Carrier;T) -> Carrier with T the type of the component
It can be built on top of constructor() + component() but i think that i should be part of the API instead of every user of the Carrier API trying to re-implement it.
In term of spec, Jim, can you rename "component getter" to "component accessor" which is the term used by records.
Rémi
> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "Jim Laskey" <james.laskey at oracle.com>, "amber-spec-experts"
> <amber-spec-experts at openjdk.java.net>
> Sent: Thursday, March 3, 2022 4:29:51 PM
> Subject: Re: Proposal: java.lang.runtime.Carrier
> Thanks Jim.
> As background, (some form of) this code originated in a prototype for pattern
> matching, where we needed a carrier for a tuple (T, U, V) to carry the results
> of a match from a deconstruction pattern (or other declared pattern) on the
> stack as a return value. We didn't want to spin a custom class per pattern, and
> we didn't want to commit to the actual layout, because we wanted to preserve
> the ability to switch later to a value class. So the idea is you describe the
> carrier you want as a MethodType, and there's a condy that gives you an MH that
> maps that shape of arguments to an opaque carrier (the constructor), and other
> condys that give you MHs that map from the carrier to the individual bindings.
> So pattern matching will stick those MHs in CP slots.
> The carrier might be some bespoke thing (e.g., record anon(T t, U u, V v)), or
> something that holds an Object[], or something with three int fields and two
> ref fields, or whatever the runtime decides to serve up.
> The template mechanism wants almost exactly the same thing for bundling the
> parameters for uninterprted template strings.
> Think of it as a macro-box; instead of boxing primitives to Object and Objects
> to varargs, there's a single boxing operation from a tuple to an opaque type.
> On 3/3/2022 8:57 AM, Jim Laskey 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 [ https://bugs.openjdk.java.net/browse/JDK-8273943 | 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 carrier
>> objects 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-observers
mailing list