1 /* 2 * Copyright (c) 2018, 2024, 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 jdk.internal.constant; 26 27 import sun.invoke.util.Wrapper; 28 29 import java.lang.constant.ClassDesc; 30 import java.lang.constant.ConstantDesc; 31 import java.lang.constant.ConstantDescs; 32 import java.lang.constant.MethodTypeDesc; 33 import java.lang.invoke.MethodType; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Set; 37 38 /** 39 * Helper methods for the implementation of {@code java.lang.constant}. 40 */ 41 public final class ConstantUtils { 42 /** an empty constant descriptor */ 43 public static final ConstantDesc[] EMPTY_CONSTANTDESC = new ConstantDesc[0]; 44 public static final ClassDesc[] EMPTY_CLASSDESC = new ClassDesc[0]; 45 public static final int MAX_ARRAY_TYPE_DESC_DIMENSIONS = 255; 46 public static final ClassDesc CD_module_info = binaryNameToDesc("module-info"); 47 48 private static final Set<String> pointyNames = Set.of(ConstantDescs.INIT_NAME, ConstantDescs.CLASS_INIT_NAME); 49 50 /** No instantiation */ 51 private ConstantUtils() {} 52 53 // Note: 54 // Non-JDK users should create their own utilities that wrap 55 // {@code .describeConstable().orElseThrow()} calls; 56 // these xxDesc methods has undefined and unsafe exceptional 57 // behavior, so they are not suitable as public APIs. 58 59 /** 60 * Creates a {@linkplain ClassDesc} from a pre-validated binary name 61 * for a class or interface type. Validated version of {@link 62 * ClassDesc#of(String)}. 63 * 64 * @param binaryName a binary name 65 */ 66 public static ClassDesc binaryNameToDesc(String binaryName) { 67 return ClassDescImpl.ofValidated("L" + binaryToInternal(binaryName) + ";"); 68 } 69 70 /** 71 * Creates a ClassDesc from a Class object, requires that this class 72 * can always be described nominally, i.e. this class is not a 73 * hidden class or interface or an array with a hidden component 74 * type. 75 */ 76 public static ClassDesc classDesc(Class<?> type) { 77 if (type.isPrimitive()) { 78 return Wrapper.forPrimitiveType(type).basicClassDescriptor(); 79 } 80 return referenceClassDesc(type); 81 } 82 83 /** 84 * Creates a ClassDesc from a Class object representing a non-hidden 85 * class or interface or an array type with a non-hidden component type. 86 */ 87 public static ClassDesc referenceClassDesc(Class<?> type) { 88 return ClassDescImpl.ofValidated(type.descriptorString()); 89 } 90 91 /** 92 * Creates a MethodTypeDesc from a MethodType object, requires that 93 * the type can be described nominally, i.e. all of its return 94 * type and parameter types can be described nominally. 95 */ 96 public static MethodTypeDesc methodTypeDesc(MethodType type) { 97 var returnDesc = classDesc(type.returnType()); 98 if (type.parameterCount() == 0) { 99 return MethodTypeDescImpl.ofValidated(returnDesc, EMPTY_CLASSDESC); 100 } 101 var paramDescs = new ClassDesc[type.parameterCount()]; 102 for (int i = 0; i < type.parameterCount(); i++) { 103 paramDescs[i] = classDesc(type.parameterType(i)); 104 } 105 return MethodTypeDescImpl.ofValidated(returnDesc, paramDescs); 106 } 107 108 /** 109 * Creates a MethodTypeDesc from return class and parameter 110 * class objects, requires that all of them can be described nominally. 111 * This version is mainly useful for working with Method objects. 112 */ 113 public static MethodTypeDesc methodTypeDesc(Class<?> returnType, Class<?>[] parameterTypes) { 114 var returnDesc = classDesc(returnType); 115 if (parameterTypes.length == 0) { 116 return MethodTypeDescImpl.ofValidated(returnDesc, EMPTY_CLASSDESC); 117 } 118 var paramDescs = new ClassDesc[parameterTypes.length]; 119 for (int i = 0; i < parameterTypes.length; i++) { 120 paramDescs[i] = classDesc(parameterTypes[i]); 121 } 122 return MethodTypeDescImpl.ofValidated(returnDesc, paramDescs); 123 } 124 125 /** 126 * Validates the correctness of a binary class name. In particular checks for the presence of 127 * invalid characters in the name. 128 * 129 * @param name the class name 130 * @return the class name passed if valid 131 * @throws IllegalArgumentException if the class name is invalid 132 * @throws NullPointerException if class name is {@code null} 133 */ 134 public static String validateBinaryClassName(String name) { 135 for (int i = 0; i < name.length(); i++) { 136 char ch = name.charAt(i); 137 if (ch == ';' || ch == '[' || ch == '/') 138 throw new IllegalArgumentException("Invalid class name: " + name); 139 } 140 return name; 141 } 142 143 /** 144 * Validates the correctness of an internal class name. 145 * In particular checks for the presence of invalid characters in the name. 146 * 147 * @param name the class name 148 * @return the class name passed if valid 149 * @throws IllegalArgumentException if the class name is invalid 150 * @throws NullPointerException if class name is {@code null} 151 */ 152 public static String validateInternalClassName(String name) { 153 for (int i = 0; i < name.length(); i++) { 154 char ch = name.charAt(i); 155 if (ch == ';' || ch == '[' || ch == '.') 156 throw new IllegalArgumentException("Invalid class name: " + name); 157 } 158 return name; 159 } 160 161 /** 162 * Validates the correctness of a binary package name. 163 * In particular checks for the presence of invalid characters in the name. 164 * Empty package name is allowed. 165 * 166 * @param name the package name 167 * @return the package name passed if valid 168 * @throws IllegalArgumentException if the package name is invalid 169 * @throws NullPointerException if the package name is {@code null} 170 */ 171 public static String validateBinaryPackageName(String name) { 172 for (int i = 0; i < name.length(); i++) { 173 char ch = name.charAt(i); 174 if (ch == ';' || ch == '[' || ch == '/') 175 throw new IllegalArgumentException("Invalid package name: " + name); 176 } 177 return name; 178 } 179 180 /** 181 * Validates the correctness of an internal package name. 182 * In particular checks for the presence of invalid characters in the name. 183 * Empty package name is allowed. 184 * 185 * @param name the package name 186 * @return the package name passed if valid 187 * @throws IllegalArgumentException if the package name is invalid 188 * @throws NullPointerException if the package name is {@code null} 189 */ 190 public static String validateInternalPackageName(String name) { 191 for (int i = 0; i < name.length(); i++) { 192 char ch = name.charAt(i); 193 if (ch == ';' || ch == '[' || ch == '.') 194 throw new IllegalArgumentException("Invalid package name: " + name); 195 } 196 return name; 197 } 198 199 /** 200 * Validates the correctness of a module name. 201 * In particular checks for the presence of invalid characters in the name. 202 * Empty module name is allowed. 203 * 204 * {@jvms 4.2.3} Module and Package Names 205 * 206 * @param name the module name 207 * @return the module name passed if valid 208 * @throws IllegalArgumentException if the module name is invalid 209 * @throws NullPointerException if the module name is {@code null} 210 */ 211 public static String validateModuleName(String name) { 212 for (int i = name.length() - 1; i >= 0; i--) { 213 char ch = name.charAt(i); 214 if ((ch >= '\u0000' && ch <= '\u001F') 215 || ((ch == '\\' || ch == ':' || ch =='@') && (i == 0 || name.charAt(--i) != '\\'))) 216 throw new IllegalArgumentException("Invalid module name: " + name); 217 } 218 return name; 219 } 220 221 /** 222 * Validates a member name 223 * 224 * @param name the name of the member 225 * @return the name passed if valid 226 * @throws IllegalArgumentException if the member name is invalid 227 * @throws NullPointerException if the member name is {@code null} 228 */ 229 public static String validateMemberName(String name, boolean method) { 230 int len = name.length(); 231 if (len == 0) 232 throw new IllegalArgumentException("zero-length member name"); 233 for (int i = 0; i < len; i++) { 234 char ch = name.charAt(i); 235 // common case fast-path 236 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) 237 continue; 238 if (ch == '.' || ch == ';' || ch == '[' || ch == '/') 239 throw new IllegalArgumentException("Invalid member name: " + name); 240 if (method && (ch == '<' || ch == '>')) { 241 if (!pointyNames.contains(name)) 242 throw new IllegalArgumentException("Invalid member name: " + name); 243 } 244 } 245 return name; 246 } 247 248 public static void validateClassOrInterface(ClassDesc classDesc) { 249 if (!classDesc.isClassOrInterface()) 250 throw new IllegalArgumentException("not a class or interface type: " + classDesc); 251 } 252 253 public static int arrayDepth(String descriptorString) { 254 int depth = 0; 255 while (descriptorString.charAt(depth) == '[') 256 depth++; 257 return depth; 258 } 259 260 public static String binaryToInternal(String name) { 261 return name.replace('.', '/'); 262 } 263 264 public static String internalToBinary(String name) { 265 return name.replace('/', '.'); 266 } 267 268 public static String dropFirstAndLastChar(String s) { 269 return s.substring(1, s.length() - 1); 270 } 271 272 /** 273 * Parses a method descriptor string, and return a list of field descriptor 274 * strings, return type first, then parameter types 275 * 276 * @param descriptor the descriptor string 277 * @return the list of types 278 * @throws IllegalArgumentException if the descriptor string is not valid 279 */ 280 public static List<ClassDesc> parseMethodDescriptor(String descriptor) { 281 int cur = 0, end = descriptor.length(); 282 ArrayList<ClassDesc> ptypes = new ArrayList<>(); 283 ptypes.add(null); // placeholder for return type 284 285 if (cur >= end || descriptor.charAt(cur) != '(') 286 throw new IllegalArgumentException("Bad method descriptor: " + descriptor); 287 288 ++cur; // skip '(' 289 while (cur < end && descriptor.charAt(cur) != ')') { 290 int len = skipOverFieldSignature(descriptor, cur, end, false); 291 if (len == 0) 292 throw new IllegalArgumentException("Bad method descriptor: " + descriptor); 293 ptypes.add(resolveClassDesc(descriptor, cur, len)); 294 cur += len; 295 } 296 if (cur >= end) 297 throw new IllegalArgumentException("Bad method descriptor: " + descriptor); 298 ++cur; // skip ')' 299 300 int rLen = skipOverFieldSignature(descriptor, cur, end, true); 301 if (rLen == 0 || cur + rLen != end) 302 throw new IllegalArgumentException("Bad method descriptor: " + descriptor); 303 ptypes.set(0, resolveClassDesc(descriptor, cur, rLen)); 304 return ptypes; 305 } 306 307 private static ClassDesc resolveClassDesc(String descriptor, int start, int len) { 308 if (len == 1) { 309 return Wrapper.forPrimitiveType(descriptor.charAt(start)).basicClassDescriptor(); 310 } 311 // Pre-verified in parseMethodDescriptor; avoid redundant verification 312 return ClassDescImpl.ofValidated(descriptor.substring(start, start + len)); 313 } 314 315 private static final char JVM_SIGNATURE_ARRAY = '['; 316 private static final char JVM_SIGNATURE_BYTE = 'B'; 317 private static final char JVM_SIGNATURE_CHAR = 'C'; 318 private static final char JVM_SIGNATURE_CLASS = 'L'; 319 private static final char JVM_SIGNATURE_ENDCLASS = ';'; 320 private static final char JVM_SIGNATURE_ENUM = 'E'; 321 private static final char JVM_SIGNATURE_FLOAT = 'F'; 322 private static final char JVM_SIGNATURE_DOUBLE = 'D'; 323 private static final char JVM_SIGNATURE_FUNC = '('; 324 private static final char JVM_SIGNATURE_ENDFUNC = ')'; 325 private static final char JVM_SIGNATURE_INT = 'I'; 326 private static final char JVM_SIGNATURE_LONG = 'J'; 327 private static final char JVM_SIGNATURE_SHORT = 'S'; 328 private static final char JVM_SIGNATURE_VOID = 'V'; 329 private static final char JVM_SIGNATURE_BOOLEAN = 'Z'; 330 331 /** 332 * Validates that the characters at [start, end) within the provided string 333 * describe a valid field type descriptor. 334 * @param descriptor the descriptor string 335 * @param start the starting index into the string 336 * @param end the ending index within the string 337 * @param voidOK is void acceptable? 338 * @return the length of the descriptor, or 0 if it is not a descriptor 339 * @throws IllegalArgumentException if the descriptor string is not valid 340 */ 341 @SuppressWarnings("fallthrough") 342 static int skipOverFieldSignature(String descriptor, int start, int end, boolean voidOK) { 343 int arrayDim = 0; 344 int index = start; 345 while (index < end) { 346 switch (descriptor.charAt(index)) { 347 case JVM_SIGNATURE_VOID: if (!voidOK) { return 0; } 348 case JVM_SIGNATURE_BOOLEAN: 349 case JVM_SIGNATURE_BYTE: 350 case JVM_SIGNATURE_CHAR: 351 case JVM_SIGNATURE_SHORT: 352 case JVM_SIGNATURE_INT: 353 case JVM_SIGNATURE_FLOAT: 354 case JVM_SIGNATURE_LONG: 355 case JVM_SIGNATURE_DOUBLE: 356 return index - start + 1; 357 case JVM_SIGNATURE_CLASS: 358 // state variable for detection of illegal states, such as: 359 // empty unqualified name, '//', leading '/', or trailing '/' 360 boolean legal = false; 361 while (++index < end) { 362 switch (descriptor.charAt(index)) { 363 case ';' -> { 364 // illegal state on parser exit indicates empty unqualified name or trailing '/' 365 return legal ? index - start + 1 : 0; 366 } 367 case '.', '[' -> { 368 // do not permit '.' or '[' 369 return 0; 370 } 371 case '/' -> { 372 // illegal state when received '/' indicates '//' or leading '/' 373 if (!legal) return 0; 374 legal = false; 375 } 376 default -> 377 legal = true; 378 } 379 } 380 return 0; 381 case JVM_SIGNATURE_ARRAY: 382 arrayDim++; 383 if (arrayDim > MAX_ARRAY_TYPE_DESC_DIMENSIONS) { 384 throw new IllegalArgumentException(String.format("Cannot create an array type descriptor with more than %d dimensions", 385 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS)); 386 } 387 // The rest of what's there better be a legal descriptor 388 index++; 389 voidOK = false; 390 break; 391 default: 392 return 0; 393 } 394 } 395 return 0; 396 } 397 }