| /* | |
| * Copyright (c) 2018, 2025, 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.constant; | |
| import java.lang.invoke.CallSite; | |
| import java.lang.invoke.MethodHandle; | |
| import java.lang.invoke.MethodHandles; | |
| import java.util.Arrays; | |
| import java.util.Objects; | |
| import java.util.stream.Stream; | |
| import static java.lang.constant.ConstantDescs.CD_String; | |
| import static java.util.Objects.requireNonNull; | |
| import static java.util.stream.Collectors.joining; | |
| import static jdk.internal.constant.ConstantUtils.EMPTY_CONSTANTDESC; | |
| import static jdk.internal.constant.ConstantUtils.validateMemberName; | |
| /** | |
| * A <a href="package-summary.html#nominal">nominal descriptor</a> for an | |
| * {@code invokedynamic} call site. | |
| * | |
| * <p>A {@code DynamicCallSiteDesc} is immutable and its behavior does not | |
| * rely on object identity. | |
| * | |
| * @since 12 | |
| */ | |
| public final class DynamicCallSiteDesc { | |
| private final DirectMethodHandleDesc bootstrapMethod; | |
| private final ConstantDesc[] bootstrapArgs; | |
| private final String invocationName; | |
| private final MethodTypeDesc invocationType; | |
| /** | |
| * Creates a nominal descriptor for an {@code invokedynamic} call site. | |
| * | |
| * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the | |
| * bootstrap method for the {@code invokedynamic} | |
| * @param invocationName The unqualified name that would appear in the {@code NameAndType} | |
| * operand of the {@code invokedynamic} | |
| * @param invocationType a {@link MethodTypeDesc} describing the invocation | |
| * type that would appear in the {@code NameAndType} | |
| * operand of the {@code invokedynamic} | |
| * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments | |
| * to the bootstrap, that would appear in the | |
| * {@code BootstrapMethods} attribute | |
| * @throws NullPointerException if any parameter or its contents are {@code null} | |
| * @throws IllegalArgumentException if the invocation name has the incorrect | |
| * format | |
| * @jvms 4.2.2 Unqualified Names | |
| */ | |
| private DynamicCallSiteDesc(DirectMethodHandleDesc bootstrapMethod, | |
| String invocationName, | |
| MethodTypeDesc invocationType, | |
| ConstantDesc[] bootstrapArgs) { | |
| this.invocationName = validateMemberName(requireNonNull(invocationName), true); | |
| this.invocationType = requireNonNull(invocationType); | |
| this.bootstrapMethod = requireNonNull(bootstrapMethod); | |
| this.bootstrapArgs = requireNonNull(bootstrapArgs.clone()); | |
| for (int i = 0; i < this.bootstrapArgs.length; i++) { | |
| requireNonNull(this.bootstrapArgs[i]); | |
| } | |
| if (invocationName.length() == 0) | |
| throw new IllegalArgumentException("Illegal invocation name: " + invocationName); | |
| } | |
| /** | |
| * Creates a nominal descriptor for an {@code invokedynamic} call site. | |
| * | |
| * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the | |
| * bootstrap method for the {@code invokedynamic} | |
| * @param invocationName The unqualified name that would appear in the {@code NameAndType} | |
| * operand of the {@code invokedynamic} | |
| * @param invocationType a {@link MethodTypeDesc} describing the invocation | |
| * type that would appear in the {@code NameAndType} | |
| * operand of the {@code invokedynamic} | |
| * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments | |
| * to the bootstrap, that would appear in the | |
| * {@code BootstrapMethods} attribute | |
| * @return the nominal descriptor | |
| * @throws NullPointerException if any parameter or its contents are {@code null} | |
| * @throws IllegalArgumentException if the invocation name has the incorrect | |
| * format | |
| * @jvms 4.2.2 Unqualified Names | |
| */ | |
| public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod, | |
| String invocationName, | |
| MethodTypeDesc invocationType, | |
| ConstantDesc... bootstrapArgs) { | |
| return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs); | |
| } | |
| /** | |
| * Creates a nominal descriptor for an {@code invokedynamic} call site whose | |
| * bootstrap method has no static arguments. | |
| * | |
| * @param bootstrapMethod The bootstrap method for the {@code invokedynamic} | |
| * @param invocationName The invocationName that would appear in the | |
| * {@code NameAndType} operand of the {@code invokedynamic} | |
| * @param invocationType The invocation invocationType that would appear | |
| * in the {@code NameAndType} operand of the {@code invokedynamic} | |
| * @return the nominal descriptor | |
| * @throws NullPointerException if any parameter is null | |
| * @throws IllegalArgumentException if the invocation name has the incorrect | |
| * format | |
| */ | |
| public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod, | |
| String invocationName, | |
| MethodTypeDesc invocationType) { | |
| return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, EMPTY_CONSTANTDESC); | |
| } | |
| /** | |
| * Creates a nominal descriptor for an {@code invokedynamic} call site whose | |
| * bootstrap method has no static arguments and for which the name parameter | |
| * is {@link ConstantDescs#DEFAULT_NAME}. | |
| * | |
| * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the | |
| * bootstrap method for the {@code invokedynamic} | |
| * @param invocationType a {@link MethodTypeDesc} describing the invocation | |
| * type that would appear in the {@code NameAndType} | |
| * operand of the {@code invokedynamic} | |
| * @return the nominal descriptor | |
| * @throws NullPointerException if any parameter is null | |
| */ | |
| public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod, | |
| MethodTypeDesc invocationType) { | |
| return of(bootstrapMethod, ConstantDescs.DEFAULT_NAME, invocationType); | |
| } | |
| /** | |
| * Returns a nominal descriptor for an {@code invokedynamic} call site whose | |
| * bootstrap method, name, and invocation type are the same as this one, but | |
| * with the specified bootstrap arguments. | |
| * | |
| * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments | |
| * to the bootstrap, that would appear in the | |
| * {@code BootstrapMethods} attribute | |
| * @return the nominal descriptor | |
| * @throws NullPointerException if the argument or its contents are {@code null} | |
| */ | |
| public DynamicCallSiteDesc withArgs(ConstantDesc... bootstrapArgs) { | |
| return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs); | |
| } | |
| /** | |
| * Returns a nominal descriptor for an {@code invokedynamic} call site whose | |
| * bootstrap and bootstrap arguments are the same as this one, but with the | |
| * specified invocationName and invocation invocationType | |
| * | |
| * @param invocationName The unqualified name that would appear in the {@code NameAndType} | |
| * operand of the {@code invokedynamic} | |
| * @param invocationType a {@link MethodTypeDesc} describing the invocation | |
| * type that would appear in the {@code NameAndType} | |
| * operand of the {@code invokedynamic} | |
| * @return the nominal descriptor | |
| * @throws NullPointerException if any parameter is null | |
| * @throws IllegalArgumentException if the invocation name has the incorrect | |
| * format | |
| * @jvms 4.2.2 Unqualified Names | |
| */ | |
| public DynamicCallSiteDesc withNameAndType(String invocationName, | |
| MethodTypeDesc invocationType) { | |
| return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs); | |
| } | |
| /** | |
| * Returns the invocation name that would appear in the {@code NameAndType} | |
| * operand of the {@code invokedynamic}. | |
| * | |
| * @return the invocation name | |
| */ | |
| public String invocationName() { | |
| return invocationName; | |
| } | |
| /** | |
| * Returns a {@link MethodTypeDesc} describing the invocation type that | |
| * would appear in the {@code NameAndType} operand of the {@code invokedynamic}. | |
| * | |
| * @return the invocation type | |
| */ | |
| public MethodTypeDesc invocationType() { | |
| return invocationType; | |
| } | |
| /** | |
| * Returns a {@link MethodHandleDesc} describing the bootstrap method for | |
| * the {@code invokedynamic}. | |
| * | |
| * @return the bootstrap method for the {@code invokedynamic} | |
| */ | |
| public MethodHandleDesc bootstrapMethod() { return bootstrapMethod; } | |
| /** | |
| * Returns {@link ConstantDesc}s describing the bootstrap arguments for the | |
| * {@code invokedynamic}. The returned array is always non-null. A zero | |
| * length array is returned if this {@linkplain DynamicCallSiteDesc} has no | |
| * bootstrap arguments. | |
| * | |
| * @return the bootstrap arguments for the {@code invokedynamic} | |
| */ | |
| public ConstantDesc[] bootstrapArgs() { return bootstrapArgs.clone(); } | |
| /** | |
| * Reflectively invokes the bootstrap method with the specified arguments, | |
| * and return the resulting {@link CallSite} | |
| * | |
| * @param lookup The {@link MethodHandles.Lookup} used to resolve class names | |
| * @return the {@link CallSite} | |
| * @throws Throwable if any exception is thrown by the bootstrap method | |
| */ | |
| public CallSite resolveCallSiteDesc(MethodHandles.Lookup lookup) throws Throwable { | |
| assert bootstrapMethod.invocationType().parameterType(1).equals(CD_String); | |
| MethodHandle bsm = bootstrapMethod.resolveConstantDesc(lookup); | |
| Object[] args = new Object[bootstrapArgs.length + 3]; | |
| args[0] = lookup; | |
| args[1] = invocationName; | |
| args[2] = invocationType.resolveConstantDesc(lookup); | |
| System.arraycopy(bootstrapArgs, 0, args, 3, bootstrapArgs.length); | |
| return (CallSite) bsm.invokeWithArguments(args); | |
| } | |
| /** | |
| * Compares the specified object with this descriptor for equality. Returns | |
| * {@code true} if and only if the specified object is also a | |
| * {@linkplain DynamicCallSiteDesc}, and both descriptors have equal | |
| * bootstrap methods, bootstrap argument lists, invocation name, and | |
| * invocation type. | |
| * | |
| * @param o the {@code DynamicCallSiteDesc} to compare to this | |
| * {@code DynamicCallSiteDesc} | |
| * @return {@code true} if the specified {@code DynamicCallSiteDesc} | |
| * is equal to this {@code DynamicCallSiteDesc}. | |
| */ | |
| @Override | |
| public final boolean equals(Object o) { | |
| if (this == o) return true; | |
| if (o == null || getClass() != o.getClass()) return false; | |
| DynamicCallSiteDesc specifier = (DynamicCallSiteDesc) o; | |
| return Objects.equals(bootstrapMethod, specifier.bootstrapMethod) && | |
| Arrays.equals(bootstrapArgs, specifier.bootstrapArgs) && | |
| Objects.equals(invocationName, specifier.invocationName) && | |
| Objects.equals(invocationType, specifier.invocationType); | |
| } | |
| @Override | |
| public final int hashCode() { | |
| int result = Objects.hash(bootstrapMethod, invocationName, invocationType); | |
| result = 31 * result + Arrays.hashCode(bootstrapArgs); | |
| return result; | |
| } | |
| /** | |
| * Returns a compact textual description of this call site description, | |
| * including the bootstrap method, the invocation name and type, and | |
| * the static bootstrap arguments. | |
| * | |
| * @return A compact textual description of this call site descriptor | |
| */ | |
| @Override | |
| public String toString() { | |
| return String.format("DynamicCallSiteDesc[%s::%s(%s%s):%s]", | |
| bootstrapMethod.owner().displayName(), | |
| bootstrapMethod.methodName(), | |
| invocationName.equals(ConstantDescs.DEFAULT_NAME) ? "" : invocationName + "/", | |
| Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")), | |
| invocationType.displayDescriptor()); | |
| } | |
| } |