1 /* 2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.invoke; 27 28 import static jdk.internal.org.objectweb.asm.Opcodes.*; 29 import static java.lang.invoke.LambdaForm.basicTypes; 30 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic; 31 import static java.lang.invoke.MethodHandleStatics.*; 32 33 import java.lang.invoke.LambdaForm.Name; 34 import java.lang.invoke.LambdaForm.NamedFunction; 35 import java.lang.invoke.MethodHandles.Lookup; 36 import java.lang.reflect.Field; 37 import java.util.Arrays; 38 import java.util.HashMap; 39 40 import sun.invoke.util.ValueConversions; 41 import sun.invoke.util.Wrapper; 42 43 import jdk.internal.org.objectweb.asm.ClassWriter; 44 import jdk.internal.org.objectweb.asm.MethodVisitor; 45 import jdk.internal.org.objectweb.asm.Type; 46 47 /** 48 * The flavor of method handle which emulates an invoke instruction 49 * on a predetermined argument. The JVM dispatches to the correct method 50 * when the handle is created, not when it is invoked. 51 * 52 * All bound arguments are encapsulated in dedicated species. 53 */ 54 /* non-public */ abstract class BoundMethodHandle extends MethodHandle { 55 56 /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) { 57 super(type, form); 58 } 59 60 // 61 // BMH API and internals 62 // 63 64 static MethodHandle bindSingle(MethodType type, LambdaForm form, char xtype, Object x) { 65 // for some type signatures, there exist pre-defined concrete BMH classes 66 try { 67 switch (xtype) { 68 case 'L': 69 if (true) return bindSingle(type, form, x); // Use known fast path. 70 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('L').constructor[0].invokeBasic(type, form, x); 71 case 'I': 72 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('I').constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x)); 73 case 'J': 74 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('J').constructor[0].invokeBasic(type, form, (long) x); 75 case 'F': 76 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('F').constructor[0].invokeBasic(type, form, (float) x); 77 case 'D': 78 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('D').constructor[0].invokeBasic(type, form, (double) x); 79 default : throw new InternalError("unexpected xtype: " + xtype); 80 } 81 } catch (Throwable t) { 82 throw newInternalError(t); 83 } 84 } 85 86 static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) { 87 return new Species_L(type, form, x); 88 } 89 90 MethodHandle cloneExtend(MethodType type, LambdaForm form, char xtype, Object x) { 91 try { 92 switch (xtype) { 93 case 'L': return cloneExtendL(type, form, x); 94 case 'I': return cloneExtendI(type, form, ValueConversions.widenSubword(x)); 95 case 'J': return cloneExtendJ(type, form, (long) x); 96 case 'F': return cloneExtendF(type, form, (float) x); 97 case 'D': return cloneExtendD(type, form, (double) x); 98 } 99 } catch (Throwable t) { 100 throw newInternalError(t); 101 } 102 throw new InternalError("unexpected type: " + xtype); 103 } 104 105 @Override 106 MethodHandle bindArgument(int pos, char basicType, Object value) { 107 MethodType type = type().dropParameterTypes(pos, pos+1); 108 LambdaForm form = internalForm().bind(1+pos, speciesData()); 109 return cloneExtend(type, form, basicType, value); 110 } 111 112 @Override 113 MethodHandle dropArguments(MethodType srcType, int pos, int drops) { 114 LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops)); 115 try { 116 return clone(srcType, form); 117 } catch (Throwable t) { 118 throw newInternalError(t); 119 } 120 } 121 122 @Override 123 MethodHandle permuteArguments(MethodType newType, int[] reorder) { 124 try { 125 return clone(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList()))); 126 } catch (Throwable t) { 127 throw newInternalError(t); 128 } 129 } 130 131 static final String EXTENSION_TYPES = "LIJFD"; 132 static final byte INDEX_L = 0, INDEX_I = 1, INDEX_J = 2, INDEX_F = 3, INDEX_D = 4; 133 static byte extensionIndex(char type) { 134 int i = EXTENSION_TYPES.indexOf(type); 135 if (i < 0) throw new InternalError(); 136 return (byte) i; 137 } 138 139 /** 140 * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a 141 * static field containing this value, and they must accordingly implement this method. 142 */ 143 protected abstract SpeciesData speciesData(); 144 145 @Override 146 final Object internalProperties() { 147 return "/BMH="+internalValues(); 148 } 149 150 @Override 151 final Object internalValues() { 152 Object[] boundValues = new Object[speciesData().fieldCount()]; 153 for (int i = 0; i < boundValues.length; ++i) { 154 boundValues[i] = arg(i); 155 } 156 return Arrays.asList(boundValues); 157 } 158 159 public final Object arg(int i) { 160 try { 161 switch (speciesData().fieldType(i)) { 162 case 'L': return argL(i); 163 case 'I': return argI(i); 164 case 'F': return argF(i); 165 case 'D': return argD(i); 166 case 'J': return argJ(i); 167 } 168 } catch (Throwable ex) { 169 throw newInternalError(ex); 170 } 171 throw new InternalError("unexpected type: " + speciesData().types+"."+i); 172 } 173 public final Object argL(int i) throws Throwable { return speciesData().getters[i].invokeBasic(this); } 174 public final int argI(int i) throws Throwable { return (int) speciesData().getters[i].invokeBasic(this); } 175 public final float argF(int i) throws Throwable { return (float) speciesData().getters[i].invokeBasic(this); } 176 public final double argD(int i) throws Throwable { return (double) speciesData().getters[i].invokeBasic(this); } 177 public final long argJ(int i) throws Throwable { return (long) speciesData().getters[i].invokeBasic(this); } 178 179 // 180 // cloning API 181 // 182 183 public abstract BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable; 184 public abstract BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable; 185 public abstract BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable; 186 public abstract BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable; 187 public abstract BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable; 188 public abstract BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable; 189 190 // The following is a grossly irregular hack: 191 @Override MethodHandle reinvokerTarget() { 192 try { 193 return (MethodHandle) argL(0); 194 } catch (Throwable ex) { 195 throw newInternalError(ex); 196 } 197 } 198 199 // 200 // concrete BMH classes required to close bootstrap loops 201 // 202 203 private // make it private to force users to access the enclosing class first 204 static final class Species_L extends BoundMethodHandle { 205 final Object argL0; 206 public Species_L(MethodType mt, LambdaForm lf, Object argL0) { 207 super(mt, lf); 208 this.argL0 = argL0; 209 } 210 // The following is a grossly irregular hack: 211 @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; } 212 @Override 213 public SpeciesData speciesData() { 214 return SPECIES_DATA; 215 } 216 public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class); 217 @Override 218 public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable { 219 return new Species_L(mt, lf, argL0); 220 } 221 @Override 222 public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable { 223 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, narg); 224 } 225 @Override 226 public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable { 227 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, narg); 228 } 229 @Override 230 public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable { 231 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, narg); 232 } 233 @Override 234 public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable { 235 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, narg); 236 } 237 @Override 238 public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable { 239 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, narg); 240 } 241 } 242 243 /* 244 static final class Species_LL extends BoundMethodHandle { 245 final Object argL0; 246 final Object argL1; 247 public Species_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) { 248 super(mt, lf); 249 this.argL0 = argL0; 250 this.argL1 = argL1; 251 } 252 @Override 253 public SpeciesData speciesData() { 254 return SPECIES_DATA; 255 } 256 public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LL", Species_LL.class); 257 @Override 258 public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable { 259 return new Species_LL(mt, lf, argL0, argL1); 260 } 261 @Override 262 public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable { 263 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg); 264 } 265 @Override 266 public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable { 267 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg); 268 } 269 @Override 270 public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable { 271 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg); 272 } 273 @Override 274 public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable { 275 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg); 276 } 277 @Override 278 public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable { 279 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg); 280 } 281 } 282 283 static final class Species_JL extends BoundMethodHandle { 284 final long argJ0; 285 final Object argL1; 286 public Species_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) { 287 super(mt, lf); 288 this.argJ0 = argJ0; 289 this.argL1 = argL1; 290 } 291 @Override 292 public SpeciesData speciesData() { 293 return SPECIES_DATA; 294 } 295 public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("JL", Species_JL.class); 296 @Override public final long argJ0() { return argJ0; } 297 @Override public final Object argL1() { return argL1; } 298 @Override 299 public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable { 300 return new Species_JL(mt, lf, argJ0, argL1); 301 } 302 @Override 303 public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable { 304 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg); 305 } 306 @Override 307 public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable { 308 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg); 309 } 310 @Override 311 public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable { 312 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg); 313 } 314 @Override 315 public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable { 316 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg); 317 } 318 @Override 319 public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable { 320 return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg); 321 } 322 } 323 */ 324 325 // 326 // BMH species meta-data 327 // 328 329 /** 330 * Meta-data wrapper for concrete BMH classes. 331 */ 332 static class SpeciesData { 333 final String types; 334 final Class<? extends BoundMethodHandle> clazz; 335 // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH 336 // Therefore, we need a non-final link in the chain. Use array elements. 337 final MethodHandle[] constructor; 338 final MethodHandle[] getters; 339 final SpeciesData[] extensions; 340 341 public int fieldCount() { 342 return types.length(); 343 } 344 public char fieldType(int i) { 345 return types.charAt(i); 346 } 347 348 public String toString() { 349 return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]"; 350 } 351 352 /** 353 * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that 354 * represents a MH bound to a generic invoker, which in turn forwards to the corresponding 355 * getter. 356 */ 357 Name getterName(Name mhName, int i) { 358 MethodHandle mh = getters[i]; 359 assert(mh != null) : this+"."+i; 360 return new Name(mh, mhName); 361 } 362 363 NamedFunction getterFunction(int i) { 364 return new NamedFunction(getters[i]); 365 } 366 367 static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); 368 369 private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) { 370 this.types = types; 371 this.clazz = clazz; 372 if (!INIT_DONE) { 373 this.constructor = new MethodHandle[1]; 374 this.getters = new MethodHandle[types.length()]; 375 } else { 376 this.constructor = Factory.makeCtors(clazz, types, null); 377 this.getters = Factory.makeGetters(clazz, types, null); 378 } 379 this.extensions = new SpeciesData[EXTENSION_TYPES.length()]; 380 } 381 382 private void initForBootstrap() { 383 assert(!INIT_DONE); 384 if (constructor[0] == null) { 385 Factory.makeCtors(clazz, types, this.constructor); 386 Factory.makeGetters(clazz, types, this.getters); 387 } 388 } 389 390 private SpeciesData(String types) { 391 // Placeholder only. 392 this.types = types; 393 this.clazz = null; 394 this.constructor = null; 395 this.getters = null; 396 this.extensions = null; 397 } 398 private boolean isPlaceholder() { return clazz == null; } 399 400 private static final HashMap<String, SpeciesData> CACHE = new HashMap<>(); 401 static { CACHE.put("", EMPTY); } // make bootstrap predictable 402 private static final boolean INIT_DONE; // set after <clinit> finishes... 403 404 SpeciesData extendWithType(char type) { 405 int i = extensionIndex(type); 406 SpeciesData d = extensions[i]; 407 if (d != null) return d; 408 extensions[i] = d = get(types+type); 409 return d; 410 } 411 412 SpeciesData extendWithIndex(byte index) { 413 SpeciesData d = extensions[index]; 414 if (d != null) return d; 415 extensions[index] = d = get(types+EXTENSION_TYPES.charAt(index)); 416 return d; 417 } 418 419 private static SpeciesData get(String types) { 420 // Acquire cache lock for query. 421 SpeciesData d = lookupCache(types); 422 if (!d.isPlaceholder()) 423 return d; 424 synchronized (d) { 425 // Use synch. on the placeholder to prevent multiple instantiation of one species. 426 // Creating this class forces a recursive call to getForClass. 427 if (lookupCache(types).isPlaceholder()) 428 Factory.generateConcreteBMHClass(types); 429 } 430 // Reacquire cache lock. 431 d = lookupCache(types); 432 // Class loading must have upgraded the cache. 433 assert(d != null && !d.isPlaceholder()); 434 return d; 435 } 436 static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) { 437 // clazz is a new class which is initializing its SPECIES_DATA field 438 return updateCache(types, new SpeciesData(types, clazz)); 439 } 440 private static synchronized SpeciesData lookupCache(String types) { 441 SpeciesData d = CACHE.get(types); 442 if (d != null) return d; 443 d = new SpeciesData(types); 444 assert(d.isPlaceholder()); 445 CACHE.put(types, d); 446 return d; 447 } 448 private static synchronized SpeciesData updateCache(String types, SpeciesData d) { 449 SpeciesData d2; 450 assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder()); 451 assert(!d.isPlaceholder()); 452 CACHE.put(types, d); 453 return d; 454 } 455 456 static { 457 // pre-fill the BMH speciesdata cache with BMH's inner classes 458 final Class<BoundMethodHandle> rootCls = BoundMethodHandle.class; 459 SpeciesData d0 = BoundMethodHandle.SPECIES_DATA; // trigger class init 460 assert(d0 == null || d0 == lookupCache("")) : d0; 461 try { 462 for (Class<?> c : rootCls.getDeclaredClasses()) { 463 if (rootCls.isAssignableFrom(c)) { 464 final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class); 465 SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh); 466 assert(d != null) : cbmh.getName(); 467 assert(d.clazz == cbmh); 468 assert(d == lookupCache(d.types)); 469 } 470 } 471 } catch (Throwable e) { 472 throw newInternalError(e); 473 } 474 475 for (SpeciesData d : CACHE.values()) { 476 d.initForBootstrap(); 477 } 478 // Note: Do not simplify this, because INIT_DONE must not be 479 // a compile-time constant during bootstrapping. 480 INIT_DONE = Boolean.TRUE; 481 } 482 } 483 484 static SpeciesData getSpeciesData(String types) { 485 return SpeciesData.get(types); 486 } 487 488 /** 489 * Generation of concrete BMH classes. 490 * 491 * A concrete BMH species is fit for binding a number of values adhering to a 492 * given type pattern. Reference types are erased. 493 * 494 * BMH species are cached by type pattern. 495 * 496 * A BMH species has a number of fields with the concrete (possibly erased) types of 497 * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs, 498 * which can be included as names in lambda forms. 499 */ 500 static class Factory { 501 502 static final String JLO_SIG = "Ljava/lang/Object;"; 503 static final String JLS_SIG = "Ljava/lang/String;"; 504 static final String JLC_SIG = "Ljava/lang/Class;"; 505 static final String MH = "java/lang/invoke/MethodHandle"; 506 static final String MH_SIG = "L"+MH+";"; 507 static final String BMH = "java/lang/invoke/BoundMethodHandle"; 508 static final String BMH_SIG = "L"+BMH+";"; 509 static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData"; 510 static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";"; 511 512 static final String SPECIES_PREFIX_NAME = "Species_"; 513 static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME; 514 515 static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG; 516 static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG; 517 static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG; 518 static final String VOID_SIG = "()V"; 519 520 static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;"; 521 522 static final Class<?>[] TYPES = new Class<?>[] { Object.class, int.class, long.class, float.class, double.class }; 523 524 static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" }; 525 526 /** 527 * Generate a concrete subclass of BMH for a given combination of bound types. 528 * 529 * A concrete BMH species adheres to the following schema: 530 * 531 * <pre> 532 * class Species_[[types]] extends BoundMethodHandle { 533 * [[fields]] 534 * final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); } 535 * } 536 * </pre> 537 * 538 * The {@code [[types]]} signature is precisely the string that is passed to this 539 * method. 540 * 541 * The {@code [[fields]]} section consists of one field definition per character in 542 * the type signature, adhering to the naming schema described in the definition of 543 * {@link #makeFieldName}. 544 * 545 * For example, a concrete BMH species for two reference and one integral bound values 546 * would have the following shape: 547 * 548 * <pre> 549 * class BoundMethodHandle { ... private static 550 * final class Species_LLI extends BoundMethodHandle { 551 * final Object argL0; 552 * final Object argL1; 553 * final int argI2; 554 * public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) { 555 * super(mt, lf); 556 * this.argL0 = argL0; 557 * this.argL1 = argL1; 558 * this.argI2 = argI2; 559 * } 560 * public final SpeciesData speciesData() { return SPECIES_DATA; } 561 * public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class); 562 * public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) { 563 * return SPECIES_DATA.constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2); 564 * } 565 * public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) { 566 * return SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); 567 * } 568 * public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) { 569 * return SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); 570 * } 571 * public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) { 572 * return SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); 573 * } 574 * public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) { 575 * return SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); 576 * } 577 * public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) { 578 * return SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); 579 * } 580 * } 581 * </pre> 582 * 583 * @param types the type signature, wherein reference types are erased to 'L' 584 * @return the generated concrete BMH class 585 */ 586 static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) { 587 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); 588 589 final String className = SPECIES_PREFIX_PATH + types; 590 final String sourceFile = SPECIES_PREFIX_NAME + types; 591 cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null); 592 cw.visitSource(sourceFile, null); 593 594 // emit static types and SPECIES_DATA fields 595 cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd(); 596 597 // emit bound argument fields 598 for (int i = 0; i < types.length(); ++i) { 599 final char t = types.charAt(i); 600 final String fieldName = makeFieldName(types, i); 601 final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t); 602 cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd(); 603 } 604 605 MethodVisitor mv; 606 607 // emit constructor 608 mv = cw.visitMethod(ACC_PUBLIC, "<init>", makeSignature(types, true), null, null); 609 mv.visitCode(); 610 mv.visitVarInsn(ALOAD, 0); 611 mv.visitVarInsn(ALOAD, 1); 612 mv.visitVarInsn(ALOAD, 2); 613 614 mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true)); 615 616 for (int i = 0, j = 0; i < types.length(); ++i, ++j) { 617 // i counts the arguments, j counts corresponding argument slots 618 char t = types.charAt(i); 619 mv.visitVarInsn(ALOAD, 0); 620 mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3 621 mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t)); 622 if (t == 'J' || t == 'D') { 623 ++j; // adjust argument register access 624 } 625 } 626 627 mv.visitInsn(RETURN); 628 mv.visitMaxs(0, 0); 629 mv.visitEnd(); 630 631 // emit implementation of reinvokerTarget() 632 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null); 633 mv.visitCode(); 634 mv.visitVarInsn(ALOAD, 0); 635 mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG); 636 mv.visitTypeInsn(CHECKCAST, MH); 637 mv.visitInsn(ARETURN); 638 mv.visitMaxs(0, 0); 639 mv.visitEnd(); 640 641 // emit implementation of speciesData() 642 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null); 643 mv.visitCode(); 644 mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); 645 mv.visitInsn(ARETURN); 646 mv.visitMaxs(0, 0); 647 mv.visitEnd(); 648 649 // emit clone() 650 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE); 651 mv.visitCode(); 652 // return speciesData().constructor[0].invokeBasic(mt, lf, argL0, ...) 653 // obtain constructor 654 mv.visitVarInsn(ALOAD, 0); 655 mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); 656 mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG); 657 mv.visitInsn(ICONST_0); 658 mv.visitInsn(AALOAD); 659 // load mt, lf 660 mv.visitVarInsn(ALOAD, 1); 661 mv.visitVarInsn(ALOAD, 2); 662 // put fields on the stack 663 emitPushFields(types, className, mv); 664 // finally, invoke the constructor and return 665 mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types, false)); 666 mv.visitInsn(ARETURN); 667 mv.visitMaxs(0, 0); 668 mv.visitEnd(); 669 670 // for each type, emit cloneExtendT() 671 for (Class<?> c : TYPES) { 672 char t = Wrapper.basicTypeChar(c); 673 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "cloneExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE); 674 mv.visitCode(); 675 // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg) 676 // obtain constructor 677 mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); 678 int iconstInsn = ICONST_0 + extensionIndex(t); 679 assert(iconstInsn <= ICONST_5); 680 mv.visitInsn(iconstInsn); 681 mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG); 682 mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG); 683 mv.visitInsn(ICONST_0); 684 mv.visitInsn(AALOAD); 685 // load mt, lf 686 mv.visitVarInsn(ALOAD, 1); 687 mv.visitVarInsn(ALOAD, 2); 688 // put fields on the stack 689 emitPushFields(types, className, mv); 690 // put narg on stack 691 mv.visitVarInsn(typeLoadOp(t), 3); 692 // finally, invoke the constructor and return 693 mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + t, false)); 694 mv.visitInsn(ARETURN); 695 mv.visitMaxs(0, 0); 696 mv.visitEnd(); 697 } 698 699 // emit class initializer 700 mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null); 701 mv.visitCode(); 702 mv.visitLdcInsn(types); 703 mv.visitLdcInsn(Type.getObjectType(className)); 704 mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG); 705 mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); 706 mv.visitInsn(RETURN); 707 mv.visitMaxs(0, 0); 708 mv.visitEnd(); 709 710 cw.visitEnd(); 711 712 // load class 713 final byte[] classFile = cw.toByteArray(); 714 InvokerBytecodeGenerator.maybeDump(className, classFile); 715 Class<? extends BoundMethodHandle> bmhClass = 716 //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class); 717 UNSAFE.defineClass(className, classFile, 0, classFile.length, 718 BoundMethodHandle.class.getClassLoader(), null) 719 .asSubclass(BoundMethodHandle.class); 720 UNSAFE.ensureClassInitialized(bmhClass); 721 722 return bmhClass; 723 } 724 725 private static int typeLoadOp(char t) { 726 switch (t) { 727 case 'L': return ALOAD; 728 case 'I': return ILOAD; 729 case 'J': return LLOAD; 730 case 'F': return FLOAD; 731 case 'D': return DLOAD; 732 default : throw new InternalError("unrecognized type " + t); 733 } 734 } 735 736 private static void emitPushFields(String types, String className, MethodVisitor mv) { 737 for (int i = 0; i < types.length(); ++i) { 738 char tc = types.charAt(i); 739 mv.visitVarInsn(ALOAD, 0); 740 mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc)); 741 } 742 } 743 744 static String typeSig(char t) { 745 return t == 'L' ? JLO_SIG : String.valueOf(t); 746 } 747 748 // 749 // Getter MH generation. 750 // 751 752 private static MethodHandle makeGetter(Class<?> cbmhClass, String types, int index) { 753 String fieldName = makeFieldName(types, index); 754 Class<?> fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType(); 755 try { 756 return LOOKUP.findGetter(cbmhClass, fieldName, fieldType); 757 } catch (NoSuchFieldException | IllegalAccessException e) { 758 throw newInternalError(e); 759 } 760 } 761 762 static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) { 763 if (mhs == null) mhs = new MethodHandle[types.length()]; 764 for (int i = 0; i < mhs.length; ++i) { 765 mhs[i] = makeGetter(cbmhClass, types, i); 766 assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass); 767 } 768 return mhs; 769 } 770 771 static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) { 772 if (mhs == null) mhs = new MethodHandle[1]; 773 mhs[0] = makeCbmhCtor(cbmh, types); 774 return mhs; 775 } 776 777 // 778 // Auxiliary methods. 779 // 780 781 static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) { 782 try { 783 Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA"); 784 return (SpeciesData) F_SPECIES_DATA.get(null); 785 } catch (ReflectiveOperationException ex) { 786 throw newInternalError(ex); 787 } 788 } 789 790 /** 791 * Field names in concrete BMHs adhere to this pattern: 792 * arg + type + index 793 * where type is a single character (L, I, J, F, D). 794 */ 795 private static String makeFieldName(String types, int index) { 796 assert index >= 0 && index < types.length(); 797 return "arg" + types.charAt(index) + index; 798 } 799 800 private static String makeSignature(String types, boolean ctor) { 801 StringBuilder buf = new StringBuilder(SIG_INCIPIT); 802 for (char c : types.toCharArray()) { 803 buf.append(typeSig(c)); 804 } 805 return buf.append(')').append(ctor ? "V" : BMH_SIG).toString(); 806 } 807 808 static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) { 809 try { 810 return linkConstructor(LOOKUP.findConstructor(cbmh, MethodType.fromMethodDescriptorString(makeSignature(types, true), null))); 811 } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) { 812 throw newInternalError(e); 813 } 814 } 815 816 /** 817 * Wrap a constructor call in a {@link LambdaForm}. 818 * 819 * If constructors ({@code <init>} methods) are called in LFs, problems might arise if the LFs 820 * are turned into bytecode, because the call to the allocator is routed through an MH, and the 821 * verifier cannot find a {@code NEW} instruction preceding the {@code INVOKESPECIAL} to 822 * {@code <init>}. To avoid this, we add an indirection by invoking {@code <init>} through 823 * {@link MethodHandle#linkToSpecial}. 824 * 825 * The last {@link LambdaForm.Name Name} in the argument's form is expected to be the {@code void} 826 * result of the {@code <init>} invocation. This entry is replaced. 827 */ 828 private static MethodHandle linkConstructor(MethodHandle cmh) { 829 final LambdaForm lf = cmh.form; 830 final int initNameIndex = lf.names.length - 1; 831 final Name initName = lf.names[initNameIndex]; 832 final MemberName ctorMN = initName.function.member; 833 final MethodType ctorMT = ctorMN.getInvocationType(); 834 835 // obtain function member (call target) 836 // linker method type replaces initial parameter (BMH species) with BMH to avoid naming a species (anonymous class!) 837 final MethodType linkerMT = ctorMT.changeParameterType(0, BoundMethodHandle.class).appendParameterTypes(MemberName.class); 838 MemberName linkerMN = new MemberName(MethodHandle.class, "linkToSpecial", linkerMT, REF_invokeStatic); 839 try { 840 linkerMN = MemberName.getFactory().resolveOrFail(REF_invokeStatic, linkerMN, null, NoSuchMethodException.class); 841 assert(linkerMN.isStatic()); 842 } catch (ReflectiveOperationException ex) { 843 throw newInternalError(ex); 844 } 845 // extend arguments array 846 Object[] newArgs = Arrays.copyOf(initName.arguments, initName.arguments.length + 1); 847 newArgs[newArgs.length - 1] = ctorMN; 848 // replace function 849 final NamedFunction nf = new NamedFunction(linkerMN); 850 final Name linkedCtor = new Name(nf, newArgs); 851 linkedCtor.initIndex(initNameIndex); 852 lf.names[initNameIndex] = linkedCtor; 853 return cmh; 854 } 855 856 } 857 858 private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP; 859 860 /** 861 * All subclasses must provide such a value describing their type signature. 862 */ 863 static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY; 864 865 private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[5]; 866 private static SpeciesData checkCache(int size, String types) { 867 int idx = size - 1; 868 SpeciesData data = SPECIES_DATA_CACHE[idx]; 869 if (data != null) return data; 870 SPECIES_DATA_CACHE[idx] = data = getSpeciesData(types); 871 return data; 872 } 873 static SpeciesData speciesData_L() { return checkCache(1, "L"); } 874 static SpeciesData speciesData_LL() { return checkCache(2, "LL"); } 875 static SpeciesData speciesData_LLL() { return checkCache(3, "LLL"); } 876 static SpeciesData speciesData_LLLL() { return checkCache(4, "LLLL"); } 877 static SpeciesData speciesData_LLLLL() { return checkCache(5, "LLLLL"); } 878 }