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.NoSuchElementException; 30 import java.util.Optional; 31 import java.util.Set; 32 33 import static java.util.Objects.requireNonNull; 34 35 /** 36 * Helper methods for the implementation of {@code java.lang.constant}. 37 */ 38 class ConstantUtils { 39 /** an empty constant descriptor */ 40 public static final ConstantDesc<?>[] EMPTY_CONSTANTDESC = new ConstantDesc<?>[0]; 41 static final Constable<?>[] EMPTY_CONSTABLE = new Constable<?>[0]; 42 43 private static final Set<String> pointyNames = Set.of("<init>", "<clinit>"); 44 45 static String validateBinaryClassName(String name) { 46 for (int i=0; i<name.length(); i++) { 47 char ch = name.charAt(i); 48 if (ch == ';' || ch == '[' || ch == '/') 49 throw new IllegalArgumentException("Invalid class name: " + name); 50 } 51 return name; 52 } 53 54 /** 55 * validates a member name 56 * @param name the name of the member 57 * @return the name passed if valid 58 */ 59 public static String validateMemberName(String name) { 60 requireNonNull(name); 61 if (name.length() == 0) 62 throw new IllegalArgumentException("zero-length member name"); 63 for (int i=0; i<name.length(); i++) { 64 char ch = name.charAt(i); 65 if (ch == '.' || ch == ';' || ch == '[' || ch == '/') 66 throw new IllegalArgumentException("Invalid member name: " + name); 67 if (ch == '<' || ch == '>') { 68 if (!pointyNames.contains(name)) 69 throw new IllegalArgumentException("Invalid member name: " + name); 70 } 71 } 72 return name; 73 } 74 75 static void validateClassOrInterface(ClassDesc clazz) { 76 if (!clazz.isClassOrInterface()) 77 throw new IllegalArgumentException("not a class or interface type: " + clazz); 78 } 79 80 static int arrayDepth(String descriptorString) { 81 int depth = 0; 82 while (descriptorString.charAt(depth) == '[') 83 depth++; 84 return depth; 85 } 86 87 static String binaryToInternal(String name) { 88 return name.replace('.', '/'); 89 } 90 91 static String internalToBinary(String name) { 92 return name.replace('/', '.'); 93 } 94 95 static String dropLastChar(String s) { 96 return s.substring(0, s.length() - 1); 97 } 98 99 static String dropFirstAndLastChar(String s) { 100 return s.substring(1, s.length() - 1); 101 } 102 103 /** 104 * Produce an {@code Optional<DynamicConstantDesc<T>>} describing the invocation 105 * of the specified bootstrap with the specified arguments. The arguments will 106 * be converted to nominal descriptors using the provided lookup. Helper 107 * method for implementing {@link Constable#describeConstable()}. 108 * 109 * @param <T> the type of the resulting constant 110 * @param bootstrap nominal descriptor for the bootstrap method 111 * @param type nominal descriptor for the type of the resulting constant 112 * @param args nominal descriptors for the bootstrap arguments 113 * @return the nominal descriptor for the dynamic constant 114 */ 115 public static<T> Optional<DynamicConstantDesc<T>> symbolizeHelper(MethodHandleDesc bootstrap, 116 ClassDesc type, 117 Constable<?>... args) { 118 requireNonNull(bootstrap); 119 requireNonNull(type); 120 requireNonNull(args); 121 try { 122 ConstantDesc<?>[] quotedArgs = new ConstantDesc<?>[args.length + 1]; 123 quotedArgs[0] = bootstrap; 124 for (int i=0; i<args.length; i++) 125 quotedArgs[i+1] = args[i].describeConstable().orElseThrow(); 126 return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, 127 ConstantDescs.DEFAULT_NAME, 128 type, quotedArgs)); 129 } 130 catch (NoSuchElementException e) { 131 return Optional.empty(); 132 } 133 } 134 135 /** 136 * Parse a method descriptor string, and return a list of field descriptor 137 * strings, return type first, then parameter types 138 * 139 * @param descriptor the descriptor string 140 * @return the list of types 141 * @throws IllegalArgumentException if the descriptor string is not valid 142 */ 143 static List<String> parseMethodDescriptor(String descriptor) { 144 int cur = 0, end = descriptor.length(); 145 ArrayList<String> ptypes = new ArrayList<>(); 146 147 if (cur >= end || descriptor.charAt(cur) != '(') 148 throw new IllegalArgumentException("Bad method descriptor: " + descriptor); 149 150 ++cur; // skip '(' 151 while (cur < end && descriptor.charAt(cur) != ')') { 152 int len = matchSig(descriptor, cur, end); 153 if (len == 0 || descriptor.charAt(cur) == 'V') 154 throw new IllegalArgumentException("Bad method descriptor: " + descriptor); 155 ptypes.add(descriptor.substring(cur, cur + len)); 156 cur += len; 157 } 158 if (cur >= end) 159 throw new IllegalArgumentException("Bad method descriptor: " + descriptor); 160 ++cur; // skip ')' 161 162 int rLen = matchSig(descriptor, cur, end); 163 if (rLen == 0 || cur + rLen != end) 164 throw new IllegalArgumentException("Bad method descriptor: " + descriptor); 165 ptypes.add(0, descriptor.substring(cur, cur + rLen)); 166 return ptypes; 167 } 168 169 /** 170 * Validate that the characters at [start, end) within the provided string 171 * describe a valid field type descriptor. 172 * 173 * @param str the descriptor string 174 * @param start the starting index into the string 175 * @param end the ending index within the string 176 * @return the length of the descriptor, or 0 if it is not a descriptor 177 * @throws IllegalArgumentException if the descriptor string is not valid 178 */ 179 static int matchSig(String str, int start, int end) { 180 if (start >= end || start >= str.length() || end > str.length()) 181 return 0; 182 char c = str.charAt(start); 183 if (c == 'L') { 184 int endc = str.indexOf(';', start); 185 int badc = str.indexOf('.', start); 186 if (badc >= 0 && badc < endc) 187 return 0; 188 badc = str.indexOf('[', start); 189 if (badc >= 0 && badc < endc) 190 return 0; 191 return (endc < 0) ? 0 : endc - start + 1; 192 } else if (c == '[') { 193 int t = matchSig(str, start+1, end); 194 return (t > 0) ? t + 1 : 0; 195 } else { 196 return ("IJCSBFDZV".indexOf(c) >= 0) ? 1 : 0; 197 } 198 } 199 }