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