--- old/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2014-02-20 01:37:14.000000000 +0400 +++ new/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2014-02-20 01:37:14.000000000 +0400 @@ -27,7 +27,6 @@ import sun.invoke.util.VerifyAccess; import java.lang.invoke.LambdaForm.Name; -import java.lang.invoke.MethodHandles.Lookup; import sun.invoke.util.Wrapper; @@ -39,8 +38,6 @@ import java.lang.reflect.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; -import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; -import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; /** @@ -51,7 +48,6 @@ class InvokerBytecodeGenerator { /** Define class names for convenience. */ private static final String MH = "java/lang/invoke/MethodHandle"; - private static final String BMH = "java/lang/invoke/BoundMethodHandle"; private static final String LF = "java/lang/invoke/LambdaForm"; private static final String LFN = "java/lang/invoke/LambdaForm$Name"; private static final String CLS = "java/lang/Class"; @@ -516,12 +512,19 @@ // FIXME: make sure this idiom is really present! emitSelectAlternative(name, lambdaForm.names[i + 1]); i++; // skip MH.invokeBasic of the selectAlternative result + } else if (isGuardWithCatch(i)) { + emitGuardWithCatch(i); + i = i+2; // Jump to the end of GWC idiom } else if (isStaticallyInvocable(member)) { emitStaticInvoke(member, name); } else { emitInvoke(name); } + // Update cached form name's info in case an intrinsic spanning multiple names was encountered. + name = lambdaForm.names[i]; + member = name.function.member(); + // store the result from evaluating to the target name in a local if required // (if this is the last value, i.e., the one that is going to be returned, // avoid store/load/return and just return) @@ -674,12 +677,34 @@ } /** + * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}. + */ + private boolean memberNameRefersTo(MemberName member, Class> declaringClass, String name) { + return member != null && + member.getDeclaringClass() == declaringClass && + member.getName().equals(name); + } + + /** * Check if MemberName is a call to MethodHandleImpl.selectAlternative. */ private boolean isSelectAlternative(MemberName member) { - return member != null && - member.getDeclaringClass() == MethodHandleImpl.class && - member.getName().equals("selectAlternative"); + return memberNameRefersTo(member, MethodHandleImpl.class, "selectAlternative"); + } + + /** + * Check if i-th name is a start of GuardWithCatch idiom. + */ + private boolean isGuardWithCatch(int pos) { + // GuardWithCatch idiom: + // t_{n}:L=ValueConversions.array(a1:L,...) + // t_{n+1}:L=MethodHandleImpl.guardWithCatch(t_{n}:L); + // t_{n+2}:I=ValueConversions.unbox(t_{n+1}) + // OR :L=ValueConversions.identity(t_{n+1}) + // OR :V=ValueConversions.ignore(t_{n+1}) + return lambdaForm.names.length-1 >= pos+2 && + memberNameRefersTo(lambdaForm.names[pos+1].function.member(), + MethodHandleImpl.class, "guardWithCatch"); } /** @@ -730,6 +755,89 @@ mv.visitLabel(L_done); } + /** + * Emit bytecode for the guardWithCatch idiom. + * + * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch): + *
+ * + * For non-primitive and void return types, t8 is ValueConversions.identity and + * ValueConversions.ignore respectively. + * + * It is compiled into bytecode equivalent of the following code: + *{@code + * guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L)=>{ + * t6:L=ValueConversions.array(a4:L,a5:L); + * t7:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t6:L); + * t8:I=ValueConversions.unbox(t7:L);t8:I} + * }
{@code + * try { + * result = target.invokeBasic(...); + * } catch (Throwable e) { + * if (exceptionClass.isInstance(e)) { + * result = catcher.invokeBasic(ex, ...); + * } else { + * throw ex; + * } + * }} + */ + private void emitGuardWithCatch(int pos) { + Name args = lambdaForm.names[pos]; + Name invoker = lambdaForm.names[pos+1]; + Name result = lambdaForm.names[pos+2]; + + Label L_startBlock = new Label(); + Label L_endBlock = new Label(); + Label L_handler = new Label(); + Label L_done = new Label(); + + MethodType type = args.function.resolvedHandle.type() + .changeReturnType(result.function.resolvedHandle.type().returnType()); + + mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable"); + + // Normal case + mv.visitLabel(L_startBlock); + // load target + emitPushArgument(invoker, 0); + emitPushArguments(args); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString()); + mv.visitLabel(L_endBlock); + mv.visitJumpInsn(Opcodes.GOTO, L_done); + + // Exceptional case + mv.visitLabel(L_handler); + + // Check exception's type + mv.visitInsn(Opcodes.DUP); + // load exception class + emitPushArgument(invoker, 1); + mv.visitInsn(Opcodes.SWAP); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z"); + Label L_rethrow = new Label(); + mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow); + + // Invoke catcher + // load catcher + emitPushArgument(invoker, 2); + mv.visitInsn(Opcodes.SWAP); + emitPushArguments(args); + MethodType catcherType = type.insertParameterTypes(0, Throwable.class); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString()); + mv.visitJumpInsn(Opcodes.GOTO, L_done); + + mv.visitLabel(L_rethrow); + mv.visitInsn(Opcodes.ATHROW); + + mv.visitLabel(L_done); + } + + private void emitPushArguments(Name args) { + for (int i = 0; i < args.arguments.length; i++) { + emitPushArgument(args, i); + } + } + private void emitPushArgument(Name name, int paramIndex) { Object arg = name.arguments[paramIndex]; char ptype = name.function.parameterType(paramIndex);