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