--- /dev/null 2017-02-19 13:16:16.735392223 -0500 +++ new/src/java.base/share/classes/java/lang/invoke/Constables.java 2017-04-17 16:13:47.726226911 -0400 @@ -0,0 +1,835 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +import java.lang.annotation.TrackableConstant; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.lang.invoke.MethodHandleInfo.REF_getField; +import static java.lang.invoke.MethodHandleInfo.REF_getStatic; +import static java.lang.invoke.MethodHandleInfo.REF_invokeSpecial; +import static java.lang.invoke.MethodHandleInfo.REF_invokeStatic; +import static java.lang.invoke.MethodHandleInfo.REF_invokeVirtual; +import static java.lang.invoke.MethodHandleInfo.REF_newInvokeSpecial; +import static java.lang.invoke.MethodHandleInfo.REF_putField; +import static java.lang.invoke.MethodHandleInfo.REF_putStatic; + +/** + * Classes for representing entries in the constant pool, which can be + * intrinsified via methods in {@link Intrinsics}. + */ +public class Constables { + private static final Pattern TYPE_DESC = Pattern.compile("(\\[*)(V|I|J|S|B|C|F|D|Z|L[^/.\\[;][^.\\[;]*;)"); + + // @@@ These 9 static fields would be unneeded with target-typing of class literals + + /** {@link ClassConstant} describing the primitive type @{code void} */ + public static final ClassConstant VOID = ClassConstant.of("V"); + + /** {@link ClassConstant} describing the primitive type @{code int} */ + public static final ClassConstant INT = ClassConstant.of("I"); + + /** {@link ClassConstant} describing the primitive type @{code long} */ + public static final ClassConstant LONG = ClassConstant.of("J"); + + /** {@link ClassConstant} describing the primitive type @{code short} */ + public static final ClassConstant SHORT = ClassConstant.of("S"); + + /** {@link ClassConstant} describing the primitive type @{code char} */ + public static final ClassConstant CHAR = ClassConstant.of("C"); + + /** {@link ClassConstant} describing the primitive type @{code byte} */ + public static final ClassConstant BYTE = ClassConstant.of("B"); + + /** {@link ClassConstant} describing the primitive type @{code float} */ + public static final ClassConstant FLOAT = ClassConstant.of("F"); + + /** {@link ClassConstant} describing the primitive type @{code double} */ + public static final ClassConstant DOUBLE = ClassConstant.of("D"); + + /** {@link ClassConstant} describing the primitive type @{code boolean} */ + public static final ClassConstant BOOLEAN = ClassConstant.of("Z"); + + /** + * A {@linkplain Constable} which is associated with a type descriptor + * string that would be the target of a {@code NameAndType} constant. + * + * @param The type to which this constant pool entry resolves + */ + public interface ConstableWithDescriptor extends Constable { + /** + * Return the descriptor string associated with this constant pool entry + * + * @return the descriptor string + */ + String descriptorString(); + } + + /** + * A descriptor for a {@linkplain Class} constant. + */ + public static final class ClassConstant implements ConstableWithDescriptor> { + private final String descriptor; + + private ClassConstant(String descriptor) { + if (!TYPE_DESC.matcher(descriptor).matches()) + throw new IllegalArgumentException(String.format("%s is not a valid type descriptor", descriptor)); + this.descriptor = descriptor; + } + + /** + * Create a {@linkplain ClassConstant} from a descriptor string. + * + * @param descriptor the descriptor string + * @return a {@linkplain ClassConstant} describing the desired class + * @throws IllegalArgumentException if the descriptor string does not + * describe a valid class descriptor + */ + @TrackableConstant + public static ClassConstant of(String descriptor) { + return new ClassConstant(descriptor); + } + + // @@@ This method would be unneeded with target-typing of class literals + /** + * Create a {@linkplain ClassConstant} from a class literal. + * + * @param clazz the class literal + * @return a {@linkplain ClassConstant} describing the desired class + */ + @TrackableConstant + public static ClassConstant of(Class clazz) { + return of(classToDescriptor(clazz)); + } + + /** + * Create a {@linkplain ClassConstant} describing an array of the type + * described by this {@linkplain ClassConstant} + * + * @return a {@linkplain ClassConstant} describing an array type + */ + @TrackableConstant + public ClassConstant arrayOf() { + return of("[" + descriptor); + } + + /** + * Returns whether this {@linkplain ClassConstant} + * describes an array type + * @return whether this {@linkplain ClassConstant} + * describes an array type + */ + public boolean isArrayType() { + return descriptor.startsWith("["); + } + + /** + * Returns whether this {@linkplain ClassConstant} + * describes a primitive type + * @return whether this {@linkplain ClassConstant} + * describes a primitive type + */ + public boolean isPrimitive() { + return descriptor.length() == 1; + } + + /** + * If this {@linkplain ClassConstant} describes an array type, return + * the depth of the array, otherwise return zero + * @return the depth of the array type described by this + * {@linkplain ClassConstant} if it describes an array type, or zero + * otherwise + */ + public int arrayDepth() { + int i = 0; + while (i < descriptor.length() && descriptor.charAt(i) == '[') + i++; + return i; + } + + /** + * The component type of this {@linkplain ClassConstant} if it describes + * an array type, otherwise the type that it describes + * @return the component type of the type described by this + * {@linkplain ClassConstant} + */ + @TrackableConstant + public ClassConstant componentType() { + return of(descriptor.substring(arrayDepth())); + } + + @Override + @TrackableConstant + public String descriptorString() { + return descriptor; + } + + @Override + public Class resolveConstant(MethodHandles.Lookup lookup) throws ReflectiveOperationException { + if (descriptor.length() == 1) { + switch (descriptor) { + case "I": return int.class; + case "J": return long.class; + case "S": return short.class; + case "B": return byte.class; + case "C": return char.class; + case "F": return float.class; + case "D": return double.class; + case "Z": return boolean.class; + case "V": return void.class; + default: throw new IllegalStateException(descriptor); + } + } + String comp = componentType().descriptor; + if (comp.length() == 1) + return Class.forName(descriptor, true, lookup.lookupClass().getClassLoader()); + else { + Class clazz = Class.forName(comp.substring(1, comp.length() - 1).replace('/', '.'), true, lookup.lookupClass().getClassLoader()); + if (isArrayType()) { + for (int i=0; i { + private static Pattern pattern = Pattern.compile("\\((.*)\\)(.*)"); + + private final ClassConstant returnType; + private final ClassConstant[] argTypes; + + private MethodTypeConstant(ClassConstant returnType, ClassConstant[] argTypes) { + this.returnType = returnType; + this.argTypes = argTypes.clone(); + } + + /** + * Create a {@linkplain MethodTypeConstant} from a descriptor string. + * + * @param descriptor the descriptor string + * @return a {@linkplain MethodTypeConstant} describing the desired method type + * @throws IllegalArgumentException if the descriptor string does not + * describe a valid method descriptor + */ + @TrackableConstant + public static MethodTypeConstant of(String descriptor) { + Matcher matcher = pattern.matcher(descriptor); + if (!matcher.matches()) + throw new IllegalArgumentException(String.format("%s is not a valid method descriptor", descriptor)); + String paramTypes = matcher.group(1); + String returnType = matcher.group(2); + if (!TYPE_DESC.matcher(returnType).matches()) + throw new IllegalArgumentException(String.format("Invalid return type %s", returnType)); + List params = new ArrayList<>(); + matcher = TYPE_DESC.matcher(paramTypes); + while (matcher.regionStart() < paramTypes.length()) { + if (matcher.lookingAt()) { + params.add(matcher.group()); + matcher.region(matcher.end(), matcher.regionEnd()); + } + else + throw new IllegalArgumentException(String.format("Invalid parameter type: %s", paramTypes.substring(matcher.regionStart(), matcher.regionEnd()))); + } + return new MethodTypeConstant(ClassConstant.of(returnType), params.stream().map(ClassConstant::of).toArray(ClassConstant[]::new)); + } + + /** + * Create a {@linkplain MethodTypeConstant} from class constant describing + * the return type and parameter types. + * + * @param returnDescriptor a {@linkplain ClassConstant} describing the return type + * @param paramDescriptors {@linkplain ClassConstant}s describing the argument types type + * @return a {@linkplain MethodTypeConstant} describing the desired method type + */ + @TrackableConstant + public static MethodTypeConstant of(ClassConstant returnDescriptor, ClassConstant... paramDescriptors) { + return new MethodTypeConstant(returnDescriptor, paramDescriptors); + } + + // @@@ Would be unneeded with target-typing of class literals + /** + * Create a {@linkplain MethodTypeConstant} from class literals describing + * the return type and parameter types. + * + * @param returnType a class literal describing the return type + * @param paramTypes class literals describing the argument types type + * @return a {@linkplain MethodTypeConstant} describing the desired method type + */ + @TrackableConstant + public static MethodTypeConstant of(Class returnType, Class... paramTypes) { + return new MethodTypeConstant(ClassConstant.of(returnType), Stream.of(paramTypes).map(ClassConstant::of).toArray(ClassConstant[]::new)); + } + + /** + * Get the return type of the method type described by this {@linkplain MethodTypeConstant} + * @return the return type + */ + @TrackableConstant + public ClassConstant returnType() { + return returnType; + } + + /** + * Get the number of parameters of the method type described by + * this {@linkplain MethodTypeConstant} + * @return the number of parameters + */ + @TrackableConstant + public int parameterCount() { + return argTypes.length; + } + + /** + * Get the parameter type of the index'th parameter of the method type + * described by this {@linkplain MethodTypeConstant} + * + * @param index the index of the parameter to retrieve + * @return the parameter type + * @throws IndexOutOfBoundsException if the index is outside the half-open + * range {[0, parameterCount)} + */ + @TrackableConstant + public ClassConstant parameterType(int index) { + return argTypes[index]; + } + + /** + * Return a {@linkplain MethodTypeConstant} that is identical to + * this one, except the return type is changed to the provided value + * @param returnType the new return type + * @return the new method type descriptor + */ + @TrackableConstant + public MethodTypeConstant changeReturnType(ClassConstant returnType) { + return of(returnType, argTypes); + } + + /** + * Return a {@linkplain MethodTypeConstant} that is identical to this one, + * except that a single parameter type has been changed to the provided + * value + * @param index the index of the parameter to change + * @param paramType the new parameter type + * @return the new method type descriptor + * @throws IndexOutOfBoundsException if the index is outside the half-open + * range {[0, parameterCount)} + */ + @TrackableConstant + public MethodTypeConstant changeParameterType(int index, ClassConstant paramType) { + ClassConstant[] newArgs = argTypes.clone(); + newArgs[index] = paramType; + return of(returnType, newArgs); + } + + /** + * Return a {@linkplain MethodTypeConstant} that is identical to this one, + * except that a range of parameters have been removed + * @param start the index of the first parameter to remove + * @param end the index after the last parameter to remove + * @return the new method type descriptor + * @throws IndexOutOfBoundsException if {@code start} is outside the half-open + * range {[0, parameterCount)}, or {@code end} is outside the closed range + * {@code [0, parameterCount]} + */ + @TrackableConstant + public MethodTypeConstant dropParameterTypes(int start, int end) { + if (start < 0 || start >= argTypes.length || end < 0 || end > argTypes.length) + throw new IndexOutOfBoundsException(); + else if (start > end) + throw new IllegalArgumentException(String.format("Range (%d, %d) not valid for size %d", start, end, argTypes.length)); + ClassConstant[] newArgs = new ClassConstant[argTypes.length - (end - start)]; + System.arraycopy(argTypes, 0, newArgs, 0, start); + System.arraycopy(argTypes, end, newArgs, start, argTypes.length - end); + return of(returnType, newArgs); + } + + /** + * Return a {@linkplain MethodTypeConstant} that is identical to this one, + * except that a range of parameters have been inserted + * @param pos the index at which to insert the first inserted parameter + * @param paramTypes the new parameter types to insert + * @return the new method type descriptor + * @throws IndexOutOfBoundsException if {@code pos} is outside the closed-open + * range {[0, parameterCount]} + */ + @TrackableConstant + public MethodTypeConstant insertParameterTypes(int pos, ClassConstant... paramTypes) { + if (pos < 0 || pos > argTypes.length) + throw new IndexOutOfBoundsException(pos); + ClassConstant[] newArgs = new ClassConstant[argTypes.length + paramTypes.length]; + System.arraycopy(argTypes, 0, newArgs, 0, pos); + System.arraycopy(paramTypes, 0, newArgs, pos, paramTypes.length); + System.arraycopy(argTypes, pos, newArgs, pos+paramTypes.length, argTypes.length - pos); + return of(returnType, newArgs); + } + + @Override + public MethodType resolveConstant(MethodHandles.Lookup lookup) throws ReflectiveOperationException { + return MethodType.fromMethodDescriptorString(descriptorString(), lookup.lookupClass().getClassLoader()); + } + + @Override + public String descriptorString() { + return String.format("(%s)%s", Stream.of(argTypes).map(ClassConstant::descriptorString).collect(Collectors.joining()), returnType.descriptorString()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MethodTypeConstant constant = (MethodTypeConstant) o; + + return (returnType != null + ? returnType.equals(constant.returnType) + : constant.returnType == null) + && Arrays.equals(argTypes, constant.argTypes); + } + + @Override + public int hashCode() { + int result = returnType != null ? returnType.hashCode() : 0; + result = 31 * result + Arrays.hashCode(argTypes); + return result; + } + + @Override + public String toString() { + return String.format("MethodTypeConstant[%s]", descriptorString()); + } + } + + /** + * A descriptor for a {@linkplain MethodHandle} constant. + */ + public static final class MethodHandleConstant implements Constable { + private enum Kind { + STATIC(REF_invokeStatic), + VIRTUAL(REF_invokeVirtual), + SPECIAL(REF_invokeSpecial), + CTOR(REF_newInvokeSpecial), + GETTER(REF_getField), + SETTER(REF_putField), + STATIC_GETTER(REF_getStatic), + STATIC_SETTER(REF_putStatic); + + final int refKind; + + Kind(int refKind) { + this.refKind = refKind; + } + } + + private final Kind kind; + private final ClassConstant owner; + private final String name; + private final MethodTypeConstant type; + + private MethodHandleConstant(Kind kind, ClassConstant owner, String name, MethodTypeConstant type) { + this.kind = kind; + this.owner = owner; + this.name = name; + this.type = type; + } + + /** + * Return a {@linkplain MethodHandleConstant} corresponding to an + * invocation of a static method + * @param clazz the class containing the method + * @param name the name of the method + * @param type the method type of the method + * @return the {@linkplain MethodHandleConstant} + */ + @TrackableConstant + public static MethodHandleConstant ofStatic(ClassConstant clazz, String name, MethodTypeConstant type) { + return new MethodHandleConstant(Kind.STATIC, clazz, name, type); + } + + /** + * Return a {@linkplain MethodHandleConstant} corresponding to invocation + * of a virtual method via {@code invokevirtual} + * @param clazz the class containing the method + * @param name the name of the method + * @param type the method type of the method + * @return the {@linkplain MethodHandleConstant} + */ + @TrackableConstant + public static MethodHandleConstant ofVirtual(ClassConstant clazz, String name, MethodTypeConstant type) { + return new MethodHandleConstant(Kind.VIRTUAL, clazz, name, type); + } + + /** + * Return a {@linkplain MethodHandleConstant} corresponding to invocation + * of a virtual method via {@code invokespecial} + * @param clazz the class containing the method + * @param name the name of the method + * @param type the method type of the method + * @return the {@linkplain MethodHandleConstant} + */ + @TrackableConstant + public static MethodHandleConstant ofSpecial(ClassConstant clazz, String name, MethodTypeConstant type) { + return new MethodHandleConstant(Kind.SPECIAL, clazz, name, type); + } + + /** + * Return a {@linkplain MethodHandleConstant} corresponding to invocation + * of a constructor + * @param clazz the class containing the method + * @param type the method type of the method + * @return the {@linkplain MethodHandleConstant} + */ + @TrackableConstant + public static MethodHandleConstant ofConstructor(ClassConstant clazz, MethodTypeConstant type) { + return new MethodHandleConstant(Kind.CTOR, clazz, "", type); + } + + /** + * Return a {@linkplain MethodHandleConstant} corresponding to invocation + * of an instance field getter + * @param clazz the class containing the field + * @param name the name of the field + * @param type the type of the field + * @return the {@linkplain MethodHandleConstant} + */ + @TrackableConstant + public static MethodHandleConstant ofGetter(ClassConstant clazz, String name, ClassConstant type) { + return new MethodHandleConstant(Kind.GETTER, clazz, name, MethodTypeConstant.of(type, clazz)); + } + + /** + * Return a {@linkplain MethodHandleConstant} corresponding to invocation + * of an instance field setter + * @param clazz the class containing the field + * @param name the name of the field + * @param type the type of the field + * @return the {@linkplain MethodHandleConstant} + */ + @TrackableConstant + public static MethodHandleConstant ofSetter(ClassConstant clazz, String name, ClassConstant type) { + return new MethodHandleConstant(Kind.SETTER, clazz, name, MethodTypeConstant.of(VOID, clazz, type)); + } + + /** + * Return a {@linkplain MethodHandleConstant} corresponding to invocation + * of a static field getter + * @param clazz the class containing the field + * @param name the name of the field + * @param type the type of the field + * @return the {@linkplain MethodHandleConstant} + */ + @TrackableConstant + public static MethodHandleConstant ofStaticGetter(ClassConstant clazz, String name, ClassConstant type) { + return new MethodHandleConstant(Kind.STATIC_GETTER, clazz, name, MethodTypeConstant.of(type)); + } + + /** + * Return a {@linkplain MethodHandleConstant} corresponding to invocation + * of a static field setter + * @param clazz the class containing the field + * @param name the name of the field + * @param type the type of the field + * @return the {@linkplain MethodHandleConstant} + */ + @TrackableConstant + public static MethodHandleConstant ofStaticSetter(ClassConstant clazz, String name, ClassConstant type) { + return new MethodHandleConstant(Kind.STATIC_SETTER, clazz, name, MethodTypeConstant.of(VOID, type)); + } + + @Override + public MethodHandle resolveConstant(MethodHandles.Lookup lookup) throws ReflectiveOperationException { + switch (kind) { + case STATIC: return lookup.findStatic(owner.resolveConstant(lookup), name, type.resolveConstant(lookup)); + case VIRTUAL: return lookup.findVirtual(owner.resolveConstant(lookup), name, type.resolveConstant(lookup)); + case SPECIAL: return lookup.findSpecial(owner.resolveConstant(lookup), name, type.resolveConstant(lookup), lookup.lookupClass()); + case CTOR: return lookup.findConstructor(owner.resolveConstant(lookup), type.resolveConstant(lookup)); + case GETTER: return lookup.findGetter(owner.resolveConstant(lookup), name, type.resolveConstant(lookup).returnType()); + case STATIC_GETTER: return lookup.findStaticGetter(owner.resolveConstant(lookup), name, type.resolveConstant(lookup).returnType()); + case SETTER: return lookup.findSetter(owner.resolveConstant(lookup), name, type.resolveConstant(lookup).parameterType(1)); + case STATIC_SETTER: return lookup.findStaticSetter(owner.resolveConstant(lookup), name, type.resolveConstant(lookup).parameterType(0)); + default: throw new IllegalStateException(kind.name()); + } + } + + /** + * Return the {@code refKind} of the method handle described by this descriptor, + * as defined by {@link MethodHandleInfo} + * @return the reference kind + */ + @TrackableConstant + public int refKind() { return kind.refKind; } + + /** + * Return the class in which the method described by this descriptor is + * declared + * + * @return the class in which the method is declared + */ + @TrackableConstant + public ClassConstant owner() { + return owner; + } + + /** + * Return the name of the method described by this descriptor + * @return the name of the method + */ + @TrackableConstant + public String name() { + return name; + } + + /** + * Return the method type of the method described by this descriptor + * @return the method type + */ + @TrackableConstant + public MethodTypeConstant type() { + return type; + } + } + + /** + * A descriptor for a {@linkplain VarHandle} constant. + */ + public static class VarHandleConstant implements Constable { + private enum Kind { + STATIC_FIELD, + INSTANCE_FIELD, + ARRAY_HANDLE + } + + private final Kind kind; + private final ClassConstant owner; + private final String name; + private final ClassConstant type; + + private VarHandleConstant(Kind kind, ClassConstant owner, String name, ClassConstant type) { + this.kind = kind; + this.owner = owner; + this.name = name; + this.type = type; + } + + /** + * Return a {@linkplain VarHandleConstant} for an instance field + * @param clazz The class containing the field + * @param name The name of the field + * @param type The type of the field + * @return the {@linkplain VarHandleConstant} + */ + public static VarHandleConstant ofField(ClassConstant clazz, String name, ClassConstant type) { + // @@@ more likely, just delegate to DynamicConstant + return new VarHandleConstant(Kind.INSTANCE_FIELD, clazz, name, type); + } + + /** + * Return a {@linkplain VarHandleConstant} for a static field + * @param clazz The class containing the field + * @param name The name of the field + * @param type The type of the field + * @return the {@linkplain VarHandleConstant} + */ + public static VarHandleConstant ofStaticField(ClassConstant clazz, String name, ClassConstant type) { + return new VarHandleConstant(Kind.STATIC_FIELD, clazz, name, type); + } + + /** + * Return a {@linkplain VarHandleConstant} for an array + * @param clazz The component type of the array + * @return the {@linkplain VarHandleConstant} + */ + public static VarHandleConstant ofArray(ClassConstant clazz) { + return new VarHandleConstant(Kind.ARRAY_HANDLE, clazz, "", clazz); + } + + // @@@ Accessor methods for kind, name + + /** + * Return the type of the field or array component described by this + * {@linkplain VarHandleConstant} + * @return the type of the field or array component described by this + * {@linkplain VarHandleConstant} + */ + @TrackableConstant + public ClassConstant type() { + return type; + } + + @Override + public VarHandle resolveConstant(MethodHandles.Lookup lookup) throws ReflectiveOperationException { + switch (kind) { + case STATIC_FIELD: return lookup.findStaticVarHandle(owner.resolveConstant(lookup), name, type.resolveConstant(lookup)); + case INSTANCE_FIELD: return lookup.findVarHandle(owner.resolveConstant(lookup), name, type.resolveConstant(lookup)); + default: throw new UnsupportedOperationException(kind.name()); + } + } + } + + /** + * A descriptor for a dynamic constant. + */ + public static final class DynamicConstant implements Constable { + private final MethodHandleConstant bsm; + private final Constable[] bsmArgs; + + private DynamicConstant(MethodHandleConstant bsm, Constable... bsmArgs) { + this.bsm = bsm; + this.bsmArgs = bsmArgs; + } + + /** + * Return a descriptor for a dynamic constant. + * @param bsm the bootstrap method for the dynamic constant + * @param bsmArgs the bootstrap arguments for the dynamic constant + * @param the type of the dynamic constant + * @return the descriptor for the dynamic constant + */ + @TrackableConstant + public static DynamicConstant of(MethodHandleConstant bsm, Constable... bsmArgs) { + return new DynamicConstant<>(bsm, bsmArgs); + } + + @Override + @SuppressWarnings("unchecked") + public T resolveConstant(MethodHandles.Lookup lookup) throws ReflectiveOperationException { + try { + return (T) bsm.resolveConstant(lookup).invokeExact(resolveArgs(lookup, bsmArgs)); + } + catch (RuntimeException|Error e) { + throw e; + } + catch (Throwable t) { + throw new RuntimeException(t); + } + } + } + + /** + * A descriptor for the {@code null} constant. + */ + public class NullConstant implements Constable { + private final NullConstant NULL = new NullConstant(); + private NullConstant() { } + + /** + * Returns a descriptor for the null constant + * @return a descriptor for the null constant + */ + public NullConstant nullConstant() { + return NULL; + } + + @Override + public Object resolveConstant(MethodHandles.Lookup lookup) throws ReflectiveOperationException { + return null; + } + }; + + /** + * A descriptor for an {@code invokedynamic} invocation + */ + public static final class BootstrapSpecifier { + final MethodHandleConstant bsm; + final String invocationName; + final MethodTypeConstant invocationDesc; + final Constable[] bsmArgs; + + private BootstrapSpecifier(MethodHandleConstant bsm, String invocationName, MethodTypeConstant invocationDesc, Constable... bsmArgs) { + this.bsm = bsm; + this.invocationName = invocationName; + this.invocationDesc = invocationDesc; + this.bsmArgs = bsmArgs; + } + + /** + * Create a descriptor for an {@code invokedynamic} invocation. + * @param bsm the bootstrap method for the {@code invokedynamic} + * @param invocationName the invocation name for the {@code invokedynamic} + * @param invocationDesc the invocation descriptor for the {@code invokedynamic} + * @param bsmArgs the bootstrap arguments for the {@code invokedynamic} + * @return the descriptor + */ + @TrackableConstant + public static BootstrapSpecifier of(MethodHandleConstant bsm, String invocationName, MethodTypeConstant invocationDesc, Constable... bsmArgs) { + return new BootstrapSpecifier(bsm, invocationName, invocationDesc, bsmArgs); + } + } + + private static Object[] resolveArgs(MethodHandles.Lookup lookup, Constable[] args) { + return Stream.of(args) + .map(arg -> { + try { + return arg.resolveConstant(lookup); + } + catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }) + .toArray(); + } + + /** + * Convert a class literal to a descriptor string + * @param clazz the class literal + * @return the descriptor string + */ + static String classToDescriptor(Class clazz) { + return MethodType.methodType(clazz).toMethodDescriptorString().substring(2); + } +} + +