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 /**
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), false);
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), 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, false);
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), 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, false);
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 }
|
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.*;
30 import static java.lang.invoke.LambdaForm.BasicType.*;
31 import static java.lang.invoke.MethodHandleStatics.*;
32
33 import java.lang.invoke.LambdaForm.NamedFunction;
34 import java.lang.invoke.MethodHandles.Lookup;
35 import java.lang.reflect.Field;
36 import java.util.Arrays;
37 import java.util.HashMap;
38
39 import sun.invoke.util.ValueConversions;
40 import sun.invoke.util.Wrapper;
41
42 import jdk.internal.org.objectweb.asm.ClassWriter;
43 import jdk.internal.org.objectweb.asm.MethodVisitor;
44 import jdk.internal.org.objectweb.asm.Type;
45
46 /**
47 * The flavor of method handle which emulates an invoke instruction
48 * on a predetermined argument. The JVM dispatches to the correct method
49 * when the handle is created, not when it is invoked.
50 *
51 * All bound arguments are encapsulated in dedicated species.
52 */
53 /* non-public */ abstract class BoundMethodHandle extends MethodHandle {
54
55 /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
56 super(type, form);
57 }
58
59 //
60 // BMH API and internals
61 //
62
63 static MethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) {
64 // for some type signatures, there exist pre-defined concrete BMH classes
65 try {
66 switch (xtype) {
67 case L_TYPE:
68 if (true) return bindSingle(type, form, x); // Use known fast path.
69 return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(L_TYPE).constructor[0].invokeBasic(type, form, x);
70 case I_TYPE:
71 return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
72 case J_TYPE:
73 return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor[0].invokeBasic(type, form, (long) x);
74 case F_TYPE:
75 return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor[0].invokeBasic(type, form, (float) x);
76 case D_TYPE:
77 return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor[0].invokeBasic(type, form, (double) x);
78 default : throw newInternalError("unexpected xtype: " + xtype);
79 }
80 } catch (Throwable t) {
81 throw newInternalError(t);
82 }
83 }
84
85 static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
86 return new Species_L(type, form, x);
87 }
88
89 MethodHandle cloneExtend(MethodType type, LambdaForm form, BasicType xtype, Object x) {
90 try {
91 switch (xtype) {
92 case L_TYPE: return copyWithExtendL(type, form, x);
93 case I_TYPE: return copyWithExtendI(type, form, ValueConversions.widenSubword(x));
94 case J_TYPE: return copyWithExtendJ(type, form, (long) x);
95 case F_TYPE: return copyWithExtendF(type, form, (float) x);
96 case D_TYPE: return copyWithExtendD(type, form, (double) x);
97 }
98 } catch (Throwable t) {
99 throw newInternalError(t);
100 }
101 throw newInternalError("unexpected type: " + xtype);
102 }
103
104 @Override
105 MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
106 MethodType type = type().dropParameterTypes(pos, pos+1);
107 LambdaForm form = internalForm().bind(1+pos, speciesData());
108 return cloneExtend(type, form, basicType, value);
109 }
110
111 @Override
112 MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
113 LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos + drops));
114 try {
115 return copyWith(srcType, form);
116 } catch (Throwable t) {
117 throw newInternalError(t);
118 }
119 }
120
121 @Override
122 MethodHandle permuteArguments(MethodType newType, int[] reorder) {
123 try {
124 return copyWith(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
125 } catch (Throwable t) {
126 throw newInternalError(t);
127 }
128 }
129
130 /**
131 * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a
132 * static field containing this value, and they must accordingly implement this method.
133 */
134 public abstract SpeciesData speciesData();
135
136 /**
137 * Return the number of fields in this BMH. Equivalent to speciesData().fieldCount().
138 */
139 public abstract int fieldCount();
140
141 @Override
142 final Object internalProperties() {
143 return "/BMH="+internalValues();
144 }
145
146 @Override
147 final Object internalValues() {
148 Object[] boundValues = new Object[speciesData().fieldCount()];
149 for (int i = 0; i < boundValues.length; ++i) {
150 boundValues[i] = arg(i);
151 }
152 return Arrays.asList(boundValues);
153 }
154
155 public final Object arg(int i) {
156 try {
157 switch (speciesData().fieldType(i)) {
158 case L_TYPE: return speciesData().getters[i].invokeBasic(this);
159 case I_TYPE: return (int) speciesData().getters[i].invokeBasic(this);
160 case J_TYPE: return (long) speciesData().getters[i].invokeBasic(this);
161 case F_TYPE: return (float) speciesData().getters[i].invokeBasic(this);
162 case D_TYPE: return (double) speciesData().getters[i].invokeBasic(this);
163 }
164 } catch (Throwable ex) {
165 throw newInternalError(ex);
166 }
167 throw new InternalError("unexpected type: " + speciesData().typeChars+"."+i);
168 }
169
170 //
171 // cloning API
172 //
173
174 // The following is a grossly irregular hack:
175 @Override MethodHandle reinvokerTarget() {
176 try {
177 return (MethodHandle) arg(0);
178 } catch (Throwable ex) {
179 throw newInternalError(ex);
180 }
181 }
182
183 @Override
184 public abstract BoundMethodHandle copyWith(MethodType mt, LambdaForm lf);
185 public abstract BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg);
186 public abstract BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg);
187 public abstract BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg);
188 public abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg);
189 public abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg);
190
191 //
192 // concrete BMH classes required to close bootstrap loops
193 //
194
195 private // make it private to force users to access the enclosing class first
196 static final class Species_L extends BoundMethodHandle {
197 final Object argL0;
198 private Species_L(MethodType mt, LambdaForm lf, Object argL0) {
199 super(mt, lf);
200 this.argL0 = argL0;
201 }
202 @Override
203 public SpeciesData speciesData() {
204 return SPECIES_DATA;
205 }
206 @Override
207 public int fieldCount() {
208 return 1;
209 }
210 public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class);
211 public static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) {
212 return new Species_L(mt, lf, argL0);
213 }
214 @Override
215 public final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
216 return new Species_L(mt, lf, argL0);
217 }
218 @Override
219 public final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
220 try {
221 return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
222 } catch (Throwable ex) {
223 throw uncaughtException(ex);
224 }
225 }
226 @Override
227 public final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
228 try {
229 return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
230 } catch (Throwable ex) {
231 throw uncaughtException(ex);
232 }
233 }
234 @Override
235 public final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
236 try {
237 return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
238 } catch (Throwable ex) {
239 throw uncaughtException(ex);
240 }
241 }
242 @Override
243 public final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
244 try {
245 return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
246 } catch (Throwable ex) {
247 throw uncaughtException(ex);
248 }
249 }
250 @Override
251 public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
252 try {
253 return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg);
254 } catch (Throwable ex) {
255 throw uncaughtException(ex);
256 }
257 }
258 }
259
260 //
261 // BMH species meta-data
262 //
263
264 /**
265 * Meta-data wrapper for concrete BMH types.
266 * Each BMH type corresponds to a given sequence of basic field types (LIJFD).
267 * The fields are immutable; their values are fully specified at object construction.
268 * Each BMH type supplies an array of getter functions which may be used in lambda forms.
269 * A BMH is constructed by cloning a shorter BMH and adding one or more new field values.
270 * As a degenerate and common case, the "shorter BMH" can be missing, and contributes zero prior fields.
271 */
272 static class SpeciesData {
273 final String typeChars;
274 final BasicType[] typeCodes;
275 final Class<? extends BoundMethodHandle> clazz;
276 // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
277 // Therefore, we need a non-final link in the chain. Use array elements.
278 final MethodHandle[] constructor;
279 final MethodHandle[] getters;
280 final NamedFunction[] nominalGetters;
281 final SpeciesData[] extensions;
282
283 public int fieldCount() {
284 return typeCodes.length;
285 }
286 public BasicType fieldType(int i) {
287 return typeCodes[i];
288 }
289 public char fieldTypeChar(int i) {
290 return typeChars.charAt(i);
291 }
292
293 public String toString() {
294 return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+typeChars+"]";
295 }
296
297 /**
298 * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
299 * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
300 * getter.
301 */
302 NamedFunction getterFunction(int i) {
303 return nominalGetters[i];
304 }
305
306 static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
307
308 private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
309 this.typeChars = types;
310 this.typeCodes = basicTypes(types);
311 this.clazz = clazz;
312 if (!INIT_DONE) {
313 this.constructor = new MethodHandle[1]; // only one ctor
314 this.getters = new MethodHandle[types.length()];
315 this.nominalGetters = new NamedFunction[types.length()];
316 } else {
317 this.constructor = Factory.makeCtors(clazz, types, null);
318 this.getters = Factory.makeGetters(clazz, types, null);
319 this.nominalGetters = Factory.makeNominalGetters(clazz, types, null, this.getters);
320 }
321 this.extensions = new SpeciesData[ARG_TYPE_LIMIT];
322 }
323
324 private void initForBootstrap() {
325 assert(!INIT_DONE);
326 if (constructor[0] == null) {
327 String types = typeChars;
328 Factory.makeCtors(clazz, types, this.constructor);
329 Factory.makeGetters(clazz, types, this.getters);
330 Factory.makeNominalGetters(clazz, types, this.nominalGetters, this.getters);
331 }
332 }
333
334 private SpeciesData(String typeChars) {
335 // Placeholder only.
336 this.typeChars = typeChars;
337 this.typeCodes = basicTypes(typeChars);
338 this.clazz = null;
339 this.constructor = null;
340 this.getters = null;
341 this.nominalGetters = null;
342 this.extensions = null;
343 }
344 private boolean isPlaceholder() { return clazz == null; }
345
346 private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
347 static { CACHE.put("", EMPTY); } // make bootstrap predictable
348 private static final boolean INIT_DONE; // set after <clinit> finishes...
349
350 SpeciesData extendWith(byte type) {
351 return extendWith(BasicType.basicType(type));
352 }
353
354 SpeciesData extendWith(BasicType type) {
355 int ord = type.ordinal();
356 SpeciesData d = extensions[ord];
357 if (d != null) return d;
358 extensions[ord] = d = get(typeChars+type.basicTypeChar());
359 return d;
360 }
361
362 SpeciesData extendWith(Class<?> type) {
363 return extendWith(basicType(type));
364 }
365
366 SpeciesData extendWithChar(char type) {
367 return extendWith(basicType(type));
368 }
369
370 private static SpeciesData get(String types) {
371 // Acquire cache lock for query.
372 SpeciesData d = lookupCache(types);
373 if (!d.isPlaceholder())
374 return d;
375 synchronized (d) {
376 // Use synch. on the placeholder to prevent multiple instantiation of one species.
377 // Creating this class forces a recursive call to getForClass.
378 if (lookupCache(types).isPlaceholder())
379 Factory.generateConcreteBMHClass(types);
380 }
381 // Reacquire cache lock.
382 d = lookupCache(types);
383 // Class loading must have upgraded the cache.
384 assert(d != null && !d.isPlaceholder());
385 return d;
386 }
387 static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
388 // clazz is a new class which is initializing its SPECIES_DATA field
389 return updateCache(types, new SpeciesData(types, clazz));
390 }
391 private static synchronized SpeciesData lookupCache(String types) {
392 SpeciesData d = CACHE.get(types);
393 if (d != null) return d;
394 d = new SpeciesData(types);
395 assert(d.isPlaceholder());
396 CACHE.put(types, d);
397 return d;
398 }
399 private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
400 SpeciesData d2;
401 assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
402 assert(!d.isPlaceholder());
403 CACHE.put(types, d);
404 return d;
405 }
406
407 static void initStatics() {}
408
409 static {
410 // pre-fill the BMH speciesdata cache with BMH's inner classes
411 final Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
412 try {
413 for (Class<?> c : rootCls.getDeclaredClasses()) {
414 if (rootCls.isAssignableFrom(c)) {
415 final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
416 SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
417 assert(d != null) : cbmh.getName();
418 assert(d.clazz == cbmh);
419 assert(d == lookupCache(d.typeChars));
420 }
421 }
422 } catch (Throwable e) {
423 throw newInternalError(e);
424 }
425
426 for (SpeciesData d : CACHE.values()) {
427 d.initForBootstrap();
428 }
429 // Note: Do not simplify this, because INIT_DONE must not be
430 // a compile-time constant during bootstrapping.
431 INIT_DONE = Boolean.TRUE;
432 }
433 }
434
435 static SpeciesData getSpeciesData(String types) {
436 return SpeciesData.get(types);
437 }
438
439 /**
450 */
451 static class Factory {
452
453 static final String JLO_SIG = "Ljava/lang/Object;";
454 static final String JLS_SIG = "Ljava/lang/String;";
455 static final String JLC_SIG = "Ljava/lang/Class;";
456 static final String MH = "java/lang/invoke/MethodHandle";
457 static final String MH_SIG = "L"+MH+";";
458 static final String BMH = "java/lang/invoke/BoundMethodHandle";
459 static final String BMH_SIG = "L"+BMH+";";
460 static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData";
461 static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
462
463 static final String SPECIES_PREFIX_NAME = "Species_";
464 static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
465
466 static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
467 static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
468 static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
469 static final String VOID_SIG = "()V";
470 static final String INT_SIG = "()I";
471
472 static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
473
474 static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
475
476 /**
477 * Generate a concrete subclass of BMH for a given combination of bound types.
478 *
479 * A concrete BMH species adheres to the following schema:
480 *
481 * <pre>
482 * class Species_[[types]] extends BoundMethodHandle {
483 * [[fields]]
484 * final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); }
485 * }
486 * </pre>
487 *
488 * The {@code [[types]]} signature is precisely the string that is passed to this
489 * method.
490 *
491 * The {@code [[fields]]} section consists of one field definition per character in
492 * the type signature, adhering to the naming schema described in the definition of
493 * {@link #makeFieldName}.
494 *
495 * For example, a concrete BMH species for two reference and one integral bound values
496 * would have the following shape:
497 *
498 * <pre>
499 * class BoundMethodHandle { ... private static
500 * final class Species_LLI extends BoundMethodHandle {
501 * final Object argL0;
502 * final Object argL1;
503 * final int argI2;
504 * private Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
505 * super(mt, lf);
506 * this.argL0 = argL0;
507 * this.argL1 = argL1;
508 * this.argI2 = argI2;
509 * }
510 * public final SpeciesData speciesData() { return SPECIES_DATA; }
511 * public final int fieldCount() { return 3; }
512 * public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
513 * public BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
514 * return new Species_LLI(mt, lf, argL0, argL1, argI2);
515 * }
516 * public final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
517 * return new Species_LLI(mt, lf, argL0, argL1, argI2);
518 * }
519 * public final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
520 * return SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
521 * }
522 * public final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
523 * return SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
524 * }
525 * public final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
526 * return SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
527 * }
528 * public final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
529 * return SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
530 * }
531 * public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
532 * return SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
533 * }
534 * }
535 * </pre>
536 *
537 * @param types the type signature, wherein reference types are erased to 'L'
538 * @return the generated concrete BMH class
539 */
540 static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
541 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
542
543 String shortTypes = LambdaForm.shortenSignature(types);
544 final String className = SPECIES_PREFIX_PATH + shortTypes;
545 final String sourceFile = SPECIES_PREFIX_NAME + shortTypes;
546 cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
547 cw.visitSource(sourceFile, null);
548
549 // emit static types and SPECIES_DATA fields
550 cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd();
551
552 // emit bound argument fields
553 for (int i = 0; i < types.length(); ++i) {
554 final char t = types.charAt(i);
555 final String fieldName = makeFieldName(types, i);
556 final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
557 cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
558 }
559
560 MethodVisitor mv;
561
562 // emit constructor
563 mv = cw.visitMethod(ACC_PRIVATE, "<init>", makeSignature(types, true), null, null);
564 mv.visitCode();
565 mv.visitVarInsn(ALOAD, 0); // this
566 mv.visitVarInsn(ALOAD, 1); // type
567 mv.visitVarInsn(ALOAD, 2); // form
568
569 mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true), false);
570
571 for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
572 // i counts the arguments, j counts corresponding argument slots
573 char t = types.charAt(i);
574 mv.visitVarInsn(ALOAD, 0);
575 mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3
576 mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t));
577 if (t == 'J' || t == 'D') {
578 ++j; // adjust argument register access
579 }
580 }
581
582 mv.visitInsn(RETURN);
583 mv.visitMaxs(0, 0);
584 mv.visitEnd();
585
586 // emit implementation of speciesData()
587 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
588 mv.visitCode();
589 mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
590 mv.visitInsn(ARETURN);
591 mv.visitMaxs(0, 0);
592 mv.visitEnd();
593
594 // emit implementation of fieldCount()
595 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "fieldCount", INT_SIG, null, null);
596 mv.visitCode();
597 int fc = types.length();
598 if (fc <= (ICONST_5 - ICONST_0)) {
599 mv.visitInsn(ICONST_0 + fc);
600 } else {
601 mv.visitIntInsn(SIPUSH, fc);
602 }
603 mv.visitInsn(IRETURN);
604 mv.visitMaxs(0, 0);
605 mv.visitEnd();
606 // emit make() ...factory method wrapping constructor
607 mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "make", makeSignature(types, false), null, null);
608 mv.visitCode();
609 // make instance
610 mv.visitTypeInsn(NEW, className);
611 mv.visitInsn(DUP);
612 // load mt, lf
613 mv.visitVarInsn(ALOAD, 0); // type
614 mv.visitVarInsn(ALOAD, 1); // form
615 // load factory method arguments
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(typeLoadOp(t), j + 2); // parameters start at 3
620 if (t == 'J' || t == 'D') {
621 ++j; // adjust argument register access
622 }
623 }
624
625 // finally, invoke the constructor and return
626 mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", makeSignature(types, true));
627 mv.visitInsn(ARETURN);
628 mv.visitMaxs(0, 0);
629 mv.visitEnd();
630
631 // emit copyWith()
632 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "copyWith", makeSignature("", false), null, null);
633 mv.visitCode();
634 // make instance
635 mv.visitTypeInsn(NEW, className);
636 mv.visitInsn(DUP);
637 // load mt, lf
638 mv.visitVarInsn(ALOAD, 1);
639 mv.visitVarInsn(ALOAD, 2);
640 // put fields on the stack
641 emitPushFields(types, className, mv);
642 // finally, invoke the constructor and return
643 mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", makeSignature(types, true));
644 mv.visitInsn(ARETURN);
645 mv.visitMaxs(0, 0);
646 mv.visitEnd();
647
648 // for each type, emit copyWithExtendT()
649 for (BasicType type : BasicType.ARG_TYPES) {
650 int ord = type.ordinal();
651 char btChar = type.basicTypeChar();
652 mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + btChar, makeSignature(String.valueOf(btChar), false), null, E_THROWABLE);
653 mv.visitCode();
654 // return SPECIES_DATA.extendWith(t).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
655 // obtain constructor
656 mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
657 int iconstInsn = ICONST_0 + ord;
658 assert(iconstInsn <= ICONST_5);
659 mv.visitInsn(iconstInsn);
660 mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG);
661 mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
662 mv.visitInsn(ICONST_0);
663 mv.visitInsn(AALOAD);
664 // load mt, lf
665 mv.visitVarInsn(ALOAD, 1);
666 mv.visitVarInsn(ALOAD, 2);
667 // put fields on the stack
668 emitPushFields(types, className, mv);
669 // put narg on stack
670 mv.visitVarInsn(typeLoadOp(btChar), 3);
671 // finally, invoke the constructor and return
672 mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + btChar, false), false);
673 mv.visitInsn(ARETURN);
674 mv.visitMaxs(0, 0);
675 mv.visitEnd();
676 }
677
678 // emit class initializer
679 mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null);
680 mv.visitCode();
681 mv.visitLdcInsn(types);
682 mv.visitLdcInsn(Type.getObjectType(className));
683 mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG, false);
684 mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
685 mv.visitInsn(RETURN);
686 mv.visitMaxs(0, 0);
687 mv.visitEnd();
688
689 cw.visitEnd();
690
691 // load class
692 final byte[] classFile = cw.toByteArray();
693 InvokerBytecodeGenerator.maybeDump(className, classFile);
694 Class<? extends BoundMethodHandle> bmhClass =
695 //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
696 UNSAFE.defineClass(className, classFile, 0, classFile.length,
697 BoundMethodHandle.class.getClassLoader(), null)
698 .asSubclass(BoundMethodHandle.class);
699 UNSAFE.ensureClassInitialized(bmhClass);
700
701 return bmhClass;
702 }
703
704 private static int typeLoadOp(char t) {
705 switch (t) {
706 case 'L': return ALOAD;
707 case 'I': return ILOAD;
708 case 'J': return LLOAD;
709 case 'F': return FLOAD;
710 case 'D': return DLOAD;
711 default : throw newInternalError("unrecognized type " + t);
712 }
713 }
714
715 private static void emitPushFields(String types, String className, MethodVisitor mv) {
716 for (int i = 0; i < types.length(); ++i) {
717 char tc = types.charAt(i);
718 mv.visitVarInsn(ALOAD, 0);
719 mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
720 }
721 }
722
723 static String typeSig(char t) {
724 return t == 'L' ? JLO_SIG : String.valueOf(t);
725 }
726
727 //
728 // Getter MH generation.
729 //
730
731 private static MethodHandle makeGetter(Class<?> cbmhClass, String types, int index) {
732 String fieldName = makeFieldName(types, index);
733 Class<?> fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType();
734 try {
735 return LOOKUP.findGetter(cbmhClass, fieldName, fieldType);
736 } catch (NoSuchFieldException | IllegalAccessException e) {
737 throw newInternalError(e);
738 }
739 }
740
741 static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
742 if (mhs == null) mhs = new MethodHandle[types.length()];
743 for (int i = 0; i < mhs.length; ++i) {
744 mhs[i] = makeGetter(cbmhClass, types, i);
745 assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
746 }
747 return mhs;
748 }
749
750 static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
751 if (mhs == null) mhs = new MethodHandle[1];
752 if (types.equals("")) return mhs; // hack for empty BMH species
753 mhs[0] = makeCbmhCtor(cbmh, types);
754 return mhs;
755 }
756
757 static NamedFunction[] makeNominalGetters(Class<?> cbmhClass, String types, NamedFunction[] nfs, MethodHandle[] getters) {
758 if (nfs == null) nfs = new NamedFunction[types.length()];
759 for (int i = 0; i < nfs.length; ++i) {
760 nfs[i] = new NamedFunction(getters[i]);
761 }
762 return nfs;
763 }
764
765 //
766 // Auxiliary methods.
767 //
768
769 static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
770 try {
771 Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
772 return (SpeciesData) F_SPECIES_DATA.get(null);
773 } catch (ReflectiveOperationException ex) {
774 throw newInternalError(ex);
775 }
776 }
777
778 /**
779 * Field names in concrete BMHs adhere to this pattern:
780 * arg + type + index
781 * where type is a single character (L, I, J, F, D).
782 */
783 private static String makeFieldName(String types, int index) {
784 assert index >= 0 && index < types.length();
785 return "arg" + types.charAt(index) + index;
786 }
787
788 private static String makeSignature(String types, boolean ctor) {
789 StringBuilder buf = new StringBuilder(SIG_INCIPIT);
790 for (char c : types.toCharArray()) {
791 buf.append(typeSig(c));
792 }
793 return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
794 }
795
796 static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
797 try {
798 return LOOKUP.findStatic(cbmh, "make", MethodType.fromMethodDescriptorString(makeSignature(types, false), null));
799 } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
800 throw newInternalError(e);
801 }
802 }
803 }
804
805 private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
806
807 static void initStatics() {}
808 static { SpeciesData.initStatics(); }
809
810 /**
811 * All subclasses must provide such a value describing their type signature.
812 */
813 static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
814
815 private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[5];
816 private static SpeciesData checkCache(int size, String types) {
817 int idx = size - 1;
818 SpeciesData data = SPECIES_DATA_CACHE[idx];
819 if (data != null) return data;
820 SPECIES_DATA_CACHE[idx] = data = getSpeciesData(types);
821 return data;
822 }
823 static SpeciesData speciesData_L() { return checkCache(1, "L"); }
824 static SpeciesData speciesData_LL() { return checkCache(2, "LL"); }
825 static SpeciesData speciesData_LLL() { return checkCache(3, "LLL"); }
826 static SpeciesData speciesData_LLLL() { return checkCache(4, "LLLL"); }
827 static SpeciesData speciesData_LLLLL() { return checkCache(5, "LLLLL"); }
828 }
|