1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file, and Oracle licenses the original version of this file under the BSD 31 * license: 32 */ 33 /* 34 Copyright 2009-2013 Attila Szegedi 35 36 Redistribution and use in source and binary forms, with or without 37 modification, are permitted provided that the following conditions are 38 met: 39 * Redistributions of source code must retain the above copyright 40 notice, this list of conditions and the following disclaimer. 41 * Redistributions in binary form must reproduce the above copyright 42 notice, this list of conditions and the following disclaimer in the 43 documentation and/or other materials provided with the distribution. 44 * Neither the name of the copyright holder nor the names of 45 contributors may be used to endorse or promote products derived from 46 this software without specific prior written permission. 47 48 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 49 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 50 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 51 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER 52 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 53 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 54 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 55 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 56 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 57 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 58 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 */ 60 61 package jdk.dynalink; 62 63 import java.lang.invoke.MethodHandles; 64 import java.lang.invoke.MethodHandles.Lookup; 65 import java.lang.invoke.MethodType; 66 import java.util.Objects; 67 import java.util.function.Supplier; 68 69 /** 70 * Call site descriptors contain all the information necessary for linking a 71 * call site. This information is normally passed as parameters to bootstrap 72 * methods and consists of the {@code MethodHandles.Lookup} object on the caller 73 * class in which the call site occurs, the dynamic operation at the call 74 * site, and the method type of the call site. {@code CallSiteDescriptor} 75 * objects are used in Dynalink to capture and store these parameters for 76 * subsequent use by the {@link DynamicLinker}. 77 * <p> 78 * The constructors of built-in {@link RelinkableCallSite} implementations all 79 * take a call site descriptor. 80 * <p> 81 * Call site descriptors must be immutable. You can use this class as-is or you 82 * can subclass it, especially if you need to add further information to the 83 * descriptors (typically, values passed in additional parameters to the 84 * bootstrap method. Since the descriptors must be immutable, you can set up a 85 * cache for equivalent descriptors to have the call sites share them. 86 * <p> 87 * The class extends {@link SecureLookupSupplier} for security-checked access to 88 * the {@code MethodHandles.Lookup} object it carries. This lookup should be used 89 * to find method handles to set as targets of the call site described by this 90 * descriptor. 91 */ 92 public class CallSiteDescriptor extends SecureLookupSupplier { 93 private final Operation operation; 94 private final MethodType methodType; 95 96 /** 97 * Creates a new call site descriptor. 98 * @param lookup the lookup object describing the class the call site belongs to. 99 * When creating descriptors from a {@link java.lang.invoke} bootstrap method, 100 * it should be the lookup passed to the bootstrap. 101 * @param operation the dynamic operation at the call site. 102 * @param methodType the method type of the call site. When creating 103 * descriptors from a {@link java.lang.invoke} bootstrap method, it should be 104 * the method type passed to the bootstrap. 105 */ CallSiteDescriptor(final Lookup lookup, final Operation operation, final MethodType methodType)106 public CallSiteDescriptor(final Lookup lookup, final Operation operation, final MethodType methodType) { 107 super(lookup); 108 this.operation = Objects.requireNonNull(operation, "name"); 109 this.methodType = Objects.requireNonNull(methodType, "methodType"); 110 } 111 112 /** 113 * Returns the operation at the call site. 114 * @return the operation at the call site. 115 */ getOperation()116 public final Operation getOperation() { 117 return operation; 118 } 119 120 /** 121 * The type of the method at the call site. 122 * 123 * @return type of the method at the call site. 124 */ getMethodType()125 public final MethodType getMethodType() { 126 return methodType; 127 } 128 129 /** 130 * Finds or creates a call site descriptor that only differs in its 131 * method type from this descriptor. 132 * Invokes {@link #changeMethodTypeInternal(MethodType)}. 133 * 134 * @param newMethodType the new method type 135 * @return a call site descriptor with changed method type. 136 * @throws NullPointerException if {@code newMethodType} is null. 137 */ changeMethodType(final MethodType newMethodType)138 public final CallSiteDescriptor changeMethodType(final MethodType newMethodType) { 139 final CallSiteDescriptor changed = changeMethodTypeInternal(newMethodType); 140 141 if (getClass() != CallSiteDescriptor.class) { 142 assertChangeInvariants(changed, "changeMethodTypeInternal"); 143 alwaysAssert(operation == changed.operation, () -> "changeMethodTypeInternal must not change the descriptor's operation"); 144 alwaysAssert(newMethodType == changed.methodType, () -> "changeMethodTypeInternal didn't set the correct new method type"); 145 } 146 return changed; 147 } 148 149 /** 150 * Finds or creates a call site descriptor that only differs in its 151 * method type from this descriptor. Subclasses must override this method 152 * to return an object of their exact class. If an overridden method changes 153 * something other than the method type in the descriptor (its class, lookup, 154 * or operation), or returns null, an {@code AssertionError} will be thrown 155 * from {@link #changeMethodType(MethodType)}. 156 * 157 * @param newMethodType the new method type 158 * @return a call site descriptor with the changed method type. 159 */ changeMethodTypeInternal(final MethodType newMethodType)160 protected CallSiteDescriptor changeMethodTypeInternal(final MethodType newMethodType) { 161 return new CallSiteDescriptor(getLookupPrivileged(), operation, newMethodType); 162 } 163 164 /** 165 * Finds or creates a call site descriptor that only differs in its 166 * operation from this descriptor. 167 * Invokes {@link #changeOperationInternal(Operation)}. 168 * 169 * @param newOperation the new operation 170 * @return a call site descriptor with the changed operation. 171 * @throws NullPointerException if {@code newOperation} is null. 172 * @throws SecurityException if the descriptor's lookup isn't the 173 * {@link MethodHandles#publicLookup()}, and a security manager is present, 174 * and a check for {@code RuntimePermission("dynalink.getLookup")} fails. 175 * This is necessary as changing the operation in the call site descriptor 176 * allows fabrication of descriptors for arbitrary operations with the lookup. 177 */ changeOperation(final Operation newOperation)178 public final CallSiteDescriptor changeOperation(final Operation newOperation) { 179 getLookup(); // force security check 180 final CallSiteDescriptor changed = changeOperationInternal(newOperation); 181 182 if (getClass() != CallSiteDescriptor.class) { 183 assertChangeInvariants(changed, "changeOperationInternal"); 184 alwaysAssert(methodType == changed.methodType, () -> "changeOperationInternal must not change the descriptor's method type"); 185 alwaysAssert(newOperation == changed.operation, () -> "changeOperationInternal didn't set the correct new operation"); 186 } 187 return changed; 188 } 189 190 /** 191 * Finds or creates a call site descriptor that only differs in its 192 * operation from this descriptor. Subclasses must override this method 193 * to return an object of their exact class. If an overridden method changes 194 * something other than the operation in the descriptor (its class, lookup, 195 * or method type), or returns null, an {@code AssertionError} will be thrown 196 * from {@link #changeOperation(Operation)}. 197 * 198 * @param newOperation the new operation 199 * @return a call site descriptor with the changed operation. 200 */ changeOperationInternal(final Operation newOperation)201 protected CallSiteDescriptor changeOperationInternal(final Operation newOperation) { 202 return new CallSiteDescriptor(getLookupPrivileged(), newOperation, methodType); 203 } 204 205 /** 206 * Returns true if this call site descriptor is equal to the passed object. 207 * It is considered equal if the other object is of the exact same class, 208 * their operations and method types are equal, and their lookups have the 209 * same {@link java.lang.invoke.MethodHandles.Lookup#lookupClass()} and 210 * {@link java.lang.invoke.MethodHandles.Lookup#lookupModes()}. 211 */ 212 @Override equals(final Object obj)213 public boolean equals(final Object obj) { 214 if (obj == this) { 215 return true; 216 } else if (obj == null) { 217 return false; 218 } else if (obj.getClass() != getClass()) { 219 return false; 220 } 221 final CallSiteDescriptor other = (CallSiteDescriptor)obj; 222 return operation.equals(other.operation) && 223 methodType.equals(other.methodType) && 224 lookupsEqual(getLookupPrivileged(), other.getLookupPrivileged()); 225 } 226 227 /** 228 * Compares two lookup objects for value-based equality. They are considered 229 * equal if they have the same 230 * {@link java.lang.invoke.MethodHandles.Lookup#lookupClass()} and 231 * {@link java.lang.invoke.MethodHandles.Lookup#lookupModes()}. 232 * @param l1 first lookup 233 * @param l2 second lookup 234 * @return true if the two lookups are equal, false otherwise. 235 */ lookupsEqual(final Lookup l1, final Lookup l2)236 private static boolean lookupsEqual(final Lookup l1, final Lookup l2) { 237 return l1.lookupClass() == l2.lookupClass() && l1.lookupModes() == l2.lookupModes(); 238 } 239 240 /** 241 * Returns a value-based hash code of this call site descriptor computed 242 * from its operation, method type, and lookup object's lookup class and 243 * lookup modes. 244 * @return value-based hash code for this call site descriptor. 245 */ 246 @Override hashCode()247 public int hashCode() { 248 return operation.hashCode() + 31 * methodType.hashCode() + 31 * 31 * lookupHashCode(getLookupPrivileged()); 249 } 250 251 /** 252 * Returns a value-based hash code for the passed lookup object. It is 253 * based on the lookup object's 254 * {@link java.lang.invoke.MethodHandles.Lookup#lookupClass()} and 255 * {@link java.lang.invoke.MethodHandles.Lookup#lookupModes()} values. 256 * @param lookup the lookup object. 257 * @return a hash code for the object.. 258 */ lookupHashCode(final Lookup lookup)259 private static int lookupHashCode(final Lookup lookup) { 260 return lookup.lookupClass().hashCode() + 31 * lookup.lookupModes(); 261 } 262 263 /** 264 * Returns the string representation of this call site descriptor, of the 265 * format {@code name(parameterTypes)returnType@lookup}. 266 */ 267 @Override toString()268 public String toString() { 269 final String mt = methodType.toString(); 270 final String l = getLookupPrivileged().toString(); 271 final String o = operation.toString(); 272 final StringBuilder b = new StringBuilder(o.length() + mt.length() + 1 + l.length()); 273 return b.append(o).append(mt).append('@').append(l).toString(); 274 } 275 assertChangeInvariants(final CallSiteDescriptor changed, final String caller)276 private void assertChangeInvariants(final CallSiteDescriptor changed, final String caller) { 277 alwaysAssert(changed != null, () -> caller + " must not return null."); 278 alwaysAssert(getClass() == changed.getClass(), () -> caller + " must not change the descriptor's class"); 279 alwaysAssert(lookupsEqual(getLookupPrivileged(), changed.getLookupPrivileged()), () -> caller + " must not change the descriptor's lookup"); 280 } 281 alwaysAssert(final boolean cond, final Supplier<String> errorMessage)282 private static void alwaysAssert(final boolean cond, final Supplier<String> errorMessage) { 283 if (!cond) { 284 throw new AssertionError(errorMessage.get()); 285 } 286 } 287 } 288