1 /* 2 * Copyright (c) 2008, 2010, 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 sun.dyn; 27 28 import sun.dyn.util.VerifyType; 29 import sun.dyn.util.Wrapper; 30 import java.dyn.*; 31 import java.util.Arrays; 32 import static sun.dyn.MethodHandleNatives.Constants.*; 33 import static sun.dyn.MemberName.newIllegalArgumentException; 34 35 /** 36 * This method handle performs simple conversion or checking of a single argument. 37 * @author jrose 38 */ 39 public class AdapterMethodHandle extends BoundMethodHandle { 40 41 //MethodHandle vmtarget; // next AMH or BMH in chain or final DMH 42 //Object argument; // parameter to the conversion if needed 43 //int vmargslot; // which argument slot is affected 44 private final int conversion; // the type of conversion: RETYPE_ONLY, etc. 45 46 // Constructors in this class *must* be package scoped or private. 47 private AdapterMethodHandle(MethodHandle target, MethodType newType, 48 long conv, Object convArg) { 49 super(newType, convArg, newType.parameterSlotDepth(1+convArgPos(conv))); 50 this.conversion = convCode(conv); 51 if (MethodHandleNatives.JVM_SUPPORT) { 52 // JVM might update VM-specific bits of conversion (ignore) 53 MethodHandleNatives.init(this, target, convArgPos(conv)); 54 } 55 } 56 private AdapterMethodHandle(MethodHandle target, MethodType newType, 57 long conv) { 58 this(target, newType, conv, null); 59 } 60 61 private static final Access IMPL_TOKEN = Access.getToken(); 62 63 // TO DO: When adapting another MH with a null conversion, clone 64 // the target and change its type, instead of adding another layer. 65 66 /** Can a JVM-level adapter directly implement the proposed 67 * argument conversions, as if by MethodHandles.convertArguments? 68 */ 69 public static boolean canPairwiseConvert(MethodType newType, MethodType oldType) { 70 // same number of args, of course 71 int len = newType.parameterCount(); 72 if (len != oldType.parameterCount()) 73 return false; 74 75 // Check return type. (Not much can be done with it.) 76 Class<?> exp = newType.returnType(); 77 Class<?> ret = oldType.returnType(); 78 if (!VerifyType.isNullConversion(ret, exp)) 79 return false; 80 81 // Check args pairwise. 82 for (int i = 0; i < len; i++) { 83 Class<?> src = newType.parameterType(i); // source type 84 Class<?> dst = oldType.parameterType(i); // destination type 85 if (!canConvertArgument(src, dst)) 86 return false; 87 } 88 89 return true; 90 } 91 92 /** Can a JVM-level adapter directly implement the proposed 93 * argument conversion, as if by MethodHandles.convertArguments? 94 */ 95 public static boolean canConvertArgument(Class<?> src, Class<?> dst) { 96 // ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes, 97 // so we don't need to repeat so much decision making. 98 if (VerifyType.isNullConversion(src, dst)) { 99 return true; 100 } else if (src.isPrimitive()) { 101 if (dst.isPrimitive()) 102 return canPrimCast(src, dst); 103 else 104 return canBoxArgument(src, dst); 105 } else { 106 if (dst.isPrimitive()) 107 return canUnboxArgument(src, dst); 108 else 109 return true; // any two refs can be interconverted 110 } 111 } 112 113 /** 114 * Create a JVM-level adapter method handle to conform the given method 115 * handle to the similar newType, using only pairwise argument conversions. 116 * For each argument, convert incoming argument to the exact type needed. 117 * Only null conversions are allowed on the return value (until 118 * the JVM supports ricochet adapters). 119 * The argument conversions allowed are casting, unboxing, 120 * integral widening or narrowing, and floating point widening or narrowing. 121 * @param token access check 122 * @param newType required call type 123 * @param target original method handle 124 * @return an adapter to the original handle with the desired new type, 125 * or the original target if the types are already identical 126 * or null if the adaptation cannot be made 127 */ 128 public static MethodHandle makePairwiseConvert(Access token, 129 MethodType newType, MethodHandle target) { 130 Access.check(token); 131 MethodType oldType = target.type(); 132 if (newType == oldType) return target; 133 134 if (!canPairwiseConvert(newType, oldType)) 135 return null; 136 // (after this point, it is an assertion error to fail to convert) 137 138 // Find last non-trivial conversion (if any). 139 int lastConv = newType.parameterCount()-1; 140 while (lastConv >= 0) { 141 Class<?> src = newType.parameterType(lastConv); // source type 142 Class<?> dst = oldType.parameterType(lastConv); // destination type 143 if (VerifyType.isNullConversion(src, dst)) { 144 --lastConv; 145 } else { 146 break; 147 } 148 } 149 // Now build a chain of one or more adapters. 150 MethodHandle adapter = target; 151 MethodType midType = oldType.changeReturnType(newType.returnType()); 152 for (int i = 0; i <= lastConv; i++) { 153 Class<?> src = newType.parameterType(i); // source type 154 Class<?> dst = midType.parameterType(i); // destination type 155 if (VerifyType.isNullConversion(src, dst)) { 156 // do nothing: difference is trivial 157 continue; 158 } 159 // Work the current type backward toward the desired caller type: 160 if (i != lastConv) { 161 midType = midType.changeParameterType(i, src); 162 } else { 163 // When doing the last (or only) real conversion, 164 // force all remaining null conversions to happen also. 165 assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src))); 166 midType = newType; 167 } 168 169 // Tricky case analysis follows. 170 // It parallels canConvertArgument() above. 171 if (src.isPrimitive()) { 172 if (dst.isPrimitive()) { 173 adapter = makePrimCast(token, midType, adapter, i, dst); 174 } else { 175 adapter = makeBoxArgument(token, midType, adapter, i, dst); 176 } 177 } else { 178 if (dst.isPrimitive()) { 179 // Caller has boxed a primitive. Unbox it for the target. 180 // The box type must correspond exactly to the primitive type. 181 // This is simpler than the powerful set of widening 182 // conversions supported by reflect.Method.invoke. 183 // Those conversions require a big nest of if/then/else logic, 184 // which we prefer to make a user responsibility. 185 adapter = makeUnboxArgument(token, midType, adapter, i, dst); 186 } else { 187 // Simple reference conversion. 188 // Note: Do not check for a class hierarchy relation 189 // between src and dst. In all cases a 'null' argument 190 // will pass the cast conversion. 191 adapter = makeCheckCast(token, midType, adapter, i, dst); 192 } 193 } 194 assert(adapter != null); 195 assert(adapter.type() == midType); 196 } 197 if (adapter.type() != newType) { 198 // Only trivial conversions remain. 199 adapter = makeRetypeOnly(IMPL_TOKEN, newType, adapter); 200 assert(adapter != null); 201 // Actually, that's because there were no non-trivial ones: 202 assert(lastConv == -1); 203 } 204 assert(adapter.type() == newType); 205 return adapter; 206 } 207 208 /** 209 * Create a JVM-level adapter method handle to permute the arguments 210 * of the given method. 211 * @param token access check 212 * @param newType required call type 213 * @param target original method handle 214 * @param argumentMap for each target argument, position of its source in newType 215 * @return an adapter to the original handle with the desired new type, 216 * or the original target if the types are already identical 217 * and the permutation is null 218 * @throws IllegalArgumentException if the adaptation cannot be made 219 * directly by a JVM-level adapter, without help from Java code 220 */ 221 public static MethodHandle makePermutation(Access token, 222 MethodType newType, MethodHandle target, 223 int[] argumentMap) { 224 MethodType oldType = target.type(); 225 boolean nullPermutation = true; 226 for (int i = 0; i < argumentMap.length; i++) { 227 int pos = argumentMap[i]; 228 if (pos != i) 229 nullPermutation = false; 230 if (pos < 0 || pos >= newType.parameterCount()) { 231 argumentMap = new int[0]; break; 232 } 233 } 234 if (argumentMap.length != oldType.parameterCount()) 235 throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap)); 236 if (nullPermutation) { 237 MethodHandle res = makePairwiseConvert(token, newType, target); 238 // well, that was easy 239 if (res == null) 240 throw newIllegalArgumentException("cannot convert pairwise: "+newType); 241 return res; 242 } 243 244 // Check return type. (Not much can be done with it.) 245 Class<?> exp = newType.returnType(); 246 Class<?> ret = oldType.returnType(); 247 if (!VerifyType.isNullConversion(ret, exp)) 248 throw newIllegalArgumentException("bad return conversion for "+newType); 249 250 // See if the argument types match up. 251 for (int i = 0; i < argumentMap.length; i++) { 252 int j = argumentMap[i]; 253 Class<?> src = newType.parameterType(j); 254 Class<?> dst = oldType.parameterType(i); 255 if (!VerifyType.isNullConversion(src, dst)) 256 throw newIllegalArgumentException("bad argument #"+j+" conversion for "+newType); 257 } 258 259 // Now figure out a nice mix of SWAP, ROT, DUP, and DROP adapters. 260 // A workable greedy algorithm is as follows: 261 // Drop unused outgoing arguments (right to left: shallowest first). 262 // Duplicate doubly-used outgoing arguments (left to right: deepest first). 263 // Then the remaining problem is a true argument permutation. 264 // Marshal the outgoing arguments as required from left to right. 265 // That is, find the deepest outgoing stack position that does not yet 266 // have the correct argument value, and correct at least that position 267 // by swapping or rotating in the misplaced value (from a shallower place). 268 // If the misplaced value is followed by one or more consecutive values 269 // (also misplaced) issue a rotation which brings as many as possible 270 // into position. Otherwise make progress with either a swap or a 271 // rotation. Prefer the swap as cheaper, but do not use it if it 272 // breaks a slot pair. Prefer the rotation over the swap if it would 273 // preserve more consecutive values shallower than the target position. 274 // When more than one rotation will work (because the required value 275 // is already adjacent to the target position), then use a rotation 276 // which moves the old value in the target position adjacent to 277 // one of its consecutive values. Also, prefer shorter rotation 278 // spans, since they use fewer memory cycles for shuffling. 279 280 throw new UnsupportedOperationException("NYI"); 281 } 282 283 private static byte basicType(Class<?> type) { 284 if (type == null) return T_VOID; 285 switch (Wrapper.forBasicType(type)) { 286 case BOOLEAN: return T_BOOLEAN; 287 case CHAR: return T_CHAR; 288 case FLOAT: return T_FLOAT; 289 case DOUBLE: return T_DOUBLE; 290 case BYTE: return T_BYTE; 291 case SHORT: return T_SHORT; 292 case INT: return T_INT; 293 case LONG: return T_LONG; 294 case OBJECT: return T_OBJECT; 295 case VOID: return T_VOID; 296 } 297 return 99; // T_ILLEGAL or some such 298 } 299 300 /** Number of stack slots for the given type. 301 * Two for T_DOUBLE and T_FLOAT, one for the rest. 302 */ 303 private static int type2size(int type) { 304 assert(type >= T_BOOLEAN && type <= T_OBJECT); 305 return (type == T_LONG || type == T_DOUBLE) ? 2 : 1; 306 } 307 private static int type2size(Class<?> type) { 308 return type2size(basicType(type)); 309 } 310 311 /** The given stackMove is the number of slots pushed. 312 * It might be negative. Scale it (multiply) by the 313 * VM's notion of how an address changes with a push, 314 * to get the raw SP change for stackMove. 315 * Then shift and mask it into the correct field. 316 */ 317 private static long insertStackMove(int stackMove) { 318 // following variable must be long to avoid sign extension after '<<' 319 long spChange = stackMove * MethodHandleNatives.JVM_STACK_MOVE_UNIT; 320 return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT; 321 } 322 323 /** Construct an adapter conversion descriptor for a single-argument conversion. */ 324 private static long makeConv(int convOp, int argnum, int src, int dest) { 325 assert(src == (src & 0xF)); 326 assert(dest == (dest & 0xF)); 327 assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF); 328 int stackMove = type2size(dest) - type2size(src); 329 return ((long) argnum << 32 | 330 (long) convOp << CONV_OP_SHIFT | 331 (int) src << CONV_SRC_TYPE_SHIFT | 332 (int) dest << CONV_DEST_TYPE_SHIFT | 333 insertStackMove(stackMove) 334 ); 335 } 336 private static long makeConv(int convOp, int argnum, int stackMove) { 337 assert(convOp >= OP_DUP_ARGS && convOp <= OP_SPREAD_ARGS); 338 byte src = 0, dest = 0; 339 if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS) 340 src = dest = T_OBJECT; 341 return ((long) argnum << 32 | 342 (long) convOp << CONV_OP_SHIFT | 343 (int) src << CONV_SRC_TYPE_SHIFT | 344 (int) dest << CONV_DEST_TYPE_SHIFT | 345 insertStackMove(stackMove) 346 ); 347 } 348 private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) { 349 assert(convOp >= OP_SWAP_ARGS && convOp <= OP_ROT_ARGS); 350 return ((long) srcArg << 32 | 351 (long) convOp << CONV_OP_SHIFT | 352 (int) type << CONV_SRC_TYPE_SHIFT | 353 (int) type << CONV_DEST_TYPE_SHIFT | 354 (int) destSlot << CONV_VMINFO_SHIFT 355 ); 356 } 357 private static long makeConv(int convOp) { 358 assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW); 359 return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero 360 } 361 private static int convCode(long conv) { 362 return (int)conv; 363 } 364 private static int convArgPos(long conv) { 365 return (int)(conv >>> 32); 366 } 367 private static boolean convOpSupported(int convOp) { 368 assert(convOp >= 0 && convOp <= CONV_OP_LIMIT); 369 return ((1<<convOp) & MethodHandleNatives.CONV_OP_IMPLEMENTED_MASK) != 0; 370 } 371 372 /** One of OP_RETYPE_ONLY, etc. */ 373 int conversionOp() { return (conversion & CONV_OP_MASK) >> CONV_OP_SHIFT; } 374 375 /* Return one plus the position of the first non-trivial difference 376 * between the given types. This is not a symmetric operation; 377 * we are considering adapting the targetType to adapterType. 378 * Trivial differences are those which could be ignored by the JVM 379 * without subverting the verifier. Otherwise, adaptable differences 380 * are ones for which we could create an adapter to make the type change. 381 * Return zero if there are no differences (other than trivial ones). 382 * Return 1+N if N is the only adaptable argument difference. 383 * Return the -2-N where N is the first of several adaptable 384 * argument differences. 385 * Return -1 if there there are differences which are not adaptable. 386 */ 387 private static int diffTypes(MethodType adapterType, 388 MethodType targetType, 389 boolean raw) { 390 int diff; 391 diff = diffReturnTypes(adapterType, targetType, raw); 392 if (diff != 0) return diff; 393 int nargs = adapterType.parameterCount(); 394 if (nargs != targetType.parameterCount()) 395 return -1; 396 diff = diffParamTypes(adapterType, 0, targetType, 0, nargs, raw); 397 //System.out.println("diff "+adapterType); 398 //System.out.println(" "+diff+" "+targetType); 399 return diff; 400 } 401 private static int diffReturnTypes(MethodType adapterType, 402 MethodType targetType, 403 boolean raw) { 404 Class<?> src = targetType.returnType(); 405 Class<?> dst = adapterType.returnType(); 406 if ((!raw 407 ? VerifyType.canPassUnchecked(src, dst) 408 : VerifyType.canPassRaw(src, dst) 409 ) > 0) 410 return 0; // no significant difference 411 if (raw && !src.isPrimitive() && !dst.isPrimitive()) 412 return 0; // can force a reference return (very carefully!) 413 //if (false) return 1; // never adaptable! 414 return -1; // some significant difference 415 } 416 private static int diffParamTypes(MethodType adapterType, int astart, 417 MethodType targetType, int tstart, 418 int nargs, boolean raw) { 419 assert(nargs >= 0); 420 int res = 0; 421 for (int i = 0; i < nargs; i++) { 422 Class<?> src = adapterType.parameterType(astart+i); 423 Class<?> dest = targetType.parameterType(tstart+i); 424 if ((!raw 425 ? VerifyType.canPassUnchecked(src, dest) 426 : VerifyType.canPassRaw(src, dest) 427 ) <= 0) { 428 // found a difference; is it the only one so far? 429 if (res != 0) 430 return -1-res; // return -2-i for prev. i 431 res = 1+i; 432 } 433 } 434 return res; 435 } 436 437 /** Can a retyping adapter (alone) validly convert the target to newType? */ 438 public static boolean canRetypeOnly(MethodType newType, MethodType targetType) { 439 return canRetype(newType, targetType, false); 440 } 441 /** Can a retyping adapter (alone) convert the target to newType? 442 * It is allowed to widen subword types and void to int, to make bitwise 443 * conversions between float/int and double/long, and to perform unchecked 444 * reference conversions on return. This last feature requires that the 445 * caller be trusted, and perform explicit cast conversions on return values. 446 */ 447 public static boolean canRetypeRaw(MethodType newType, MethodType targetType) { 448 return canRetype(newType, targetType, true); 449 } 450 static boolean canRetype(MethodType newType, MethodType targetType, boolean raw) { 451 if (!convOpSupported(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY)) return false; 452 int diff = diffTypes(newType, targetType, raw); 453 // %%% This assert is too strong. Factor diff into VerifyType and reconcile. 454 assert(raw || (diff == 0) == VerifyType.isNullConversion(newType, targetType)); 455 return diff == 0; 456 } 457 458 /** Factory method: Performs no conversions; simply retypes the adapter. 459 * Allows unchecked argument conversions pairwise, if they are safe. 460 * Returns null if not possible. 461 */ 462 public static MethodHandle makeRetypeOnly(Access token, 463 MethodType newType, MethodHandle target) { 464 return makeRetype(token, newType, target, false); 465 } 466 public static MethodHandle makeRetypeRaw(Access token, 467 MethodType newType, MethodHandle target) { 468 return makeRetype(token, newType, target, true); 469 } 470 static MethodHandle makeRetype(Access token, 471 MethodType newType, MethodHandle target, boolean raw) { 472 Access.check(token); 473 MethodType oldType = target.type(); 474 if (oldType == newType) return target; 475 if (!canRetype(newType, oldType, raw)) 476 return null; 477 // TO DO: clone the target guy, whatever he is, with new type. 478 return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY)); 479 } 480 481 static MethodHandle makeTypeHandler(Access token, 482 MethodHandle target, MethodHandle typeHandler) { 483 Access.check(token); 484 return new WithTypeHandler(target, typeHandler); 485 } 486 487 static class WithTypeHandler extends AdapterMethodHandle { 488 final MethodHandle target, typeHandler; 489 WithTypeHandler(MethodHandle target, MethodHandle typeHandler) { 490 super(target, target.type(), makeConv(OP_RETYPE_ONLY)); 491 this.target = target; 492 this.typeHandler = typeHandler.asType(TYPE_HANDLER_TYPE); 493 } 494 495 public MethodHandle asType(MethodType newType) { 496 if (this.type() == newType) 497 return this; 498 try { 499 MethodHandle retyped = (MethodHandle) typeHandler.invokeExact(target, newType); 500 // Contract: Must return the desired type, or throw WMT 501 if (retyped.type() != newType) 502 throw new WrongMethodTypeException(retyped.toString()); 503 return retyped; 504 } catch (Throwable ex) { 505 if (ex instanceof Error) throw (Error)ex; 506 if (ex instanceof RuntimeException) throw (RuntimeException)ex; 507 throw new RuntimeException(ex); 508 } 509 } 510 private static final MethodType TYPE_HANDLER_TYPE 511 = MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class); 512 } 513 514 /** Can a checkcast adapter validly convert the target to newType? 515 * The JVM supports all kind of reference casts, even silly ones. 516 */ 517 public static boolean canCheckCast(MethodType newType, MethodType targetType, 518 int arg, Class<?> castType) { 519 if (!convOpSupported(OP_CHECK_CAST)) return false; 520 Class<?> src = newType.parameterType(arg); 521 Class<?> dst = targetType.parameterType(arg); 522 if (!canCheckCast(src, castType) 523 || !VerifyType.isNullConversion(castType, dst)) 524 return false; 525 int diff = diffTypes(newType, targetType, false); 526 return (diff == arg+1); // arg is sole non-trivial diff 527 } 528 /** Can an primitive conversion adapter validly convert src to dst? */ 529 public static boolean canCheckCast(Class<?> src, Class<?> dst) { 530 return (!src.isPrimitive() && !dst.isPrimitive()); 531 } 532 533 /** Factory method: Forces a cast at the given argument. 534 * The castType is the target of the cast, and can be any type 535 * with a null conversion to the corresponding target parameter. 536 * Return null if this cannot be done. 537 */ 538 public static MethodHandle makeCheckCast(Access token, 539 MethodType newType, MethodHandle target, 540 int arg, Class<?> castType) { 541 Access.check(token); 542 if (!canCheckCast(newType, target.type(), arg, castType)) 543 return null; 544 long conv = makeConv(OP_CHECK_CAST, arg, T_OBJECT, T_OBJECT); 545 return new AdapterMethodHandle(target, newType, conv, castType); 546 } 547 548 /** Can an primitive conversion adapter validly convert the target to newType? 549 * The JVM currently supports all conversions except those between 550 * floating and integral types. 551 */ 552 public static boolean canPrimCast(MethodType newType, MethodType targetType, 553 int arg, Class<?> convType) { 554 if (!convOpSupported(OP_PRIM_TO_PRIM)) return false; 555 Class<?> src = newType.parameterType(arg); 556 Class<?> dst = targetType.parameterType(arg); 557 if (!canPrimCast(src, convType) 558 || !VerifyType.isNullConversion(convType, dst)) 559 return false; 560 int diff = diffTypes(newType, targetType, false); 561 return (diff == arg+1); // arg is sole non-trivial diff 562 } 563 /** Can an primitive conversion adapter validly convert src to dst? */ 564 public static boolean canPrimCast(Class<?> src, Class<?> dst) { 565 if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) { 566 return false; 567 } else if (Wrapper.forPrimitiveType(dst).isFloating()) { 568 // both must be floating types 569 return Wrapper.forPrimitiveType(src).isFloating(); 570 } else { 571 // both are integral, and all combinations work fine 572 assert(Wrapper.forPrimitiveType(src).isIntegral() && 573 Wrapper.forPrimitiveType(dst).isIntegral()); 574 return true; 575 } 576 } 577 578 /** Factory method: Truncate the given argument with zero or sign extension, 579 * and/or convert between single and doubleword versions of integer or float. 580 * The convType is the target of the conversion, and can be any type 581 * with a null conversion to the corresponding target parameter. 582 * Return null if this cannot be done. 583 */ 584 public static MethodHandle makePrimCast(Access token, 585 MethodType newType, MethodHandle target, 586 int arg, Class<?> convType) { 587 Access.check(token); 588 MethodType oldType = target.type(); 589 if (!canPrimCast(newType, oldType, arg, convType)) 590 return null; 591 Class<?> src = newType.parameterType(arg); 592 long conv = makeConv(OP_PRIM_TO_PRIM, arg, basicType(src), basicType(convType)); 593 return new AdapterMethodHandle(target, newType, conv); 594 } 595 596 /** Can an unboxing conversion validly convert src to dst? 597 * The JVM currently supports all kinds of casting and unboxing. 598 * The convType is the unboxed type; it can be either a primitive or wrapper. 599 */ 600 public static boolean canUnboxArgument(MethodType newType, MethodType targetType, 601 int arg, Class<?> convType) { 602 if (!convOpSupported(OP_REF_TO_PRIM)) return false; 603 Class<?> src = newType.parameterType(arg); 604 Class<?> dst = targetType.parameterType(arg); 605 Class<?> boxType = Wrapper.asWrapperType(convType); 606 convType = Wrapper.asPrimitiveType(convType); 607 if (!canCheckCast(src, boxType) 608 || boxType == convType 609 || !VerifyType.isNullConversion(convType, dst)) 610 return false; 611 int diff = diffTypes(newType, targetType, false); 612 return (diff == arg+1); // arg is sole non-trivial diff 613 } 614 /** Can an primitive unboxing adapter validly convert src to dst? */ 615 public static boolean canUnboxArgument(Class<?> src, Class<?> dst) { 616 return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive()); 617 } 618 619 /** Factory method: Unbox the given argument. 620 * Return null if this cannot be done. 621 */ 622 public static MethodHandle makeUnboxArgument(Access token, 623 MethodType newType, MethodHandle target, 624 int arg, Class<?> convType) { 625 MethodType oldType = target.type(); 626 Class<?> src = newType.parameterType(arg); 627 Class<?> dst = oldType.parameterType(arg); 628 Class<?> boxType = Wrapper.asWrapperType(convType); 629 Class<?> primType = Wrapper.asPrimitiveType(convType); 630 if (!canUnboxArgument(newType, oldType, arg, convType)) 631 return null; 632 MethodType castDone = newType; 633 if (!VerifyType.isNullConversion(src, boxType)) 634 castDone = newType.changeParameterType(arg, boxType); 635 long conv = makeConv(OP_REF_TO_PRIM, arg, T_OBJECT, basicType(primType)); 636 MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType); 637 if (castDone == newType) 638 return adapter; 639 return makeCheckCast(token, newType, adapter, arg, boxType); 640 } 641 642 /** Can an primitive boxing adapter validly convert src to dst? */ 643 public static boolean canBoxArgument(Class<?> src, Class<?> dst) { 644 if (!convOpSupported(OP_PRIM_TO_REF)) return false; 645 throw new UnsupportedOperationException("NYI"); 646 } 647 648 /** Factory method: Unbox the given argument. 649 * Return null if this cannot be done. 650 */ 651 public static MethodHandle makeBoxArgument(Access token, 652 MethodType newType, MethodHandle target, 653 int arg, Class<?> convType) { 654 // this is difficult to do in the JVM because it must GC 655 return null; 656 } 657 658 /** Can an adapter simply drop arguments to convert the target to newType? */ 659 public static boolean canDropArguments(MethodType newType, MethodType targetType, 660 int dropArgPos, int dropArgCount) { 661 if (dropArgCount == 0) 662 return canRetypeOnly(newType, targetType); 663 if (!convOpSupported(OP_DROP_ARGS)) return false; 664 if (diffReturnTypes(newType, targetType, false) != 0) 665 return false; 666 int nptypes = newType.parameterCount(); 667 // parameter types must be the same up to the drop point 668 if (dropArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, dropArgPos, false) != 0) 669 return false; 670 int afterPos = dropArgPos + dropArgCount; 671 int afterCount = nptypes - afterPos; 672 if (dropArgPos < 0 || dropArgPos >= nptypes || 673 dropArgCount < 1 || afterPos > nptypes || 674 targetType.parameterCount() != nptypes - dropArgCount) 675 return false; 676 // parameter types after the drop point must also be the same 677 if (afterCount != 0 && diffParamTypes(newType, afterPos, targetType, dropArgPos, afterCount, false) != 0) 678 return false; 679 return true; 680 } 681 682 /** Factory method: Drop selected arguments. 683 * Allow unchecked retyping of remaining arguments, pairwise. 684 * Return null if this is not possible. 685 */ 686 public static MethodHandle makeDropArguments(Access token, 687 MethodType newType, MethodHandle target, 688 int dropArgPos, int dropArgCount) { 689 Access.check(token); 690 if (dropArgCount == 0) 691 return makeRetypeOnly(IMPL_TOKEN, newType, target); 692 if (!canDropArguments(newType, target.type(), dropArgPos, dropArgCount)) 693 return null; 694 // in arglist: [0: ...keep1 | dpos: drop... | dpos+dcount: keep2... ] 695 // out arglist: [0: ...keep1 | dpos: keep2... ] 696 int keep2InPos = dropArgPos + dropArgCount; 697 int dropSlot = newType.parameterSlotDepth(keep2InPos); 698 int keep1InSlot = newType.parameterSlotDepth(dropArgPos); 699 int slotCount = keep1InSlot - dropSlot; 700 assert(slotCount >= dropArgCount); 701 assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount()); 702 long conv = makeConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount); 703 return new AdapterMethodHandle(target, newType, conv); 704 } 705 706 /** Can an adapter duplicate an argument to convert the target to newType? */ 707 public static boolean canDupArguments(MethodType newType, MethodType targetType, 708 int dupArgPos, int dupArgCount) { 709 if (!convOpSupported(OP_DUP_ARGS)) return false; 710 if (diffReturnTypes(newType, targetType, false) != 0) 711 return false; 712 int nptypes = newType.parameterCount(); 713 if (dupArgCount < 0 || dupArgPos + dupArgCount > nptypes) 714 return false; 715 if (targetType.parameterCount() != nptypes + dupArgCount) 716 return false; 717 // parameter types must be the same up to the duplicated arguments 718 if (diffParamTypes(newType, 0, targetType, 0, nptypes, false) != 0) 719 return false; 720 // duplicated types must be, well, duplicates 721 if (diffParamTypes(newType, dupArgPos, targetType, nptypes, dupArgCount, false) != 0) 722 return false; 723 return true; 724 } 725 726 /** Factory method: Duplicate the selected argument. 727 * Return null if this is not possible. 728 */ 729 public static MethodHandle makeDupArguments(Access token, 730 MethodType newType, MethodHandle target, 731 int dupArgPos, int dupArgCount) { 732 Access.check(token); 733 if (!canDupArguments(newType, target.type(), dupArgPos, dupArgCount)) 734 return null; 735 if (dupArgCount == 0) 736 return target; 737 // in arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... ] 738 // out arglist: [0: ...keep1 | dpos: dup... | dpos+dcount: keep2... | dup... ] 739 int keep2InPos = dupArgPos + dupArgCount; 740 int dupSlot = newType.parameterSlotDepth(keep2InPos); 741 int keep1InSlot = newType.parameterSlotDepth(dupArgPos); 742 int slotCount = keep1InSlot - dupSlot; 743 assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount()); 744 long conv = makeConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount); 745 return new AdapterMethodHandle(target, newType, conv); 746 } 747 748 /** Can an adapter swap two arguments to convert the target to newType? */ 749 public static boolean canSwapArguments(MethodType newType, MethodType targetType, 750 int swapArg1, int swapArg2) { 751 if (!convOpSupported(OP_SWAP_ARGS)) return false; 752 if (diffReturnTypes(newType, targetType, false) != 0) 753 return false; 754 if (swapArg1 >= swapArg2) return false; // caller resp 755 int nptypes = newType.parameterCount(); 756 if (targetType.parameterCount() != nptypes) 757 return false; 758 if (swapArg1 < 0 || swapArg2 >= nptypes) 759 return false; 760 if (diffParamTypes(newType, 0, targetType, 0, swapArg1, false) != 0) 761 return false; 762 if (diffParamTypes(newType, swapArg1, targetType, swapArg2, 1, false) != 0) 763 return false; 764 if (diffParamTypes(newType, swapArg1+1, targetType, swapArg1+1, swapArg2-swapArg1-1, false) != 0) 765 return false; 766 if (diffParamTypes(newType, swapArg2, targetType, swapArg1, 1, false) != 0) 767 return false; 768 if (diffParamTypes(newType, swapArg2+1, targetType, swapArg2+1, nptypes-swapArg2-1, false) != 0) 769 return false; 770 return true; 771 } 772 773 /** Factory method: Swap the selected arguments. 774 * Return null if this is not possible. 775 */ 776 public static MethodHandle makeSwapArguments(Access token, 777 MethodType newType, MethodHandle target, 778 int swapArg1, int swapArg2) { 779 Access.check(token); 780 if (swapArg1 == swapArg2) 781 return target; 782 if (swapArg1 > swapArg2) { int t = swapArg1; swapArg1 = swapArg2; swapArg2 = t; } 783 if (!canSwapArguments(newType, target.type(), swapArg1, swapArg2)) 784 return null; 785 Class<?> swapType = newType.parameterType(swapArg1); 786 // in arglist: [0: ...keep1 | pos1: a1 | pos1+1: keep2... | pos2: a2 | pos2+1: keep3... ] 787 // out arglist: [0: ...keep1 | pos1: a2 | pos1+1: keep2... | pos2: a1 | pos2+1: keep3... ] 788 int swapSlot2 = newType.parameterSlotDepth(swapArg2 + 1); 789 long conv = makeSwapConv(OP_SWAP_ARGS, swapArg1, basicType(swapType), swapSlot2); 790 return new AdapterMethodHandle(target, newType, conv); 791 } 792 793 static int positiveRotation(int argCount, int rotateBy) { 794 assert(argCount > 0); 795 if (rotateBy >= 0) { 796 if (rotateBy < argCount) 797 return rotateBy; 798 return rotateBy % argCount; 799 } else if (rotateBy >= -argCount) { 800 return rotateBy + argCount; 801 } else { 802 return (-1-((-1-rotateBy) % argCount)) + argCount; 803 } 804 } 805 806 final static int MAX_ARG_ROTATION = 1; 807 808 /** Can an adapter rotate arguments to convert the target to newType? */ 809 public static boolean canRotateArguments(MethodType newType, MethodType targetType, 810 int firstArg, int argCount, int rotateBy) { 811 if (!convOpSupported(OP_ROT_ARGS)) return false; 812 if (argCount <= 2) return false; // must be a swap, not a rotate 813 rotateBy = positiveRotation(argCount, rotateBy); 814 if (rotateBy == 0) return false; // no rotation 815 if (rotateBy > MAX_ARG_ROTATION && rotateBy < argCount - MAX_ARG_ROTATION) 816 return false; // too many argument positions 817 // Rotate incoming args right N to the out args, N in 1..(argCouunt-1). 818 if (diffReturnTypes(newType, targetType, false) != 0) 819 return false; 820 int nptypes = newType.parameterCount(); 821 if (targetType.parameterCount() != nptypes) 822 return false; 823 if (firstArg < 0 || firstArg >= nptypes) return false; 824 int argLimit = firstArg + argCount; 825 if (argLimit > nptypes) return false; 826 if (diffParamTypes(newType, 0, targetType, 0, firstArg, false) != 0) 827 return false; 828 int newChunk1 = argCount - rotateBy, newChunk2 = rotateBy; 829 // swap new chunk1 with target chunk2 830 if (diffParamTypes(newType, firstArg, targetType, argLimit-newChunk1, newChunk1, false) != 0) 831 return false; 832 // swap new chunk2 with target chunk1 833 if (diffParamTypes(newType, firstArg+newChunk1, targetType, firstArg, newChunk2, false) != 0) 834 return false; 835 return true; 836 } 837 838 /** Factory method: Rotate the selected argument range. 839 * Return null if this is not possible. 840 */ 841 public static MethodHandle makeRotateArguments(Access token, 842 MethodType newType, MethodHandle target, 843 int firstArg, int argCount, int rotateBy) { 844 Access.check(token); 845 rotateBy = positiveRotation(argCount, rotateBy); 846 if (!canRotateArguments(newType, target.type(), firstArg, argCount, rotateBy)) 847 return null; 848 // Decide whether it should be done as a right or left rotation, 849 // on the JVM stack. Return the number of stack slots to rotate by, 850 // positive if right, negative if left. 851 int limit = firstArg + argCount; 852 int depth0 = newType.parameterSlotDepth(firstArg); 853 int depth1 = newType.parameterSlotDepth(limit-rotateBy); 854 int depth2 = newType.parameterSlotDepth(limit); 855 int chunk1Slots = depth0 - depth1; assert(chunk1Slots > 0); 856 int chunk2Slots = depth1 - depth2; assert(chunk2Slots > 0); 857 // From here on out, it assumes a single-argument shift. 858 assert(MAX_ARG_ROTATION == 1); 859 int srcArg, dstArg; 860 byte basicType; 861 if (chunk2Slots <= chunk1Slots) { 862 // Rotate right/down N (rotateBy = +N, N small, c2 small): 863 // in arglist: [0: ...keep1 | arg1: c1... | limit-N: c2 | limit: keep2... ] 864 // out arglist: [0: ...keep1 | arg1: c2 | arg1+N: c1... | limit: keep2... ] 865 srcArg = limit-1; 866 dstArg = firstArg; 867 basicType = basicType(newType.parameterType(srcArg)); 868 assert(chunk2Slots == type2size(basicType)); 869 } else { 870 // Rotate left/up N (rotateBy = -N, N small, c1 small): 871 // in arglist: [0: ...keep1 | arg1: c1 | arg1+N: c2... | limit: keep2... ] 872 // out arglist: [0: ...keep1 | arg1: c2 ... | limit-N: c1 | limit: keep2... ] 873 srcArg = firstArg; 874 dstArg = limit-1; 875 basicType = basicType(newType.parameterType(srcArg)); 876 assert(chunk1Slots == type2size(basicType)); 877 } 878 int dstSlot = newType.parameterSlotDepth(dstArg + 1); 879 long conv = makeSwapConv(OP_ROT_ARGS, srcArg, basicType, dstSlot); 880 return new AdapterMethodHandle(target, newType, conv); 881 } 882 883 /** Can an adapter spread an argument to convert the target to newType? */ 884 public static boolean canSpreadArguments(MethodType newType, MethodType targetType, 885 Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) { 886 if (!convOpSupported(OP_SPREAD_ARGS)) return false; 887 if (diffReturnTypes(newType, targetType, false) != 0) 888 return false; 889 int nptypes = newType.parameterCount(); 890 // parameter types must be the same up to the spread point 891 if (spreadArgPos != 0 && diffParamTypes(newType, 0, targetType, 0, spreadArgPos, false) != 0) 892 return false; 893 int afterPos = spreadArgPos + spreadArgCount; 894 int afterCount = nptypes - (spreadArgPos + 1); 895 if (spreadArgPos < 0 || spreadArgPos >= nptypes || 896 spreadArgCount < 0 || 897 targetType.parameterCount() != afterPos + afterCount) 898 return false; 899 // parameter types after the spread point must also be the same 900 if (afterCount != 0 && diffParamTypes(newType, spreadArgPos+1, targetType, afterPos, afterCount, false) != 0) 901 return false; 902 // match the array element type to the spread arg types 903 Class<?> rawSpreadArgType = newType.parameterType(spreadArgPos); 904 if (rawSpreadArgType != spreadArgType && !canCheckCast(rawSpreadArgType, spreadArgType)) 905 return false; 906 for (int i = 0; i < spreadArgCount; i++) { 907 Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i); 908 Class<?> dst = targetType.parameterType(spreadArgPos + i); 909 if (src == null || !VerifyType.isNullConversion(src, dst)) 910 return false; 911 } 912 return true; 913 } 914 915 916 /** Factory method: Spread selected argument. */ 917 public static MethodHandle makeSpreadArguments(Access token, 918 MethodType newType, MethodHandle target, 919 Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) { 920 Access.check(token); 921 MethodType targetType = target.type(); 922 if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount)) 923 return null; 924 // in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ] 925 // out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ] 926 int keep2OutPos = spreadArgPos + spreadArgCount; 927 int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); 928 int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); 929 int slotCount = keep1OutSlot - spreadSlot; 930 assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+1)); 931 assert(slotCount >= spreadArgCount); 932 long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, slotCount-1); 933 MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType); 934 assert(res.type().parameterType(spreadArgPos) == spreadArgType); 935 return res; 936 } 937 938 // TO DO: makeCollectArguments, makeFlyby, makeRicochet 939 940 @Override 941 public String toString() { 942 return nonAdapter((MethodHandle)vmtarget).toString(); 943 } 944 945 private static MethodHandle nonAdapter(MethodHandle mh) { 946 while (mh instanceof AdapterMethodHandle) { 947 mh = (MethodHandle) mh.vmtarget; 948 } 949 return mh; 950 } 951 }