nashorn/src/jdk/internal/dynalink/support/CatchExceptionCombinator.java
changeset 24719 f726e9d67629
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/internal/dynalink/support/CatchExceptionCombinator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,180 @@
+package jdk.internal.dynalink.support;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
+import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
+import jdk.nashorn.internal.runtime.RewriteException;
+import sun.misc.Unsafe;
+
+/**
+ * Generates method handles that combine an invocation and a handler for a {@link RewriteException}. Always immediately
+ * generates compiled bytecode.
+ */
+public class CatchExceptionCombinator {
+    static {
+        System.err.println("*** Running with fast catch combinator handler ***");
+    }
+    private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
+    private static final String METHOD_HANDLE_TYPE_NAME = METHOD_HANDLE_TYPE.getInternalName();
+    private static final String OBJECT_TYPE_NAME = Type.getInternalName(Object.class);
+
+    private static final String HANDLER_TYPE_NAME = "java.lang.invoke.CatchExceptionCombinator$MH";
+    private static final String INVOKE_METHOD_NAME = "invoke";
+
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+    private static final ConcurrentMap<CombinatorParameters, ClassTemplate> handlerClassBytes = new ConcurrentHashMap<>();
+
+    private static final class CombinatorParameters {
+        final MethodType targetType;
+        final Class<? extends Throwable> exType;
+        final MethodType handlerType;
+
+        CombinatorParameters(final MethodType targetType, final Class<? extends Throwable> exType, MethodType handlerType) {
+            this.targetType = targetType;
+            this.exType = exType;
+            this.handlerType = handlerType;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if(obj instanceof CombinatorParameters) {
+                final CombinatorParameters p = (CombinatorParameters)obj;
+                return targetType.equals(p.targetType) && exType.equals(p.exType) && handlerType.equals(p.handlerType);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return targetType.hashCode() ^ exType.hashCode() ^ handlerType.hashCode();
+        }
+    }
+
+    /**
+     * Catch exception - create combinator
+     * @param target  target
+     * @param exType  type to check for
+     * @param handler catch handler
+     * @return target wrapped in catch handler
+     */
+    public static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
+        final MethodType targetType = target.type();
+        final MethodType handlerType = handler.type();
+
+        final ClassTemplate classTemplate = handlerClassBytes.computeIfAbsent(
+                new CombinatorParameters(targetType, exType, handlerType), new Function<CombinatorParameters, ClassTemplate>() {
+            @Override
+            public ClassTemplate apply(final CombinatorParameters parameters) {
+                return generateClassTemplate(parameters);
+            }
+        });
+        return classTemplate.instantiate(target, handler, targetType);
+    }
+
+    private static final class ClassTemplate {
+        final byte[] bytes;
+        final int target_cp_index;
+        final int handler_cp_index;
+        final int cp_size;
+
+        ClassTemplate(final byte[] bytes, final int target_cp_index, final int handler_cp_index, final int cp_size) {
+            this.bytes = bytes;
+            this.target_cp_index = target_cp_index;
+            this.handler_cp_index = handler_cp_index;
+            this.cp_size = cp_size;
+        }
+
+        MethodHandle instantiate(final MethodHandle target, final MethodHandle handler, final MethodType type) {
+            final Object[] cpPatch = new Object[cp_size];
+            cpPatch[target_cp_index] = target;
+            cpPatch[handler_cp_index] = handler;
+            final Class<?> handlerClass = UNSAFE.defineAnonymousClass(CatchExceptionCombinator.class, bytes, cpPatch);
+            try {
+                return MethodHandles.lookup().findStatic(handlerClass, INVOKE_METHOD_NAME, type);
+            } catch (NoSuchMethodException | IllegalAccessException e) {
+                throw new AssertionError(e);
+            }
+        }
+    }
+
+    private static ClassTemplate generateClassTemplate(final CombinatorParameters combinatorParameters) {
+        final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+        w.visit(V1_7, ACC_PUBLIC | ACC_SUPER, HANDLER_TYPE_NAME, null, OBJECT_TYPE_NAME, null);
+
+        final MethodType targetType = combinatorParameters.targetType;
+        final Class<? extends Throwable> exType = combinatorParameters.exType;
+        final String methodDescriptor = targetType.toMethodDescriptorString();
+        final Class<?> returnType = targetType.returnType();
+        final MethodType handlerType = combinatorParameters.handlerType;
+
+        // NOTE: must use strings as placeholders in the constant pool, even if we'll be replacing them with method handles.
+        final String targetPlaceholder = "T_PLACEHOLDER";
+        final String handlerPlaceholder = "H_PLACEHOLDER";
+        final int target_cp_index = w.newConst(targetPlaceholder);
+        final int handler_cp_index = w.newConst(handlerPlaceholder);
+
+        final InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC | ACC_STATIC, INVOKE_METHOD_NAME, methodDescriptor, null, null));
+        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
+        mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
+
+        mv.visitCode();
+
+        final Label _try = new Label();
+        final Label _end_try= new Label();
+
+        mv.visitLabel(_try);
+        // Invoke
+        mv.aconst(targetPlaceholder);
+        mv.checkcast(METHOD_HANDLE_TYPE);
+        final Class<?>[] paramTypes = targetType.parameterArray();
+        for(int i = 0, slot = 0; i < paramTypes.length; ++i) {
+            final Type paramType = Type.getType(paramTypes[i]);
+            mv.load(slot, paramType);
+            slot += paramType.getSize();
+        }
+        generateInvokeBasic(mv, methodDescriptor);
+        final Type asmReturnType = Type.getType(returnType);
+        mv.areturn(asmReturnType);
+
+        mv.visitTryCatchBlock(_try, _end_try, _end_try, Type.getInternalName(exType));
+        mv.visitLabel(_end_try);
+        // Handle exception
+        mv.aconst(handlerPlaceholder);
+        mv.checkcast(METHOD_HANDLE_TYPE);
+        mv.swap();
+        final Class<?>[] handlerParamTypes = handlerType.parameterArray();
+        for(int i = 1, slot = 0; i < handlerParamTypes.length; ++i) {
+            final Type paramType = Type.getType(handlerParamTypes[i]);
+            mv.load(slot, paramType);
+            slot += paramType.getSize();
+        }
+        generateInvokeBasic(mv, handlerType.toMethodDescriptorString());
+        mv.areturn(asmReturnType);
+
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        w.visitEnd();
+        final byte[] bytes = w.toByteArray();
+        final int cp_size = (((bytes[8] & 0xFF) << 8) | (bytes[9] & 0xFF));
+        return new ClassTemplate(bytes, target_cp_index, handler_cp_index, cp_size);
+    }
+
+    private static void generateInvokeBasic(final InstructionAdapter mv, final String methodDesc) {
+        mv.invokevirtual(METHOD_HANDLE_TYPE_NAME, "invokeBasic", methodDesc, false);
+    }
+}