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.go...@oracle.com>
> To: "Jim Laskey" <james.las...@oracle.com>, "amber-spec-experts"
> <amber-spec-experts@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);

Reply via email to