1 /* 2 * Copyright (c) 2015, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jdk.test.lib.jittester; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.io.StringReader; 29 import java.lang.reflect.Executable; 30 import java.lang.reflect.Method; 31 import java.lang.reflect.Modifier; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.HashMap; 37 import java.util.HashSet; 38 import java.util.LinkedList; 39 import java.util.List; 40 import java.util.Set; 41 import jdk.test.lib.Asserts; 42 import jdk.test.lib.jittester.functions.FunctionInfo; 43 import jdk.test.lib.jittester.types.TypeArray; 44 import jdk.test.lib.jittester.types.TypeKlass; 45 import jdk.test.lib.jittester.types.TypeVoid; 46 47 /** 48 * Class used for parsing included classes file and excluded methods file 49 */ 50 public class TypesParser { 51 52 private static final HashMap<Class<?>, Type> TYPE_CACHE = new HashMap<>(); 53 54 /** 55 * Parses included classes file and excluded methods file to TypeList and SymbolTable. 56 * This routine takes all classes named in the classes file and puts them to the TypeList, 57 * it also puts all the methods of the classes to the SymbolTable. 58 * Excluded methods file is used to remove methods from the SymbolTable. 59 * @param klassesFileName - name of the included classes file 60 * @param exMethodsFileName - name of the excluded method file 61 */ 62 public static void parseTypesAndMethods(String klassesFileName, String exMethodsFileName) { 63 Asserts.assertNotNull(klassesFileName, "Classes input file name is null"); 64 Asserts.assertFalse(klassesFileName.isEmpty(), "Classes input file name is empty"); 65 Set<Class<?>> klasses = parseKlasses(klassesFileName); 66 Set<Executable> methodsToExclude; 67 if (exMethodsFileName != null && !exMethodsFileName.isEmpty()) { 68 methodsToExclude = parseMethods(exMethodsFileName); 69 } else { 70 methodsToExclude = new HashSet<>(); 71 } 72 klasses.stream().forEach(klass -> { 73 TypeKlass typeKlass = (TypeKlass) getType(klass); 74 if (TypeList.isReferenceType(typeKlass)) { 75 return; 76 } 77 TypeList.add(typeKlass); 78 Set<Executable> methods = new HashSet<>(); 79 methods.addAll(Arrays.asList(klass.getMethods())); 80 methods.addAll(Arrays.asList(klass.getConstructors())); 81 methods.removeAll(methodsToExclude); 82 methods.stream().forEach(method -> { 83 if (method.isSynthetic()) { 84 return; 85 } 86 String name = method.getName(); 87 boolean isConstructor = false; 88 Type returnType; 89 if (name.equals(klass.getName())) { 90 isConstructor = true; 91 returnType = typeKlass; 92 } else { 93 returnType = getType(((Method) method).getReturnType()); 94 } 95 ArrayList<VariableInfo> paramList = new ArrayList<>(); 96 int flags = getMethodFlags(method); 97 if (!isConstructor && ((flags & FunctionInfo.STATIC) == 0)) { 98 paramList.add(new VariableInfo("this", typeKlass, typeKlass, 99 VariableInfo.LOCAL | VariableInfo.INITIALIZED)); 100 } 101 Class<?>[] paramKlasses = method.getParameterTypes(); 102 int argNum = 0; 103 for (Class<?> paramKlass : paramKlasses) { 104 argNum++; 105 Type paramType = getType(paramKlass); 106 paramList.add(new VariableInfo("arg" + argNum, typeKlass, paramType, 107 VariableInfo.LOCAL | VariableInfo.INITIALIZED)); 108 } 109 typeKlass.addSymbol(new FunctionInfo(name, typeKlass, returnType, 1, flags, 110 paramList)); 111 }); 112 }); 113 } 114 115 private static Type getType(Class<?> klass) { 116 Type type = TYPE_CACHE.get(klass); 117 if (type != null) { 118 return type; 119 } 120 if (klass.isPrimitive()) { 121 if (klass.equals(void.class)) { 122 type = new TypeVoid(); 123 } else { 124 type = TypeList.find(klass.getName()); 125 } 126 } else { 127 int flags = getKlassFlags(klass); 128 if (klass.isArray()) { 129 TypeKlass elementType 130 = new TypeKlass(klass.getCanonicalName().replaceAll("\\[\\]", ""), flags); 131 int dim = getArrayClassDimension(klass); 132 type = new TypeArray(elementType, dim); 133 } else { 134 String canonicalName = klass.getCanonicalName(); 135 if (!"java.lang.Object".equals(canonicalName)) { 136 flags |= TypeKlass.FINAL; 137 } 138 type = new TypeKlass(canonicalName, flags); 139 } 140 Class<?> parentKlass = klass.getSuperclass(); 141 if (parentKlass != null) { 142 TypeKlass parentTypeKlass = (TypeKlass) getType(parentKlass); 143 ((TypeKlass) type).addParent(parentTypeKlass.getName()); 144 ((TypeKlass) type).setParent(parentTypeKlass); 145 } 146 } 147 TYPE_CACHE.put(klass, type); 148 return type; 149 } 150 151 private static int getArrayClassDimension(Class<?> klass) { 152 if (!klass.isArray()) { 153 return 0; 154 } 155 String name = klass.getName(); 156 int begin = name.indexOf('['); 157 name = name.substring(begin, name.length()); 158 return name.length() / 2; 159 } 160 161 private static int getKlassFlags(Class<?> klass) { 162 int flags = TypeKlass.NONE; 163 if (klass.isInterface()) { 164 flags = flags | TypeKlass.INTERFACE; 165 } else if ((klass.getModifiers() & Modifier.ABSTRACT) != 0) { 166 flags = flags | TypeKlass.ABSTRACT; 167 } else if ((klass.getModifiers() & Modifier.FINAL) != 0) { 168 flags = flags | TypeKlass.FINAL; 169 } 170 return flags; 171 } 172 173 private static int getMethodFlags(Executable method) { 174 int flags = FunctionInfo.NONE; 175 int modifiers = method.getModifiers(); 176 if (Modifier.isAbstract(modifiers)) { 177 flags |= FunctionInfo.ABSTRACT; 178 } 179 if (Modifier.isFinal(modifiers)) { 180 flags |= FunctionInfo.FINAL; 181 } 182 if (Modifier.isPublic(modifiers)) { 183 flags |= FunctionInfo.PUBLIC; 184 } else if (Modifier.isProtected(modifiers)) { 185 flags |= FunctionInfo.PROTECTED; 186 } else if (Modifier.isPrivate(modifiers)) { 187 flags |= FunctionInfo.PRIVATE; 188 } else { 189 flags |= FunctionInfo.DEFAULT; 190 } 191 if (Modifier.isStatic(modifiers)) { 192 flags |= FunctionInfo.STATIC; 193 } 194 if (Modifier.isSynchronized(modifiers)) { 195 flags |= FunctionInfo.SYNCHRONIZED; 196 } 197 return flags; 198 } 199 200 private static Set<Class<?>> parseKlasses(String klassesFileName) { 201 Asserts.assertNotNull(klassesFileName, "Classes input file name is null"); 202 Asserts.assertFalse(klassesFileName.isEmpty(), "Classes input file name is empty"); 203 Set<String> klassNamesSet = new HashSet<>(); 204 Path klassesFilePath = (new File(klassesFileName)).toPath(); 205 try { 206 Files.lines(klassesFilePath).forEach(line -> { 207 line = line.trim(); 208 if (line.isEmpty()) { 209 return; 210 } 211 String msg = String.format("Format of the classes input file \"%s\" is incorrect," 212 + " line \"%s\" has wrong format", klassesFileName, line); 213 Asserts.assertTrue(line.matches("\\w[\\w\\.$]*"), msg); 214 klassNamesSet.add(line.replaceAll(";", "")); 215 }); 216 } catch (IOException ex) { 217 throw new Error("Error reading klasses file", ex); 218 } 219 Set<Class<?>> klassesSet = new HashSet<>(); 220 klassNamesSet.stream().forEach(klassName -> { 221 try { 222 klassesSet.add(Class.forName(klassName)); 223 } catch (ClassNotFoundException ex) { 224 throw new Error("Unexpected exception while parsing klasses file", ex); 225 } 226 }); 227 return klassesSet; 228 } 229 230 private static Set<Executable> parseMethods(String methodsFileName) { 231 Asserts.assertNotNull(methodsFileName, "Methods exclude input file name is null"); 232 Asserts.assertFalse(methodsFileName.isEmpty(), "Methods exclude input file name is empty"); 233 LinkedList<String> methodNamesList = new LinkedList<>(); 234 Path klassesFilePath = (new File(methodsFileName)).toPath(); 235 try { 236 Files.lines(klassesFilePath).forEach(line -> { 237 line = line.trim(); 238 if (line.isEmpty()) { 239 return; 240 } 241 String msg = String.format("Format of the methods exclude input file \"%s\" is incorrect," 242 + " line \"%s\" has wrong format", methodsFileName, line); 243 Asserts.assertTrue(line.matches("\\w[\\w/$]*::[\\w$]+\\((\\[?[ZBSCIJFD]|\\[?L[\\w/$]+;)*\\)"), msg); 244 methodNamesList.add(line.substring(0, line.length() - 1)); 245 }); 246 } catch (IOException ex) { 247 throw new Error("Error reading exclude method file", ex); 248 } 249 Set<Executable> methodsList = new HashSet<>(); 250 methodNamesList.stream().forEach(methodName -> { 251 String[] klassAndNameAndSig = methodName.split("::"); 252 String klassName = klassAndNameAndSig[0].replaceAll("/", "\\."); 253 String[] nameAndSig = klassAndNameAndSig[1].split("[\\(\\)]"); 254 String name = nameAndSig[0]; 255 String signature = ""; 256 if (nameAndSig.length > 1) { 257 signature = nameAndSig[1]; 258 } 259 Class<?> klass = null; 260 List<Class<?>> signatureTypes = null; 261 try { 262 klass = Class.forName(klassName); 263 signatureTypes = parseSignature(signature); 264 } catch (ClassNotFoundException ex) { 265 throw new Error("Unexpected exception while parsing exclude methods file", ex); 266 } 267 try { 268 Executable method; 269 if (name.equals(klass.getSimpleName())) { 270 method = klass.getConstructor(signatureTypes.toArray(new Class<?>[0])); 271 } else { 272 method = klass.getMethod(name, signatureTypes.toArray(new Class<?>[0])); 273 } 274 methodsList.add(method); 275 } catch (NoSuchMethodException | SecurityException ex) { 276 throw new Error("Unexpected exception while parsing exclude methods file", ex); 277 } 278 }); 279 return methodsList; 280 } 281 282 private static List<Class<?>> parseSignature(String signature) throws ClassNotFoundException { 283 LinkedList<Class<?>> sigClasses = new LinkedList<>(); 284 char typeChar; 285 boolean isArray; 286 String klassName; 287 StringBuilder sb; 288 StringBuilder arrayDim; 289 try (StringReader str = new StringReader(signature)) { 290 int symbol = str.read(); 291 while (symbol != -1){ 292 typeChar = (char) symbol; 293 arrayDim = new StringBuilder(); 294 Class<?> primArrayClass = null; 295 if (typeChar == '[') { 296 isArray = true; 297 arrayDim.append('['); 298 symbol = str.read(); 299 while (symbol == '['){ 300 arrayDim.append('['); 301 symbol = str.read(); 302 } 303 typeChar = (char) symbol; 304 if (typeChar != 'L') { 305 primArrayClass = Class.forName(arrayDim.toString() + typeChar); 306 } 307 } else { 308 isArray = false; 309 } 310 switch (typeChar) { 311 case 'Z': 312 sigClasses.add(isArray ? primArrayClass : boolean.class); 313 break; 314 case 'I': 315 sigClasses.add(isArray ? primArrayClass : int.class); 316 break; 317 case 'J': 318 sigClasses.add(isArray ? primArrayClass : long.class); 319 break; 320 case 'F': 321 sigClasses.add(isArray ? primArrayClass : float.class); 322 break; 323 case 'D': 324 sigClasses.add(isArray ? primArrayClass : double.class); 325 break; 326 case 'B': 327 sigClasses.add(isArray ? primArrayClass : byte.class); 328 break; 329 case 'S': 330 sigClasses.add(isArray ? primArrayClass : short.class); 331 break; 332 case 'C': 333 sigClasses.add(isArray ? primArrayClass : char.class); 334 break; 335 case 'L': 336 sb = new StringBuilder(); 337 symbol = str.read(); 338 while (symbol != ';') { 339 sb.append((char) symbol); 340 symbol = str.read(); 341 } 342 klassName = sb.toString().replaceAll("/", "\\."); 343 if (isArray) { 344 klassName = arrayDim.toString() + "L" + klassName + ";"; 345 } 346 Class<?> klass = Class.forName(klassName); 347 sigClasses.add(klass); 348 break; 349 default: 350 throw new Error("Unknown type " + typeChar); 351 } 352 symbol = str.read(); 353 } 354 } catch (IOException ex) { 355 throw new Error("Unexpected exception while parsing exclude methods file", ex); 356 } 357 return sigClasses; 358 } 359 }