Path: blob/master/test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/shared/Util.java
41161 views
/*1* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package vm.runtime.defmeth.shared;2425import vm.runtime.defmeth.shared.data.Clazz;26import java.io.PrintWriter;27import java.lang.instrument.ClassFileTransformer;28import java.lang.instrument.IllegalClassFormatException;29import java.lang.instrument.Instrumentation;30import java.lang.instrument.UnmodifiableClassException;31import java.security.ProtectionDomain;32import java.util.ArrayList;33import java.util.Arrays;34import java.util.List;35import java.util.regex.Matcher;36import java.util.regex.Pattern;37import nsk.share.Pair;38import nsk.share.TestFailure;39import vm.runtime.defmeth.shared.data.method.param.*;404142/**43* Utility class with auxiliary miscellaneous methods.44*/45public class Util {46public static class Transformer {47private static Instrumentation inst;4849public static void premain(String agentArgs, Instrumentation inst) {50Transformer.inst = inst;5152/*53inst.addTransformer(new ClassFileTransformer() {54@Override55public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {56System.out.println("Retransform (initial): " + className);57return classfileBuffer;58}59});60*/61}62}6364/**65* Concatenate {@code strings} array interleaving with {@code sep} in between.66*67* @param sep68* @param strings69* @return70*/71public static String intersperse(String sep, String... strings) {72StringBuilder sb = new StringBuilder();73if (strings.length == 0) {74return "";75} else if (strings.length == 1) {76return strings[0];77} else {78sb.append(strings[0]);7980for (int i=1; i<strings.length; i++) {81sb.append(sep).append(strings[i]);82}8384return sb.toString();85}86}8788/**89* Construct array of names for an array of {@code Clazz} instances.90*91* @param clazzes92* @return93*/94public static String[] asStrings(Clazz[] clazzes) {95String[] result = new String[clazzes.length];96for (int i = 0; i < clazzes.length; i++) {97result[i] = clazzes[i].intlName();98}99100return result;101}102103/**104* Get the name of the test currently being executed105*106* @return name of the test being executed107*/108public static String getTestName() {109// Hack: examine stack trace and extract test method's name from there110try {111throw new Exception();112} catch (Exception e) {113for (StackTraceElement elem : e.getStackTrace()) {114String className = elem.getClassName();115String methodName = elem.getMethodName();116117if (className.startsWith("vm.runtime.defmeth.") &&118methodName.startsWith("test")) {119return String.format("%s.%s",120className.replaceAll(".*\\.", ""), methodName);121}122}123124return "Unknown";125}126}127128/**129* Pretty-print {@code byte[] classFile} to stdout.130*131* @param classFile132*/133public static void printClassFile(byte[] classFile) {134int flags = jdk.internal.org.objectweb.asm.ClassReader.SKIP_DEBUG;135136classFile = classFile.clone();137138jdk.internal.org.objectweb.asm.ClassReader cr =139new jdk.internal.org.objectweb.asm.ClassReader(classFile);140141cr.accept(new jdk.internal.org.objectweb.asm.util.TraceClassVisitor(new PrintWriter(System.out)), flags);142}143144/**145* Print ASM version (sequence of calls to ASM API to produce same class file)146* of {@code classFile} to stdout.147*148* @param classFile149*/150public static void asmifyClassFile(byte[] classFile) {151int flags = jdk.internal.org.objectweb.asm.ClassReader.SKIP_DEBUG;152153jdk.internal.org.objectweb.asm.ClassReader cr =154new jdk.internal.org.objectweb.asm.ClassReader(classFile);155156//cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), flags);157cr.accept(new jdk.internal.org.objectweb.asm.util.TraceClassVisitor(null,158new jdk.internal.org.objectweb.asm.util.ASMifier(),159new PrintWriter(System.out)), flags);160}161162/**163* Parse method descriptor and split it into parameter types names and164* return type name.165*166* @param desc167* @return {@code Pair} of parameter types names and168*/169public static Pair<String[],String> parseDesc(String desc) {170Pattern p = Pattern.compile("\\((.*)\\)(.*)");171Matcher m = p.matcher(desc);172173if (m.matches()) {174String opts = m.group(1);175String returnVal = m.group(2);176177return Pair.of(parseParams(opts), returnVal);178} else {179throw new IllegalArgumentException(desc);180}181182}183184/**185* Check whether a type isn't Void by it's name.186*187* @param type return type name188* @return189*/190public static boolean isNonVoid(String type) {191return !("V".equals(type));192}193194/**195* Split a sequence of type names (in VM internal form).196*197* Example:198* "BCD[[ALA;I" => [ "B", "C", "D", "[[A", "LA;", "I" ]199*200* @param str201* @return202*/203public static String[] parseParams(String str) {204List<String> params = new ArrayList<>();205206/* VM basic type notation:207B byte signed byte208C char Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16209D double double-precision floating-point value210F float single-precision floating-point value211I int integer212J long long integer213L Classname ; reference an instance of class Classname214S short signed short215Z boolean true or false216[ reference one array dimension217*/218int i = 0;219int start = 0;220while (i < str.length()) {221char c = str.charAt(i);222switch (c) {223case 'B': case 'C': case 'D': case 'F':224case 'I': case 'J': case 'S': case 'Z':225params.add(str.substring(start, i+1));226start = i+1;227break;228case 'L':229int k = str.indexOf(';', i);230if (k != 1) {231params.add(str.substring(start, k+1));232start = k+1;233i = k;234} else {235throw new IllegalArgumentException(str);236}237break;238case '[':239break;240default:241throw new IllegalArgumentException(242String.format("%d(%d): %c \'%s\'", i, start, c, str));243}244245i++;246}247248if (start != str.length()) {249throw new IllegalArgumentException(str);250}251252return params.toArray(new String[0]);253}254255/**256* Returns default values for different types:257* - byte: 0258* - short: 0259* - int: 0260* - long: 0L261* - char: \U0000262* - boolean: false263* - float: 0.0f264* - double: 0.0d265* - array: null266* - Object: null267*268* @param types269* @return270*/271public static Param[] getDefaultValues(String[] types) {272List<Param> values = new ArrayList<>();273274for (String type : types) {275switch (type) {276case "I": case "B": case "C": case "Z": case "S":277values.add(new IntParam(0));278break;279case "J":280values.add(new LongParam(0L));281break;282case "D":283values.add(new DoubleParam(0.0d));284break;285case "F":286values.add(new FloatParam(0.0f));287break;288default:289if (type.startsWith("L") || type.startsWith("[")) {290values.add(new NullParam());291} else {292throw new IllegalArgumentException(Arrays.toString(types));293}294break;295}296}297298return values.toArray(new Param[0]);299}300301/**302* Decode class name from internal VM representation into normal Java name.303* Internal class naming convention is extensively used to describe method type (descriptor).304*305* Examples:306* "Ljava/lang/Object" => "java.lang.Object"307* "I" => "int"308* "[[[C" => "char[][][]"309*310* @param name311* @return312*/313public static String decodeClassName(String name) {314switch (name) {315case "Z": return "boolean";316case "B": return "byte";317case "S": return "short";318case "C": return "char";319case "I": return "int";320case "J": return "long";321case "F": return "float";322case "D": return "double";323default:324if (name.startsWith("L")) {325// "Ljava/lang/String;" => "java.lang.String"326return name.substring(1, name.length()-1).replaceAll("/", ".");327} else if (name.startsWith("[")) {328// "[[[C" => "char[][][]"329return decodeClassName(name.substring(1)) + "[]";330} else {331throw new IllegalArgumentException(name);332}333}334}335336/**337* Decode class name from internal VM format into regular name and resolve it using {@code cl} {@code ClassLoader}.338* It is used during conversion of method type from string representation to strongly typed variants (e.g. MethodType).339*340* @param name341* @param cl342* @return343*/344public static Class decodeClass(String name, ClassLoader cl) {345switch (name) {346case "Z": return boolean.class;347case "B": return byte.class;348case "S": return short.class;349case "C": return char.class;350case "I": return int.class;351case "J": return long.class;352case "F": return float.class;353case "D": return double.class;354case "V": return void.class;355default:356if (name.startsWith("L")) {357// "Ljava/lang/String;" => "java.lang.String"358String decodedName = name.substring(1, name.length()-1).replaceAll("/", ".");359try {360return cl.loadClass(decodedName);361} catch (Exception e) {362throw new Error(e);363}364} else if (name.startsWith("[")) {365// "[[[C" => "char[][][]"366//return decodeClassName(name.substring(1)) + "[]";367throw new UnsupportedOperationException("Resolution of arrays isn't supported yet: "+name);368} else {369throw new IllegalArgumentException(name);370}371}372}373374/**375* Redefine a class with a new version.376*377* @param clz class for redefinition378*/379static public void retransformClass(final Class<?> clz, final byte[] classFile) {380ClassFileTransformer transformer = new ClassFileTransformer() {381@Override382public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {383if (clz.getClassLoader() == loader && className.equals(clz.getName())) {384if (Constants.TRACE_CLASS_REDEF) System.out.println("RETRANSFORM: " + className);385386return classFile;387} else {388// leave the class as-is389return classfileBuffer;390}391}392};393394Transformer.inst.addTransformer(transformer, true);395try {396Transformer.inst.retransformClasses(clz);397} catch (UnmodifiableClassException e) {398throw new TestFailure(e);399} finally {400Transformer.inst.removeTransformer(transformer);401}402}403404/**405* Redefine a class with a new version (class file in byte array).406*407* @param clz class for redefinition408* @param classFile new version as a byte array409* @return false if any errors occurred during class redefinition410*/411static public void redefineClass(Class<?> clz, byte[] classFile) {412if (clz == null) {413throw new IllegalArgumentException("clz == null");414}415416if (classFile == null || classFile.length == 0) {417throw new IllegalArgumentException("Incorrect classFile");418}419420if (Constants.TRACE_CLASS_REDEF) System.out.println("REDEFINE: "+clz.getName());421422if (!redefineClassIntl(clz, classFile)) {423throw new TestFailure("redefineClass failed: "+clz.getName());424}425}426427428native static public boolean redefineClassIntl(Class<?> clz, byte[] classFile);429430/**431* Get VM internal name of {@code Class<?> clz}.432*433* @param clz434* @return435*/436public static String getInternalName(Class<?> clz) {437if (!clz.isPrimitive()) {438return clz.getName().replaceAll("\\.", "/");439} else {440throw new UnsupportedOperationException();441}442}443}444445446