hotspot/test/compiler/calls/common/InvokeDynamicPatcher.java
changeset 35128 bb8baf284c67
child 40059 c2304140ed64
equal deleted inserted replaced
35127:483603d4c7b2 35128:bb8baf284c67
       
     1 /*
       
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 package compiler.calls.common;
       
    25 
       
    26 import java.io.FileInputStream;
       
    27 import java.io.IOException;
       
    28 import java.lang.invoke.CallSite;
       
    29 import java.lang.invoke.MethodHandles;
       
    30 import java.lang.invoke.MethodType;
       
    31 import java.net.URISyntaxException;
       
    32 import java.nio.file.Files;
       
    33 import java.nio.file.Path;
       
    34 import java.nio.file.Paths;
       
    35 import java.nio.file.StandardOpenOption;
       
    36 import jdk.internal.org.objectweb.asm.ClassReader;
       
    37 import jdk.internal.org.objectweb.asm.ClassVisitor;
       
    38 import jdk.internal.org.objectweb.asm.ClassWriter;
       
    39 import jdk.internal.org.objectweb.asm.Handle;
       
    40 import jdk.internal.org.objectweb.asm.Label;
       
    41 import jdk.internal.org.objectweb.asm.MethodVisitor;
       
    42 import jdk.internal.org.objectweb.asm.Opcodes;
       
    43 
       
    44 /**
       
    45  * A class which patch InvokeDynamic class bytecode with invokydynamic
       
    46  instruction, rewriting "caller" method to call "callee" method using
       
    47  invokedynamic
       
    48  */
       
    49 public class InvokeDynamicPatcher extends ClassVisitor {
       
    50 
       
    51     private static final String CLASS = InvokeDynamic.class.getName()
       
    52             .replace('.', '/');
       
    53     private static final String CALLER_METHOD_NAME = "caller";
       
    54     private static final String CALLEE_METHOD_NAME = "callee";
       
    55     private static final String NATIVE_CALLEE_METHOD_NAME = "calleeNative";
       
    56     private static final String BOOTSTRAP_METHOD_NAME = "bootstrapMethod";
       
    57     private static final String CALL_NATIVE_FIELD = "nativeCallee";
       
    58     private static final String CALL_NATIVE_FIELD_DESC = "Z";
       
    59     private static final String CALLEE_METHOD_DESC
       
    60             = "(L" + CLASS + ";IJFDLjava/lang/String;)Z";
       
    61     private static final String ASSERTTRUE_METHOD_DESC
       
    62             = "(ZLjava/lang/String;)V";
       
    63     private static final String ASSERTS_CLASS = "jdk/test/lib/Asserts";
       
    64     private static final String ASSERTTRUE_METHOD_NAME = "assertTrue";
       
    65 
       
    66     public static void main(String args[]) {
       
    67         ClassReader cr;
       
    68         Path filePath;
       
    69         try {
       
    70             filePath = Paths.get(InvokeDynamic.class.getProtectionDomain().getCodeSource()
       
    71                     .getLocation().toURI()).resolve(CLASS + ".class");
       
    72         } catch (URISyntaxException ex) {
       
    73             throw new Error("TESTBUG: Can't get code source" + ex, ex);
       
    74         }
       
    75         try (FileInputStream fis = new FileInputStream(filePath.toFile())) {
       
    76             cr = new ClassReader(fis);
       
    77         } catch (IOException e) {
       
    78             throw new Error("Error reading file", e);
       
    79         }
       
    80         ClassWriter cw = new ClassWriter(cr,
       
    81                 ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
       
    82         cr.accept(new InvokeDynamicPatcher(Opcodes.ASM5, cw), 0);
       
    83         try {
       
    84             Files.write(filePath, cw.toByteArray(),
       
    85                     StandardOpenOption.WRITE);
       
    86         } catch (IOException e) {
       
    87             throw new Error(e);
       
    88         }
       
    89     }
       
    90 
       
    91     public InvokeDynamicPatcher(int api, ClassWriter cw) {
       
    92         super(api, cw);
       
    93     }
       
    94 
       
    95     @Override
       
    96     public MethodVisitor visitMethod(final int access, final String name,
       
    97             final String desc, final String signature,
       
    98             final String[] exceptions) {
       
    99         /* a code generate looks like
       
   100          *  0: aload_0
       
   101          *  1: ldc           #125  // int 1
       
   102          *  3: ldc2_w        #126  // long 2l
       
   103          *  6: ldc           #128  // float 3.0f
       
   104          *  8: ldc2_w        #129  // double 4.0d
       
   105          * 11: ldc           #132  // String 5
       
   106          * 13: aload_0
       
   107          * 14: getfield      #135  // Field nativeCallee:Z
       
   108          * 17: ifeq          28
       
   109          * 20: invokedynamic #181,  0            // InvokeDynamic #1:calleeNative:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z
       
   110          * 25: goto          33
       
   111          * 28: invokedynamic #183,  0            // InvokeDynamic #1:callee:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z
       
   112          * 33: ldc           #185                // String Call insuccessfull
       
   113          * 35: invokestatic  #191                // Method jdk/test/lib/Asserts.assertTrue:(ZLjava/lang/String;)V
       
   114          * 38: return
       
   115          *
       
   116          * or, using java-like pseudo-code
       
   117          * if (this.nativeCallee == false) {
       
   118          *     invokedynamic-call-return-value = invokedynamic-of-callee
       
   119          * } else {
       
   120          *     invokedynamic-call-return-value = invokedynamic-of-nativeCallee
       
   121          * }
       
   122          * Asserts.assertTrue(invokedynamic-call-return-value, error-message);
       
   123          * return;
       
   124          */
       
   125         if (name.equals(CALLER_METHOD_NAME)) {
       
   126             MethodVisitor mv = cv.visitMethod(access, name, desc,
       
   127                     signature, exceptions);
       
   128             Label nonNativeLabel = new Label();
       
   129             Label checkLabel = new Label();
       
   130             MethodType mtype = MethodType.methodType(CallSite.class,
       
   131                     MethodHandles.Lookup.class, String.class, MethodType.class);
       
   132             Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, CLASS,
       
   133                     BOOTSTRAP_METHOD_NAME, mtype.toMethodDescriptorString());
       
   134             mv.visitCode();
       
   135             // push callee parameters onto stack
       
   136             mv.visitVarInsn(Opcodes.ALOAD, 0);//push "this"
       
   137             mv.visitLdcInsn(1);
       
   138             mv.visitLdcInsn(2L);
       
   139             mv.visitLdcInsn(3.0f);
       
   140             mv.visitLdcInsn(4.0d);
       
   141             mv.visitLdcInsn("5");
       
   142             // params loaded. let's decide what method to call
       
   143             mv.visitVarInsn(Opcodes.ALOAD, 0); // push "this"
       
   144             // get nativeCallee field
       
   145             mv.visitFieldInsn(Opcodes.GETFIELD, CLASS, CALL_NATIVE_FIELD,
       
   146                     CALL_NATIVE_FIELD_DESC);
       
   147             // if nativeCallee == false goto nonNativeLabel
       
   148             mv.visitJumpInsn(Opcodes.IFEQ, nonNativeLabel);
       
   149             // invokedynamic nativeCalleeMethod using bootstrap method
       
   150             mv.visitInvokeDynamicInsn(NATIVE_CALLEE_METHOD_NAME,
       
   151                     CALLEE_METHOD_DESC, bootstrap);
       
   152             // goto checkLabel
       
   153             mv.visitJumpInsn(Opcodes.GOTO, checkLabel);
       
   154             // label: nonNativeLabel
       
   155             mv.visitLabel(nonNativeLabel);
       
   156             // invokedynamic calleeMethod using bootstrap method
       
   157             mv.visitInvokeDynamicInsn(CALLEE_METHOD_NAME, CALLEE_METHOD_DESC,
       
   158                     bootstrap);
       
   159             mv.visitLabel(checkLabel);
       
   160             mv.visitLdcInsn(CallsBase.CALL_ERR_MSG);
       
   161             mv.visitMethodInsn(Opcodes.INVOKESTATIC, ASSERTS_CLASS,
       
   162                     ASSERTTRUE_METHOD_NAME, ASSERTTRUE_METHOD_DESC, false);
       
   163             // label: return
       
   164             mv.visitInsn(Opcodes.RETURN);
       
   165             mv.visitMaxs(0, 0);
       
   166             mv.visitEnd();
       
   167             return null;
       
   168         }
       
   169         return super.visitMethod(access, name, desc, signature, exceptions);
       
   170     }
       
   171 }