10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import sun.invoke.util.VerifyAccess;
29 import java.lang.invoke.LambdaForm.Name;
30 import java.lang.invoke.MethodHandles.Lookup;
31
32 import sun.invoke.util.Wrapper;
33
34 import java.io.*;
35 import java.util.*;
36
37 import jdk.internal.org.objectweb.asm.*;
38
39 import java.lang.reflect.*;
40 import static java.lang.invoke.MethodHandleStatics.*;
41 import static java.lang.invoke.MethodHandleNatives.Constants.*;
42 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
43 import sun.invoke.util.ValueConversions;
44 import sun.invoke.util.VerifyType;
45
46 /**
47 * Code generation backend for LambdaForm.
48 * <p>
49 * @author John Rose, JSR 292 EG
50 */
51 class InvokerBytecodeGenerator {
52 /** Define class names for convenience. */
53 private static final String MH = "java/lang/invoke/MethodHandle";
54 private static final String BMH = "java/lang/invoke/BoundMethodHandle";
55 private static final String LF = "java/lang/invoke/LambdaForm";
56 private static final String LFN = "java/lang/invoke/LambdaForm$Name";
57 private static final String CLS = "java/lang/Class";
58 private static final String OBJ = "java/lang/Object";
59 private static final String OBJARY = "[Ljava/lang/Object;";
60
61 private static final String LF_SIG = "L" + LF + ";";
62 private static final String LFN_SIG = "L" + LFN + ";";
63 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
64
65 /** Name of its super class*/
66 private static final String superName = LF;
67
68 /** Name of new class */
69 private final String className;
70
71 /** Name of the source file (for stack trace printing). */
72 private final String sourceFile;
73
74 private final LambdaForm lambdaForm;
494 * Generate an invoker method for the passed {@link LambdaForm}.
495 */
496 private byte[] generateCustomizedCodeBytes() {
497 classFilePrologue();
498
499 // Suppress this method in backtraces displayed to the user.
500 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
501
502 // Mark this method as a compiled LambdaForm
503 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
504
505 // Force inlining of this invoker method.
506 mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
507
508 // iterate over the form's names, generating bytecode instructions for each
509 // start iterating at the first name following the arguments
510 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
511 Name name = lambdaForm.names[i];
512 MemberName member = name.function.member();
513
514 if (isSelectAlternative(member)) {
515 // selectAlternative idiom
516 // FIXME: make sure this idiom is really present!
517 emitSelectAlternative(name, lambdaForm.names[i + 1]);
518 i++; // skip MH.invokeBasic of the selectAlternative result
519 } else if (isStaticallyInvocable(member)) {
520 emitStaticInvoke(member, name);
521 } else {
522 emitInvoke(name);
523 }
524
525 // store the result from evaluating to the target name in a local if required
526 // (if this is the last value, i.e., the one that is going to be returned,
527 // avoid store/load/return and just return)
528 if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
529 // return value - do nothing
530 } else if (name.type != 'V') {
531 // non-void: actually assign
532 emitStoreInsn(name.type, name.index());
533 }
534 }
535
536 // return statement
537 emitReturn();
538
539 classFileEpilogue();
540 bogusMethod(lambdaForm);
541
542 final byte[] classFile = cw.toByteArray();
543 maybeDump(className, classFile);
544 return classFile;
657 } else {
658 mtype = MethodType.toFieldDescriptorString(member.getFieldType());
659 mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
660 }
661 }
662 int refKindOpcode(byte refKind) {
663 switch (refKind) {
664 case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;
665 case REF_invokeStatic: return Opcodes.INVOKESTATIC;
666 case REF_invokeSpecial: return Opcodes.INVOKESPECIAL;
667 case REF_invokeInterface: return Opcodes.INVOKEINTERFACE;
668 case REF_getField: return Opcodes.GETFIELD;
669 case REF_putField: return Opcodes.PUTFIELD;
670 case REF_getStatic: return Opcodes.GETSTATIC;
671 case REF_putStatic: return Opcodes.PUTSTATIC;
672 }
673 throw new InternalError("refKind="+refKind);
674 }
675
676 /**
677 * Check if MemberName is a call to MethodHandleImpl.selectAlternative.
678 */
679 private boolean isSelectAlternative(MemberName member) {
680 return member != null &&
681 member.getDeclaringClass() == MethodHandleImpl.class &&
682 member.getName().equals("selectAlternative");
683 }
684
685 /**
686 * Emit bytecode for the selectAlternative idiom.
687 *
688 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
689 * <blockquote><pre>{@code
690 * Lambda(a0:L,a1:I)=>{
691 * t2:I=foo.test(a1:I);
692 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
693 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
694 * }</pre></blockquote>
695 */
696 private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
697 MethodType type = selectAlternativeName.function.methodType();
698
699 Name receiver = (Name) invokeBasicName.arguments[0];
700
701 Label L_fallback = new Label();
702 Label L_done = new Label();
703
704 // load test result
705 emitPushArgument(selectAlternativeName, 0);
706 mv.visitInsn(Opcodes.ICONST_1);
707
708 // if_icmpne L_fallback
709 mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
710
711 // invoke selectAlternativeName.arguments[1]
712 MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1];
713 emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
714 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
715 emitInvoke(invokeBasicName);
716
717 // goto L_done
718 mv.visitJumpInsn(Opcodes.GOTO, L_done);
719
720 // L_fallback:
721 mv.visitLabel(L_fallback);
722
723 // invoke selectAlternativeName.arguments[2]
724 MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
725 emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
726 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
727 emitInvoke(invokeBasicName);
728
729 // L_done:
730 mv.visitLabel(L_done);
731 }
732
733 private void emitPushArgument(Name name, int paramIndex) {
734 Object arg = name.arguments[paramIndex];
735 char ptype = name.function.parameterType(paramIndex);
736 MethodType mtype = name.function.methodType();
737 if (arg instanceof Name) {
738 Name n = (Name) arg;
739 emitLoadInsn(n.type, n.index());
740 emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
741 } else if ((arg == null || arg instanceof String) && ptype == 'L') {
742 emitConst(arg);
743 } else {
744 if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
745 emitConst(arg);
746 } else {
747 mv.visitLdcInsn(constantPlaceholder(arg));
748 emitImplicitConversion('L', mtype.parameterType(paramIndex));
749 }
750 }
751 }
752
|
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import sun.invoke.util.VerifyAccess;
29 import java.lang.invoke.LambdaForm.Name;
30
31 import sun.invoke.util.Wrapper;
32
33 import java.io.*;
34 import java.util.*;
35
36 import jdk.internal.org.objectweb.asm.*;
37
38 import java.lang.reflect.*;
39 import static java.lang.invoke.MethodHandleStatics.*;
40 import static java.lang.invoke.MethodHandleNatives.Constants.*;
41 import sun.invoke.util.VerifyType;
42
43 /**
44 * Code generation backend for LambdaForm.
45 * <p>
46 * @author John Rose, JSR 292 EG
47 */
48 class InvokerBytecodeGenerator {
49 /** Define class names for convenience. */
50 private static final String MH = "java/lang/invoke/MethodHandle";
51 private static final String LF = "java/lang/invoke/LambdaForm";
52 private static final String LFN = "java/lang/invoke/LambdaForm$Name";
53 private static final String CLS = "java/lang/Class";
54 private static final String OBJ = "java/lang/Object";
55 private static final String OBJARY = "[Ljava/lang/Object;";
56
57 private static final String LF_SIG = "L" + LF + ";";
58 private static final String LFN_SIG = "L" + LFN + ";";
59 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
60
61 /** Name of its super class*/
62 private static final String superName = LF;
63
64 /** Name of new class */
65 private final String className;
66
67 /** Name of the source file (for stack trace printing). */
68 private final String sourceFile;
69
70 private final LambdaForm lambdaForm;
490 * Generate an invoker method for the passed {@link LambdaForm}.
491 */
492 private byte[] generateCustomizedCodeBytes() {
493 classFilePrologue();
494
495 // Suppress this method in backtraces displayed to the user.
496 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
497
498 // Mark this method as a compiled LambdaForm
499 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
500
501 // Force inlining of this invoker method.
502 mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
503
504 // iterate over the form's names, generating bytecode instructions for each
505 // start iterating at the first name following the arguments
506 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
507 Name name = lambdaForm.names[i];
508 MemberName member = name.function.member();
509
510 if (isSelectAlternative(i)) {
511 emitSelectAlternative(name, lambdaForm.names[i + 1]);
512 i++; // skip MH.invokeBasic of the selectAlternative result
513 } else if (isGuardWithCatch(i)) {
514 emitGuardWithCatch(i);
515 i = i+2; // Jump to the end of GWC idiom
516 } else if (isStaticallyInvocable(member)) {
517 emitStaticInvoke(member, name);
518 } else {
519 emitInvoke(name);
520 }
521
522 // Update cached form name's info in case an intrinsic spanning multiple names was encountered.
523 name = lambdaForm.names[i];
524 member = name.function.member();
525
526 // store the result from evaluating to the target name in a local if required
527 // (if this is the last value, i.e., the one that is going to be returned,
528 // avoid store/load/return and just return)
529 if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
530 // return value - do nothing
531 } else if (name.type != 'V') {
532 // non-void: actually assign
533 emitStoreInsn(name.type, name.index());
534 }
535 }
536
537 // return statement
538 emitReturn();
539
540 classFileEpilogue();
541 bogusMethod(lambdaForm);
542
543 final byte[] classFile = cw.toByteArray();
544 maybeDump(className, classFile);
545 return classFile;
658 } else {
659 mtype = MethodType.toFieldDescriptorString(member.getFieldType());
660 mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
661 }
662 }
663 int refKindOpcode(byte refKind) {
664 switch (refKind) {
665 case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;
666 case REF_invokeStatic: return Opcodes.INVOKESTATIC;
667 case REF_invokeSpecial: return Opcodes.INVOKESPECIAL;
668 case REF_invokeInterface: return Opcodes.INVOKEINTERFACE;
669 case REF_getField: return Opcodes.GETFIELD;
670 case REF_putField: return Opcodes.PUTFIELD;
671 case REF_getStatic: return Opcodes.GETSTATIC;
672 case REF_putStatic: return Opcodes.PUTSTATIC;
673 }
674 throw new InternalError("refKind="+refKind);
675 }
676
677 /**
678 * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
679 */
680 private boolean memberRefersTo(MemberName member, Class<?> declaringClass, String name) {
681 return member != null &&
682 member.getDeclaringClass() == declaringClass &&
683 member.getName().equals(name);
684 }
685 private boolean nameRefersTo(Name name, Class<?> declaringClass, String methodName) {
686 return name.function != null &&
687 memberRefersTo(name.function.member(), declaringClass, methodName);
688 }
689
690 /**
691 * Check if MemberName is a call to MethodHandle.invokeBasic.
692 */
693 private boolean isInvokeBasic(Name name) {
694 if (name.function == null)
695 return false;
696 if (name.arguments.length < 1)
697 return false; // must have MH argument
698 MemberName member = name.function.member();
699 return memberRefersTo(member, MethodHandle.class, "invokeBasic") &&
700 !member.isPublic() && !member.isStatic();
701 }
702
703 /**
704 * Check if i-th name is a call to MethodHandleImpl.selectAlternative.
705 */
706 private boolean isSelectAlternative(int pos) {
707 // selectAlternative idiom:
708 // t_{n}:L=MethodHandleImpl.selectAlternative(...)
709 // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
710 if (pos+1 >= lambdaForm.names.length) return false;
711 Name name0 = lambdaForm.names[pos];
712 Name name1 = lambdaForm.names[pos+1];
713 return nameRefersTo(name0, MethodHandleImpl.class, "selectAlternative") &&
714 isInvokeBasic(name1) &&
715 name1.lastUseIndex(name0) == 0 && // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
716 lambdaForm.lastUseIndex(name0) == pos+1; // t_{n} is local: used only in t_{n+1}
717 }
718
719 /**
720 * Check if i-th name is a start of GuardWithCatch idiom.
721 */
722 private boolean isGuardWithCatch(int pos) {
723 // GuardWithCatch idiom:
724 // t_{n}:L=MethodHandle.invokeBasic(...)
725 // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
726 // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1})
727 if (pos+2 >= lambdaForm.names.length) return false;
728 Name name0 = lambdaForm.names[pos];
729 Name name1 = lambdaForm.names[pos+1];
730 Name name2 = lambdaForm.names[pos+2];
731 return nameRefersTo(name1, MethodHandleImpl.class, "guardWithCatch") &&
732 isInvokeBasic(name0) &&
733 isInvokeBasic(name2) &&
734 name1.lastUseIndex(name0) == 3 && // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
735 lambdaForm.lastUseIndex(name0) == pos+1 && // t_{n} is local: used only in t_{n+1}
736 name2.lastUseIndex(name1) == 1 && // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1})
737 lambdaForm.lastUseIndex(name1) == pos+2; // t_{n+1} is local: used only in t_{n+2}
738 }
739
740 /**
741 * Emit bytecode for the selectAlternative idiom.
742 *
743 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
744 * <blockquote><pre>{@code
745 * Lambda(a0:L,a1:I)=>{
746 * t2:I=foo.test(a1:I);
747 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
748 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
749 * }</pre></blockquote>
750 */
751 private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
752 Name receiver = (Name) invokeBasicName.arguments[0];
753
754 Label L_fallback = new Label();
755 Label L_done = new Label();
756
757 // load test result
758 emitPushArgument(selectAlternativeName, 0);
759 mv.visitInsn(Opcodes.ICONST_1);
760
761 // if_icmpne L_fallback
762 mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback);
763
764 // invoke selectAlternativeName.arguments[1]
765 emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
766 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
767 emitInvoke(invokeBasicName);
768
769 // goto L_done
770 mv.visitJumpInsn(Opcodes.GOTO, L_done);
771
772 // L_fallback:
773 mv.visitLabel(L_fallback);
774
775 // invoke selectAlternativeName.arguments[2]
776 emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
777 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
778 emitInvoke(invokeBasicName);
779
780 // L_done:
781 mv.visitLabel(L_done);
782 }
783
784 /**
785 * Emit bytecode for the guardWithCatch idiom.
786 *
787 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch):
788 * <blockquote><pre>{@code
789 * guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
790 * t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L);
791 * t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L);
792 * t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I}
793 * }</pre></blockquote>
794 *
795 * It is compiled into bytecode equivalent of the following code:
796 * <blockquote><pre>{@code
797 * try {
798 * return a1.invokeBasic(a6, a7);
799 * } catch (Throwable e) {
800 * if (!a2.isInstance(e)) throw e;
801 * return a3.invokeBasic(ex, a6, a7);
802 * }}
803 */
804 private void emitGuardWithCatch(int pos) {
805 Name args = lambdaForm.names[pos];
806 Name invoker = lambdaForm.names[pos+1];
807 Name result = lambdaForm.names[pos+2];
808
809 Label L_startBlock = new Label();
810 Label L_endBlock = new Label();
811 Label L_handler = new Label();
812 Label L_done = new Label();
813
814 Class<?> returnType = result.function.resolvedHandle.type().returnType();
815 MethodType type = args.function.resolvedHandle.type()
816 .dropParameterTypes(0,1)
817 .changeReturnType(returnType);
818
819 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable");
820
821 // Normal case
822 mv.visitLabel(L_startBlock);
823 // load target
824 emitPushArgument(invoker, 0);
825 emitPushArguments(args, 1); // skip 1st argument: method handle
826 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
827 mv.visitLabel(L_endBlock);
828 mv.visitJumpInsn(Opcodes.GOTO, L_done);
829
830 // Exceptional case
831 mv.visitLabel(L_handler);
832
833 // Check exception's type
834 mv.visitInsn(Opcodes.DUP);
835 // load exception class
836 emitPushArgument(invoker, 1);
837 mv.visitInsn(Opcodes.SWAP);
838 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z", false);
839 Label L_rethrow = new Label();
840 mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow);
841
842 // Invoke catcher
843 // load catcher
844 emitPushArgument(invoker, 2);
845 mv.visitInsn(Opcodes.SWAP);
846 emitPushArguments(args, 1); // skip 1st argument: method handle
847 MethodType catcherType = type.insertParameterTypes(0, Throwable.class);
848 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString(), false);
849 mv.visitJumpInsn(Opcodes.GOTO, L_done);
850
851 mv.visitLabel(L_rethrow);
852 mv.visitInsn(Opcodes.ATHROW);
853
854 mv.visitLabel(L_done);
855 }
856
857 private void emitPushArguments(Name args, int start) {
858 for (int i = start; i < args.arguments.length; i++) {
859 emitPushArgument(args, i);
860 }
861 }
862
863 private void emitPushArgument(Name name, int paramIndex) {
864 Object arg = name.arguments[paramIndex];
865 char ptype = name.function.parameterType(paramIndex);
866 MethodType mtype = name.function.methodType();
867 if (arg instanceof Name) {
868 Name n = (Name) arg;
869 emitLoadInsn(n.type, n.index());
870 emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
871 } else if ((arg == null || arg instanceof String) && ptype == 'L') {
872 emitConst(arg);
873 } else {
874 if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
875 emitConst(arg);
876 } else {
877 mv.visitLdcInsn(constantPlaceholder(arg));
878 emitImplicitConversion('L', mtype.parameterType(paramIndex));
879 }
880 }
881 }
882
|