1 /*
   2  * Copyright (c) 2018, 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 package java.lang.constant;
  26 
  27 import java.util.ArrayList;
  28 import java.util.List;
  29 import java.util.Set;
  30 
  31 import static java.util.Objects.requireNonNull;
  32 
  33 /**
  34  * Helper methods for the implementation of {@code java.lang.constant}.
  35  */
  36 class ConstantUtils {
  37     /** an empty constant descriptor */
  38     public static final ConstantDesc[] EMPTY_CONSTANTDESC = new ConstantDesc[0];
  39     static final Constable[] EMPTY_CONSTABLE = new Constable[0];
  40 
  41     private static final Set<String> pointyNames = Set.of("<init>", "<clinit>");
  42 
  43     static String validateBinaryClassName(String name) {
  44         for (int i=0; i<name.length(); i++) {
  45             char ch = name.charAt(i);
  46             if (ch == ';' || ch == '[' || ch == '/')
  47                 throw new IllegalArgumentException("Invalid class name: " + name);
  48         }
  49         return name;
  50     }
  51 
  52     /**
  53      * validates a member name
  54      * @param name the name of the member
  55      * @return the name passed if valid
  56      */
  57     public static String validateMemberName(String name) {
  58         requireNonNull(name);
  59         if (name.length() == 0)
  60             throw new IllegalArgumentException("zero-length member name");
  61         for (int i=0; i<name.length(); i++) {
  62             char ch = name.charAt(i);
  63             if (ch == '.' || ch == ';' || ch == '[' || ch == '/')
  64                 throw new IllegalArgumentException("Invalid member name: " + name);
  65             if (ch == '<' || ch == '>') {
  66                 if (!pointyNames.contains(name))
  67                     throw new IllegalArgumentException("Invalid member name: " + name);
  68             }
  69         }
  70         return name;
  71     }
  72 
  73     static void validateClassOrInterface(ClassDesc classDesc) {
  74         if (!classDesc.isClassOrInterface())
  75             throw new IllegalArgumentException("not a class or interface type: " + classDesc);
  76     }
  77 
  78     static int arrayDepth(String descriptorString) {
  79         int depth = 0;
  80         while (descriptorString.charAt(depth) == '[')
  81             depth++;
  82         return depth;
  83     }
  84 
  85     static String binaryToInternal(String name) {
  86         return name.replace('.', '/');
  87     }
  88 
  89     static String internalToBinary(String name) {
  90         return name.replace('/', '.');
  91     }
  92 
  93     static String dropLastChar(String s) {
  94         return s.substring(0, s.length() - 1);
  95     }
  96 
  97     static String dropFirstAndLastChar(String s) {
  98         return s.substring(1, s.length() - 1);
  99     }
 100 
 101     /**
 102      * Parses a method descriptor string, and return a list of field descriptor
 103      * strings, return type first, then parameter types
 104      *
 105      * @param descriptor the descriptor string
 106      * @return the list of types
 107      * @throws IllegalArgumentException if the descriptor string is not valid
 108      */
 109     static List<String> parseMethodDescriptor(String descriptor) {
 110         int cur = 0, end = descriptor.length();
 111         ArrayList<String> ptypes = new ArrayList<>();
 112 
 113         if (cur >= end || descriptor.charAt(cur) != '(')
 114             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
 115 
 116         ++cur;  // skip '('
 117         while (cur < end && descriptor.charAt(cur) != ')') {
 118             int len = matchSig(descriptor, cur, end);
 119             if (len == 0 || descriptor.charAt(cur) == 'V')
 120                 throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
 121             ptypes.add(descriptor.substring(cur, cur + len));
 122             cur += len;
 123         }
 124         if (cur >= end)
 125             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
 126         ++cur;  // skip ')'
 127 
 128         int rLen = matchSig(descriptor, cur, end);
 129         if (rLen == 0 || cur + rLen != end)
 130             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
 131         ptypes.add(0, descriptor.substring(cur, cur + rLen));
 132         return ptypes;
 133     }
 134 
 135     /**
 136      * Validates that the characters at [start, end) within the provided string
 137      * describe a valid field type descriptor.
 138      *
 139      * @param str the descriptor string
 140      * @param start the starting index into the string
 141      * @param end the ending index within the string
 142      * @return the length of the descriptor, or 0 if it is not a descriptor
 143      * @throws IllegalArgumentException if the descriptor string is not valid
 144      */
 145     static int matchSig(String str, int start, int end) {
 146         if (start >= end || start >= str.length() || end > str.length())
 147             return 0;
 148         char c = str.charAt(start);
 149         if (c == 'L') {
 150             int endc = str.indexOf(';', start);
 151             int badc = str.indexOf('.', start);
 152             if (badc >= 0 && badc < endc)
 153                 return 0;
 154             badc = str.indexOf('[', start);
 155             if (badc >= 0 && badc < endc)
 156                 return 0;
 157             return (endc < 0) ? 0 : endc - start + 1;
 158         } else if (c == '[') {
 159             int t = matchSig(str, start+1, end);
 160             return (t > 0) ? t + 1 : 0;
 161         } else {
 162             return ("IJCSBFDZV".indexOf(c) >= 0) ? 1 : 0;
 163         }
 164     }
 165 }