1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * The contents of this file are subject to the terms of either the Universal Permissive License 7 * v 1.0 as shown at http://oss.oracle.com/licenses/upl 8 * 9 * or the following license: 10 * 11 * Redistribution and use in source and binary forms, with or without modification, are permitted 12 * provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions 15 * and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 18 * conditions and the following disclaimer in the documentation and/or other materials provided with 19 * the distribution. 20 * 21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to 22 * endorse or promote products derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 package org.openjdk.jmc.agent.util; 34 35 import java.lang.reflect.Array; 36 import java.lang.reflect.Field; 37 import java.util.List; 38 import java.util.logging.Level; 39 import java.util.logging.Logger; 40 41 import org.openjdk.jmc.agent.Parameter; 42 import org.openjdk.jmc.agent.jfr.impl.JFRUtils; 43 import org.objectweb.asm.MethodVisitor; 44 import org.objectweb.asm.Opcodes; 45 import org.objectweb.asm.Type; 46 47 import sun.misc.Unsafe; 48 49 /** 50 * Helper methods for doing transforms. 51 */ 52 public final class TypeUtils { 53 private static final String NULL_REFERENCE_STRING = "null"; //$NON-NLS-1$ 54 /** 55 * The internal name of this class. 56 */ 57 public static final String INAME = Type.getInternalName(TypeUtils.class); 58 public static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); //$NON-NLS-1$ 59 public static final Type OBJECT_ARRAY_TYPE = Type.getObjectType("[Ljava/lang/Object;"); //$NON-NLS-1$ 60 public static final Type STRING_TYPE = Type.getType("Ljava/lang/String;"); //$NON-NLS-1$ 61 62 public static final Object STRING_INTERNAL_NAME = "java/lang/String"; //$NON-NLS-1$ 63 64 /** 65 * The file extension for java source files (.java). 66 */ 67 public static final String JAVA_FILE_EXTENSION = ".java"; //$NON-NLS-1$ 68 69 private TypeUtils() { 70 throw new UnsupportedOperationException("Toolkit!"); //$NON-NLS-1$ 71 } 72 73 public static Object box(byte val) { 74 return val; 75 } 76 77 public static Object box(short val) { 78 return val; 79 } 80 81 public static Object box(char val) { 82 return val; 83 } 84 85 public static Object box(int val) { 86 return val; 87 } 88 89 public static Object box(long val) { 90 return val; 91 } 92 93 public static Object box(float val) { 94 return val; 95 } 96 97 public static Object box(double val) { 98 return val; 99 } 100 101 public static String toString(Object o) { 102 if (o == null) { 103 return NULL_REFERENCE_STRING; 104 } 105 if (o.getClass().isArray()) { 106 return toString(o, Array.getLength(o)); 107 } 108 return String.valueOf(o); 109 } 110 111 /** 112 * Type agnostic array toString() which also handles primitive arrays. 113 */ 114 private static String toString(Object o, int length) { 115 int iMax = length - 1; 116 if (iMax == -1) { 117 return "[]"; //$NON-NLS-1$ 118 } 119 120 StringBuilder b = new StringBuilder(); 121 b.append('['); 122 for (int i = 0;; i++) { 123 b.append(Array.get(o, i)); 124 if (i == iMax) { 125 return b.append(']').toString(); 126 } 127 b.append(", "); //$NON-NLS-1$ 128 } 129 } 130 131 /** 132 * Ensure that the operand is on the stack before calling. If type is void, this is a noop, and 133 * depending on your use case you may instead want to push Opcodes.ACONST_NULL. 134 */ 135 public static void visitBox(MethodVisitor mv, Type type) { 136 switch (type.getSort()) { 137 case Type.VOID: 138 break; 139 case Type.BOOLEAN: 140 emitBox(mv, "(Z)Ljava/lang/Object;"); //$NON-NLS-1$ 141 break; 142 case Type.BYTE: 143 emitBox(mv, "(B)Ljava/lang/Object;"); //$NON-NLS-1$ 144 break; 145 case Type.CHAR: 146 emitBox(mv, "(C)Ljava/lang/Object;"); //$NON-NLS-1$ 147 break; 148 case Type.SHORT: 149 emitBox(mv, "(S)Ljava/lang/Object;"); //$NON-NLS-1$ 150 break; 151 case Type.INT: 152 emitBox(mv, "(I)Ljava/lang/Object;"); //$NON-NLS-1$ 153 break; 154 case Type.LONG: 155 emitBox(mv, "(J)Ljava/lang/Object;"); //$NON-NLS-1$ 156 break; 157 case Type.FLOAT: 158 emitBox(mv, "(F)Ljava/lang/Object;"); //$NON-NLS-1$ 159 break; 160 case Type.DOUBLE: 161 emitBox(mv, "(D)Ljava/lang/Object;"); //$NON-NLS-1$ 162 break; 163 } 164 } 165 166 private static void emitBox(MethodVisitor mv, String desc) { 167 mv.visitMethodInsn(Opcodes.INVOKESTATIC, INAME, "box", desc, false); //$NON-NLS-1$ 168 } 169 170 public static boolean isValidJavaIdentifier(String identifier) { 171 if (identifier == null || identifier.length() == 0) { 172 return false; 173 } 174 175 if (!Character.isJavaIdentifierStart(identifier.charAt(0))) { 176 return false; 177 } 178 179 for (int i = 1; i < identifier.length(); i++) { 180 if (!Character.isJavaIdentifierPart(identifier.charAt(i))) { 181 return false; 182 } 183 } 184 return true; 185 } 186 187 public static String deriveIdentifierPart(String str) { 188 StringBuilder builder = new StringBuilder(); 189 190 for (int i = 0; i < str.length(); i++) { 191 char c = str.charAt(i); 192 if (Character.isJavaIdentifierPart(c)) { 193 builder.append(c); 194 } 195 } 196 builder.setCharAt(0, Character.toUpperCase(builder.toString().charAt(0))); 197 return builder.toString(); 198 } 199 200 public static String getPathPart(String fqcn) { 201 int lastSlashIndex = fqcn.lastIndexOf('/'); 202 if (lastSlashIndex >= 0) { 203 return fqcn.substring(0, lastSlashIndex + 1); 204 } 205 return fqcn; 206 } 207 208 public static String getNamePart(String fqcn) { 209 int lastSlashIndex = fqcn.lastIndexOf('/'); 210 if (lastSlashIndex >= 0) { 211 return fqcn.substring(lastSlashIndex + 1); 212 } 213 return fqcn; 214 } 215 216 public static Unsafe getUnsafe() { 217 // Lovely, but this seems to be the only way 218 try { 219 Field f = Unsafe.class.getDeclaredField("theUnsafe"); //$NON-NLS-1$ 220 f.setAccessible(true); 221 return (Unsafe) f.get(null); 222 } catch (Exception e) { 223 Logger.getLogger(JFRUtils.class.getName()).log(Level.SEVERE, "Could not access Unsafe!", e); //$NON-NLS-1$ 224 } 225 return null; 226 } 227 228 public static void stringify(MethodVisitor mv, Parameter param, Type argumentType) { 229 mv.visitMethodInsn(Opcodes.INVOKESTATIC, INAME, "toString", //$NON-NLS-1$ 230 "(Ljava/lang/Object;)Ljava/lang/String;", false); //$NON-NLS-1$ 231 } 232 233 public static boolean shouldStringify(Parameter param, Type argumentType) { 234 if (argumentType.getSort() == Type.ARRAY || argumentType.getSort() == Type.OBJECT) { 235 return !argumentType.getInternalName().equals(STRING_INTERNAL_NAME); 236 } 237 return false; 238 } 239 240 public static Parameter findReturnParam(List<Parameter> parameters) { 241 for (Parameter p : parameters) { 242 if (p.isReturn()) { 243 return p; 244 } 245 } 246 return null; 247 } 248 249 /** 250 * Transforms a FQN in internal form, so that it can be used in e.g. formal descriptors. 251 * 252 * @param className 253 * the fully qualified class name in internal form. 254 * @return the transformed class name. 255 */ 256 public static String parameterize(String className) { 257 return "L" + className + ";"; //$NON-NLS-1$ //$NON-NLS-2$ 258 } 259 }