/* * 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); } }