test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/shared/Util.java
changeset 50243 4fac3c99487d
equal deleted inserted replaced
50242:9a87afc49148 50243:4fac3c99487d
       
     1 /*
       
     2  * Copyright (c) 2013, 2018, 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 vm.runtime.defmeth.shared;
       
    25 
       
    26 import vm.runtime.defmeth.shared.data.Clazz;
       
    27 import java.io.PrintWriter;
       
    28 import java.lang.instrument.ClassFileTransformer;
       
    29 import java.lang.instrument.IllegalClassFormatException;
       
    30 import java.lang.instrument.Instrumentation;
       
    31 import java.lang.instrument.UnmodifiableClassException;
       
    32 import java.security.ProtectionDomain;
       
    33 import java.util.ArrayList;
       
    34 import java.util.Arrays;
       
    35 import java.util.List;
       
    36 import java.util.regex.Matcher;
       
    37 import java.util.regex.Pattern;
       
    38 import nsk.share.Pair;
       
    39 import nsk.share.TestFailure;
       
    40 import vm.runtime.defmeth.shared.data.method.param.*;
       
    41 
       
    42 
       
    43 /**
       
    44  * Utility class with auxiliary miscellaneous methods.
       
    45  */
       
    46 public class Util {
       
    47     public static class Transformer {
       
    48         private static Instrumentation inst;
       
    49 
       
    50         public static void premain(String agentArgs, Instrumentation inst) {
       
    51             Transformer.inst = inst;
       
    52 
       
    53             /*
       
    54             inst.addTransformer(new ClassFileTransformer() {
       
    55                 @Override
       
    56                 public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
       
    57                     System.out.println("Retransform (initial): " + className);
       
    58                     return classfileBuffer;
       
    59                 }
       
    60             });
       
    61             */
       
    62         }
       
    63     }
       
    64 
       
    65     /**
       
    66      * Concatenate {@code strings} array interleaving with {@code sep} in between.
       
    67      *
       
    68      * @param sep
       
    69      * @param strings
       
    70      * @return
       
    71      */
       
    72     public static String intersperse(String sep, String... strings) {
       
    73         StringBuilder sb = new StringBuilder();
       
    74         if (strings.length == 0) {
       
    75             return "";
       
    76         } else if (strings.length == 1) {
       
    77             return strings[0];
       
    78         } else {
       
    79             sb.append(strings[0]);
       
    80 
       
    81             for (int i=1; i<strings.length; i++) {
       
    82                 sb.append(sep).append(strings[i]);
       
    83             }
       
    84 
       
    85             return sb.toString();
       
    86         }
       
    87     }
       
    88 
       
    89     /**
       
    90      * Construct array of names for an array of {@code Clazz} instances.
       
    91      *
       
    92      * @param clazzes
       
    93      * @return
       
    94      */
       
    95     public static String[] asStrings(Clazz[] clazzes) {
       
    96         String[] result = new String[clazzes.length];
       
    97         for (int i = 0; i < clazzes.length; i++) {
       
    98             result[i] = clazzes[i].intlName();
       
    99         }
       
   100 
       
   101         return result;
       
   102     }
       
   103 
       
   104     /**
       
   105      * Get the name of the test currently being executed
       
   106      *
       
   107      * @return name of the test being executed
       
   108      */
       
   109     public static String getTestName() {
       
   110         // Hack: examine stack trace and extract test method's name from there
       
   111         try {
       
   112             throw new Exception();
       
   113         } catch (Exception e) {
       
   114             for (StackTraceElement elem : e.getStackTrace()) {
       
   115                 String className = elem.getClassName();
       
   116                 String methodName = elem.getMethodName();
       
   117 
       
   118                 if (className.startsWith("vm.runtime.defmeth.") &&
       
   119                         methodName.startsWith("test")) {
       
   120                     return String.format("%s.%s",
       
   121                             className.replaceAll(".*\\.", ""), methodName);
       
   122                 }
       
   123             }
       
   124 
       
   125             return "Unknown";
       
   126         }
       
   127     }
       
   128 
       
   129     /**
       
   130      * Pretty-print {@code byte[] classFile} to stdout.
       
   131      *
       
   132      * @param classFile
       
   133      */
       
   134     public static void printClassFile(byte[] classFile) {
       
   135         int flags =  jdk.internal.org.objectweb.asm.ClassReader.SKIP_DEBUG;
       
   136 
       
   137         classFile = classFile.clone();
       
   138 
       
   139         // FIXME: workaround for V1_7 limiation of current ASM
       
   140         // Artificially downgrade
       
   141         if (classFile[7] == 52) {
       
   142             System.out.println("WARNING: downgraded major verson from 52 to 0");
       
   143             classFile[7] = 0;
       
   144         }
       
   145 
       
   146          jdk.internal.org.objectweb.asm.ClassReader cr =
       
   147                 new  jdk.internal.org.objectweb.asm.ClassReader(classFile);
       
   148 
       
   149         cr.accept(new  jdk.internal.org.objectweb.asm.util.TraceClassVisitor(new PrintWriter(System.out)),
       
   150                 flags);
       
   151 
       
   152     }
       
   153 
       
   154     /**
       
   155      * Print ASM version (sequence of calls to ASM API to produce same class file)
       
   156      * of {@code classFile} to stdout.
       
   157      *
       
   158      * @param classFile
       
   159      */
       
   160     public static void asmifyClassFile(byte[] classFile) {
       
   161         int flags =  jdk.internal.org.objectweb.asm.ClassReader.SKIP_DEBUG;
       
   162 
       
   163         // FIXME: workaround for V1_7 limiation of current ASM
       
   164         // Artificially downgrade
       
   165         if (classFile[7] == 52) {
       
   166             // Need to patch the bytecode, so make a copy of the class file
       
   167             classFile = classFile.clone();
       
   168 
       
   169             System.out.println("WARNING: downgraded major verson from 52 to 0");
       
   170             classFile[7] = 0;
       
   171         }
       
   172 
       
   173          jdk.internal.org.objectweb.asm.ClassReader cr =
       
   174                 new  jdk.internal.org.objectweb.asm.ClassReader(classFile);
       
   175 
       
   176         //cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), flags);
       
   177         cr.accept(new  jdk.internal.org.objectweb.asm.util.TraceClassVisitor(null,
       
   178                         new  jdk.internal.org.objectweb.asm.util.ASMifier(),
       
   179                         new PrintWriter(System.out)), flags);
       
   180     }
       
   181 
       
   182     /**
       
   183      * Parse method descriptor and split it into parameter types names and
       
   184      * return type name.
       
   185      *
       
   186      * @param desc
       
   187      * @return {@code Pair} of parameter types names and
       
   188      */
       
   189     public static Pair<String[],String> parseDesc(String desc) {
       
   190         Pattern p = Pattern.compile("\\((.*)\\)(.*)");
       
   191         Matcher m = p.matcher(desc);
       
   192 
       
   193         if (m.matches()) {
       
   194             String opts = m.group(1);
       
   195             String returnVal = m.group(2);
       
   196 
       
   197             return Pair.of(parseParams(opts), returnVal);
       
   198         } else {
       
   199             throw new IllegalArgumentException(desc);
       
   200         }
       
   201 
       
   202     }
       
   203 
       
   204     /**
       
   205      * Check whether a type isn't Void by it's name.
       
   206      *
       
   207      * @param type return type name
       
   208      * @return
       
   209      */
       
   210     public static boolean isNonVoid(String type) {
       
   211         return !("V".equals(type));
       
   212     }
       
   213 
       
   214     /**
       
   215      * Split a sequence of type names (in VM internal form).
       
   216      *
       
   217      * Example:
       
   218      *   "BCD[[ALA;I" => [ "B", "C", "D", "[[A", "LA;", "I" ]
       
   219      *
       
   220      * @param str
       
   221      * @return
       
   222      */
       
   223     public static String[] parseParams(String str) {
       
   224         List<String> params = new ArrayList<>();
       
   225 
       
   226         /* VM basic type notation:
       
   227                 B   byte    signed byte
       
   228                 C   char    Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
       
   229                 D   double  double-precision floating-point value
       
   230                 F   float   single-precision floating-point value
       
   231                 I   int     integer
       
   232                 J   long    long integer
       
   233                 L Classname ;   reference   an instance of class Classname
       
   234                 S   short   signed short
       
   235                 Z   boolean true or false
       
   236                 [   reference   one array dimension
       
   237          */
       
   238         int i = 0;
       
   239         int start = 0;
       
   240         while (i < str.length()) {
       
   241             char c = str.charAt(i);
       
   242             switch (c) {
       
   243                 case 'B': case 'C': case 'D': case 'F':
       
   244                 case 'I': case 'J': case 'S': case 'Z':
       
   245                     params.add(str.substring(start, i+1));
       
   246                     start = i+1;
       
   247                     break;
       
   248                 case 'L':
       
   249                     int k = str.indexOf(';', i);
       
   250                     if (k != 1) {
       
   251                         params.add(str.substring(start, k+1));
       
   252                         start = k+1;
       
   253                         i = k;
       
   254                     } else {
       
   255                         throw new IllegalArgumentException(str);
       
   256                     }
       
   257                     break;
       
   258                 case '[':
       
   259                     break;
       
   260                 default:
       
   261                     throw new IllegalArgumentException(
       
   262                             String.format("%d(%d): %c \'%s\'", i, start, c, str));
       
   263             }
       
   264 
       
   265             i++;
       
   266         }
       
   267 
       
   268         if (start != str.length()) {
       
   269             throw new IllegalArgumentException(str);
       
   270         }
       
   271 
       
   272         return params.toArray(new String[0]);
       
   273     }
       
   274 
       
   275     /**
       
   276      * Returns default values for different types:
       
   277      *   - byte:    0
       
   278      *   - short:   0
       
   279      *   - int:     0
       
   280      *   - long:    0L
       
   281      *   - char:    \U0000
       
   282      *   - boolean: false
       
   283      *   - float:   0.0f
       
   284      *   - double:  0.0d
       
   285      *   - array:   null
       
   286      *   - Object:  null
       
   287      *
       
   288      * @param types
       
   289      * @return
       
   290      */
       
   291     public static Param[] getDefaultValues(String[] types) {
       
   292         List<Param> values = new ArrayList<>();
       
   293 
       
   294         for (String type : types) {
       
   295             switch (type) {
       
   296                 case "I":  case "B": case "C": case "Z": case "S":
       
   297                     values.add(new IntParam(0));
       
   298                     break;
       
   299                 case "J":
       
   300                     values.add(new LongParam(0L));
       
   301                     break;
       
   302                 case "D":
       
   303                     values.add(new DoubleParam(0.0d));
       
   304                     break;
       
   305                 case "F":
       
   306                     values.add(new FloatParam(0.0f));
       
   307                     break;
       
   308                 default:
       
   309                     if (type.startsWith("L") || type.startsWith("[")) {
       
   310                         values.add(new NullParam());
       
   311                     } else {
       
   312                         throw new IllegalArgumentException(Arrays.toString(types));
       
   313                     }
       
   314                     break;
       
   315             }
       
   316         }
       
   317 
       
   318         return values.toArray(new Param[0]);
       
   319     }
       
   320 
       
   321     /**
       
   322      * Decode class name from internal VM representation into normal Java name.
       
   323      * Internal class naming convention is extensively used to describe method type (descriptor).
       
   324      *
       
   325      * Examples:
       
   326      *    "Ljava/lang/Object" => "java.lang.Object"
       
   327      *    "I" => "int"
       
   328      *    "[[[C" => "char[][][]"
       
   329      *
       
   330      * @param name
       
   331      * @return
       
   332      */
       
   333     public static String decodeClassName(String name) {
       
   334         switch (name) {
       
   335             case "Z": return "boolean";
       
   336             case "B": return "byte";
       
   337             case "S": return "short";
       
   338             case "C": return "char";
       
   339             case "I": return "int";
       
   340             case "J": return "long";
       
   341             case "F": return "float";
       
   342             case "D": return "double";
       
   343             default:
       
   344                 if (name.startsWith("L")) {
       
   345                     // "Ljava/lang/String;" => "java.lang.String"
       
   346                     return name.substring(1, name.length()-1).replaceAll("/", ".");
       
   347                 } else if (name.startsWith("[")) {
       
   348                     // "[[[C" => "char[][][]"
       
   349                     return decodeClassName(name.substring(1)) + "[]";
       
   350                 } else {
       
   351                     throw new IllegalArgumentException(name);
       
   352                 }
       
   353         }
       
   354     }
       
   355 
       
   356     /**
       
   357      * Decode class name from internal VM format into regular name and resolve it using {@code cl} {@code ClassLoader}.
       
   358      * It is used during conversion of method type from string representation to strongly typed variants (e.g. MethodType).
       
   359      *
       
   360      * @param name
       
   361      * @param cl
       
   362      * @return
       
   363      */
       
   364     public static Class decodeClass(String name, ClassLoader cl) {
       
   365         switch (name) {
       
   366             case "Z": return boolean.class;
       
   367             case "B": return byte.class;
       
   368             case "S": return short.class;
       
   369             case "C": return char.class;
       
   370             case "I": return int.class;
       
   371             case "J": return long.class;
       
   372             case "F": return float.class;
       
   373             case "D": return double.class;
       
   374             case "V": return void.class;
       
   375             default:
       
   376                 if (name.startsWith("L")) {
       
   377                     // "Ljava/lang/String;" => "java.lang.String"
       
   378                     String decodedName = name.substring(1, name.length()-1).replaceAll("/", ".");
       
   379                     try {
       
   380                         return cl.loadClass(decodedName);
       
   381                     } catch (Exception e) {
       
   382                         throw new Error(e);
       
   383                     }
       
   384                 } else if (name.startsWith("[")) {
       
   385                     // "[[[C" => "char[][][]"
       
   386                     //return decodeClassName(name.substring(1)) + "[]";
       
   387                     throw new UnsupportedOperationException("Resolution of arrays isn't supported yet: "+name);
       
   388                 } else {
       
   389                     throw new IllegalArgumentException(name);
       
   390                 }
       
   391         }
       
   392     }
       
   393 
       
   394     /**
       
   395      * Redefine a class with a new version.
       
   396      *
       
   397      * @param clz class for redefinition
       
   398      */
       
   399     static public void retransformClass(final Class<?> clz, final byte[] classFile) {
       
   400         ClassFileTransformer transformer = new ClassFileTransformer() {
       
   401             @Override
       
   402             public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
       
   403                 if (clz.getClassLoader() == loader && className.equals(clz.getName())) {
       
   404                     if (Constants.TRACE_CLASS_REDEF)  System.out.println("RETRANSFORM: " + className);
       
   405 
       
   406                     return classFile;
       
   407                 } else {
       
   408                     // leave the class as-is
       
   409                     return classfileBuffer;
       
   410                 }
       
   411             }
       
   412         };
       
   413 
       
   414         Transformer.inst.addTransformer(transformer, true);
       
   415         try {
       
   416             Transformer.inst.retransformClasses(clz);
       
   417         } catch (UnmodifiableClassException e) {
       
   418             throw new TestFailure(e);
       
   419         } finally {
       
   420             Transformer.inst.removeTransformer(transformer);
       
   421         }
       
   422     }
       
   423 
       
   424     /**
       
   425      * Redefine a class with a new version (class file in byte array).
       
   426      *
       
   427      * @param clz class for redefinition
       
   428      * @param classFile new version as a byte array
       
   429      * @return false if any errors occurred during class redefinition
       
   430      */
       
   431     static public void redefineClass(Class<?> clz, byte[] classFile) {
       
   432         if (clz ==  null) {
       
   433             throw new IllegalArgumentException("clz == null");
       
   434         }
       
   435 
       
   436         if (classFile == null || classFile.length == 0) {
       
   437             throw new IllegalArgumentException("Incorrect classFile");
       
   438         }
       
   439 
       
   440         if (Constants.TRACE_CLASS_REDEF)  System.out.println("REDEFINE: "+clz.getName());
       
   441 
       
   442         if (!redefineClassIntl(clz, classFile)) {
       
   443             throw new TestFailure("redefineClass failed: "+clz.getName());
       
   444         }
       
   445     }
       
   446 
       
   447 
       
   448     native static public boolean redefineClassIntl(Class<?> clz, byte[] classFile);
       
   449 
       
   450     /**
       
   451      * Get VM internal name of {@code Class<?> clz}.
       
   452      *
       
   453      * @param clz
       
   454      * @return
       
   455      */
       
   456     public static String getInternalName(Class<?> clz) {
       
   457         if (!clz.isPrimitive()) {
       
   458             return clz.getName().replaceAll("\\.", "/");
       
   459         } else {
       
   460             throw new UnsupportedOperationException();
       
   461         }
       
   462     }
       
   463 }