src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File
*** old/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Feb 26 15:43:14 2014
--- new/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Feb 26 15:43:14 2014

*** 25,35 **** --- 25,34 ---- package java.lang.invoke; import sun.invoke.util.VerifyAccess; import java.lang.invoke.LambdaForm.Name; import java.lang.invoke.MethodHandles.Lookup; import sun.invoke.util.Wrapper; import java.io.*; import java.util.*;
*** 37,59 **** --- 36,55 ---- import jdk.internal.org.objectweb.asm.*; 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; /** * Code generation backend for LambdaForm. * <p> * @author John Rose, JSR 292 EG */ 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"; private static final String OBJ = "java/lang/Object"; private static final String OBJARY = "[Ljava/lang/Object;";
*** 509,529 **** --- 505,530 ---- // start iterating at the first name following the arguments for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { Name name = lambdaForm.names[i]; MemberName member = name.function.member(); ! if (isSelectAlternative(member)) { // selectAlternative idiom // FIXME: make sure this idiom is really present! ! if (isSelectAlternative(i)) { 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) if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) { // return value - do nothing
*** 672,687 **** --- 673,742 ---- } throw new InternalError("refKind="+refKind); } /** ! * Check if MemberName is a call to MethodHandleImpl.selectAlternative. ! * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}. */ ! private boolean isSelectAlternative(MemberName member) { ! private boolean memberRefersTo(MemberName member, Class<?> declaringClass, String name) { return member != null && ! member.getDeclaringClass() == MethodHandleImpl.class && ! member.getName().equals("selectAlternative"); ! member.getDeclaringClass() == declaringClass && ! member.getName().equals(name); + } + private boolean nameRefersTo(Name name, Class<?> declaringClass, String methodName) { + return name.function != null && + memberRefersTo(name.function.member(), declaringClass, methodName); + } + + /** + * Check if MemberName is a call to MethodHandle.invokeBasic. + */ + private boolean isInvokeBasic(Name name) { + if (name.function == null) + return false; + if (name.arguments.length < 1) + return false; // must have MH argument + MemberName member = name.function.member(); + return memberRefersTo(member, MethodHandle.class, "invokeBasic") && + !member.isPublic() && !member.isStatic(); + } + + /** + * Check if i-th name is a call to MethodHandleImpl.selectAlternative. + */ + private boolean isSelectAlternative(int pos) { + // selectAlternative idiom: + // t_{n}:L=MethodHandleImpl.selectAlternative(...) + // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...) + if (pos+1 >= lambdaForm.names.length) return false; + Name name0 = lambdaForm.names[pos]; + Name name1 = lambdaForm.names[pos+1]; + return nameRefersTo(name0, MethodHandleImpl.class, "selectAlternative") && + isInvokeBasic(name1) && + name1.lastUseIndex(name0) == 0 && // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...) + lambdaForm.lastUseIndex(name0) == pos+1; // t_{n} is local: used only in t_{n+1} + } + + /** + * Check if i-th name is a start of GuardWithCatch idiom. + */ + private boolean isGuardWithCatch(int pos) { + // GuardWithCatch idiom: + // t_{n}:L=MethodHandle.invokeBasic(...) + // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n}); + // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1}) + if (pos+2 >= lambdaForm.names.length) return false; + Name name0 = lambdaForm.names[pos]; + Name name1 = lambdaForm.names[pos+1]; + Name name2 = lambdaForm.names[pos+2]; + return nameRefersTo(name1, MethodHandleImpl.class, "guardWithCatch") && + isInvokeBasic(name0) && + isInvokeBasic(name2) && + name1.lastUseIndex(name0) == 3 && // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n}); + lambdaForm.lastUseIndex(name0) == pos+1 && // t_{n} is local: used only in t_{n+1} + name2.lastUseIndex(name1) == 1 && // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1}) + lambdaForm.lastUseIndex(name1) == pos+2; // t_{n+1} is local: used only in t_{n+2} } /** * Emit bytecode for the selectAlternative idiom. *
*** 692,703 **** --- 747,756 ---- * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int)); * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} * }</pre></blockquote> */ private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { MethodType type = selectAlternativeName.function.methodType(); Name receiver = (Name) invokeBasicName.arguments[0]; Label L_fallback = new Label(); Label L_done = new Label();
*** 707,717 **** --- 760,769 ---- // if_icmpne L_fallback mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback); // invoke selectAlternativeName.arguments[1] MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1]; emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); // goto L_done
*** 719,737 **** --- 771,867 ---- // L_fallback: mv.visitLabel(L_fallback); // invoke selectAlternativeName.arguments[2] MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2]; emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); // L_done: mv.visitLabel(L_done); } + /** + * Emit bytecode for the guardWithCatch idiom. + * + * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch): + * <blockquote><pre>{@code + * guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{ + * t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L); + * t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L); + * t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I} + * }</pre></blockquote> + * + * It is compiled into bytecode equivalent of the following code: + * <blockquote><pre>{@code + * try { + * return a1.invokeBasic(a6, a7); + * } catch (Throwable e) { + * if (!a2.isInstance(e)) throw e; + * return a3.invokeBasic(ex, a6, a7); + * }} + */ + 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(); + + Class<?> returnType = result.function.resolvedHandle.type().returnType(); + MethodType type = args.function.resolvedHandle.type() + .dropParameterTypes(0,1) + .changeReturnType(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, 1); // skip 1st argument: method handle + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false); + 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", false); + Label L_rethrow = new Label(); + mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow); + + // Invoke catcher + // load catcher + emitPushArgument(invoker, 2); + mv.visitInsn(Opcodes.SWAP); + emitPushArguments(args, 1); // skip 1st argument: method handle + MethodType catcherType = type.insertParameterTypes(0, Throwable.class); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString(), false); + mv.visitJumpInsn(Opcodes.GOTO, L_done); + + mv.visitLabel(L_rethrow); + mv.visitInsn(Opcodes.ATHROW); + + mv.visitLabel(L_done); + } + + private void emitPushArguments(Name args, int start) { + for (int i = start; 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); MethodType mtype = name.function.methodType(); if (arg instanceof Name) {

src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File