# HG changeset patch # Parent d6195774dd1fd41e8c73aa9590547cc4165fdebd diff --git a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java --- a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -25,6 +25,7 @@ package java.lang.invoke; +import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; import sun.invoke.util.VerifyAccess; import java.lang.invoke.LambdaForm.Name; import java.lang.invoke.MethodHandles.Lookup; @@ -77,6 +78,7 @@ private final int[] localsMap; /** ASM bytecode generation. */ +// private TraceClassVisitor cw; private ClassWriter cw; private MethodVisitor mv; @@ -275,6 +277,8 @@ */ private void classFilePrologue() { cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); +// cw = new TraceClassVisitor(new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES), +// new PrintWriter(System.out)); cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null); cw.visitSource(sourceFile, null); @@ -494,6 +498,7 @@ * Generate an invoker method for the passed {@link LambdaForm}. */ private byte[] generateCustomizedCodeBytes() { + try { classFilePrologue(); // Suppress this method in backtraces displayed to the user. @@ -516,6 +521,9 @@ // FIXME: make sure this idiom is really present! emitSelectAlternative(name, lambdaForm.names[i + 1]); i++; // skip MH.invokeBasic of the selectAlternative result + } else if (i+1 < lambdaForm.names.length && isGuardWithCatch(lambdaForm.names[i+1].function.member())) { + emitGuardWithCatch(name, lambdaForm.names[i+1]); + i++; } else if (isStaticallyInvocable(member)) { emitStaticInvoke(member, name); } else { @@ -538,7 +546,14 @@ classFileEpilogue(); bogusMethod(lambdaForm); - +// } catch (Throwable e) { +// System.out.println(lambdaForm); +// System.out.println(e); +// e.printStackTrace(); +// throw e; + } finally { + cw.visitEnd(); + } final byte[] classFile = cw.toByteArray(); maybeDump(className, classFile); return classFile; @@ -677,6 +692,15 @@ } /** + * Check if MemberName is a call to MethodHandleImpl.selectAlternative. + */ + private boolean isGuardWithCatch(MemberName member) { + return member != null && + member.getDeclaringClass() == MethodHandleImpl.class && + member.getName().equals("invokeWithCatch"); + } + + /** * Emit bytecode for the selectAlternative idiom. * * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): @@ -724,6 +748,90 @@ mv.visitLabel(L_done); } + /** + * Emit bytecode for the guardWithCatch idiom. + * + * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch): + *
{@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.invokeWithCatch(a1:L,a2:L,a3:L,t6:L);
+      *   t8:I=ValueConversions.unbox(t7:L);t8:I}
+      * }
+ */ + private void emitGuardWithCatch(Name parameters, Name invokeWithCatch) { + Label L_startBlock = new Label(); + Label L_endBlock = new Label(); + Label L_handler = new Label(); + Label L_done = new Label(); + + MethodType type = parameters.function.resolvedHandle.type() + .changeReturnType(invokeWithCatch.function.resolvedHandle.type().returnType()); + + mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable"); + + // Invoke target + mv.visitLabel(L_startBlock); + + // push receiver + emitPushArgument(invokeWithCatch, 0); // get 1rd argument of invokeWithCatch + + // invocation + // push arguments + for (int i = 0; i < parameters.arguments.length; i++) { + emitPushArgument(parameters, i); + } + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString()); + + mv.visitLabel(L_endBlock); + mv.visitJumpInsn(Opcodes.GOTO, L_done); + + // Dispatch exception + mv.visitLabel(L_handler); + + mv.visitInsn(Opcodes.DUP); + + emitPushArgument(invokeWithCatch, 1); // load exception class + + 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 + emitPushArgument(invokeWithCatch, 2); // get 3rd argument of invokeWithCatch + //mv.visitTypeInsn(Opcodes.CHECKCAST, MH); + + mv.visitInsn(Opcodes.SWAP); + // push arguments + for (int i = 0; i < parameters.arguments.length; i++) { + emitPushArgument(parameters, i); + } + + // invocation + 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 static Class basicTypeToClass(char t) { + switch(t) { + case 'I': return int.class; + case 'J': return long.class; + case 'F': return float.class; + case 'D': return double.class; + case 'L': return Object.class; + default: throw new InternalError("Unknown basic type: "+t); + + } + } + private void emitPushArgument(Name name, int paramIndex) { Object arg = name.arguments[paramIndex]; char ptype = name.function.parameterType(paramIndex); diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java --- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -567,6 +567,53 @@ } static + Object invokeWithCatch(MethodHandle target, Class exType, MethodHandle catcher, Object[] args) throws Throwable { +// System.out.println("target: "+target.type()); +// System.out.println("exType: "+exType); +// System.out.println("catcher: "+catcher.type()); +// System.out.println("args: "+args); +// System.out.println("args[]: "+Arrays.deepToString(args)); +// System.out.println("nargs: "+target.type().parameterCount()); + + MethodType type = target.type(); + MethodType ctype = catcher.type(); + int nargs = type.parameterCount(); + + target = target.asType(type.changeReturnType(Object.class)); + MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs); + MethodType catcherType = ctype.changeParameterType(0, Throwable.class) + .changeReturnType(Object.class); + catcher = catcher.asType(catcherType); + MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs); + + Object result; + try { + result = gtarget.invokeExact(args); + } catch (Throwable ex) { + if (exType.isInstance(ex)) { + result = gcatcher.invokeExact(ex, args); + } else { + throw ex; + } + } + return result; + } + + static MethodHandle INVOKE_WITH_CATCH; + static MethodHandle invokeWithCatch() { + if (INVOKE_WITH_CATCH != null) return INVOKE_WITH_CATCH; + try { + INVOKE_WITH_CATCH + = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "invokeWithCatch", + MethodType.methodType(Object.class, MethodHandle.class, Class.class, MethodHandle.class, Object[].class)); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + return INVOKE_WITH_CATCH; + } + + + static MethodHandle makeGuardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback) { @@ -725,37 +772,67 @@ } } + static final int MAX_ARITY = 255; + static MethodHandle[] gwcInvokers = new MethodHandle[MAX_ARITY]; + + static MethodHandle getGWCInvoker(int arity) { + if (gwcInvokers[arity] == null) { + gwcInvokers[arity] = makeGWCInvoker(arity); + } + assert gwcInvokers[arity] != null; + return gwcInvokers[arity]; + } + + static private MethodHandle makeGWCInvoker(int arity) { + int extraParams = 4; + int totalParams = extraParams + arity; + int extraNames = 2; + + Name[] names = new Name[totalParams+extraNames]; + names[0] = argument(0, 'L'); // MethodHandle + names[1] = argument(1, 'L'); // target: MethodHandle + names[2] = argument(2, 'L'); // exType: Class + names[3] = argument(3, 'L'); // catcher: MethodHandle + + MethodType type = MethodType.methodType(Object.class); + for (int i = 0; i < arity; i++) { + names[extraParams + i] = argument(extraParams + i, 'L'); + type = type.appendParameterTypes(Object.class); + } + MethodType lambdaType = type.insertParameterTypes(0, MethodHandle.class, Class.class, MethodHandle.class); + + // t6:L=ValueConversions.array(a4:L,a5:L) + Object[] args = Arrays.copyOfRange(names, extraParams, totalParams, Object[].class); + MethodHandle varargsArray = ValueConversions.varargsArray(args.length); + names[totalParams] = new Name(varargsArray, args); + + // t7:L=MethodHandleImpl.invokeWithCatch(a1:L,a2:L,a3:L,t6:L); + Object[] gwcArgs = new Object[] { names[1], names[2], names[3], names[totalParams]}; + names[totalParams + 1] = new Name(MethodHandleImpl.invokeWithCatch(), gwcArgs); + + LambdaForm form = new LambdaForm("guardWithCatch", totalParams, names); + return SimpleMethodHandle.make(lambdaType, form); + } static MethodHandle makeGuardWithCatch(MethodHandle target, Class exType, MethodHandle catcher) { MethodType type = target.type(); - MethodType ctype = catcher.type(); - int nargs = type.parameterCount(); - if (nargs < GuardWithCatch.INVOKES.length) { - MethodType gtype = type.generic(); - MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); - // Note: convertArguments(...2) avoids interface casts present in convertArguments(...0) - MethodHandle gtarget = makePairwiseConvert(target, gtype, 2); - MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2); - GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher); - if (gtarget == null || gcatcher == null) throw new InternalError(); - MethodHandle ginvoker = GuardWithCatch.INVOKES[nargs].bindReceiver(gguard); - return makePairwiseConvert(ginvoker, type, 2); - } else { - target = target.asType(type.changeReturnType(Object.class)); - MethodHandle gtarget = makeSpreadArguments(target, Object[].class, 0, nargs); - MethodType catcherType = ctype.changeParameterType(0, Throwable.class) - .changeReturnType(Object.class); - catcher = catcher.asType(catcherType); - MethodHandle gcatcher = makeSpreadArguments(catcher, Object[].class, 1, nargs); - GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher); - if (gtarget == null || gcatcher == null) throw new InternalError(); - MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard); - MethodHandle gcollect = makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false); - return makePairwiseConvert(gcollect, type, 2); - } + MethodType gtype = type.generic(); + MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class); + MethodHandle gtarget = makePairwiseConvert(target, gtype, 2); + MethodHandle gcatcher = makePairwiseConvert(catcher, gcatchType, 2); + + if (gtarget == null || gcatcher == null) throw new InternalError(); + + MethodHandle ginvoker = getGWCInvoker(target.type().parameterCount()); + + ginvoker = ginvoker.bindTo(gtarget) + .bindTo(exType) + .bindTo(gcatcher); + + return makePairwiseConvert(ginvoker, type, 2); } static diff --git a/src/share/classes/jdk/internal/org/objectweb/asm/util/TraceClassVisitor.java b/src/share/classes/jdk/internal/org/objectweb/asm/util/TraceClassVisitor.java --- a/src/share/classes/jdk/internal/org/objectweb/asm/util/TraceClassVisitor.java +++ b/src/share/classes/jdk/internal/org/objectweb/asm/util/TraceClassVisitor.java @@ -63,6 +63,7 @@ import jdk.internal.org.objectweb.asm.AnnotationVisitor; import jdk.internal.org.objectweb.asm.Attribute; import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.FieldVisitor; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.Opcodes; @@ -246,4 +247,12 @@ } super.visitEnd(); } + + public int newConst(final Object cst) { + return ((ClassWriter)cv).newConst(cst); + } + + public byte[] toByteArray() { + return ((ClassWriter)cv).toByteArray(); + } }