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 jdk.internal.vm.annotation.Stable;
29 import jdk.internal.org.objectweb.asm.ClassWriter;
30 import jdk.internal.org.objectweb.asm.FieldVisitor;
31 import jdk.internal.org.objectweb.asm.MethodVisitor;
32 import sun.invoke.util.ValueConversions;
33 import sun.invoke.util.Wrapper;
34
35 import java.lang.invoke.LambdaForm.NamedFunction;
36 import java.lang.invoke.MethodHandles.Lookup;
37 import java.lang.reflect.Field;
38 import java.util.Arrays;
39 import java.util.concurrent.ConcurrentHashMap;
40 import java.util.concurrent.ConcurrentMap;
41 import java.util.function.Function;
42
43 import static java.lang.invoke.LambdaForm.BasicType;
44 import static java.lang.invoke.LambdaForm.BasicType.*;
45 import static java.lang.invoke.MethodHandleStatics.*;
46 import static jdk.internal.org.objectweb.asm.Opcodes.*;
47
48 /**
49 * The flavor of method handle which emulates an invoke instruction
50 * on a predetermined argument. The JVM dispatches to the correct method
51 * when the handle is created, not when it is invoked.
52 *
53 * All bound arguments are encapsulated in dedicated species.
54 */
55 /*non-public*/ abstract class BoundMethodHandle extends MethodHandle {
56
57 /*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) {
58 super(type, form);
446 *
447 * A BMH species has a number of fields with the concrete (possibly erased) types of
448 * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
449 * which can be included as names in lambda forms.
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 static final String STABLE_SIG = "Ljdk/internal/vm/annotation/Stable;";
463
464 static final String SPECIES_PREFIX_NAME = "Species_";
465 static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
466
467 static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
468 static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
469 static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
470 static final String VOID_SIG = "()V";
471 static final String INT_SIG = "()I";
472
473 static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
474
475 static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
476
477 static final ConcurrentMap<String, Class<? extends BoundMethodHandle>> CLASS_CACHE = new ConcurrentHashMap<>();
478
479 /**
480 * Get a concrete subclass of BMH for a given combination of bound types.
481 *
482 * @param types the type signature, wherein reference types are erased to 'L'
483 * @return the concrete BMH class
484 */
485 static Class<? extends BoundMethodHandle> getConcreteBMHClass(String types) {
486 // CHM.computeIfAbsent ensures generateConcreteBMHClass is called
487 // only once per key.
488 return CLASS_CACHE.computeIfAbsent(
489 types, new Function<String, Class<? extends BoundMethodHandle>>() {
490 @Override
491 public Class<? extends BoundMethodHandle> apply(String types) {
492 return generateConcreteBMHClass(types);
493 }
494 });
495 }
496
497 /**
498 * Generate a concrete subclass of BMH for a given combination of bound types.
499 *
500 * A concrete BMH species adheres to the following schema:
501 *
502 * <pre>
503 * class Species_[[types]] extends BoundMethodHandle {
504 * [[fields]]
505 * final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); }
506 * }
507 * </pre>
508 *
509 * The {@code [[types]]} signature is precisely the string that is passed to this
510 * method.
511 *
512 * The {@code [[fields]]} section consists of one field definition per character in
541 * return SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
542 * }
543 * final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
544 * return SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
545 * }
546 * final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
547 * return SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
548 * }
549 * final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
550 * return SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
551 * }
552 * public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
553 * return SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
554 * }
555 * }
556 * </pre>
557 *
558 * @param types the type signature, wherein reference types are erased to 'L'
559 * @return the generated concrete BMH class
560 */
561 static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
562 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
563
564 String shortTypes = LambdaForm.shortenSignature(types);
565 final String className = SPECIES_PREFIX_PATH + shortTypes;
566 final String sourceFile = SPECIES_PREFIX_NAME + shortTypes;
567 final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
568 cw.visit(V1_6, NOT_ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
569 cw.visitSource(sourceFile, null);
570
571 // emit static types and SPECIES_DATA fields
572 FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null);
573 fw.visitAnnotation(STABLE_SIG, true);
574 fw.visitEnd();
575
576 // emit bound argument fields
577 for (int i = 0; i < types.length(); ++i) {
578 final char t = types.charAt(i);
579 final String fieldName = makeFieldName(types, i);
580 final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
581 cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
582 }
583
584 MethodVisitor mv;
585
586 // emit constructor
682 assert(iconstInsn <= ICONST_5);
683 mv.visitInsn(iconstInsn);
684 mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG, false);
685 mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "constructor", "()" + MH_SIG, false);
686 // load mt, lf
687 mv.visitVarInsn(ALOAD, 1);
688 mv.visitVarInsn(ALOAD, 2);
689 // put fields on the stack
690 emitPushFields(types, className, mv);
691 // put narg on stack
692 mv.visitVarInsn(typeLoadOp(btChar), 3);
693 // finally, invoke the constructor and return
694 mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + btChar, false), false);
695 mv.visitInsn(ARETURN);
696 mv.visitMaxs(0, 0);
697 mv.visitEnd();
698 }
699
700 cw.visitEnd();
701
702 // load class
703 final byte[] classFile = cw.toByteArray();
704 InvokerBytecodeGenerator.maybeDump(className, classFile);
705 Class<? extends BoundMethodHandle> bmhClass =
706 //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
707 UNSAFE.defineClass(className, classFile, 0, classFile.length,
708 BoundMethodHandle.class.getClassLoader(), null)
709 .asSubclass(BoundMethodHandle.class);
710
711 return bmhClass;
712 }
713
714 private static int typeLoadOp(char t) {
715 switch (t) {
716 case 'L': return ALOAD;
717 case 'I': return ILOAD;
718 case 'J': return LLOAD;
719 case 'F': return FLOAD;
720 case 'D': return DLOAD;
721 default : throw newInternalError("unrecognized type " + t);
722 }
723 }
724
725 private static void emitPushFields(String types, String className, MethodVisitor mv) {
726 for (int i = 0; i < types.length(); ++i) {
727 char tc = types.charAt(i);
728 mv.visitVarInsn(ALOAD, 0);
729 mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
730 }
731 }
|
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 jdk.internal.loader.BootLoader;
29 import jdk.internal.vm.annotation.Stable;
30 import jdk.internal.org.objectweb.asm.ClassWriter;
31 import jdk.internal.org.objectweb.asm.FieldVisitor;
32 import jdk.internal.org.objectweb.asm.MethodVisitor;
33 import sun.invoke.util.ValueConversions;
34 import sun.invoke.util.Wrapper;
35
36 import java.lang.invoke.LambdaForm.NamedFunction;
37 import java.lang.invoke.MethodHandles.Lookup;
38 import java.lang.reflect.Field;
39 import java.util.Arrays;
40 import java.util.Map;
41 import java.util.concurrent.ConcurrentHashMap;
42 import java.util.concurrent.ConcurrentMap;
43 import java.util.function.Function;
44
45 import static java.lang.invoke.LambdaForm.BasicType;
46 import static java.lang.invoke.LambdaForm.BasicType.*;
47 import static java.lang.invoke.MethodHandleStatics.*;
48 import static jdk.internal.org.objectweb.asm.Opcodes.*;
49
50 /**
51 * The flavor of method handle which emulates an invoke instruction
52 * on a predetermined argument. The JVM dispatches to the correct method
53 * when the handle is created, not when it is invoked.
54 *
55 * All bound arguments are encapsulated in dedicated species.
56 */
57 /*non-public*/ abstract class BoundMethodHandle extends MethodHandle {
58
59 /*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) {
60 super(type, form);
448 *
449 * A BMH species has a number of fields with the concrete (possibly erased) types of
450 * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
451 * which can be included as names in lambda forms.
452 */
453 static class Factory {
454
455 static final String JLO_SIG = "Ljava/lang/Object;";
456 static final String JLS_SIG = "Ljava/lang/String;";
457 static final String JLC_SIG = "Ljava/lang/Class;";
458 static final String MH = "java/lang/invoke/MethodHandle";
459 static final String MH_SIG = "L"+MH+";";
460 static final String BMH = "java/lang/invoke/BoundMethodHandle";
461 static final String BMH_SIG = "L"+BMH+";";
462 static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData";
463 static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
464 static final String STABLE_SIG = "Ljdk/internal/vm/annotation/Stable;";
465
466 static final String SPECIES_PREFIX_NAME = "Species_";
467 static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
468 static final String SPECIES_CLASS_PREFIX = SPECIES_PREFIX_PATH.replace('/', '.');
469
470 static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
471 static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
472 static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
473 static final String VOID_SIG = "()V";
474 static final String INT_SIG = "()I";
475
476 static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
477
478 static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
479
480 static final ConcurrentMap<String, Class<? extends BoundMethodHandle>> CLASS_CACHE = new ConcurrentHashMap<>();
481
482 /**
483 * Get a concrete subclass of BMH for a given combination of bound types.
484 *
485 * @param types the type signature, wherein reference types are erased to 'L'
486 * @return the concrete BMH class
487 */
488 static Class<? extends BoundMethodHandle> getConcreteBMHClass(String types) {
489 // CHM.computeIfAbsent ensures generateConcreteBMHClass is called
490 // only once per key.
491 return CLASS_CACHE.computeIfAbsent(
492 types, new Function<String, Class<? extends BoundMethodHandle>>() {
493 @Override
494 public Class<? extends BoundMethodHandle> apply(String types) {
495 String shortTypes = LambdaForm.shortenSignature(types);
496 String className = SPECIES_CLASS_PREFIX + shortTypes;
497 Class<?> c = BootLoader.loadClassOrNull(className);
498 if (c != null) {
499 return c.asSubclass(BoundMethodHandle.class);
500 } else {
501 // Not pregenerated, generate the class
502 return generateConcreteBMHClass(shortTypes, types);
503 }
504 }
505 });
506 }
507
508 /**
509 * Generate a concrete subclass of BMH for a given combination of bound types.
510 *
511 * A concrete BMH species adheres to the following schema:
512 *
513 * <pre>
514 * class Species_[[types]] extends BoundMethodHandle {
515 * [[fields]]
516 * final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); }
517 * }
518 * </pre>
519 *
520 * The {@code [[types]]} signature is precisely the string that is passed to this
521 * method.
522 *
523 * The {@code [[fields]]} section consists of one field definition per character in
552 * return SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
553 * }
554 * final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
555 * return SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
556 * }
557 * final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
558 * return SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
559 * }
560 * final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
561 * return SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
562 * }
563 * public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
564 * return SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg);
565 * }
566 * }
567 * </pre>
568 *
569 * @param types the type signature, wherein reference types are erased to 'L'
570 * @return the generated concrete BMH class
571 */
572 static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String shortTypes,
573 String types) {
574 final String className = speciesInternalClassName(shortTypes);
575 byte[] classFile = generateConcreteBMHClassBytes(shortTypes, types, className);
576
577 // load class
578 InvokerBytecodeGenerator.maybeDump(className, classFile);
579 Class<? extends BoundMethodHandle> bmhClass =
580 UNSAFE.defineClass(className, classFile, 0, classFile.length,
581 BoundMethodHandle.class.getClassLoader(), null)
582 .asSubclass(BoundMethodHandle.class);
583
584 return bmhClass;
585 }
586
587 /**
588 * @implNote this method is used by GenerateBMHClassesPlugin to enable
589 * ahead-of-time generation of BMH classes at link time. It does
590 * added validation since this string may be user provided.
591 */
592 static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(
593 final String types) {
594 for (char c : types.toCharArray()) {
595 if ("LIJFD".indexOf(c) < 0) {
596 throw new IllegalArgumentException("All characters must "
597 + "correspond to a basic field type: LIJFD");
598 }
599 }
600 String shortTypes = LambdaForm.shortenSignature(types);
601 final String className = speciesInternalClassName(shortTypes);
602 return Map.entry(className,
603 generateConcreteBMHClassBytes(shortTypes, types, className));
604 }
605
606 private static String speciesInternalClassName(String shortTypes) {
607 return SPECIES_PREFIX_PATH + shortTypes;
608 }
609
610 static byte[] generateConcreteBMHClassBytes(final String shortTypes,
611 final String types, final String className) {
612 final String sourceFile = SPECIES_PREFIX_NAME + shortTypes;
613
614 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
615 final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
616 cw.visit(V1_6, NOT_ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
617 cw.visitSource(sourceFile, null);
618
619 // emit static types and SPECIES_DATA fields
620 FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null);
621 fw.visitAnnotation(STABLE_SIG, true);
622 fw.visitEnd();
623
624 // emit bound argument fields
625 for (int i = 0; i < types.length(); ++i) {
626 final char t = types.charAt(i);
627 final String fieldName = makeFieldName(types, i);
628 final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
629 cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
630 }
631
632 MethodVisitor mv;
633
634 // emit constructor
730 assert(iconstInsn <= ICONST_5);
731 mv.visitInsn(iconstInsn);
732 mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG, false);
733 mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "constructor", "()" + MH_SIG, false);
734 // load mt, lf
735 mv.visitVarInsn(ALOAD, 1);
736 mv.visitVarInsn(ALOAD, 2);
737 // put fields on the stack
738 emitPushFields(types, className, mv);
739 // put narg on stack
740 mv.visitVarInsn(typeLoadOp(btChar), 3);
741 // finally, invoke the constructor and return
742 mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + btChar, false), false);
743 mv.visitInsn(ARETURN);
744 mv.visitMaxs(0, 0);
745 mv.visitEnd();
746 }
747
748 cw.visitEnd();
749
750 return cw.toByteArray();
751 }
752
753 private static int typeLoadOp(char t) {
754 switch (t) {
755 case 'L': return ALOAD;
756 case 'I': return ILOAD;
757 case 'J': return LLOAD;
758 case 'F': return FLOAD;
759 case 'D': return DLOAD;
760 default : throw newInternalError("unrecognized type " + t);
761 }
762 }
763
764 private static void emitPushFields(String types, String className, MethodVisitor mv) {
765 for (int i = 0; i < types.length(); ++i) {
766 char tc = types.charAt(i);
767 mv.visitVarInsn(ALOAD, 0);
768 mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
769 }
770 }
|