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);