• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2018, 2019, 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     static final int MAX_ARRAY_TYPE_DESC_DIMENSIONS = 255;
41 
42     private static final Set<String> pointyNames = Set.of("<init>", "<clinit>");
43 
44     /**
45      * Validates the correctness of a binary class name. In particular checks for the presence of
46      * invalid characters in the name.
47      *
48      * @param name the class name
49      * @return the class name passed if valid
50      * @throws IllegalArgumentException if the class name is invalid
51      */
validateBinaryClassName(String name)52     static String validateBinaryClassName(String name) {
53         for (int i=0; i<name.length(); i++) {
54             char ch = name.charAt(i);
55             if (ch == ';' || ch == '[' || ch == '/')
56                 throw new IllegalArgumentException("Invalid class name: " + name);
57         }
58         return name;
59     }
60 
61     /**
62      * Validates a member name
63      *
64      * @param name the name of the member
65      * @return the name passed if valid
66      * @throws IllegalArgumentException if the member name is invalid
67      */
validateMemberName(String name, boolean method)68     public static String validateMemberName(String name, boolean method) {
69         requireNonNull(name);
70         if (name.length() == 0)
71             throw new IllegalArgumentException("zero-length member name");
72         for (int i=0; i<name.length(); i++) {
73             char ch = name.charAt(i);
74             if (ch == '.' || ch == ';' || ch == '[' || ch == '/')
75                 throw new IllegalArgumentException("Invalid member name: " + name);
76             if (method && (ch == '<' || ch == '>')) {
77                 if (!pointyNames.contains(name))
78                     throw new IllegalArgumentException("Invalid member name: " + name);
79             }
80         }
81         return name;
82     }
83 
validateClassOrInterface(ClassDesc classDesc)84     static void validateClassOrInterface(ClassDesc classDesc) {
85         if (!classDesc.isClassOrInterface())
86             throw new IllegalArgumentException("not a class or interface type: " + classDesc);
87     }
88 
arrayDepth(String descriptorString)89     static int arrayDepth(String descriptorString) {
90         int depth = 0;
91         while (descriptorString.charAt(depth) == '[')
92             depth++;
93         return depth;
94     }
95 
binaryToInternal(String name)96     static String binaryToInternal(String name) {
97         return name.replace('.', '/');
98     }
99 
internalToBinary(String name)100     static String internalToBinary(String name) {
101         return name.replace('/', '.');
102     }
103 
dropLastChar(String s)104     static String dropLastChar(String s) {
105         return s.substring(0, s.length() - 1);
106     }
107 
dropFirstAndLastChar(String s)108     static String dropFirstAndLastChar(String s) {
109         return s.substring(1, s.length() - 1);
110     }
111 
112     /**
113      * Parses a method descriptor string, and return a list of field descriptor
114      * strings, return type first, then parameter types
115      *
116      * @param descriptor the descriptor string
117      * @return the list of types
118      * @throws IllegalArgumentException if the descriptor string is not valid
119      */
parseMethodDescriptor(String descriptor)120     static List<String> parseMethodDescriptor(String descriptor) {
121         int cur = 0, end = descriptor.length();
122         ArrayList<String> ptypes = new ArrayList<>();
123 
124         if (cur >= end || descriptor.charAt(cur) != '(')
125             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
126 
127         ++cur;  // skip '('
128         while (cur < end && descriptor.charAt(cur) != ')') {
129             int len = skipOverFieldSignature(descriptor, cur, end, false);
130             if (len == 0)
131                 throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
132             ptypes.add(descriptor.substring(cur, cur + len));
133             cur += len;
134         }
135         if (cur >= end)
136             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
137         ++cur;  // skip ')'
138 
139         int rLen = skipOverFieldSignature(descriptor, cur, end, true);
140         if (rLen == 0 || cur + rLen != end)
141             throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
142         ptypes.add(0, descriptor.substring(cur, cur + rLen));
143         return ptypes;
144     }
145 
146     private static final char JVM_SIGNATURE_ARRAY = '[';
147     private static final char JVM_SIGNATURE_BYTE = 'B';
148     private static final char JVM_SIGNATURE_CHAR = 'C';
149     private static final char JVM_SIGNATURE_CLASS = 'L';
150     private static final char JVM_SIGNATURE_ENDCLASS = ';';
151     private static final char JVM_SIGNATURE_ENUM = 'E';
152     private static final char JVM_SIGNATURE_FLOAT = 'F';
153     private static final char JVM_SIGNATURE_DOUBLE = 'D';
154     private static final char JVM_SIGNATURE_FUNC = '(';
155     private static final char JVM_SIGNATURE_ENDFUNC = ')';
156     private static final char JVM_SIGNATURE_INT = 'I';
157     private static final char JVM_SIGNATURE_LONG = 'J';
158     private static final char JVM_SIGNATURE_SHORT = 'S';
159     private static final char JVM_SIGNATURE_VOID = 'V';
160     private static final char JVM_SIGNATURE_BOOLEAN = 'Z';
161 
162     /**
163      * Validates that the characters at [start, end) within the provided string
164      * describe a valid field type descriptor.
165      * @param descriptor the descriptor string
166      * @param start the starting index into the string
167      * @param end the ending index within the string
168      * @param voidOK is void acceptable?
169      * @return the length of the descriptor, or 0 if it is not a descriptor
170      * @throws IllegalArgumentException if the descriptor string is not valid
171      */
172     @SuppressWarnings("fallthrough")
skipOverFieldSignature(String descriptor, int start, int end, boolean voidOK)173     static int skipOverFieldSignature(String descriptor, int start, int end, boolean voidOK) {
174         int arrayDim = 0;
175         int index = start;
176         while (index < end) {
177             switch (descriptor.charAt(index)) {
178                 case JVM_SIGNATURE_VOID: if (!voidOK) { return index; }
179                 case JVM_SIGNATURE_BOOLEAN:
180                 case JVM_SIGNATURE_BYTE:
181                 case JVM_SIGNATURE_CHAR:
182                 case JVM_SIGNATURE_SHORT:
183                 case JVM_SIGNATURE_INT:
184                 case JVM_SIGNATURE_FLOAT:
185                 case JVM_SIGNATURE_LONG:
186                 case JVM_SIGNATURE_DOUBLE:
187                     return index - start + 1;
188                 case JVM_SIGNATURE_CLASS:
189                     // Skip leading 'L' and ignore first appearance of ';'
190                     index++;
191                     int indexOfSemi = descriptor.indexOf(';', index);
192                     if (indexOfSemi != -1) {
193                         String unqualifiedName = descriptor.substring(index, indexOfSemi);
194                         boolean legal = verifyUnqualifiedClassName(unqualifiedName);
195                         if (!legal) {
196                             return 0;
197                         }
198                         return index - start + unqualifiedName.length() + 1;
199                     }
200                     return 0;
201                 case JVM_SIGNATURE_ARRAY:
202                     arrayDim++;
203                     if (arrayDim > MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
204                         throw new IllegalArgumentException(String.format("Cannot create an array type descriptor with more than %d dimensions",
205                                 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS));
206                     }
207                     // The rest of what's there better be a legal descriptor
208                     index++;
209                     voidOK = false;
210                     break;
211                 default:
212                     return 0;
213             }
214         }
215         return 0;
216     }
217 
verifyUnqualifiedClassName(String name)218     static boolean verifyUnqualifiedClassName(String name) {
219         for (int index = 0; index < name.length(); index++) {
220             char ch = name.charAt(index);
221             if (ch < 128) {
222                 if (ch == '.' || ch == ';' || ch == '[' ) {
223                     return false;   // do not permit '.', ';', or '['
224                 }
225                 if (ch == '/') {
226                     // check for '//' or leading or trailing '/' which are not legal
227                     // unqualified name must not be empty
228                     if (index == 0 || index + 1 >= name.length() || name.charAt(index + 1) == '/') {
229                         return false;
230                     }
231                 }
232             } else {
233                 index ++;
234             }
235         }
236         return true;
237     }
238 }
239