1 /*
   2  * Copyright (c) 1998, 2020, 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 com.sun.tools.jdi;
  27 
  28 import java.util.ArrayList;
  29 import java.util.List;
  30 
  31 public class JNITypeParser {
  32 
  33     static final char SIGNATURE_ENDCLASS = ';';
  34     static final char SIGNATURE_FUNC = '(';
  35     static final char SIGNATURE_ENDFUNC = ')';
  36 
  37     private String signature;
  38     private List<String> typeNameList;
  39     private List<String> signatureList;
  40     private int currentIndex;
  41 
  42     JNITypeParser(String signature) {
  43         this.signature = signature;
  44     }
  45 
  46     static String typeNameToSignature(String typeName) {
  47         StringBuilder sb = new StringBuilder();
  48         int firstIndex = typeName.indexOf('[');
  49         int index = firstIndex;
  50         while (index != -1) {
  51             sb.append('[');
  52             index = typeName.indexOf('[', index + 1);
  53         }
  54 
  55         if (firstIndex != -1) {
  56             typeName = typeName.substring(0, firstIndex);
  57         }
  58 
  59         if (typeName.equals("boolean")) {
  60             sb.append('Z');
  61         } else if (typeName.equals("byte")) {
  62             sb.append('B');
  63         } else if (typeName.equals("char")) {
  64             sb.append('C');
  65         } else if (typeName.equals("short")) {
  66             sb.append('S');
  67         } else if (typeName.equals("int")) {
  68             sb.append('I');
  69         } else if (typeName.equals("long")) {
  70             sb.append('J');
  71         } else if (typeName.equals("float")) {
  72             sb.append('F');
  73         } else if (typeName.equals("double")) {
  74             sb.append('D');
  75         } else {
  76             sb.append('L');
  77             index = typeName.indexOf("/");   // check if it's a hidden class
  78             if (index < 0) {
  79                 sb.append(typeName.replace('.', '/'));
  80                 sb.append(";");
  81             } else {
  82                 sb.append(typeName.substring(0, index).replace('.', '/'));
  83                 sb.append(";");
  84                 sb.append(typeName.substring(index, typeName.length()));
  85             }
  86         }
  87 
  88         return sb.toString();
  89     }
  90 
  91     String typeName() {
  92         return typeNameList().get(typeNameList().size()-1);
  93     }
  94 
  95     List<String> argumentTypeNames() {
  96         return typeNameList().subList(0, typeNameList().size() - 1);
  97     }
  98 
  99     String signature() {
 100         return signatureList().get(signatureList().size()-1);
 101     }
 102 
 103     List<String> argumentSignatures() {
 104         return signatureList().subList(0, signatureList().size() - 1);
 105     }
 106 
 107     int dimensionCount() {
 108         int count = 0;
 109         String signature = signature();
 110         while (signature.charAt(count) == '[') {
 111             count++;
 112         }
 113         return count;
 114     }
 115 
 116     String componentSignature(int level) {
 117         return signature().substring(level);
 118     }
 119 
 120     private synchronized List<String> signatureList() {
 121         if (signatureList == null) {
 122             signatureList = new ArrayList<>(10);
 123             String elem;
 124 
 125             currentIndex = 0;
 126 
 127             while(currentIndex < signature.length()) {
 128                 elem = nextSignature();
 129                 signatureList.add(elem);
 130             }
 131             if (signatureList.size() == 0) {
 132                 throw new IllegalArgumentException("Invalid JNI signature '" +
 133                                                    signature + "'");
 134             }
 135         }
 136         return signatureList;
 137     }
 138 
 139     private synchronized List<String> typeNameList() {
 140         if (typeNameList == null) {
 141             typeNameList = new ArrayList<>(10);
 142             String elem;
 143 
 144             currentIndex = 0;
 145 
 146             while(currentIndex < signature.length()) {
 147                 elem = nextTypeName();
 148                 typeNameList.add(elem);
 149             }
 150             if (typeNameList.size() == 0) {
 151                 throw new IllegalArgumentException("Invalid JNI signature '" +
 152                                                    signature + "'");
 153             }
 154         }
 155         return typeNameList;
 156     }
 157 
 158     private String nextSignature() {
 159         char key = signature.charAt(currentIndex++);
 160 
 161         switch(key) {
 162             case (JDWP.Tag.ARRAY):
 163                 return  key + nextSignature();
 164 
 165             case (JDWP.Tag.OBJECT):
 166                 int endClass = signature.indexOf(SIGNATURE_ENDCLASS,
 167                                                  currentIndex);
 168                 String retVal;
 169                 if ((endClass+1) < signature.length() && signature.charAt(endClass+1) == '/') {
 170                     // hidden class
 171                     retVal = signature.substring(currentIndex - 1);
 172                     currentIndex = signature.length();
 173                 } else {
 174                     retVal = signature.substring(currentIndex - 1, endClass + 1);
 175                     currentIndex = endClass + 1;
 176                 }
 177                 return retVal;
 178 
 179             case (JDWP.Tag.VOID):
 180             case (JDWP.Tag.BOOLEAN):
 181             case (JDWP.Tag.BYTE):
 182             case (JDWP.Tag.CHAR):
 183             case (JDWP.Tag.SHORT):
 184             case (JDWP.Tag.INT):
 185             case (JDWP.Tag.LONG):
 186             case (JDWP.Tag.FLOAT):
 187             case (JDWP.Tag.DOUBLE):
 188                 return String.valueOf(key);
 189 
 190             case SIGNATURE_ENDFUNC:
 191             case SIGNATURE_FUNC:
 192                 return nextSignature();
 193 
 194             default:
 195                 throw new IllegalArgumentException(
 196                     "Invalid JNI signature character '" + key + "'");
 197 
 198         }
 199     }
 200 
 201     private String nextTypeName() {
 202         char key = signature.charAt(currentIndex++);
 203 
 204         switch(key) {
 205             case (JDWP.Tag.ARRAY):
 206                 return  nextTypeName() + "[]";
 207 
 208             case (JDWP.Tag.BYTE):
 209                 return "byte";
 210 
 211             case (JDWP.Tag.CHAR):
 212                 return "char";
 213 
 214             case (JDWP.Tag.OBJECT):
 215                 int endClass = signature.indexOf(SIGNATURE_ENDCLASS,
 216                                                  currentIndex);
 217                 String retVal = signature.substring(currentIndex,
 218                                                     endClass);
 219                 if ((endClass+1) < signature.length() && signature.charAt(endClass+1) == '/') {
 220                     // hidden class
 221                     retVal = retVal.replace('/', '.') + signature.substring(endClass + 1);
 222                     currentIndex = signature.length();
 223                 } else {
 224                     retVal = retVal.replace('/', '.');
 225                     currentIndex = endClass + 1;
 226                 }
 227                 return retVal;
 228 
 229             case (JDWP.Tag.FLOAT):
 230                 return "float";
 231 
 232             case (JDWP.Tag.DOUBLE):
 233                 return "double";
 234 
 235             case (JDWP.Tag.INT):
 236                 return "int";
 237 
 238             case (JDWP.Tag.LONG):
 239                 return "long";
 240 
 241             case (JDWP.Tag.SHORT):
 242                 return "short";
 243 
 244             case (JDWP.Tag.VOID):
 245                 return "void";
 246 
 247             case (JDWP.Tag.BOOLEAN):
 248                 return "boolean";
 249 
 250             case SIGNATURE_ENDFUNC:
 251             case SIGNATURE_FUNC:
 252                 return nextTypeName();
 253 
 254             default:
 255                 throw new IllegalArgumentException(
 256                     "Invalid JNI signature character '" + key + "'");
 257         }
 258     }
 259 }