1 /* 2 * Copyright (c) 2008, 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 package java.lang.invoke; 27 28 import static java.lang.invoke.MethodHandleStatics.*; 29 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; 30 import java.lang.reflect.Field; 31 import sun.misc.Cleaner; 32 33 /** 34 * A {@code CallSite} is a holder for a variable {@link MethodHandle}, 35 * which is called its {@code target}. 36 * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates 37 * all calls to the site's current target. 38 * A {@code CallSite} may be associated with several {@code invokedynamic} 39 * instructions, or it may be "free floating", associated with none. 40 * In any case, it may be invoked through an associated method handle 41 * called its {@linkplain #dynamicInvoker dynamic invoker}. 42 * <p> 43 * {@code CallSite} is an abstract class which does not allow 44 * direct subclassing by users. It has three immediate, 45 * concrete subclasses that may be either instantiated or subclassed. 46 * <ul> 47 * <li>If a mutable target is not required, an {@code invokedynamic} instruction 48 * may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}. 49 * <li>If a mutable target is required which has volatile variable semantics, 50 * because updates to the target must be immediately and reliably witnessed by other threads, 51 * a {@linkplain VolatileCallSite volatile call site} may be used. 52 * <li>Otherwise, if a mutable target is required, 53 * a {@linkplain MutableCallSite mutable call site} may be used. 54 * </ul> 55 * <p> 56 * A non-constant call site may be <em>relinked</em> by changing its target. 57 * The new target must have the same {@linkplain MethodHandle#type() type} 58 * as the previous target. 59 * Thus, though a call site can be relinked to a series of 60 * successive targets, it cannot change its type. 61 * <p> 62 * Here is a sample use of call sites and bootstrap methods which links every 63 * dynamic call site to print its arguments: 64 <blockquote><pre>{@code 65 static void test() throws Throwable { 66 // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION 67 InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14); 68 } 69 private static void printArgs(Object... args) { 70 System.out.println(java.util.Arrays.deepToString(args)); 71 } 72 private static final MethodHandle printArgs; 73 static { 74 MethodHandles.Lookup lookup = MethodHandles.lookup(); 75 Class thisClass = lookup.lookupClass(); // (who am I?) 76 printArgs = lookup.findStatic(thisClass, 77 "printArgs", MethodType.methodType(void.class, Object[].class)); 78 } 79 private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) { 80 // ignore caller and name, but match the type: 81 return new ConstantCallSite(printArgs.asType(type)); 82 } 83 }</pre></blockquote> 84 * @author John Rose, JSR 292 EG 85 */ 86 abstract 87 public class CallSite { 88 static { MethodHandleImpl.initStatics(); } 89 90 // The actual payload of this call site: 91 /*package-private*/ 92 MethodHandle target; // Note: This field is known to the JVM. Do not change. 93 94 /** 95 * Make a blank call site object with the given method type. 96 * An initial target method is supplied which will throw 97 * an {@link IllegalStateException} if called. 98 * <p> 99 * Before this {@code CallSite} object is returned from a bootstrap method, 100 * it is usually provided with a more useful target method, 101 * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}. 102 * @throws NullPointerException if the proposed type is null 103 */ 104 /*package-private*/ 105 CallSite(MethodType type) { 106 target = makeUninitializedCallSite(type); 107 } 108 109 /** 110 * Make a call site object equipped with an initial target method handle. 111 * @param target the method handle which will be the initial target of the call site 112 * @throws NullPointerException if the proposed target is null 113 */ 114 /*package-private*/ 115 CallSite(MethodHandle target) { 116 target.type(); // null check 117 this.target = target; 118 } 119 120 /** 121 * Make a call site object equipped with an initial target method handle. 122 * @param targetType the desired type of the call site 123 * @param createTargetHook a hook which will bind the call site to the target method handle 124 * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments, 125 * or if the target returned by the hook is not of the given {@code targetType} 126 * @throws NullPointerException if the hook returns a null value 127 * @throws ClassCastException if the hook returns something other than a {@code MethodHandle} 128 * @throws Throwable anything else thrown by the hook function 129 */ 130 /*package-private*/ 131 CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable { 132 this(targetType); 133 ConstantCallSite selfCCS = (ConstantCallSite) this; 134 MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS); 135 checkTargetChange(this.target, boundTarget); 136 this.target = boundTarget; 137 } 138 139 /** 140 * {@code CallSite} dependency context. 141 * VM uses context class to store nmethod dependencies on the call site target. 142 * Can be in 2 states: (a) null; or (b) {@code Cleaner} instance pointing to some Class instance. 143 * Lazily initialized when CallSite instance is linked to some indy call site or VM needs 144 * it to store dependencies. As a corollary, "null" context means there are no dependencies 145 * registered yet. {@code Cleaner} is used in 2 roles: 146 * (a) context class access for VM; 147 * (b) stale context class cleanup. 148 * {@code Cleaner} holds the context class until cleanup action is finished (see {@code PhantomReference}). 149 * Though it's impossible to get the context class using {@code Reference.get()}, VM extracts it directly 150 * from {@code Reference.referent} field. 151 */ 152 private volatile Cleaner context = null; 153 154 /** 155 * Default context. 156 * VM uses it to initialize non-linked CallSite context. 157 */ 158 private static class DefaultContext {} 159 private static final Cleaner DEFAULT_CONTEXT = makeContext(DefaultContext.class, null); 160 161 private static Cleaner makeContext(Class<?> referent, final CallSite holder) { 162 return Cleaner.create(referent, 163 new Runnable() { 164 @Override public void run() { 165 MethodHandleNatives.invalidateDependentNMethods(holder); 166 } 167 }); 168 } 169 170 /** Initialize context class used for nmethod dependency tracking */ 171 /*package-private*/ 172 void initContext(Class<?> newContext) { 173 // If there are concurrent actions, exactly one succeeds. 174 if (context == null) { 175 UNSAFE.compareAndSwapObject(this, CONTEXT_OFFSET, /*expected=*/null, makeContext(newContext, this)); 176 // No need to care about failed CAS attempt. 177 // Since initContext is called from indy call site linkage in newContext class, there's no risk 178 // that the context class becomes dead while corresponding context cleaner is alive (causing cleanup 179 // action in the wrong context). 180 } 181 } 182 183 /** 184 * Returns the type of this call site's target. 185 * Although targets may change, any call site's type is permanent, and can never change to an unequal type. 186 * The {@code setTarget} method enforces this invariant by refusing any new target that does 187 * not have the previous target's type. 188 * @return the type of the current target, which is also the type of any future target 189 */ 190 public MethodType type() { 191 // warning: do not call getTarget here, because CCS.getTarget can throw IllegalStateException 192 return target.type(); 193 } 194 195 /** 196 * Returns the target method of the call site, according to the 197 * behavior defined by this call site's specific class. 198 * The immediate subclasses of {@code CallSite} document the 199 * class-specific behaviors of this method. 200 * 201 * @return the current linkage state of the call site, its target method handle 202 * @see ConstantCallSite 203 * @see VolatileCallSite 204 * @see #setTarget 205 * @see ConstantCallSite#getTarget 206 * @see MutableCallSite#getTarget 207 * @see VolatileCallSite#getTarget 208 */ 209 public abstract MethodHandle getTarget(); 210 211 /** 212 * Updates the target method of this call site, according to the 213 * behavior defined by this call site's specific class. 214 * The immediate subclasses of {@code CallSite} document the 215 * class-specific behaviors of this method. 216 * <p> 217 * The type of the new target must be {@linkplain MethodType#equals equal to} 218 * the type of the old target. 219 * 220 * @param newTarget the new target 221 * @throws NullPointerException if the proposed new target is null 222 * @throws WrongMethodTypeException if the proposed new target 223 * has a method type that differs from the previous target 224 * @see CallSite#getTarget 225 * @see ConstantCallSite#setTarget 226 * @see MutableCallSite#setTarget 227 * @see VolatileCallSite#setTarget 228 */ 229 public abstract void setTarget(MethodHandle newTarget); 230 231 void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) { 232 MethodType oldType = oldTarget.type(); 233 MethodType newType = newTarget.type(); // null check! 234 if (!newType.equals(oldType)) 235 throw wrongTargetType(newTarget, oldType); 236 } 237 238 private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) { 239 return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type); 240 } 241 242 /** 243 * Produces a method handle equivalent to an invokedynamic instruction 244 * which has been linked to this call site. 245 * <p> 246 * This method is equivalent to the following code: 247 * <blockquote><pre>{@code 248 * MethodHandle getTarget, invoker, result; 249 * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class)); 250 * invoker = MethodHandles.exactInvoker(this.type()); 251 * result = MethodHandles.foldArguments(invoker, getTarget) 252 * }</pre></blockquote> 253 * 254 * @return a method handle which always invokes this call site's current target 255 */ 256 public abstract MethodHandle dynamicInvoker(); 257 258 /*non-public*/ MethodHandle makeDynamicInvoker() { 259 MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this); 260 MethodHandle invoker = MethodHandles.exactInvoker(this.type()); 261 return MethodHandles.foldArguments(invoker, getTarget); 262 } 263 264 private static final MethodHandle GET_TARGET; 265 private static final MethodHandle THROW_UCS; 266 static { 267 try { 268 GET_TARGET = IMPL_LOOKUP. 269 findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); 270 THROW_UCS = IMPL_LOOKUP. 271 findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class)); 272 } catch (ReflectiveOperationException e) { 273 throw newInternalError(e); 274 } 275 } 276 277 /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */ 278 private static Object uninitializedCallSite(Object... ignore) { 279 throw new IllegalStateException("uninitialized call site"); 280 } 281 282 private MethodHandle makeUninitializedCallSite(MethodType targetType) { 283 MethodType basicType = targetType.basicType(); 284 MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS); 285 if (invoker == null) { 286 invoker = THROW_UCS.asType(basicType); 287 invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker); 288 } 289 // unchecked view is OK since no values will be received or returned 290 return invoker.viewAsType(targetType, false); 291 } 292 293 // unsafe stuff: 294 private static final long TARGET_OFFSET; 295 private static final long CONTEXT_OFFSET; 296 static { 297 try { 298 TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target")); 299 CONTEXT_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("context")); 300 } catch (Exception ex) { throw newInternalError(ex); } 301 } 302 303 /*package-private*/ 304 void setTargetNormal(MethodHandle newTarget) { 305 MethodHandleNatives.setCallSiteTargetNormal(this, newTarget); 306 } 307 /*package-private*/ 308 MethodHandle getTargetVolatile() { 309 return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET); 310 } 311 /*package-private*/ 312 void setTargetVolatile(MethodHandle newTarget) { 313 MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget); 314 } 315 316 // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite: 317 static CallSite makeSite(MethodHandle bootstrapMethod, 318 // Callee information: 319 String name, MethodType type, 320 // Extra arguments for BSM, if any: 321 Object info, 322 // Caller information: 323 Class<?> callerClass) { 324 MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass); 325 CallSite site; 326 try { 327 Object binding; 328 info = maybeReBox(info); 329 if (info == null) { 330 binding = bootstrapMethod.invoke(caller, name, type); 331 } else if (!info.getClass().isArray()) { 332 binding = bootstrapMethod.invoke(caller, name, type, info); 333 } else { 334 Object[] argv = (Object[]) info; 335 maybeReBoxElements(argv); 336 switch (argv.length) { 337 case 0: 338 binding = bootstrapMethod.invoke(caller, name, type); 339 break; 340 case 1: 341 binding = bootstrapMethod.invoke(caller, name, type, 342 argv[0]); 343 break; 344 case 2: 345 binding = bootstrapMethod.invoke(caller, name, type, 346 argv[0], argv[1]); 347 break; 348 case 3: 349 binding = bootstrapMethod.invoke(caller, name, type, 350 argv[0], argv[1], argv[2]); 351 break; 352 case 4: 353 binding = bootstrapMethod.invoke(caller, name, type, 354 argv[0], argv[1], argv[2], argv[3]); 355 break; 356 case 5: 357 binding = bootstrapMethod.invoke(caller, name, type, 358 argv[0], argv[1], argv[2], argv[3], argv[4]); 359 break; 360 case 6: 361 binding = bootstrapMethod.invoke(caller, name, type, 362 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); 363 break; 364 default: 365 final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) 366 if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY) 367 throw new BootstrapMethodError("too many bootstrap method arguments"); 368 MethodType bsmType = bootstrapMethod.type(); 369 MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); 370 MethodHandle typedBSM = bootstrapMethod.asType(invocationType); 371 MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); 372 binding = spreader.invokeExact(typedBSM, (Object)caller, (Object)name, (Object)type, argv); 373 } 374 } 375 //System.out.println("BSM for "+name+type+" => "+binding); 376 if (binding instanceof CallSite) { 377 site = (CallSite) binding; 378 } else { 379 throw new ClassCastException("bootstrap method failed to produce a CallSite"); 380 } 381 if (!site.getTarget().type().equals(type)) 382 throw wrongTargetType(site.getTarget(), type); 383 } catch (Throwable ex) { 384 BootstrapMethodError bex; 385 if (ex instanceof BootstrapMethodError) 386 bex = (BootstrapMethodError) ex; 387 else 388 bex = new BootstrapMethodError("call site initialization exception", ex); 389 throw bex; 390 } 391 return site; 392 } 393 394 private static Object maybeReBox(Object x) { 395 if (x instanceof Integer) { 396 int xi = (int) x; 397 if (xi == (byte) xi) 398 x = xi; // must rebox; see JLS 5.1.7 399 } 400 return x; 401 } 402 private static void maybeReBoxElements(Object[] xa) { 403 for (int i = 0; i < xa.length; i++) { 404 xa[i] = maybeReBox(xa[i]); 405 } 406 } 407 }