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 }