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;
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();
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;
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(member)) {
511 // selectAlternative idiom
512 // FIXME: make sure this idiom is really present!
513 emitSelectAlternative(name, lambdaForm.names[i + 1]);
514 i++; // skip MH.invokeBasic of the selectAlternative result
515 } else if (isGuardWithCatch(i)) {
516 emitGuardWithCatch(i);
517 i = i+2; // Jump to the end of GWC idiom
518 } else if (isStaticallyInvocable(member)) {
519 emitStaticInvoke(member, name);
520 } else {
521 emitInvoke(name);
522 }
523
524 // Update cached form name's info in case an intrinsic spanning multiple names was encountered.
525 name = lambdaForm.names[i];
526 member = name.function.member();
527
528 // store the result from evaluating to the target name in a local if required
529 // (if this is the last value, i.e., the one that is going to be returned,
530 // avoid store/load/return and just return)
531 if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) {
532 // return value - do nothing
533 } else if (name.type != 'V') {
534 // non-void: actually assign
535 emitStoreInsn(name.type, name.index());
536 }
537 }
538
539 // return statement
540 emitReturn();
541
542 classFileEpilogue();
543 bogusMethod(lambdaForm);
544
545 final byte[] classFile = cw.toByteArray();
546 maybeDump(className, classFile);
547 return classFile;
660 } else {
661 mtype = MethodType.toFieldDescriptorString(member.getFieldType());
662 mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype);
663 }
664 }
665 int refKindOpcode(byte refKind) {
666 switch (refKind) {
667 case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;
668 case REF_invokeStatic: return Opcodes.INVOKESTATIC;
669 case REF_invokeSpecial: return Opcodes.INVOKESPECIAL;
670 case REF_invokeInterface: return Opcodes.INVOKEINTERFACE;
671 case REF_getField: return Opcodes.GETFIELD;
672 case REF_putField: return Opcodes.PUTFIELD;
673 case REF_getStatic: return Opcodes.GETSTATIC;
674 case REF_putStatic: return Opcodes.PUTSTATIC;
675 }
676 throw new InternalError("refKind="+refKind);
677 }
678
679 /**
680 * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
681 */
682 private boolean memberNameRefersTo(MemberName member, Class<?> declaringClass, String name) {
683 return member != null &&
684 member.getDeclaringClass() == declaringClass &&
685 member.getName().equals(name);
686 }
687
688 /**
689 * Check if MemberName is a call to MethodHandleImpl.selectAlternative.
690 */
691 private boolean isSelectAlternative(MemberName member) {
692 return memberNameRefersTo(member, MethodHandleImpl.class, "selectAlternative");
693 }
694
695 /**
696 * Emit bytecode for the selectAlternative idiom.
697 *
698 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
699 * <blockquote><pre>{@code
700 * Lambda(a0:L,a1:I)=>{
701 * t2:I=foo.test(a1:I);
702 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
703 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
704 * }</pre></blockquote>
705 */
706 private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
707 MethodType type = selectAlternativeName.function.methodType();
708
709 Name receiver = (Name) invokeBasicName.arguments[0];
710
711 Label L_fallback = new Label();
712 Label L_done = new Label();
723 emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
724 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
725 emitInvoke(invokeBasicName);
726
727 // goto L_done
728 mv.visitJumpInsn(Opcodes.GOTO, L_done);
729
730 // L_fallback:
731 mv.visitLabel(L_fallback);
732
733 // invoke selectAlternativeName.arguments[2]
734 MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2];
735 emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
736 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
737 emitInvoke(invokeBasicName);
738
739 // L_done:
740 mv.visitLabel(L_done);
741 }
742
743 /**
744 * Check if i-th name is a start of GuardWithCatch idiom.
745 */
746 private boolean isGuardWithCatch(int pos) {
747 // GuardWithCatch idiom:
748 // t_{n}:L=MethodHandle.invokeBasic(...)
749 // t_{n+1}:L=MethodHandleImpl.guardWithCatch(...);
750 // t_{n+2}:I=MethodHandle.invokeBasic(...)
751 return lambdaForm.names.length-1 >= pos+2 &&
752 memberNameRefersTo(lambdaForm.names[pos+1].function.member(),
753 MethodHandleImpl.class, "guardWithCatch");
754 }
755
756 /**
757 * Emit bytecode for the guardWithCatch idiom.
758 *
759 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch):
760 * <blockquote><pre>{@code
761 * guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
762 * t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L);
763 * t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L);
764 * t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I}
765 * }</pre></blockquote>
766 *
767 * It is compiled into bytecode equivalent of the following code:
768 * <blockquote><pre>{@code
769 * try {
770 * return a1.invokeBasic(a6, a7);
771 * } catch (Throwable e) {
772 * if (!a2.isInstance(e)) throw e;
773 * return a3.invokeBasic(ex, a6, a7);
774 * }}
775 */
776 private void emitGuardWithCatch(int pos) {
777 Name args = lambdaForm.names[pos];
778 Name invoker = lambdaForm.names[pos+1];
779 Name result = lambdaForm.names[pos+2];
780
781 Label L_startBlock = new Label();
782 Label L_endBlock = new Label();
783 Label L_handler = new Label();
784 Label L_done = new Label();
785
786 Class<?> returnType = result.function.resolvedHandle.type().returnType();
787 MethodType type = args.function.resolvedHandle.type()
788 .dropParameterTypes(0,1)
789 .changeReturnType(returnType);
790
791 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable");
792
793 // Normal case
794 mv.visitLabel(L_startBlock);
795 // load target
796 emitPushArgument(invoker, 0);
797 emitPushArguments(args, 1); // skip 1st argument: method handle
798 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString());
799 mv.visitLabel(L_endBlock);
800 mv.visitJumpInsn(Opcodes.GOTO, L_done);
801
802 // Exceptional case
803 mv.visitLabel(L_handler);
804
805 // Check exception's type
806 mv.visitInsn(Opcodes.DUP);
807 // load exception class
808 emitPushArgument(invoker, 1);
809 mv.visitInsn(Opcodes.SWAP);
810 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z");
811 Label L_rethrow = new Label();
812 mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow);
813
814 // Invoke catcher
815 // load catcher
816 emitPushArgument(invoker, 2);
817 mv.visitInsn(Opcodes.SWAP);
818 emitPushArguments(args, 1); // skip 1st argument: method handle
819 MethodType catcherType = type.insertParameterTypes(0, Throwable.class);
820 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString());
821 mv.visitJumpInsn(Opcodes.GOTO, L_done);
822
823 mv.visitLabel(L_rethrow);
824 mv.visitInsn(Opcodes.ATHROW);
825
826 mv.visitLabel(L_done);
827 }
828
829 private void emitPushArguments(Name args, int start) {
830 for (int i = start; i < args.arguments.length; i++) {
831 emitPushArgument(args, i);
832 }
833 }
834
835 private void emitPushArgument(Name name, int paramIndex) {
836 Object arg = name.arguments[paramIndex];
837 char ptype = name.function.parameterType(paramIndex);
838 MethodType mtype = name.function.methodType();
839 if (arg instanceof Name) {
840 Name n = (Name) arg;
841 emitLoadInsn(n.type, n.index());
842 emitImplicitConversion(n.type, mtype.parameterType(paramIndex));
843 } else if ((arg == null || arg instanceof String) && ptype == 'L') {
844 emitConst(arg);
845 } else {
846 if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') {
847 emitConst(arg);
848 } else {
849 mv.visitLdcInsn(constantPlaceholder(arg));
850 emitImplicitConversion('L', mtype.parameterType(paramIndex));
851 }
852 }
853 }
854
|