jdk/make/modules/tools/src/com/sun/classanalyzer/BootAnalyzer.java
changeset 8588 36e3e74be5ae
parent 8582 403d8b87612c
parent 8587 97a3a22edacf
child 8590 f9036b3e408a
child 8793 a25480ff1a6b
equal deleted inserted replaced
8582:403d8b87612c 8588:36e3e74be5ae
     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 &lt;clinit&gt;.</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 visitString(CONSTANT_String_info info, Void p) {
       
   793                 // skip
       
   794                 return null;
       
   795             }
       
   796 
       
   797             public String visitUtf8(CONSTANT_Utf8_info info, Void p) {
       
   798                 return null;
       
   799             }
       
   800         };
       
   801     }
       
   802     static boolean traceOn = System.getProperty("classanalyzer.debug") != null;
       
   803 
       
   804     private static void trace(String format, Object... args) {
       
   805         if (traceOn) {
       
   806             System.out.format(format, args);
       
   807         }
       
   808     }
       
   809 
       
   810     private static void usage() {
       
   811         System.out.println("Usage: BootAnalyzer <options>");
       
   812         System.out.println("Options: ");
       
   813         System.out.println("\t-jdkhome <JDK home> where all jars will be parsed");
       
   814         System.out.println("\t-config  <roots for the boot module>");
       
   815         System.out.println("\t-output  <output dir>");
       
   816         System.out.println("\t-classlist print class list and summary");
       
   817         System.exit(-1);
       
   818     }
       
   819 }