1 /* 2 * Copyright (c) 2014, 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 java.lang.ref.SoftReference; 29 import java.util.Arrays; 30 import static java.lang.invoke.LambdaForm.*; 31 import static java.lang.invoke.LambdaForm.BasicType.*; 32 import static java.lang.invoke.MethodHandleImpl.Intrinsic; 33 import java.util.Collections; 34 import java.util.concurrent.ConcurrentHashMap; 35 36 import sun.invoke.util.Wrapper; 37 38 /** Transforms on LFs. 39 * A lambda-form editor can derive new LFs from its base LF. 40 * The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes. 41 * To support this caching, a LF has an optional pointer to its editor. 42 */ 43 class LambdaFormEditor { 44 final LambdaForm lambdaForm; 45 46 private LambdaFormEditor(LambdaForm lambdaForm) { 47 this.lambdaForm = lambdaForm; 48 } 49 50 // Factory method. 51 static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) { 52 // TO DO: Consider placing intern logic here, to cut down on duplication. 53 // lambdaForm = findPreexistingEquivalent(lambdaForm) 54 return new LambdaFormEditor(lambdaForm); 55 } 56 57 /** A description of a cached transform, possibly associated with the result of the transform. 58 * The logical content is a sequence of byte values, starting with a Kind.ordinal value. 59 * The sequence is unterminated, ending with an indefinite number of zero bytes. 60 * Sequences that are simple (short enough and with small enough values) pack into a 64-bit long. 61 */ 62 private static final class Transform extends SoftReference<LambdaForm> { 63 final long packedBytes; 64 final byte[] fullBytes; 65 66 private enum Kind { 67 NO_KIND, // necessary because ordinal must be greater than zero 68 BIND_ARG, ADD_ARG, DUP_ARG, 69 SPREAD_ARGS, 70 FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO, 71 COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY, 72 FOLD_ARGS, FOLD_ARGS_TO_VOID, 73 PERMUTE_ARGS 74 //maybe add more for guard with test, catch exception, pointwise type conversions 75 } 76 77 private static final boolean STRESS_TEST = false; // turn on to disable most packing 78 private static final int 79 PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4), 80 PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1, 81 PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE); 82 83 private static long packedBytes(byte[] bytes) { 84 if (bytes.length > PACKED_BYTE_MAX_LENGTH) return 0; 85 long pb = 0; 86 int bitset = 0; 87 for (int i = 0; i < bytes.length; i++) { 88 int b = bytes[i] & 0xFF; 89 bitset |= b; 90 pb |= (long)b << (i * PACKED_BYTE_SIZE); 91 } 92 if (!inRange(bitset)) 93 return 0; 94 return pb; 95 } 96 private static long packedBytes(int b0, int b1) { 97 assert(inRange(b0 | b1)); 98 return ( (b0 << 0*PACKED_BYTE_SIZE) 99 | (b1 << 1*PACKED_BYTE_SIZE)); 100 } 101 private static long packedBytes(int b0, int b1, int b2) { 102 assert(inRange(b0 | b1 | b2)); 103 return ( (b0 << 0*PACKED_BYTE_SIZE) 104 | (b1 << 1*PACKED_BYTE_SIZE) 105 | (b2 << 2*PACKED_BYTE_SIZE)); 106 } 107 private static long packedBytes(int b0, int b1, int b2, int b3) { 108 assert(inRange(b0 | b1 | b2 | b3)); 109 return ( (b0 << 0*PACKED_BYTE_SIZE) 110 | (b1 << 1*PACKED_BYTE_SIZE) 111 | (b2 << 2*PACKED_BYTE_SIZE) 112 | (b3 << 3*PACKED_BYTE_SIZE)); 113 } 114 private static boolean inRange(int bitset) { 115 assert((bitset & 0xFF) == bitset); // incoming values must fit in *unsigned* byte 116 return ((bitset & ~PACKED_BYTE_MASK) == 0); 117 } 118 private static byte[] fullBytes(int... byteValues) { 119 byte[] bytes = new byte[byteValues.length]; 120 int i = 0; 121 for (int bv : byteValues) { 122 bytes[i++] = bval(bv); 123 } 124 assert(packedBytes(bytes) == 0); 125 return bytes; 126 } 127 128 private byte byteAt(int i) { 129 long pb = packedBytes; 130 if (pb == 0) { 131 if (i >= fullBytes.length) return 0; 132 return fullBytes[i]; 133 } 134 assert(fullBytes == null); 135 if (i > PACKED_BYTE_MAX_LENGTH) return 0; 136 int pos = (i * PACKED_BYTE_SIZE); 137 return (byte)((pb >>> pos) & PACKED_BYTE_MASK); 138 } 139 140 Kind kind() { return Kind.values()[byteAt(0)]; } 141 142 private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) { 143 super(result); 144 this.packedBytes = packedBytes; 145 this.fullBytes = fullBytes; 146 } 147 private Transform(long packedBytes) { 148 this(packedBytes, null, null); 149 assert(packedBytes != 0); 150 } 151 private Transform(byte[] fullBytes) { 152 this(0, fullBytes, null); 153 } 154 155 private static byte bval(int b) { 156 assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte 157 return (byte)b; 158 } 159 private static byte bval(Kind k) { 160 return bval(k.ordinal()); 161 } 162 static Transform of(Kind k, int b1) { 163 byte b0 = bval(k); 164 if (inRange(b0 | b1)) 165 return new Transform(packedBytes(b0, b1)); 166 else 167 return new Transform(fullBytes(b0, b1)); 168 } 169 static Transform of(Kind k, int b1, int b2) { 170 byte b0 = (byte) k.ordinal(); 171 if (inRange(b0 | b1 | b2)) 172 return new Transform(packedBytes(b0, b1, b2)); 173 else 174 return new Transform(fullBytes(b0, b1, b2)); 175 } 176 static Transform of(Kind k, int b1, int b2, int b3) { 177 byte b0 = (byte) k.ordinal(); 178 if (inRange(b0 | b1 | b2 | b3)) 179 return new Transform(packedBytes(b0, b1, b2, b3)); 180 else 181 return new Transform(fullBytes(b0, b1, b2, b3)); 182 } 183 private static final byte[] NO_BYTES = {}; 184 static Transform of(Kind k, int... b123) { 185 return ofBothArrays(k, b123, NO_BYTES); 186 } 187 static Transform of(Kind k, int b1, byte[] b234) { 188 return ofBothArrays(k, new int[]{ b1 }, b234); 189 } 190 static Transform of(Kind k, int b1, int b2, byte[] b345) { 191 return ofBothArrays(k, new int[]{ b1, b2 }, b345); 192 } 193 private static Transform ofBothArrays(Kind k, int[] b123, byte[] b456) { 194 byte[] fullBytes = new byte[1 + b123.length + b456.length]; 195 int i = 0; 196 fullBytes[i++] = bval(k); 197 for (int bv : b123) { 198 fullBytes[i++] = bval(bv); 199 } 200 for (byte bv : b456) { 201 fullBytes[i++] = bv; 202 } 203 long packedBytes = packedBytes(fullBytes); 204 if (packedBytes != 0) 205 return new Transform(packedBytes); 206 else 207 return new Transform(fullBytes); 208 } 209 210 Transform withResult(LambdaForm result) { 211 return new Transform(this.packedBytes, this.fullBytes, result); 212 } 213 214 @Override 215 public boolean equals(Object obj) { 216 return obj instanceof Transform && equals((Transform)obj); 217 } 218 public boolean equals(Transform that) { 219 return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); 220 } 221 @Override 222 public int hashCode() { 223 if (packedBytes != 0) { 224 assert(fullBytes == null); 225 return Long.hashCode(packedBytes); 226 } 227 return Arrays.hashCode(fullBytes); 228 } 229 @Override 230 public String toString() { 231 StringBuilder buf = new StringBuilder(); 232 long bits = packedBytes; 233 if (bits != 0) { 234 buf.append("("); 235 while (bits != 0) { 236 buf.append(bits & PACKED_BYTE_MASK); 237 bits >>>= PACKED_BYTE_SIZE; 238 if (bits != 0) buf.append(","); 239 } 240 buf.append(")"); 241 } 242 if (fullBytes != null) { 243 buf.append("unpacked"); 244 buf.append(Arrays.toString(fullBytes)); 245 } 246 LambdaForm result = get(); 247 if (result != null) { 248 buf.append(" result="); 249 buf.append(result); 250 } 251 return buf.toString(); 252 } 253 } 254 255 /** Find a previously cached transform equivalent to the given one, and return its result. */ 256 private LambdaForm getInCache(Transform key) { 257 assert(key.get() == null); 258 // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap. 259 Object c = lambdaForm.transformCache; 260 Transform k = null; 261 if (c instanceof ConcurrentHashMap) { 262 @SuppressWarnings("unchecked") 263 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c; 264 k = m.get(key); 265 } else if (c == null) { 266 return null; 267 } else if (c instanceof Transform) { 268 // one-element cache avoids overhead of an array 269 Transform t = (Transform)c; 270 if (t.equals(key)) k = t; 271 } else { 272 Transform[] ta = (Transform[])c; 273 for (int i = 0; i < ta.length; i++) { 274 Transform t = ta[i]; 275 if (t == null) break; 276 if (t.equals(key)) { k = t; break; } 277 } 278 } 279 assert(k == null || key.equals(k)); 280 return (k != null) ? k.get() : null; 281 } 282 283 /** Arbitrary but reasonable limits on Transform[] size for cache. */ 284 private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16; 285 286 /** Cache a transform with its result, and return that result. 287 * But if an equivalent transform has already been cached, return its result instead. 288 */ 289 private LambdaForm putInCache(Transform key, LambdaForm form) { 290 key = key.withResult(form); 291 for (int pass = 0; ; pass++) { 292 Object c = lambdaForm.transformCache; 293 if (c instanceof ConcurrentHashMap) { 294 @SuppressWarnings("unchecked") 295 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c; 296 Transform k = m.putIfAbsent(key, key); 297 if (k == null) return form; 298 LambdaForm result = k.get(); 299 if (result != null) { 300 return result; 301 } else { 302 if (m.replace(key, k, key)) { 303 return form; 304 } else { 305 continue; 306 } 307 } 308 } 309 assert(pass == 0); 310 synchronized (lambdaForm) { 311 c = lambdaForm.transformCache; 312 if (c instanceof ConcurrentHashMap) 313 continue; 314 if (c == null) { 315 lambdaForm.transformCache = key; 316 return form; 317 } 318 Transform[] ta; 319 if (c instanceof Transform) { 320 Transform k = (Transform)c; 321 if (k.equals(key)) { 322 LambdaForm result = k.get(); 323 if (result == null) { 324 lambdaForm.transformCache = key; 325 return form; 326 } else { 327 return result; 328 } 329 } else if (k.get() == null) { // overwrite stale entry 330 lambdaForm.transformCache = key; 331 return form; 332 } 333 // expand one-element cache to small array 334 ta = new Transform[MIN_CACHE_ARRAY_SIZE]; 335 ta[0] = k; 336 lambdaForm.transformCache = ta; 337 } else { 338 // it is already expanded 339 ta = (Transform[])c; 340 } 341 int len = ta.length; 342 int stale = -1; 343 int i; 344 for (i = 0; i < len; i++) { 345 Transform k = ta[i]; 346 if (k == null) { 347 break; 348 } 349 if (k.equals(key)) { 350 LambdaForm result = k.get(); 351 if (result == null) { 352 ta[i] = key; 353 return form; 354 } else { 355 return result; 356 } 357 } else if (stale < 0 && k.get() == null) { 358 stale = i; // remember 1st stale entry index 359 } 360 } 361 if (i < len || stale >= 0) { 362 // just fall through to cache update 363 } else if (len < MAX_CACHE_ARRAY_SIZE) { 364 len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE); 365 ta = Arrays.copyOf(ta, len); 366 lambdaForm.transformCache = ta; 367 } else { 368 ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2); 369 for (Transform k : ta) { 370 m.put(k, k); 371 } 372 lambdaForm.transformCache = m; 373 // The second iteration will update for this query, concurrently. 374 continue; 375 } 376 int idx = (stale >= 0) ? stale : i; 377 ta[idx] = key; 378 return form; 379 } 380 } 381 } 382 383 private LambdaFormBuffer buffer() { 384 return new LambdaFormBuffer(lambdaForm); 385 } 386 387 /// Editing methods for method handles. These need to have fast paths. 388 389 private BoundMethodHandle.SpeciesData oldSpeciesData() { 390 return BoundMethodHandle.speciesData(lambdaForm); 391 } 392 private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) { 393 return oldSpeciesData().extendWith(type); 394 } 395 396 BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) { 397 assert(mh.speciesData() == oldSpeciesData()); 398 BasicType bt = L_TYPE; 399 MethodType type2 = bindArgumentType(mh, pos, bt); 400 LambdaForm form2 = bindArgumentForm(1+pos); 401 return mh.copyWithExtendL(type2, form2, value); 402 } 403 BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) { 404 assert(mh.speciesData() == oldSpeciesData()); 405 BasicType bt = I_TYPE; 406 MethodType type2 = bindArgumentType(mh, pos, bt); 407 LambdaForm form2 = bindArgumentForm(1+pos); 408 return mh.copyWithExtendI(type2, form2, value); 409 } 410 411 BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) { 412 assert(mh.speciesData() == oldSpeciesData()); 413 BasicType bt = J_TYPE; 414 MethodType type2 = bindArgumentType(mh, pos, bt); 415 LambdaForm form2 = bindArgumentForm(1+pos); 416 return mh.copyWithExtendJ(type2, form2, value); 417 } 418 419 BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) { 420 assert(mh.speciesData() == oldSpeciesData()); 421 BasicType bt = F_TYPE; 422 MethodType type2 = bindArgumentType(mh, pos, bt); 423 LambdaForm form2 = bindArgumentForm(1+pos); 424 return mh.copyWithExtendF(type2, form2, value); 425 } 426 427 BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) { 428 assert(mh.speciesData() == oldSpeciesData()); 429 BasicType bt = D_TYPE; 430 MethodType type2 = bindArgumentType(mh, pos, bt); 431 LambdaForm form2 = bindArgumentForm(1+pos); 432 return mh.copyWithExtendD(type2, form2, value); 433 } 434 435 private MethodType bindArgumentType(BoundMethodHandle mh, int pos, BasicType bt) { 436 assert(mh.form == lambdaForm); 437 assert(mh.form.names[1+pos].type == bt); 438 assert(BasicType.basicType(mh.type().parameterType(pos)) == bt); 439 return mh.type().dropParameterTypes(pos, pos+1); 440 } 441 442 /// Editing methods for lambda forms. 443 // Each editing method can (potentially) cache the edited LF so that it can be reused later. 444 445 LambdaForm bindArgumentForm(int pos) { 446 Transform key = Transform.of(Transform.Kind.BIND_ARG, pos); 447 LambdaForm form = getInCache(key); 448 if (form != null) { 449 assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos))); 450 return form; 451 } 452 LambdaFormBuffer buf = buffer(); 453 buf.startEdit(); 454 455 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 456 BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos)); 457 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 458 Name newBaseAddress; 459 NamedFunction getter = newData.getterFunction(oldData.fieldCount()); 460 461 if (pos != 0) { 462 // The newly created LF will run with a different BMH. 463 // Switch over any pre-existing BMH field references to the new BMH class. 464 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 465 newBaseAddress = oldBaseAddress.withConstraint(newData); 466 buf.renameParameter(0, newBaseAddress); 467 buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); 468 } else { 469 // cannot bind the MH arg itself, unless oldData is empty 470 assert(oldData == BoundMethodHandle.SpeciesData.EMPTY); 471 newBaseAddress = new Name(L_TYPE).withConstraint(newData); 472 buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress)); 473 buf.insertParameter(0, newBaseAddress); 474 } 475 476 form = buf.endEdit(); 477 return putInCache(key, form); 478 } 479 480 LambdaForm addArgumentForm(int pos, BasicType type) { 481 Transform key = Transform.of(Transform.Kind.ADD_ARG, pos, type.ordinal()); 482 LambdaForm form = getInCache(key); 483 if (form != null) { 484 assert(form.arity == lambdaForm.arity+1); 485 assert(form.parameterType(pos) == type); 486 return form; 487 } 488 LambdaFormBuffer buf = buffer(); 489 buf.startEdit(); 490 491 buf.insertParameter(pos, new Name(type)); 492 493 form = buf.endEdit(); 494 return putInCache(key, form); 495 } 496 497 LambdaForm dupArgumentForm(int srcPos, int dstPos) { 498 Transform key = Transform.of(Transform.Kind.DUP_ARG, srcPos, dstPos); 499 LambdaForm form = getInCache(key); 500 if (form != null) { 501 assert(form.arity == lambdaForm.arity-1); 502 return form; 503 } 504 LambdaFormBuffer buf = buffer(); 505 buf.startEdit(); 506 507 assert(lambdaForm.parameter(srcPos).constraint == null); 508 assert(lambdaForm.parameter(dstPos).constraint == null); 509 buf.replaceParameterByCopy(dstPos, srcPos); 510 511 form = buf.endEdit(); 512 return putInCache(key, form); 513 } 514 515 LambdaForm spreadArgumentsForm(int pos, Class<?> arrayType, int arrayLength) { 516 Class<?> elementType = arrayType.getComponentType(); 517 Class<?> erasedArrayType = arrayType; 518 if (!elementType.isPrimitive()) 519 erasedArrayType = Object[].class; 520 BasicType bt = basicType(elementType); 521 int elementTypeKey = bt.ordinal(); 522 if (bt.basicTypeClass() != elementType) { 523 if (elementType.isPrimitive()) { 524 elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); 525 } 526 } 527 Transform key = Transform.of(Transform.Kind.SPREAD_ARGS, pos, elementTypeKey, arrayLength); 528 LambdaForm form = getInCache(key); 529 if (form != null) { 530 assert(form.arity == lambdaForm.arity - arrayLength + 1); 531 return form; 532 } 533 LambdaFormBuffer buf = buffer(); 534 buf.startEdit(); 535 536 assert(pos <= MethodType.MAX_JVM_ARITY); 537 assert(pos + arrayLength <= lambdaForm.arity); 538 assert(pos > 0); // cannot spread the MH arg itself 539 540 Name spreadParam = new Name(L_TYPE); 541 Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength); 542 543 // insert the new expressions 544 int exprPos = lambdaForm.arity(); 545 buf.insertExpression(exprPos++, checkSpread); 546 // adjust the arguments 547 MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType); 548 for (int i = 0; i < arrayLength; i++) { 549 Name loadArgument = new Name(aload, spreadParam, i); 550 buf.insertExpression(exprPos + i, loadArgument); 551 buf.replaceParameterByCopy(pos + i, exprPos + i); 552 } 553 buf.insertParameter(pos, spreadParam); 554 555 form = buf.endEdit(); 556 return putInCache(key, form); 557 } 558 559 LambdaForm collectArgumentsForm(int pos, MethodType collectorType) { 560 int collectorArity = collectorType.parameterCount(); 561 boolean dropResult = (collectorType.returnType() == void.class); 562 if (collectorArity == 1 && !dropResult) { 563 return filterArgumentForm(pos, basicType(collectorType.parameterType(0))); 564 } 565 BasicType[] newTypes = BasicType.basicTypes(collectorType.parameterList()); 566 Transform.Kind kind = (dropResult 567 ? Transform.Kind.COLLECT_ARGS_TO_VOID 568 : Transform.Kind.COLLECT_ARGS); 569 if (dropResult && collectorArity == 0) pos = 1; // pure side effect 570 Transform key = Transform.of(kind, pos, collectorArity, BasicType.basicTypesOrd(newTypes)); 571 LambdaForm form = getInCache(key); 572 if (form != null) { 573 assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1) + collectorArity); 574 return form; 575 } 576 form = makeArgumentCombinationForm(pos, collectorType, false, dropResult); 577 return putInCache(key, form); 578 } 579 580 LambdaForm collectArgumentArrayForm(int pos, MethodHandle arrayCollector) { 581 MethodType collectorType = arrayCollector.type(); 582 int collectorArity = collectorType.parameterCount(); 583 assert(arrayCollector.intrinsicName() == Intrinsic.NEW_ARRAY); 584 Class<?> arrayType = collectorType.returnType(); 585 Class<?> elementType = arrayType.getComponentType(); 586 BasicType argType = basicType(elementType); 587 int argTypeKey = argType.ordinal(); 588 if (argType.basicTypeClass() != elementType) { 589 // return null if it requires more metadata (like String[].class) 590 if (!elementType.isPrimitive()) 591 return null; 592 argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); 593 } 594 assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType))); 595 Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY; 596 Transform key = Transform.of(kind, pos, collectorArity, argTypeKey); 597 LambdaForm form = getInCache(key); 598 if (form != null) { 599 assert(form.arity == lambdaForm.arity - 1 + collectorArity); 600 return form; 601 } 602 LambdaFormBuffer buf = buffer(); 603 buf.startEdit(); 604 605 assert(pos + 1 <= lambdaForm.arity); 606 assert(pos > 0); // cannot filter the MH arg itself 607 608 Name[] newParams = new Name[collectorArity]; 609 for (int i = 0; i < collectorArity; i++) { 610 newParams[i] = new Name(pos + i, argType); 611 } 612 Name callCombiner = new Name(arrayCollector, (Object[]) /*...*/ newParams); 613 614 // insert the new expression 615 int exprPos = lambdaForm.arity(); 616 buf.insertExpression(exprPos, callCombiner); 617 618 // insert new arguments 619 int argPos = pos + 1; // skip result parameter 620 for (Name newParam : newParams) { 621 buf.insertParameter(argPos++, newParam); 622 } 623 assert(buf.lastIndexOf(callCombiner) == exprPos+newParams.length); 624 buf.replaceParameterByCopy(pos, exprPos+newParams.length); 625 626 form = buf.endEdit(); 627 return putInCache(key, form); 628 } 629 630 LambdaForm filterArgumentForm(int pos, BasicType newType) { 631 Transform key = Transform.of(Transform.Kind.FILTER_ARG, pos, newType.ordinal()); 632 LambdaForm form = getInCache(key); 633 if (form != null) { 634 assert(form.arity == lambdaForm.arity); 635 assert(form.parameterType(pos) == newType); 636 return form; 637 } 638 639 BasicType oldType = lambdaForm.parameterType(pos); 640 MethodType filterType = MethodType.methodType(oldType.basicTypeClass(), 641 newType.basicTypeClass()); 642 form = makeArgumentCombinationForm(pos, filterType, false, false); 643 return putInCache(key, form); 644 } 645 646 private LambdaForm makeArgumentCombinationForm(int pos, 647 MethodType combinerType, 648 boolean keepArguments, boolean dropResult) { 649 LambdaFormBuffer buf = buffer(); 650 buf.startEdit(); 651 int combinerArity = combinerType.parameterCount(); 652 int resultArity = (dropResult ? 0 : 1); 653 654 assert(pos <= MethodType.MAX_JVM_ARITY); 655 assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity); 656 assert(pos > 0); // cannot filter the MH arg itself 657 assert(combinerType == combinerType.basicType()); 658 assert(combinerType.returnType() != void.class || dropResult); 659 660 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 661 BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); 662 663 // The newly created LF will run with a different BMH. 664 // Switch over any pre-existing BMH field references to the new BMH class. 665 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 666 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 667 Name newBaseAddress = oldBaseAddress.withConstraint(newData); 668 buf.renameParameter(0, newBaseAddress); 669 670 Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); 671 Object[] combinerArgs = new Object[1 + combinerArity]; 672 combinerArgs[0] = getCombiner; 673 Name[] newParams; 674 if (keepArguments) { 675 newParams = new Name[0]; 676 System.arraycopy(lambdaForm.names, pos + resultArity, 677 combinerArgs, 1, combinerArity); 678 } else { 679 newParams = new Name[combinerArity]; 680 BasicType[] newTypes = basicTypes(combinerType.parameterList()); 681 for (int i = 0; i < newTypes.length; i++) { 682 newParams[i] = new Name(pos + i, newTypes[i]); 683 } 684 System.arraycopy(newParams, 0, 685 combinerArgs, 1, combinerArity); 686 } 687 Name callCombiner = new Name(combinerType, combinerArgs); 688 689 // insert the two new expressions 690 int exprPos = lambdaForm.arity(); 691 buf.insertExpression(exprPos+0, getCombiner); 692 buf.insertExpression(exprPos+1, callCombiner); 693 694 // insert new arguments, if needed 695 int argPos = pos + resultArity; // skip result parameter 696 for (Name newParam : newParams) { 697 buf.insertParameter(argPos++, newParam); 698 } 699 assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length); 700 if (!dropResult) { 701 buf.replaceParameterByCopy(pos, exprPos+1+newParams.length); 702 } 703 704 return buf.endEdit(); 705 } 706 707 LambdaForm filterReturnForm(BasicType newType, boolean constantZero) { 708 Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN); 709 Transform key = Transform.of(kind, newType.ordinal()); 710 LambdaForm form = getInCache(key); 711 if (form != null) { 712 assert(form.arity == lambdaForm.arity); 713 assert(form.returnType() == newType); 714 return form; 715 } 716 LambdaFormBuffer buf = buffer(); 717 buf.startEdit(); 718 719 int insPos = lambdaForm.names.length; 720 Name callFilter; 721 if (constantZero) { 722 // Synthesize a constant zero value for the given type. 723 if (newType == V_TYPE) 724 callFilter = null; 725 else 726 callFilter = new Name(constantZero(newType)); 727 } else { 728 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 729 BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); 730 731 // The newly created LF will run with a different BMH. 732 // Switch over any pre-existing BMH field references to the new BMH class. 733 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 734 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 735 Name newBaseAddress = oldBaseAddress.withConstraint(newData); 736 buf.renameParameter(0, newBaseAddress); 737 738 Name getFilter = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); 739 buf.insertExpression(insPos++, getFilter); 740 BasicType oldType = lambdaForm.returnType(); 741 if (oldType == V_TYPE) { 742 MethodType filterType = MethodType.methodType(newType.basicTypeClass()); 743 callFilter = new Name(filterType, getFilter); 744 } else { 745 MethodType filterType = MethodType.methodType(newType.basicTypeClass(), oldType.basicTypeClass()); 746 callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]); 747 } 748 } 749 750 if (callFilter != null) 751 buf.insertExpression(insPos++, callFilter); 752 buf.setResult(callFilter); 753 754 form = buf.endEdit(); 755 return putInCache(key, form); 756 } 757 758 LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) { 759 int combinerArity = combinerType.parameterCount(); 760 Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS); 761 Transform key = Transform.of(kind, foldPos, combinerArity); 762 LambdaForm form = getInCache(key); 763 if (form != null) { 764 assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0)); 765 return form; 766 } 767 form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult); 768 return putInCache(key, form); 769 } 770 771 LambdaForm permuteArgumentsForm(int skip, int[] reorder) { 772 assert(skip == 1); // skip only the leading MH argument, names[0] 773 int length = lambdaForm.names.length; 774 int outArgs = reorder.length; 775 int inTypes = 0; 776 boolean nullPerm = true; 777 for (int i = 0; i < reorder.length; i++) { 778 int inArg = reorder[i]; 779 if (inArg != i) nullPerm = false; 780 inTypes = Math.max(inTypes, inArg+1); 781 } 782 assert(skip + reorder.length == lambdaForm.arity); 783 if (nullPerm) return lambdaForm; // do not bother to cache 784 Transform key = Transform.of(Transform.Kind.PERMUTE_ARGS, reorder); 785 LambdaForm form = getInCache(key); 786 if (form != null) { 787 assert(form.arity == skip+inTypes) : form; 788 return form; 789 } 790 791 BasicType[] types = new BasicType[inTypes]; 792 for (int i = 0; i < outArgs; i++) { 793 int inArg = reorder[i]; 794 types[inArg] = lambdaForm.names[skip + i].type; 795 } 796 assert (skip + outArgs == lambdaForm.arity); 797 assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip)); 798 int pos = 0; 799 while (pos < outArgs && reorder[pos] == pos) { 800 pos += 1; 801 } 802 Name[] names2 = new Name[length - outArgs + inTypes]; 803 System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos); 804 int bodyLength = length - lambdaForm.arity; 805 System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength); 806 int arity2 = names2.length - bodyLength; 807 int result2 = lambdaForm.result; 808 if (result2 >= 0) { 809 if (result2 < skip + outArgs) { 810 result2 = reorder[result2 - skip]; 811 } else { 812 result2 = result2 - outArgs + inTypes; 813 } 814 } 815 for (int j = pos; j < outArgs; j++) { 816 Name n = lambdaForm.names[skip + j]; 817 int i = reorder[j]; 818 Name n2 = names2[skip + i]; 819 if (n2 == null) { 820 names2[skip + i] = n2 = new Name(types[i]); 821 } else { 822 assert (n2.type == types[i]); 823 } 824 for (int k = arity2; k < names2.length; k++) { 825 names2[k] = names2[k].replaceName(n, n2); 826 } 827 } 828 for (int i = skip + pos; i < arity2; i++) { 829 if (names2[i] == null) { 830 names2[i] = argument(i, types[i - skip]); 831 } 832 } 833 for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) { 834 int i = j - lambdaForm.arity + arity2; 835 Name n = lambdaForm.names[j]; 836 Name n2 = names2[i]; 837 if (n != n2) { 838 for (int k = i + 1; k < names2.length; k++) { 839 names2[k] = names2[k].replaceName(n, n2); 840 } 841 } 842 } 843 844 form = new LambdaForm(lambdaForm.debugName, arity2, names2, result2); 845 return putInCache(key, form); 846 } 847 848 static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) { 849 for (int i = 0; i < reorder.length; i++) { 850 assert (names[skip + i].isParam()); 851 assert (names[skip + i].type == types[reorder[i]]); 852 } 853 return true; 854 } 855 }