1 /* 2 * Copyright (c) 2009, 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 com.sun.classanalyzer; 25 26 import java.io.BufferedReader; 27 import java.io.FileInputStream; 28 import java.io.IOException; 29 import java.io.InputStreamReader; 30 import java.io.PrintWriter; 31 import java.io.File; 32 import java.util.ArrayDeque; 33 import java.util.Deque; 34 import java.util.HashMap; 35 import java.util.LinkedList; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.TreeSet; 40 41 import com.sun.tools.classfile.*; 42 import com.sun.tools.classfile.ConstantPool.*; 43 import static com.sun.tools.classfile.ConstantPool.*; 44 import com.sun.tools.classfile.Instruction.TypeKind; 45 import com.sun.tools.classfile.Type.*; 46 47 /** 48 * Generate the module config for the boot module with 49 * a given set of roots (classes or methods) and exclude list. 50 * 51 * This tool does method-level dependency analysis starting 52 * from the root set and follows references transitively as follows: 53 * <ul> 54 * <li>For a given class, it will parse the ClassFile to 55 * find its superclass and superinterfaces and also 56 * its static initializer <clinit>.</li> 57 * <li>For each method, it will parse its Code attribute 58 * to look for a Methodref, Fieldref, and InterfaceMethodref. 59 * </li> 60 * <li>For each Fieldref, it will include the type of 61 * the field in the dependency.</li> 62 * <li>For each MethodRef, it will follow all references in 63 * that method.</li> 64 * <li>For each InterfaceMethodref, it will follow all references in 65 * that method defined its implementation classes in 66 * the resulting dependency list.</li> 67 * </ul> 68 * 69 * Limitation: 70 * <ul> 71 * <li>For each Methodref, it only parses the method of 72 * the specified type. It doesn't analyze the class hierarchy 73 * and follow references of its subclasses since it ends up 74 * pulls in many unnecessary dependencies. For now, 75 * the list of subclasses and methods need to be listed in 76 * the root set.</li> 77 * </ul> 78 * 79 * @author Mandy Chung 80 */ 81 public class BootAnalyzer { 82 83 public static void main(String[] args) throws Exception { 84 String jdkhome = null; 85 String config = null; 86 String output = "."; 87 boolean printClassList = false; 88 89 // process arguments 90 int i = 0; 91 while (i < args.length) { 92 String arg = args[i++]; 93 if (arg.equals("-jdkhome")) { 94 if (i < args.length) { 95 jdkhome = args[i++]; 96 } else { 97 usage(); 98 } 99 } else if (arg.equals("-config")) { 100 config = args[i++]; 101 } else if (arg.equals("-output")) { 102 output = args[i++]; 103 } else if (arg.equals("-classlist")) { 104 printClassList = true; 105 } else { 106 usage(); 107 } 108 } 109 110 111 112 if (jdkhome == null || config == null) { 113 usage(); 114 } 115 116 File jre = new File(jdkhome, "jre"); 117 if (jre.exists()) { 118 ClassPath.setJDKHome(jdkhome); 119 } else { 120 File classes = new File(jdkhome, "classes"); 121 if (classes.exists()) { 122 ClassPath.setClassPath(classes.getCanonicalPath()); 123 } else { 124 throw new RuntimeException("Invalid jdkhome: " + jdkhome); 125 } 126 } 127 128 parseConfigFile(config); 129 followRoots(); 130 131 // create output directory if it doesn't exist 132 File dir = new File(output); 133 if (!dir.isDirectory()) { 134 if (!dir.exists()) { 135 boolean created = dir.mkdir(); 136 if (!created) { 137 throw new RuntimeException("Unable to create `" + dir + "'"); 138 } 139 } 140 } 141 142 String bootmodule = "boot"; 143 String bootconfig = resolve(dir, bootmodule, "config"); 144 printBootConfig(bootconfig, bootmodule); 145 146 List<ModuleConfig> list = ModuleConfig.readConfigurationFile(bootconfig); 147 Module module = Module.addModule(list.get(0)); 148 for (Klass k : Klass.getAllClasses()) { 149 module.addKlass(k); 150 } 151 module.fixupDependencies(); 152 153 if (printClassList) { 154 module.printClassListTo(resolve(dir, bootmodule, "classlist")); 155 module.printSummaryTo(resolve(dir, bootmodule, "summary")); 156 } 157 } 158 159 // print boot.config file as an input to the ClassAnalyzer 160 private static void printBootConfig(String output, String bootmodule) throws IOException { 161 162 File f = new File(output); 163 PrintWriter writer = new PrintWriter(f); 164 try { 165 int count = 0; 166 writer.format("module %s {%n", bootmodule); 167 for (Klass k : Klass.getAllClasses()) { 168 if (count++ == 0) { 169 writer.format("%4s%7s %s", "", "include", k); 170 } else { 171 writer.format(",%n"); 172 writer.format("%4s%7s %s", "", "", k); 173 } 174 } 175 writer.format(";%n}%n"); 176 } finally { 177 writer.close(); 178 } 179 } 180 181 private static String resolve(File dir, String mname, String suffix) { 182 File f = new File(dir, mname + "." + suffix); 183 return f.toString(); 184 185 } 186 static List<MethodDescriptor> methods = new LinkedList<MethodDescriptor>(); 187 static Deque<MethodDescriptor> pending = new ArrayDeque<MethodDescriptor>(); 188 static Deque<MethodDescriptor> interfaceMethodRefs = new ArrayDeque<MethodDescriptor>(); 189 static Filter filter = new Filter(); 190 191 private static void followRoots() throws IOException { 192 MethodDescriptor md = null; 193 194 while ((md = pending.poll()) != null) { 195 if (!methods.contains(md)) { 196 methods.add(md); 197 if (md.classname.isEmpty()) { 198 trace("Warning: class missing %s%n", md); 199 continue; 200 } 201 202 if (filter.isExcluded(md.classname)) { 203 trace("excluded %s%n", md); 204 } else { 205 KlassInfo kinfo = getKlassInfo(md.classname); 206 if (kinfo.classname.contains("$")) { 207 int pos = kinfo.classname.lastIndexOf('$'); 208 String outer = kinfo.classname.substring(0, pos); 209 if (!cache.containsKey(outer)) { 210 trace(" include outer class %s%n", outer); 211 getKlassInfo(outer).ensureParse(); 212 } 213 } 214 215 kinfo.ensureParse(); 216 if (md.methodname.length() > 0) { 217 if (filter.isExcluded(md.name)) { 218 trace("excluded %s%n", md); 219 } else { 220 if (md.interfaceMethodRef) { 221 trace("interface methodref %s%n", md); 222 interfaceMethodRefs.add(md); 223 } else { 224 List<String> descriptors = kinfo.parse(md); 225 if (descriptors.isEmpty()) { 226 if (kinfo.getSuperclass() != null) { 227 String sn = kinfo.getSuperclass().classname; 228 MethodDescriptor superMD = new MethodDescriptor(sn + "." + md.methodname, md.descriptor, false); 229 if (!methods.contains(superMD) && !pending.contains(superMD)) { 230 trace(" delegated %s to %s%n", md, superMD); 231 pending.add(superMD); 232 } 233 } else if (kinfo.isClass()) { 234 trace(" %s (not found)%n", md); 235 } else { 236 trace(" %s (interface)%n", md); 237 } 238 } else { 239 if (md.descriptor.equals("*")) { 240 trace(" parsed %s : ", md.name); 241 for (String s : descriptors) { 242 trace(" %s", s); 243 } 244 trace("%n"); 245 } 246 } 247 } 248 } 249 } 250 } 251 } 252 if (pending.isEmpty()) { 253 for (Klass k : Klass.getAllClasses()) { 254 if (k.getFileSize() == 0) { 255 getKlassInfo(k.getClassName()).ensureParse(); 256 } 257 } 258 while ((md = interfaceMethodRefs.poll()) != null) { 259 addSubClassMethods(md); 260 } 261 } 262 } 263 } 264 265 static void addSubClassMethods(MethodDescriptor md) throws IOException { 266 for (KlassInfo kinfo : getSubClasses(md.classname)) { 267 String methodname = kinfo.classname + "." + md.methodname; 268 MethodDescriptor other = new MethodDescriptor(methodname, md.descriptor, false); 269 if (!methods.contains(other) && !pending.contains(other)) { 270 trace("Warning: subclass from %s to %s%n", md.classname, other); 271 pending.add(other); 272 } 273 } 274 } 275 private final static String privilegedActionInterf = "java.security.PrivilegedAction"; 276 private final static String privilegedExceptionActionInterf = "java.security.PrivilegedExceptionAction"; 277 278 static boolean isPrivilegedAction(String classname) { 279 if (classname.isEmpty()) { 280 return false; 281 } 282 KlassInfo kinfo = getKlassInfo(classname); 283 for (KlassInfo ki : kinfo.getInterfaces()) { 284 String interf = ki.classname; 285 if (interf.equals(privilegedActionInterf) || 286 interf.equals(privilegedExceptionActionInterf)) { 287 return true; 288 } 289 } 290 return false; 291 } 292 static Map<String, KlassInfo> cache = new HashMap<String, KlassInfo>(); 293 294 static KlassInfo getKlassInfo(String classname) { 295 classname = classname.replace('/', '.'); 296 297 KlassInfo kinfo = cache.get(classname); 298 if (kinfo == null) { 299 kinfo = new KlassInfo(classname); 300 cache.put(classname, kinfo); 301 } 302 return kinfo; 303 } 304 305 static class KlassInfo { 306 307 final String classname; 308 private ClassFileParser parser; 309 private KlassInfo superclass; 310 private List<KlassInfo> interfaces = new LinkedList<KlassInfo>(); 311 312 KlassInfo(String classname) { 313 this.classname = classname; 314 } 315 316 boolean isClass() { 317 ensureParse(); 318 return parser.classfile.isClass(); 319 } 320 321 KlassInfo getSuperclass() { 322 ensureParse(); 323 return superclass; 324 } 325 326 List<KlassInfo> getInterfaces() { 327 ensureParse(); 328 return java.util.Collections.unmodifiableList(interfaces); 329 } 330 331 void ensureParse() { 332 try { 333 getClassFileParser(); 334 } catch (IOException e) { 335 throw new RuntimeException(e); 336 } 337 } 338 339 synchronized ClassFileParser getClassFileParser() throws IOException { 340 if (parser == null) { 341 parser = ClassPath.parserForClass(classname); 342 if (parser != null) { 343 parseClassFile(); 344 List<String> descriptors = parse(new MethodDescriptor(classname + ".<clinit>", "()V", false)); 345 } 346 } 347 return parser; 348 } 349 350 List<String> parse(MethodDescriptor md) { 351 ensureParse(); 352 try { 353 List<String> descriptors = new LinkedList<String>(); 354 for (Method m : parser.classfile.methods) { 355 String name = m.getName(parser.classfile.constant_pool); 356 String desc = parser.constantPoolParser.getDescriptor(m.descriptor.index); 357 if (name.equals(md.methodname)) { 358 if (md.descriptor.equals("*") || md.descriptor.equals(desc)) { 359 parseMethod(parser, m); 360 descriptors.add(desc); 361 } 362 } 363 } 364 return descriptors; 365 } catch (ConstantPoolException ex) { 366 throw new RuntimeException(ex); 367 } 368 } 369 370 private void parseClassFile() throws IOException { 371 parser.parseClassInfo(); 372 373 ClassFile classfile = parser.classfile; 374 try { 375 if (classfile.super_class > 0) { 376 superclass = getKlassInfo(classfile.getSuperclassName()); 377 } 378 if (classfile.interfaces != null) { 379 for (int i = 0; i < classfile.interfaces.length; i++) { 380 interfaces.add(getKlassInfo(classfile.getInterfaceName(i))); 381 } 382 } 383 } catch (ConstantPoolException ex) { 384 throw new RuntimeException(ex); 385 } 386 } 387 } 388 389 static List<KlassInfo> getSubClasses(String classname) throws IOException { 390 List<KlassInfo> result = new LinkedList<KlassInfo>(); 391 List<KlassInfo> list = new LinkedList<KlassInfo>(); 392 list.addAll(cache.values()); 393 for (KlassInfo kinfo : list) { 394 if (kinfo.getSuperclass() != null && classname.equals(kinfo.getSuperclass().classname)) { 395 result.add(kinfo); 396 } 397 for (KlassInfo interf : kinfo.getInterfaces()) { 398 if (classname.equals(interf.classname)) { 399 result.add(kinfo); 400 } 401 } 402 } 403 return result; 404 } 405 406 private static void parseConfigFile(String config) throws IOException { 407 FileInputStream in = new FileInputStream(config); 408 try { 409 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 410 String line; 411 int lineNumber = 0; 412 while ((line = reader.readLine()) != null) { 413 lineNumber++; 414 if ((line = line.trim()).length() > 0) { 415 if (line.startsWith("#")) { 416 continue; 417 } 418 419 String[] s = line.split("\\s+"); 420 if ("exclude".equals(s[0])) { 421 filter.exclude(s[1]); 422 } else { 423 String name = s[0].replace('/', '.'); 424 if (name.length() > 0) { 425 String classname = name.replace('/', '.'); 426 if (s.length == 2) { 427 // method name 428 int pos = classname.lastIndexOf('.'); 429 classname = classname.substring(0, pos); 430 } 431 432 KlassInfo kinfo = getKlassInfo(classname); 433 if (kinfo.getClassFileParser() != null) { 434 // class exists 435 MethodDescriptor md = (s.length == 1) ? new MethodDescriptor(name) : new MethodDescriptor(name, s[1], false); 436 if (!pending.contains(md)) { 437 pending.add(md); 438 } 439 } else { 440 // class not found 441 trace("Class %s not found%n", classname); 442 } 443 } 444 } 445 } 446 } 447 448 } finally { 449 in.close(); 450 } 451 } 452 453 private static void parseMethod(ClassFileParser cfparser, Method m) { 454 Klass.Method kmethod = cfparser.parseMethod(m); 455 Code_attribute c_attr = (Code_attribute) m.attributes.get(Attribute.Code); 456 if (c_attr != null) { 457 LineNumberTable_attribute lineNumTable = 458 (LineNumberTable_attribute) c_attr.attributes.get(Attribute.LineNumberTable); 459 InstructorVisitor visitor = new InstructorVisitor(cfparser, lineNumTable); 460 trace("parseMethod %s %s %n", cfparser.this_klass, kmethod); 461 for (Instruction instr : c_attr.getInstructions()) { 462 try { 463 instr.accept(visitor, kmethod); 464 } catch (ArrayIndexOutOfBoundsException e) { 465 throw new RuntimeException("error at or after byte " + instr.getPC()); 466 } 467 468 } 469 470 if (c_attr.exception_table_langth > 0) { 471 for (int i = 0; i < 472 c_attr.exception_table.length; i++) { 473 Code_attribute.Exception_data handler = c_attr.exception_table[i]; 474 int catch_type = handler.catch_type; 475 if (catch_type > 0) { 476 visitor.addConstantPoolRef(catch_type, kmethod, handler.start_pc); 477 } 478 479 } 480 } 481 } 482 } 483 484 static class MethodDescriptor { 485 486 final String name; 487 final String classname; 488 final String methodname; 489 final String descriptor; 490 final boolean interfaceMethodRef; 491 492 MethodDescriptor(String classname) { 493 this.classname = classname.replace('/', '.'); 494 this.name = this.classname; 495 this.methodname = ""; 496 this.descriptor = ""; 497 this.interfaceMethodRef = false; 498 if (this.classname.length() == 1) { 499 throw new RuntimeException("invalid " + this); 500 } 501 } 502 503 MethodDescriptor(String name, String descriptor, boolean interfaceMethodRef) { 504 name = name.replace('/', '.'); 505 this.name = name; 506 int pos = name.lastIndexOf('.'); 507 this.classname = name.substring(0, pos); 508 this.methodname = name.substring(pos + 1, name.length()); 509 this.descriptor = descriptor; 510 this.interfaceMethodRef = interfaceMethodRef; 511 if (this.classname.length() == 1) { 512 throw new RuntimeException("invalid " + this); 513 } 514 } 515 516 @Override 517 public boolean equals(Object obj) { 518 MethodDescriptor m = (MethodDescriptor) obj; 519 520 return this.name.equals(m.name) && 521 this.descriptor.equals(m.descriptor); 522 } 523 524 @Override 525 public int hashCode() { 526 int hash = 7; 527 hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0); 528 hash = 97 * hash + (this.descriptor != null ? this.descriptor.hashCode() : 0); 529 return hash; 530 } 531 532 public String toString() { 533 if (descriptor.isEmpty()) { 534 return name; 535 } else { 536 return name + " : " + descriptor; 537 } 538 } 539 } 540 541 static class Filter { 542 543 private Set<String> excludes = new TreeSet<String>(); 544 545 Filter exclude(String pattern) { 546 excludes.add(pattern); 547 return this; 548 } 549 550 boolean isExcluded(String klass) { 551 for (String pattern : excludes) { 552 if (matches(klass, pattern)) { 553 return true; 554 } 555 } 556 return false; 557 } 558 559 private boolean matches(String klass, String pattern) { 560 int pos = klass.lastIndexOf('.'); 561 String packageName = pos > 0 ? klass.substring(0, pos) : "<unnamed>"; 562 if (pattern.endsWith("**")) { 563 String p = pattern.substring(0, pattern.length() - 2); 564 return klass.startsWith(p); 565 } else if (pattern.endsWith("*")) { 566 pos = pattern.lastIndexOf('.'); 567 String pkg = pos > 0 ? pattern.substring(0, pos) : "<unnamed>"; 568 if (packageName.equals(pkg)) { 569 // package name has to be exact match 570 String p = pattern.substring(0, pattern.length() - 1); 571 return klass.startsWith(p); 572 } else { 573 return false; 574 } 575 } else { 576 // exact match or inner class 577 return klass.equals(pattern) || klass.startsWith(pattern + "$"); 578 } 579 } 580 } 581 582 static class InstructorVisitor implements Instruction.KindVisitor<Void, Klass.Method> { 583 584 private final ClassFileParser parser; 585 private final LineNumberTable_attribute lineNumTable; 586 587 InstructorVisitor(ClassFileParser parser, LineNumberTable_attribute lineNumTable) { 588 this.parser = parser; 589 this.lineNumTable = lineNumTable; 590 } 591 592 int getLineNumber(int pc) { 593 if (lineNumTable != null) { 594 int start_pc = 0; 595 int lineno = 0; 596 for (int i = 0; i < lineNumTable.line_number_table_length; i++) { 597 int cur_start_pc = lineNumTable.line_number_table[i].start_pc; 598 if (pc == 0 && cur_start_pc == 0) { 599 return lineNumTable.line_number_table[i].line_number; 600 } else if (pc >= start_pc && pc < cur_start_pc) { 601 return lineno; 602 } 603 start_pc = cur_start_pc; 604 lineno = lineNumTable.line_number_table[i].line_number; 605 } 606 } 607 return 0; 608 } 609 610 void addConstantPoolRef(int index, Klass.Method m, int pc) { 611 try { 612 CPInfo cpInfo = parser.classfile.constant_pool.get(index); 613 String name = cpInfo.accept(typeFinder, null); 614 if (name != null) { 615 trace(" %s %s at line %d%n", parser.constantPoolParser.tagName(index), name, getLineNumber(pc)); 616 } 617 } catch (InvalidIndex ex) { 618 throw new RuntimeException(ex); 619 } 620 } 621 622 public Void visitNoOperands(Instruction instr, Klass.Method m) { 623 return null; 624 } 625 626 public Void visitArrayType(Instruction instr, TypeKind kind, Klass.Method m) { 627 return null; 628 } 629 630 public Void visitBranch(Instruction instr, int offset, Klass.Method m) { 631 return null; 632 } 633 634 public Void visitConstantPoolRef(Instruction instr, int index, Klass.Method m) { 635 addConstantPoolRef(index, m, instr.getPC()); 636 return null; 637 } 638 639 public Void visitConstantPoolRefAndValue(Instruction instr, int index, int value, Klass.Method m) { 640 addConstantPoolRef(index, m, instr.getPC()); 641 return null; 642 } 643 644 public Void visitLocal(Instruction instr, int index, Klass.Method m) { 645 return null; 646 } 647 648 public Void visitLocalAndValue(Instruction instr, int index, int value, Klass.Method m) { 649 return null; 650 } 651 652 public Void visitLookupSwitch(Instruction instr, int default_, int npairs, int[] matches, int[] offsets, Klass.Method m) { 653 return null; 654 } 655 656 public Void visitTableSwitch(Instruction instr, int default_, int low, int high, int[] offsets, Klass.Method m) { 657 return null; 658 } 659 660 public Void visitValue(Instruction instr, int value, Klass.Method m) { 661 return null; 662 } 663 664 public Void visitUnknown(Instruction instr, Klass.Method m) { 665 return null; 666 } 667 private ConstantPool.Visitor<String, Void> typeFinder = new ConstantPool.Visitor<String, Void>() { 668 669 String getClassName(CPRefInfo info, Void p) { 670 try { 671 return parser.checkClassName(info.getClassName()).replace('/', '.'); 672 } catch (ConstantPoolException ex) { 673 throw new RuntimeException(ex); 674 } 675 } 676 677 boolean addReferencedClass(String name) { 678 if (Klass.findKlass(name) == null) { 679 MethodDescriptor md = new MethodDescriptor(name); 680 if (!methods.contains(md) && !pending.contains(md)) { 681 pending.add(md); 682 } 683 return true; 684 } 685 return false; 686 } 687 private String privilegedActionClass = ""; 688 689 void cachePrivilegedAction(String classname) { 690 trace(" found PrivilegedAction %s%n", classname); 691 privilegedActionClass = classname; 692 } 693 694 void doPrivilegedCall(String method) { 695 if (privilegedActionClass.length() > 0) { 696 MethodDescriptor md = new MethodDescriptor(privilegedActionClass + ".run", "*", false); 697 if (!methods.contains(md) && !pending.contains(md)) { 698 trace(" doPrivileged %s%n", md); 699 pending.add(md); 700 } 701 } 702 } 703 704 private String addMethodDescriptor(CPRefInfo info, Void p) { 705 try { 706 String classname = getClassName(info, null); 707 String method = classname + "." + info.getNameAndTypeInfo().getName(); 708 String descriptor = info.getNameAndTypeInfo().getType(); 709 710 if (method.endsWith(".<init>") && isPrivilegedAction(classname)) { 711 cachePrivilegedAction(classname); 712 } 713 if (method.equals("java.security.AccessController.doPrivileged")) { 714 doPrivilegedCall(method); 715 return method; 716 } 717 718 boolean interfaceMethodRef = info instanceof CONSTANT_InterfaceMethodref_info; 719 MethodDescriptor md = new MethodDescriptor(method, descriptor, interfaceMethodRef); 720 if (!methods.contains(md) && !pending.contains(md)) { 721 pending.add(md); 722 } 723 return method; 724 } catch (ConstantPoolException e) { 725 throw new RuntimeException(e); 726 } 727 } 728 729 public String visitClass(CONSTANT_Class_info info, Void p) { 730 try { 731 String classname = parser.checkClassName(info.getName()).replace('/', '.'); 732 if (classname.length() > 0) { 733 addReferencedClass(classname); 734 } 735 return classname; 736 } catch (ConstantPoolException ex) { 737 throw new RuntimeException(ex); 738 } 739 } 740 741 public String visitDouble(CONSTANT_Double_info info, Void p) { 742 // skip 743 return null; 744 } 745 746 public String visitFieldref(CONSTANT_Fieldref_info info, Void p) { 747 try { 748 String classname = getClassName(info, p); 749 if (classname.length() > 0) { 750 addReferencedClass(classname); 751 } 752 753 String type = info.getNameAndTypeInfo().getType(); 754 String fieldType = parser.checkClassName(type).replace('/', '.'); 755 if (fieldType.length() > 0) { 756 addReferencedClass(classname); 757 } 758 return parser.constantPoolParser.stringValue(info); 759 } catch (ConstantPoolException e) { 760 throw new RuntimeException(e); 761 } 762 } 763 764 public String visitFloat(CONSTANT_Float_info info, Void p) { 765 // skip 766 return null; 767 } 768 769 public String visitInteger(CONSTANT_Integer_info info, Void p) { 770 // skip 771 return null; 772 } 773 774 public String visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) { 775 return addMethodDescriptor(info, p); 776 } 777 778 public String visitLong(CONSTANT_Long_info info, Void p) { 779 // skip 780 return null; 781 } 782 783 public String visitNameAndType(CONSTANT_NameAndType_info info, Void p) { 784 // skip 785 return null; 786 } 787 788 public String visitMethodref(CONSTANT_Methodref_info info, Void p) { 789 return addMethodDescriptor(info, p); 790 } 791 792 public String visitModuleId(CONSTANT_ModuleId_info info, Void p) { 793 // skip 794 return null; 795 } 796 797 public String visitString(CONSTANT_String_info info, Void p) { 798 // skip 799 return null; 800 } 801 802 public String visitUtf8(CONSTANT_Utf8_info info, Void p) { 803 return null; 804 } 805 }; 806 } 807 static boolean traceOn = System.getProperty("classanalyzer.debug") != null; 808 809 private static void trace(String format, Object... args) { 810 if (traceOn) { 811 System.out.format(format, args); 812 } 813 } 814 815 private static void usage() { 816 System.out.println("Usage: BootAnalyzer <options>"); 817 System.out.println("Options: "); 818 System.out.println("\t-jdkhome <JDK home> where all jars will be parsed"); 819 System.out.println("\t-config <roots for the boot module>"); 820 System.out.println("\t-output <output dir>"); 821 System.out.println("\t-classlist print class list and summary"); 822 System.exit(-1); 823 } 824 }