1 /*
   2  * Copyright (c) 2008, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.invoke.util;
  27 
  28 import java.lang.invoke.MethodType;
  29 import java.util.ArrayList;
  30 import java.util.List;
  31 
  32 /**
  33  * Utility routines for dealing with bytecode-level signatures.
  34  * @author jrose
  35  */
  36 public class BytecodeDescriptor {
  37 
  38     private BytecodeDescriptor() { }  // cannot instantiate
  39 
  40     /**
  41      * @param loader the class loader in which to look up the types (null means
  42      *               bootstrap class loader)
  43      */
  44     public static List<Class<?>> parseMethod(String bytecodeSignature, ClassLoader loader) {
  45         return parseMethod(bytecodeSignature, 0, bytecodeSignature.length(), loader);
  46     }
  47 
  48     /**
  49      * @param loader the class loader in which to look up the types (null means
  50      *               bootstrap class loader)
  51      */
  52     static List<Class<?>> parseMethod(String bytecodeSignature,
  53             int start, int end, ClassLoader loader) {
  54         String str = bytecodeSignature;
  55         int[] i = {start};
  56         ArrayList<Class<?>> ptypes = new ArrayList<Class<?>>();
  57         if (i[0] < end && str.charAt(i[0]) == '(') {
  58             ++i[0];  // skip '('
  59             while (i[0] < end && str.charAt(i[0]) != ')') {
  60                 Class<?> pt = parseSig(str, i, end, loader);
  61                 if (pt == null || pt == void.class)
  62                     parseError(str, "bad argument type");
  63                 ptypes.add(pt);
  64             }
  65             ++i[0];  // skip ')'
  66         } else {
  67             parseError(str, "not a method type");
  68         }
  69         Class<?> rtype = parseSig(str, i, end, loader);
  70         if (rtype == null || i[0] != end)
  71             parseError(str, "bad return type");
  72         ptypes.add(rtype);
  73         return ptypes;
  74     }
  75 
  76     private static void parseError(String str, String msg) {
  77         throw new IllegalArgumentException("bad signature: "+str+": "+msg);
  78     }
  79 
  80     /**
  81      * @param loader the class loader in which to look up the types (null means
  82      *               bootstrap class loader)
  83      */
  84     private static Class<?> parseSig(String str, int[] i, int end, ClassLoader loader) {
  85         if (i[0] == end)  return null;
  86         char c = str.charAt(i[0]++);
  87         if (c == 'L') {
  88             int begc = i[0], endc = str.indexOf(';', begc);
  89             if (endc < 0)  return null;
  90             i[0] = endc+1;
  91             String name = str.substring(begc, endc).replace('/', '.');
  92             try {
  93                 return Class.forName(name, false, loader);
  94             } catch (ClassNotFoundException ex) {
  95                 throw new TypeNotPresentException(name, ex);
  96             }
  97         } else if (c == '[') {
  98             Class<?> t = parseSig(str, i, end, loader);
  99             if (t != null)
 100                 t = java.lang.reflect.Array.newInstance(t, 0).getClass();
 101             return t;
 102         } else {
 103             return Wrapper.forBasicType(c).primitiveType();
 104         }
 105     }
 106 
 107     public static String unparse(Class<?> type) {
 108         if (type == Object.class) {
 109             return "Ljava/lang/Object;";
 110         } else if (type == int.class) {
 111             return "I";
 112         }
 113         StringBuilder sb = new StringBuilder();
 114         unparseSig(type, sb);
 115         return sb.toString();
 116     }
 117 
 118     public static String unparse(MethodType type) {
 119         return unparseMethod(type.returnType(), type.parameterArray());
 120     }
 121 
 122     public static String unparse(Object type) {
 123         if (type instanceof Class<?>)
 124             return unparse((Class<?>) type);
 125         if (type instanceof MethodType)
 126             return unparse((MethodType) type);
 127         return (String) type;
 128     }
 129 
 130     public static String unparseMethod(Class<?> rtype, List<Class<?>> ptypes) {
 131         StringBuilder sb = new StringBuilder();
 132         sb.append('(');
 133         for (Class<?> pt : ptypes)
 134             unparseSig(pt, sb);
 135         sb.append(')');
 136         unparseSig(rtype, sb);
 137         return sb.toString();
 138     }
 139 
 140     public static String unparseMethod(Class<?> rtype, Class<?>[] ptypes) {
 141         StringBuilder sb = new StringBuilder();
 142         sb.append('(');
 143         for (Class<?> pt : ptypes)
 144             unparseSig(pt, sb);
 145         sb.append(')');
 146         unparseSig(rtype, sb);
 147         return sb.toString();
 148     }
 149 
 150     private static void unparseSig(Class<?> t, StringBuilder sb) {
 151         char c = Wrapper.forBasicType(t).basicTypeChar();
 152         if (c != 'L') {
 153             sb.append(c);
 154         } else if (t == Object.class) {
 155             sb.append("Ljava/lang/Object;");
 156         } else {
 157             boolean lsemi = (!t.isArray());
 158             if (lsemi)  sb.append('L');
 159             sb.append(t.getName().replace('.', '/'));
 160             if (lsemi)  sb.append(';');
 161         }
 162     }
 163 
 164 }