1 /* 2 * Copyright (c) 2017, 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 package java.lang.invoke; 26 27 import sun.invoke.util.Wrapper; 28 29 import java.lang.invoke.AbstractConstantGroup.BSCIWithCache; 30 import java.util.Arrays; 31 32 import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo; 33 import static java.lang.invoke.ConstantGroup.makeConstantGroup; 34 import static java.lang.invoke.MethodHandleNatives.*; 35 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE; 36 import static java.lang.invoke.MethodHandles.Lookup; 37 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; 38 39 final class BootstrapMethodInvoker { 40 /** 41 * Factored code for invoking a bootstrap method for invokedynamic 42 * or a dynamic constant. 43 * @param resultType the expected return type (either CallSite or a constant type) 44 * @param bootstrapMethod the BSM to call 45 * @param name the method name or constant name 46 * @param type the method type or constant type 47 * @param info information passed up from the JVM, to derive static arguments 48 * @param callerClass the class containing the resolved method call or constant load 49 * @param <T> the expected return type 50 * @return the expected value, either a CallSite or a constant value 51 */ 52 static <T> T invoke(Class<T> resultType, 53 MethodHandle bootstrapMethod, 54 // Callee information: 55 String name, Object type, 56 // Extra arguments for BSM, if any: 57 Object info, 58 // Caller information: 59 Class<?> callerClass) { 60 MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass); 61 Object result; 62 boolean pullMode = isPullModeBSM(bootstrapMethod); // default value is false 63 boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true 64 MethodHandle pullModeBSM; 65 // match the VM with the BSM 66 if (vmIsPushing) { 67 // VM is pushing arguments at us 68 pullModeBSM = null; 69 if (pullMode) { 70 bootstrapMethod = pushMePullYou(bootstrapMethod, true); 71 } 72 } else { 73 // VM wants us to pull args from it 74 pullModeBSM = pullMode ? bootstrapMethod : 75 pushMePullYou(bootstrapMethod, false); 76 bootstrapMethod = null; 77 } 78 try { 79 info = maybeReBox(info); 80 if (info == null) { 81 // VM is allowed to pass up a null meaning no BSM args 82 result = bootstrapMethod.invoke(caller, name, type); 83 } 84 else if (!info.getClass().isArray()) { 85 // VM is allowed to pass up a single BSM arg directly 86 result = bootstrapMethod.invoke(caller, name, type, info); 87 } 88 else if (info.getClass() == int[].class) { 89 // VM is allowed to pass up a pair {argc, index} 90 // referring to 'argc' BSM args at some place 'index' 91 // in the guts of the VM (associated with callerClass). 92 // The format of this index pair is private to the 93 // handshake between the VM and this class only. 94 // This supports "pulling" of arguments. 95 // The VM is allowed to do this for any reason. 96 // The code in this method makes up for any mismatches. 97 BootstrapCallInfo<Object> bsci 98 = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info); 99 // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object 100 result = pullModeBSM.invoke(caller, bsci); 101 } 102 else { 103 // VM is allowed to pass up a full array of resolved BSM args 104 Object[] argv = (Object[]) info; 105 maybeReBoxElements(argv); 106 switch (argv.length) { 107 case 0: 108 result = bootstrapMethod.invoke(caller, name, type); 109 break; 110 case 1: 111 result = bootstrapMethod.invoke(caller, name, type, 112 argv[0]); 113 break; 114 case 2: 115 result = bootstrapMethod.invoke(caller, name, type, 116 argv[0], argv[1]); 117 break; 118 case 3: 119 // Special case the LambdaMetafactory::metafactory BSM 120 // 121 // By invoking exactly, we can avoid generating a number of 122 // classes on first (and subsequent) lambda initialization, 123 // most of which won't be shared with other invoke uses. 124 MethodType bsmType = bootstrapMethod.type(); 125 if (isLambdaMetafactoryIndyBSM(bsmType)) { 126 result = (CallSite)bootstrapMethod 127 .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0], 128 (MethodHandle)argv[1], (MethodType)argv[2]); 129 } else if (isLambdaMetafactoryCondyBSM(bsmType)) { 130 result = bootstrapMethod 131 .invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0], 132 (MethodHandle)argv[1], (MethodType)argv[2]); 133 } else { 134 result = bootstrapMethod.invoke(caller, name, type, 135 argv[0], argv[1], argv[2]); 136 } 137 break; 138 case 4: 139 result = bootstrapMethod.invoke(caller, name, type, 140 argv[0], argv[1], argv[2], argv[3]); 141 break; 142 case 5: 143 result = bootstrapMethod.invoke(caller, name, type, 144 argv[0], argv[1], argv[2], argv[3], argv[4]); 145 break; 146 case 6: 147 result = bootstrapMethod.invoke(caller, name, type, 148 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); 149 break; 150 default: 151 final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) 152 final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT; 153 if (argv.length >= MAX_SAFE_SIZE) { 154 // to be on the safe side, use invokeWithArguments which handles jumbo lists 155 Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length]; 156 newargv[0] = caller; 157 newargv[1] = name; 158 newargv[2] = type; 159 System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length); 160 result = bootstrapMethod.invokeWithArguments(newargv); 161 break; 162 } 163 MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); 164 MethodHandle typedBSM = bootstrapMethod.asType(invocationType); 165 MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); 166 result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv); 167 } 168 } 169 if (resultType.isPrimitive()) { 170 // Non-reference conversions are more than just plain casts. 171 // By pushing the value through a funnel of the form (T x)->x, 172 // the boxed result can be widened as needed. See MH::asType. 173 MethodHandle funnel = MethodHandles.identity(resultType); 174 result = funnel.invoke(result); 175 // Now it is the wrapper type for resultType. 176 resultType = Wrapper.asWrapperType(resultType); 177 } 178 return resultType.cast(result); 179 } 180 catch (Error e) { 181 // Pass through an Error, including BootstrapMethodError, any other 182 // form of linkage error, such as IllegalAccessError if the bootstrap 183 // method is inaccessible, or say ThreadDeath/OutOfMemoryError 184 // See the "Linking Exceptions" section for the invokedynamic 185 // instruction in JVMS 6.5. 186 throw e; 187 } 188 catch (Throwable ex) { 189 // Wrap anything else in BootstrapMethodError 190 throw new BootstrapMethodError("bootstrap method initialization exception", ex); 191 } 192 } 193 194 private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class, 195 Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class); 196 197 private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class, 198 Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class); 199 200 /** 201 * @return true iff the BSM method type exactly matches 202 * {@see java.lang.invoke.LambdaMetafactory#metafactory( 203 * MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)} 204 */ 205 private static boolean isLambdaMetafactoryCondyBSM(MethodType bsmType) { 206 return bsmType == LMF_CONDY_MT; 207 } 208 209 /** 210 * @return true iff the BSM method type exactly matches 211 * {@see java.lang.invoke.LambdaMetafactory#metafactory( 212 * MethodHandles.Lookup,String,MethodType,MethodType,MethodHandle,MethodType)} 213 */ 214 private static boolean isLambdaMetafactoryIndyBSM(MethodType bsmType) { 215 return bsmType == LMF_INDY_MT; 216 } 217 218 /** The JVM produces java.lang.Integer values to box 219 * CONSTANT_Integer boxes but does not intern them. 220 * Let's intern them. This is slightly wrong for 221 * a {@code CONSTANT_Dynamic} which produces an 222 * un-interned integer (e.g., {@code new Integer(0)}). 223 */ 224 private static Object maybeReBox(Object x) { 225 if (x instanceof Integer) { 226 int xi = (int) x; 227 if (xi == (byte) xi) 228 x = xi; // must rebox; see JLS 5.1.7 229 } 230 return x; 231 } 232 233 private static void maybeReBoxElements(Object[] xa) { 234 for (int i = 0; i < xa.length; i++) { 235 xa[i] = maybeReBox(xa[i]); 236 } 237 } 238 239 /** Canonical VM-aware implementation of BootstrapCallInfo. 240 * Knows how to dig into the JVM for lazily resolved (pull-mode) constants. 241 */ 242 private static final class VM_BSCI<T> extends BSCIWithCache<T> { 243 private final int[] indexInfo; 244 private final Class<?> caller; // for index resolution only 245 246 VM_BSCI(MethodHandle bsm, String name, T type, 247 Lookup lookup, int[] indexInfo) { 248 super(bsm, name, type, indexInfo[0]); 249 if (!lookup.hasPrivateAccess()) //D.I.D. 250 throw new AssertionError("bad Lookup object"); 251 this.caller = lookup.lookupClass(); 252 this.indexInfo = indexInfo; 253 // scoop up all the easy stuff right away: 254 prefetchIntoCache(0, size()); 255 } 256 257 @Override Object fillCache(int i) { 258 Object[] buf = { null }; 259 copyConstants(i, i+1, buf, 0); 260 Object res = wrapNull(buf[0]); 261 cache[i] = res; 262 int next = i + 1; 263 if (next < cache.length && cache[next] == null) 264 maybePrefetchIntoCache(next, false); // try to prefetch 265 return res; 266 } 267 268 @Override public int copyConstants(int start, int end, 269 Object[] buf, int pos) { 270 int i = start, bufi = pos; 271 while (i < end) { 272 Object x = cache[i]; 273 if (x == null) break; 274 buf[bufi++] = unwrapNull(x); 275 i++; 276 } 277 // give up at first null and grab the rest in one big block 278 if (i >= end) return i; 279 Object[] temp = new Object[end - i]; 280 if (TRACE_METHOD_LINKAGE) { 281 System.out.println("resolving more BSM arguments: " + 282 Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end)); 283 } 284 copyOutBootstrapArguments(caller, indexInfo, 285 i, end, temp, 0, 286 true, null); 287 for (Object x : temp) { 288 x = maybeReBox(x); 289 buf[bufi++] = x; 290 cache[i++] = wrapNull(x); 291 } 292 if (end < cache.length && cache[end] == null) 293 maybePrefetchIntoCache(end, true); // try to prefetch 294 return i; 295 } 296 297 private static final int MIN_PF = 4; 298 private void maybePrefetchIntoCache(int i, boolean bulk) { 299 int len = cache.length; 300 assert(0 <= i && i <= len); 301 int pfLimit = i; 302 if (bulk) pfLimit += i; // exponential prefetch expansion 303 // try to prefetch at least MIN_PF elements 304 if (pfLimit < i + MIN_PF) pfLimit = i + MIN_PF; 305 if (pfLimit > len || pfLimit < 0) pfLimit = len; 306 // stop prefetching where cache is more full than empty 307 int empty = 0, nonEmpty = 0, lastEmpty = i; 308 for (int j = i; j < pfLimit; j++) { 309 if (cache[j] == null) { 310 empty++; 311 lastEmpty = j; 312 } else { 313 nonEmpty++; 314 if (nonEmpty > empty) { 315 pfLimit = lastEmpty + 1; 316 break; 317 } 318 if (pfLimit < len) pfLimit++; 319 } 320 } 321 if (bulk && empty < MIN_PF && pfLimit < len) 322 return; // not worth the effort 323 prefetchIntoCache(i, pfLimit); 324 } 325 326 private void prefetchIntoCache(int i, int pfLimit) { 327 if (pfLimit <= i) return; // corner case 328 Object[] temp = new Object[pfLimit - i]; 329 if (TRACE_METHOD_LINKAGE) { 330 System.out.println("prefetching BSM arguments: " + 331 Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit)); 332 } 333 copyOutBootstrapArguments(caller, indexInfo, 334 i, pfLimit, temp, 0, 335 false, NOT_PRESENT); 336 for (Object x : temp) { 337 if (x != NOT_PRESENT && cache[i] == null) { 338 cache[i] = wrapNull(maybeReBox(x)); 339 } 340 i++; 341 } 342 } 343 } 344 345 /*non-public*/ static final 346 class PushAdapter { 347 // skeleton for push-mode BSM which wraps a pull-mode BSM: 348 static Object pushToBootstrapMethod(MethodHandle pullModeBSM, 349 MethodHandles.Lookup lookup, String name, Object type, 350 Object... arguments) throws Throwable { 351 ConstantGroup cons = makeConstantGroup(Arrays.asList(arguments)); 352 BootstrapCallInfo<?> bsci = makeBootstrapCallInfo(pullModeBSM, name, type, cons); 353 if (TRACE_METHOD_LINKAGE) 354 System.out.println("pull-mode BSM gets pushed arguments from fake BSCI"); 355 return pullModeBSM.invoke(lookup, bsci); 356 } 357 358 static final MethodHandle MH_pushToBootstrapMethod; 359 static { 360 final Class<?> THIS_CLASS = PushAdapter.class; 361 try { 362 MH_pushToBootstrapMethod = IMPL_LOOKUP 363 .findStatic(THIS_CLASS, "pushToBootstrapMethod", 364 MethodType.methodType(Object.class, MethodHandle.class, 365 Lookup.class, String.class, Object.class, Object[].class)); 366 } catch (Throwable ex) { 367 throw new InternalError(ex); 368 } 369 } 370 } 371 372 /*non-public*/ static final 373 class PullAdapter { 374 // skeleton for pull-mode BSM which wraps a push-mode BSM: 375 static Object pullFromBootstrapMethod(MethodHandle pushModeBSM, 376 MethodHandles.Lookup lookup, 377 BootstrapCallInfo<?> bsci) 378 throws Throwable { 379 int argc = bsci.size(); 380 switch (argc) { 381 case 0: 382 return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType()); 383 case 1: 384 return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), 385 bsci.get(0)); 386 case 2: 387 return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), 388 bsci.get(0), bsci.get(1)); 389 case 3: 390 return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), 391 bsci.get(0), bsci.get(1), bsci.get(2)); 392 case 4: 393 return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), 394 bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3)); 395 case 5: 396 return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), 397 bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4)); 398 case 6: 399 return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), 400 bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5)); 401 default: 402 final int NON_SPREAD_ARG_COUNT = 3; // (lookup, name, type) 403 final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT; 404 if (argc >= MAX_SAFE_SIZE) { 405 // to be on the safe side, use invokeWithArguments which handles jumbo lists 406 Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc]; 407 newargv[0] = lookup; 408 newargv[1] = bsci.invocationName(); 409 newargv[2] = bsci.invocationType(); 410 bsci.copyConstants(0, argc, newargv, NON_SPREAD_ARG_COUNT); 411 return pushModeBSM.invokeWithArguments(newargv); 412 } 413 MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc); 414 MethodHandle typedBSM = pushModeBSM.asType(invocationType); 415 MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); 416 Object[] argv = new Object[argc]; 417 bsci.copyConstants(0, argc, argv, 0); 418 return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv); 419 } 420 } 421 422 static final MethodHandle MH_pullFromBootstrapMethod; 423 424 static { 425 final Class<?> THIS_CLASS = PullAdapter.class; 426 try { 427 MH_pullFromBootstrapMethod = IMPL_LOOKUP 428 .findStatic(THIS_CLASS, "pullFromBootstrapMethod", 429 MethodType.methodType(Object.class, MethodHandle.class, 430 Lookup.class, BootstrapCallInfo.class)); 431 } catch (Throwable ex) { 432 throw new InternalError(ex); 433 } 434 } 435 } 436 437 /** Given a push-mode BSM (taking one argument) convert it to a 438 * pull-mode BSM (taking N pre-resolved arguments). 439 * This method is used when, in fact, the JVM is passing up 440 * pre-resolved arguments, but the BSM is expecting lazy stuff. 441 * Or, when goToPushMode is true, do the reverse transform. 442 * (The two transforms are exactly inverse.) 443 */ 444 static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) { 445 if (TRACE_METHOD_LINKAGE) { 446 System.out.println("converting BSM of type " + bsm.type() + " to " 447 + (goToPushMode ? "push mode" : "pull mode")); 448 } 449 assert(isPullModeBSM(bsm) == goToPushMode); // there must be a change 450 if (goToPushMode) { 451 return PushAdapter.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true); 452 } else { 453 return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false); 454 } 455 } 456 }