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