583 // call test |
584 // call test |
584 names[arity + 1] = new Name(test, testArgs); |
585 names[arity + 1] = new Name(test, testArgs); |
585 |
586 |
586 // call selectAlternative |
587 // call selectAlternative |
587 Object[] selectArgs = { names[arity + 1], target, fallback }; |
588 Object[] selectArgs = { names[arity + 1], target, fallback }; |
588 names[arity + 2] = new Name(MethodHandleImpl.selectAlternative(), selectArgs); |
589 names[arity + 2] = new Name(Lazy.NF_selectAlternative, selectArgs); |
589 targetArgs[0] = names[arity + 2]; |
590 targetArgs[0] = names[arity + 2]; |
590 |
591 |
591 // call target or fallback |
592 // call target or fallback |
592 names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs); |
593 names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs); |
593 |
594 |
594 LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names); |
595 LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names); |
595 return SimpleMethodHandle.make(target.type(), form); |
596 return SimpleMethodHandle.make(target.type(), form); |
596 } |
597 } |
597 |
598 |
598 private static class GuardWithCatch { |
599 /** |
599 private final MethodHandle target; |
600 * The LambaForm shape for catchException combinator is the following: |
600 private final Class<? extends Throwable> exType; |
601 * <blockquote><pre>{@code |
601 private final MethodHandle catcher; |
602 * guardWithCatch=Lambda(a0:L,a1:L,a2:L)=>{ |
602 // FIXME: Build the control flow out of foldArguments. |
603 * t3:L=BoundMethodHandle$Species_LLLLL.argL0(a0:L); |
603 GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) { |
604 * t4:L=BoundMethodHandle$Species_LLLLL.argL1(a0:L); |
604 this.target = target; |
605 * t5:L=BoundMethodHandle$Species_LLLLL.argL2(a0:L); |
605 this.exType = exType; |
606 * t6:L=BoundMethodHandle$Species_LLLLL.argL3(a0:L); |
606 this.catcher = catcher; |
607 * t7:L=BoundMethodHandle$Species_LLLLL.argL4(a0:L); |
607 } |
608 * t8:L=MethodHandle.invokeBasic(t6:L,a1:L,a2:L); |
608 @LambdaForm.Hidden |
609 * t9:L=MethodHandleImpl.guardWithCatch(t3:L,t4:L,t5:L,t8:L); |
609 private Object invoke_V(Object... av) throws Throwable { |
610 * t10:I=MethodHandle.invokeBasic(t7:L,t9:L);t10:I} |
610 try { |
611 * }</pre></blockquote> |
611 return target.invokeExact(av); |
612 * |
612 } catch (Throwable t) { |
613 * argL0 and argL2 are target and catcher method handles. argL1 is exception class. |
613 if (!exType.isInstance(t)) throw t; |
614 * argL3 and argL4 are auxiliary method handles: argL3 boxes arguments and wraps them into Object[] |
614 return catcher.invokeExact(t, av); |
615 * (ValueConversions.array()) and argL4 unboxes result if necessary (ValueConversions.unbox()). |
615 } |
616 * |
616 } |
617 * Having t8 and t10 passed outside and not hardcoded into a lambda form allows to share lambda forms |
617 @LambdaForm.Hidden |
618 * among catchException combinators with the same basic type. |
618 private Object invoke_L0() throws Throwable { |
619 */ |
619 try { |
620 private static LambdaForm makeGuardWithCatchForm(MethodType basicType) { |
620 return target.invokeExact(); |
621 MethodType lambdaType = basicType.invokerType(); |
621 } catch (Throwable t) { |
622 |
622 if (!exType.isInstance(t)) throw t; |
623 LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_GWC); |
623 return catcher.invokeExact(t); |
624 if (lform != null) { |
624 } |
625 return lform; |
625 } |
626 } |
626 @LambdaForm.Hidden |
627 final int THIS_MH = 0; // the BMH_LLLLL |
627 private Object invoke_L1(Object a0) throws Throwable { |
628 final int ARG_BASE = 1; // start of incoming arguments |
628 try { |
629 final int ARG_LIMIT = ARG_BASE + basicType.parameterCount(); |
629 return target.invokeExact(a0); |
630 |
630 } catch (Throwable t) { |
631 int nameCursor = ARG_LIMIT; |
631 if (!exType.isInstance(t)) throw t; |
632 final int GET_TARGET = nameCursor++; |
632 return catcher.invokeExact(t, a0); |
633 final int GET_CLASS = nameCursor++; |
633 } |
634 final int GET_CATCHER = nameCursor++; |
634 } |
635 final int GET_COLLECT_ARGS = nameCursor++; |
635 @LambdaForm.Hidden |
636 final int GET_UNBOX_RESULT = nameCursor++; |
636 private Object invoke_L2(Object a0, Object a1) throws Throwable { |
637 final int BOXED_ARGS = nameCursor++; |
637 try { |
638 final int TRY_CATCH = nameCursor++; |
638 return target.invokeExact(a0, a1); |
639 final int UNBOX_RESULT = nameCursor++; |
639 } catch (Throwable t) { |
640 |
640 if (!exType.isInstance(t)) throw t; |
641 Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); |
641 return catcher.invokeExact(t, a0, a1); |
642 |
642 } |
643 BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); |
643 } |
644 names[GET_TARGET] = new Name(data.getterFunction(0), names[THIS_MH]); |
644 @LambdaForm.Hidden |
645 names[GET_CLASS] = new Name(data.getterFunction(1), names[THIS_MH]); |
645 private Object invoke_L3(Object a0, Object a1, Object a2) throws Throwable { |
646 names[GET_CATCHER] = new Name(data.getterFunction(2), names[THIS_MH]); |
646 try { |
647 names[GET_COLLECT_ARGS] = new Name(data.getterFunction(3), names[THIS_MH]); |
647 return target.invokeExact(a0, a1, a2); |
648 names[GET_UNBOX_RESULT] = new Name(data.getterFunction(4), names[THIS_MH]); |
648 } catch (Throwable t) { |
649 |
649 if (!exType.isInstance(t)) throw t; |
650 // FIXME: rework argument boxing/result unboxing logic for LF interpretation |
650 return catcher.invokeExact(t, a0, a1, a2); |
651 |
651 } |
652 // t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...); |
652 } |
653 MethodType collectArgsType = basicType.changeReturnType(Object.class); |
653 @LambdaForm.Hidden |
654 MethodHandle invokeBasic = MethodHandles.basicInvoker(collectArgsType); |
654 private Object invoke_L4(Object a0, Object a1, Object a2, Object a3) throws Throwable { |
655 Object[] args = new Object[invokeBasic.type().parameterCount()]; |
655 try { |
656 args[0] = names[GET_COLLECT_ARGS]; |
656 return target.invokeExact(a0, a1, a2, a3); |
657 System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT-ARG_BASE); |
657 } catch (Throwable t) { |
658 names[BOXED_ARGS] = new Name(new NamedFunction(invokeBasic), args); |
658 if (!exType.isInstance(t)) throw t; |
659 |
659 return catcher.invokeExact(t, a0, a1, a2, a3); |
660 // t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L); |
660 } |
661 Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]}; |
661 } |
662 names[TRY_CATCH] = new Name(Lazy.NF_guardWithCatch, gwcArgs); |
662 @LambdaForm.Hidden |
663 |
663 private Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) throws Throwable { |
664 // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L); |
664 try { |
665 MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class)); |
665 return target.invokeExact(a0, a1, a2, a3, a4); |
666 Object[] unboxArgs = new Object[] {names[GET_UNBOX_RESULT], names[TRY_CATCH]}; |
666 } catch (Throwable t) { |
667 names[UNBOX_RESULT] = new Name(new NamedFunction(invokeBasicUnbox), unboxArgs); |
667 if (!exType.isInstance(t)) throw t; |
668 |
668 return catcher.invokeExact(t, a0, a1, a2, a3, a4); |
669 lform = new LambdaForm("guardWithCatch", lambdaType.parameterCount(), names); |
669 } |
670 |
670 } |
671 basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWC, lform); |
671 @LambdaForm.Hidden |
672 return lform; |
672 private Object invoke_L6(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5) throws Throwable { |
673 } |
673 try { |
|
674 return target.invokeExact(a0, a1, a2, a3, a4, a5); |
|
675 } catch (Throwable t) { |
|
676 if (!exType.isInstance(t)) throw t; |
|
677 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5); |
|
678 } |
|
679 } |
|
680 @LambdaForm.Hidden |
|
681 private Object invoke_L7(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) throws Throwable { |
|
682 try { |
|
683 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6); |
|
684 } catch (Throwable t) { |
|
685 if (!exType.isInstance(t)) throw t; |
|
686 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6); |
|
687 } |
|
688 } |
|
689 @LambdaForm.Hidden |
|
690 private Object invoke_L8(Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) throws Throwable { |
|
691 try { |
|
692 return target.invokeExact(a0, a1, a2, a3, a4, a5, a6, a7); |
|
693 } catch (Throwable t) { |
|
694 if (!exType.isInstance(t)) throw t; |
|
695 return catcher.invokeExact(t, a0, a1, a2, a3, a4, a5, a6, a7); |
|
696 } |
|
697 } |
|
698 static MethodHandle[] makeInvokes() { |
|
699 ArrayList<MethodHandle> invokes = new ArrayList<>(); |
|
700 MethodHandles.Lookup lookup = IMPL_LOOKUP; |
|
701 for (;;) { |
|
702 int nargs = invokes.size(); |
|
703 String name = "invoke_L"+nargs; |
|
704 MethodHandle invoke = null; |
|
705 try { |
|
706 invoke = lookup.findVirtual(GuardWithCatch.class, name, MethodType.genericMethodType(nargs)); |
|
707 } catch (ReflectiveOperationException ex) { |
|
708 } |
|
709 if (invoke == null) break; |
|
710 invokes.add(invoke); |
|
711 } |
|
712 assert(invokes.size() == 9); // current number of methods |
|
713 return invokes.toArray(new MethodHandle[0]); |
|
714 }; |
|
715 static final MethodHandle[] INVOKES = makeInvokes(); |
|
716 // For testing use this: |
|
717 //static final MethodHandle[] INVOKES = Arrays.copyOf(makeInvokes(), 2); |
|
718 static final MethodHandle VARARGS_INVOKE; |
|
719 static { |
|
720 try { |
|
721 VARARGS_INVOKE = IMPL_LOOKUP.findVirtual(GuardWithCatch.class, "invoke_V", MethodType.genericMethodType(0, true)); |
|
722 } catch (ReflectiveOperationException ex) { |
|
723 throw uncaughtException(ex); |
|
724 } |
|
725 } |
|
726 } |
|
727 |
|
728 |
674 |
729 static |
675 static |
730 MethodHandle makeGuardWithCatch(MethodHandle target, |
676 MethodHandle makeGuardWithCatch(MethodHandle target, |
731 Class<? extends Throwable> exType, |
677 Class<? extends Throwable> exType, |
732 MethodHandle catcher) { |
678 MethodHandle catcher) { |
733 MethodType type = target.type(); |
679 MethodType type = target.type(); |
734 MethodType ctype = catcher.type(); |
680 LambdaForm form = makeGuardWithCatchForm(type.basicType()); |
735 int nargs = type.parameterCount(); |
681 |
736 if (nargs < GuardWithCatch.INVOKES.length) { |
682 // Prepare auxiliary method handles used during LambdaForm interpreation. |
737 MethodType gtype = type.generic(); |
683 // Box arguments and wrap them into Object[]: ValueConversions.array(). |
738 MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); |
684 MethodType varargsType = type.changeReturnType(Object[].class); |
739 // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0) |
685 MethodHandle collectArgs = ValueConversions.varargsArray(type.parameterCount()) |
740 MethodHandle gtarget = makePairwiseConvert(target, gtype, 2); |
686 .asType(varargsType); |
741 MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2); |
687 // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). |
742 GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher); |
688 MethodHandle unboxResult; |
743 if (gtarget == null || gcatcher == null) throw new InternalError(); |
689 if (type.returnType().isPrimitive()) { |
744 MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard); |
690 unboxResult = ValueConversions.unbox(type.returnType()); |
745 return makePairwiseConvert(ginvoker, type, 2); |
|
746 } else { |
691 } else { |
747 target = target.asType(type.changeReturnType(Object.class)); |
692 unboxResult = ValueConversions.identity(); |
748 MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs); |
693 } |
749 MethodType catcherType = ctype.changeParameterType(0, Throwable.class) |
694 |
750 .changeReturnType(Object.class); |
695 BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); |
751 catcher = catcher.asType(catcherType); |
696 BoundMethodHandle mh; |
752 MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs); |
697 try { |
753 GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher); |
698 mh = (BoundMethodHandle) |
754 if (gtarget == null || gcatcher == null) throw new InternalError(); |
699 data.constructor[0].invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher, |
755 MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard); |
700 (Object) collectArgs, (Object) unboxResult); |
756 MethodHandle gcollect = makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false); |
701 } catch (Throwable ex) { |
757 return makePairwiseConvert(gcollect, type, 2); |
702 throw uncaughtException(ex); |
758 } |
703 } |
|
704 assert(mh.type() == type); |
|
705 return mh; |
|
706 } |
|
707 |
|
708 /** |
|
709 * Intrinsified during LambdaForm compilation |
|
710 * (see {@link InvokerBytecodeGenerator#emitGuardWithCatch emitGuardWithCatch}). |
|
711 */ |
|
712 @LambdaForm.Hidden |
|
713 static Object guardWithCatch(MethodHandle target, Class exType, MethodHandle catcher, |
|
714 Object... av) throws Throwable { |
|
715 try { |
|
716 return target.invokeWithArguments(av); |
|
717 } catch (Throwable t) { |
|
718 if (!exType.isInstance(t)) throw t; |
|
719 Object[] args = prepend(t, av); |
|
720 return catcher.invokeWithArguments(args); |
|
721 } |
|
722 } |
|
723 |
|
724 /** Prepend an element {@code elem} to an {@code array}. */ |
|
725 private static Object[] prepend(Object elem, Object[] array) { |
|
726 Object[] newArray = new Object[array.length+1]; |
|
727 newArray[0] = elem; |
|
728 System.arraycopy(array, 0, newArray, 1, array.length); |
|
729 return newArray; |
759 } |
730 } |
760 |
731 |
761 static |
732 static |
762 MethodHandle throwException(MethodType type) { |
733 MethodHandle throwException(MethodType type) { |
763 assert(Throwable.class.isAssignableFrom(type.parameterType(0))); |
734 assert(Throwable.class.isAssignableFrom(type.parameterType(0))); |
764 int arity = type.parameterCount(); |
735 int arity = type.parameterCount(); |
765 if (arity > 1) { |
736 if (arity > 1) { |
766 return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1); |
737 return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1); |
767 } |
738 } |
768 return makePairwiseConvert(throwException(), type, 2); |
739 return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, 2); |
769 } |
740 } |
770 |
741 |
771 static MethodHandle THROW_EXCEPTION; |
|
772 static MethodHandle throwException() { |
|
773 MethodHandle mh = THROW_EXCEPTION; |
|
774 if (mh != null) return mh; |
|
775 try { |
|
776 mh |
|
777 = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "throwException", |
|
778 MethodType.methodType(Empty.class, Throwable.class)); |
|
779 } catch (ReflectiveOperationException ex) { |
|
780 throw new RuntimeException(ex); |
|
781 } |
|
782 THROW_EXCEPTION = mh; |
|
783 return mh; |
|
784 } |
|
785 static <T extends Throwable> Empty throwException(T t) throws T { throw t; } |
742 static <T extends Throwable> Empty throwException(T t) throws T { throw t; } |
786 |
743 |
787 static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2]; |
744 static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2]; |
788 static MethodHandle fakeMethodHandleInvoke(MemberName method) { |
745 static MethodHandle fakeMethodHandleInvoke(MemberName method) { |
789 int idx; |
746 int idx; |