diff -r cd1dceb2d665 -r a25480ff1a6b jdk/make/modules/tools/src/com/sun/classanalyzer/BootAnalyzer.java --- a/jdk/make/modules/tools/src/com/sun/classanalyzer/BootAnalyzer.java Tue Mar 15 19:52:42 2011 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,819 +0,0 @@ -/* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ -package com.sun.classanalyzer; - -import java.io.BufferedReader; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.File; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -import com.sun.tools.classfile.*; -import com.sun.tools.classfile.ConstantPool.*; -import static com.sun.tools.classfile.ConstantPool.*; -import com.sun.tools.classfile.Instruction.TypeKind; -import com.sun.tools.classfile.Type.*; - -/** - * Generate the module config for the boot module with - * a given set of roots (classes or methods) and exclude list. - * - * This tool does method-level dependency analysis starting - * from the root set and follows references transitively as follows: - * - * - * Limitation: - * - * - * @author Mandy Chung - */ -public class BootAnalyzer { - - public static void main(String[] args) throws Exception { - String jdkhome = null; - String config = null; - String output = "."; - boolean printClassList = false; - - // process arguments - int i = 0; - while (i < args.length) { - String arg = args[i++]; - if (arg.equals("-jdkhome")) { - if (i < args.length) { - jdkhome = args[i++]; - } else { - usage(); - } - } else if (arg.equals("-config")) { - config = args[i++]; - } else if (arg.equals("-output")) { - output = args[i++]; - } else if (arg.equals("-classlist")) { - printClassList = true; - } else { - usage(); - } - } - - - - if (jdkhome == null || config == null) { - usage(); - } - - File jre = new File(jdkhome, "jre"); - if (jre.exists()) { - ClassPath.setJDKHome(jdkhome); - } else { - File classes = new File(jdkhome, "classes"); - if (classes.exists()) { - ClassPath.setClassPath(classes.getCanonicalPath()); - } else { - throw new RuntimeException("Invalid jdkhome: " + jdkhome); - } - } - - parseConfigFile(config); - followRoots(); - - // create output directory if it doesn't exist - File dir = new File(output); - if (!dir.isDirectory()) { - if (!dir.exists()) { - boolean created = dir.mkdir(); - if (!created) { - throw new RuntimeException("Unable to create `" + dir + "'"); - } - } - } - - String bootmodule = "boot"; - String bootconfig = resolve(dir, bootmodule, "config"); - printBootConfig(bootconfig, bootmodule); - - List list = ModuleConfig.readConfigurationFile(bootconfig); - Module module = Module.addModule(list.get(0)); - for (Klass k : Klass.getAllClasses()) { - module.addKlass(k); - } - module.fixupDependencies(); - - if (printClassList) { - module.printClassListTo(resolve(dir, bootmodule, "classlist")); - module.printSummaryTo(resolve(dir, bootmodule, "summary")); - } - } - - // print boot.config file as an input to the ClassAnalyzer - private static void printBootConfig(String output, String bootmodule) throws IOException { - - File f = new File(output); - PrintWriter writer = new PrintWriter(f); - try { - int count = 0; - writer.format("module %s {%n", bootmodule); - for (Klass k : Klass.getAllClasses()) { - if (count++ == 0) { - writer.format("%4s%7s %s", "", "include", k); - } else { - writer.format(",%n"); - writer.format("%4s%7s %s", "", "", k); - } - } - writer.format(";%n}%n"); - } finally { - writer.close(); - } - } - - private static String resolve(File dir, String mname, String suffix) { - File f = new File(dir, mname + "." + suffix); - return f.toString(); - - } - static List methods = new LinkedList(); - static Deque pending = new ArrayDeque(); - static Deque interfaceMethodRefs = new ArrayDeque(); - static Filter filter = new Filter(); - - private static void followRoots() throws IOException { - MethodDescriptor md = null; - - while ((md = pending.poll()) != null) { - if (!methods.contains(md)) { - methods.add(md); - if (md.classname.isEmpty()) { - trace("Warning: class missing %s%n", md); - continue; - } - - if (filter.isExcluded(md.classname)) { - trace("excluded %s%n", md); - } else { - KlassInfo kinfo = getKlassInfo(md.classname); - if (kinfo.classname.contains("$")) { - int pos = kinfo.classname.lastIndexOf('$'); - String outer = kinfo.classname.substring(0, pos); - if (!cache.containsKey(outer)) { - trace(" include outer class %s%n", outer); - getKlassInfo(outer).ensureParse(); - } - } - - kinfo.ensureParse(); - if (md.methodname.length() > 0) { - if (filter.isExcluded(md.name)) { - trace("excluded %s%n", md); - } else { - if (md.interfaceMethodRef) { - trace("interface methodref %s%n", md); - interfaceMethodRefs.add(md); - } else { - List descriptors = kinfo.parse(md); - if (descriptors.isEmpty()) { - if (kinfo.getSuperclass() != null) { - String sn = kinfo.getSuperclass().classname; - MethodDescriptor superMD = new MethodDescriptor(sn + "." + md.methodname, md.descriptor, false); - if (!methods.contains(superMD) && !pending.contains(superMD)) { - trace(" delegated %s to %s%n", md, superMD); - pending.add(superMD); - } - } else if (kinfo.isClass()) { - trace(" %s (not found)%n", md); - } else { - trace(" %s (interface)%n", md); - } - } else { - if (md.descriptor.equals("*")) { - trace(" parsed %s : ", md.name); - for (String s : descriptors) { - trace(" %s", s); - } - trace("%n"); - } - } - } - } - } - } - } - if (pending.isEmpty()) { - for (Klass k : Klass.getAllClasses()) { - if (k.getFileSize() == 0) { - getKlassInfo(k.getClassName()).ensureParse(); - } - } - while ((md = interfaceMethodRefs.poll()) != null) { - addSubClassMethods(md); - } - } - } - } - - static void addSubClassMethods(MethodDescriptor md) throws IOException { - for (KlassInfo kinfo : getSubClasses(md.classname)) { - String methodname = kinfo.classname + "." + md.methodname; - MethodDescriptor other = new MethodDescriptor(methodname, md.descriptor, false); - if (!methods.contains(other) && !pending.contains(other)) { - trace("Warning: subclass from %s to %s%n", md.classname, other); - pending.add(other); - } - } - } - private final static String privilegedActionInterf = "java.security.PrivilegedAction"; - private final static String privilegedExceptionActionInterf = "java.security.PrivilegedExceptionAction"; - - static boolean isPrivilegedAction(String classname) { - if (classname.isEmpty()) { - return false; - } - KlassInfo kinfo = getKlassInfo(classname); - for (KlassInfo ki : kinfo.getInterfaces()) { - String interf = ki.classname; - if (interf.equals(privilegedActionInterf) || - interf.equals(privilegedExceptionActionInterf)) { - return true; - } - } - return false; - } - static Map cache = new HashMap(); - - static KlassInfo getKlassInfo(String classname) { - classname = classname.replace('/', '.'); - - KlassInfo kinfo = cache.get(classname); - if (kinfo == null) { - kinfo = new KlassInfo(classname); - cache.put(classname, kinfo); - } - return kinfo; - } - - static class KlassInfo { - - final String classname; - private ClassFileParser parser; - private KlassInfo superclass; - private List interfaces = new LinkedList(); - - KlassInfo(String classname) { - this.classname = classname; - } - - boolean isClass() { - ensureParse(); - return parser.classfile.isClass(); - } - - KlassInfo getSuperclass() { - ensureParse(); - return superclass; - } - - List getInterfaces() { - ensureParse(); - return java.util.Collections.unmodifiableList(interfaces); - } - - void ensureParse() { - try { - getClassFileParser(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - synchronized ClassFileParser getClassFileParser() throws IOException { - if (parser == null) { - parser = ClassPath.parserForClass(classname); - if (parser != null) { - parseClassFile(); - List descriptors = parse(new MethodDescriptor(classname + ".", "()V", false)); - } - } - return parser; - } - - List parse(MethodDescriptor md) { - ensureParse(); - try { - List descriptors = new LinkedList(); - for (Method m : parser.classfile.methods) { - String name = m.getName(parser.classfile.constant_pool); - String desc = parser.constantPoolParser.getDescriptor(m.descriptor.index); - if (name.equals(md.methodname)) { - if (md.descriptor.equals("*") || md.descriptor.equals(desc)) { - parseMethod(parser, m); - descriptors.add(desc); - } - } - } - return descriptors; - } catch (ConstantPoolException ex) { - throw new RuntimeException(ex); - } - } - - private void parseClassFile() throws IOException { - parser.parseClassInfo(); - - ClassFile classfile = parser.classfile; - try { - if (classfile.super_class > 0) { - superclass = getKlassInfo(classfile.getSuperclassName()); - } - if (classfile.interfaces != null) { - for (int i = 0; i < classfile.interfaces.length; i++) { - interfaces.add(getKlassInfo(classfile.getInterfaceName(i))); - } - } - } catch (ConstantPoolException ex) { - throw new RuntimeException(ex); - } - } - } - - static List getSubClasses(String classname) throws IOException { - List result = new LinkedList(); - List list = new LinkedList(); - list.addAll(cache.values()); - for (KlassInfo kinfo : list) { - if (kinfo.getSuperclass() != null && classname.equals(kinfo.getSuperclass().classname)) { - result.add(kinfo); - } - for (KlassInfo interf : kinfo.getInterfaces()) { - if (classname.equals(interf.classname)) { - result.add(kinfo); - } - } - } - return result; - } - - private static void parseConfigFile(String config) throws IOException { - FileInputStream in = new FileInputStream(config); - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String line; - int lineNumber = 0; - while ((line = reader.readLine()) != null) { - lineNumber++; - if ((line = line.trim()).length() > 0) { - if (line.startsWith("#")) { - continue; - } - - String[] s = line.split("\\s+"); - if ("exclude".equals(s[0])) { - filter.exclude(s[1]); - } else { - String name = s[0].replace('/', '.'); - if (name.length() > 0) { - String classname = name.replace('/', '.'); - if (s.length == 2) { - // method name - int pos = classname.lastIndexOf('.'); - classname = classname.substring(0, pos); - } - - KlassInfo kinfo = getKlassInfo(classname); - if (kinfo.getClassFileParser() != null) { - // class exists - MethodDescriptor md = (s.length == 1) ? new MethodDescriptor(name) : new MethodDescriptor(name, s[1], false); - if (!pending.contains(md)) { - pending.add(md); - } - } else { - // class not found - trace("Class %s not found%n", classname); - } - } - } - } - } - - } finally { - in.close(); - } - } - - private static void parseMethod(ClassFileParser cfparser, Method m) { - Klass.Method kmethod = cfparser.parseMethod(m); - Code_attribute c_attr = (Code_attribute) m.attributes.get(Attribute.Code); - if (c_attr != null) { - LineNumberTable_attribute lineNumTable = - (LineNumberTable_attribute) c_attr.attributes.get(Attribute.LineNumberTable); - InstructorVisitor visitor = new InstructorVisitor(cfparser, lineNumTable); - trace("parseMethod %s %s %n", cfparser.this_klass, kmethod); - for (Instruction instr : c_attr.getInstructions()) { - try { - instr.accept(visitor, kmethod); - } catch (ArrayIndexOutOfBoundsException e) { - throw new RuntimeException("error at or after byte " + instr.getPC()); - } - - } - - if (c_attr.exception_table_langth > 0) { - for (int i = 0; i < - c_attr.exception_table.length; i++) { - Code_attribute.Exception_data handler = c_attr.exception_table[i]; - int catch_type = handler.catch_type; - if (catch_type > 0) { - visitor.addConstantPoolRef(catch_type, kmethod, handler.start_pc); - } - - } - } - } - } - - static class MethodDescriptor { - - final String name; - final String classname; - final String methodname; - final String descriptor; - final boolean interfaceMethodRef; - - MethodDescriptor(String classname) { - this.classname = classname.replace('/', '.'); - this.name = this.classname; - this.methodname = ""; - this.descriptor = ""; - this.interfaceMethodRef = false; - if (this.classname.length() == 1) { - throw new RuntimeException("invalid " + this); - } - } - - MethodDescriptor(String name, String descriptor, boolean interfaceMethodRef) { - name = name.replace('/', '.'); - this.name = name; - int pos = name.lastIndexOf('.'); - this.classname = name.substring(0, pos); - this.methodname = name.substring(pos + 1, name.length()); - this.descriptor = descriptor; - this.interfaceMethodRef = interfaceMethodRef; - if (this.classname.length() == 1) { - throw new RuntimeException("invalid " + this); - } - } - - @Override - public boolean equals(Object obj) { - MethodDescriptor m = (MethodDescriptor) obj; - - return this.name.equals(m.name) && - this.descriptor.equals(m.descriptor); - } - - @Override - public int hashCode() { - int hash = 7; - hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0); - hash = 97 * hash + (this.descriptor != null ? this.descriptor.hashCode() : 0); - return hash; - } - - public String toString() { - if (descriptor.isEmpty()) { - return name; - } else { - return name + " : " + descriptor; - } - } - } - - static class Filter { - - private Set excludes = new TreeSet(); - - Filter exclude(String pattern) { - excludes.add(pattern); - return this; - } - - boolean isExcluded(String klass) { - for (String pattern : excludes) { - if (matches(klass, pattern)) { - return true; - } - } - return false; - } - - private boolean matches(String klass, String pattern) { - int pos = klass.lastIndexOf('.'); - String packageName = pos > 0 ? klass.substring(0, pos) : ""; - if (pattern.endsWith("**")) { - String p = pattern.substring(0, pattern.length() - 2); - return klass.startsWith(p); - } else if (pattern.endsWith("*")) { - pos = pattern.lastIndexOf('.'); - String pkg = pos > 0 ? pattern.substring(0, pos) : ""; - if (packageName.equals(pkg)) { - // package name has to be exact match - String p = pattern.substring(0, pattern.length() - 1); - return klass.startsWith(p); - } else { - return false; - } - } else { - // exact match or inner class - return klass.equals(pattern) || klass.startsWith(pattern + "$"); - } - } - } - - static class InstructorVisitor implements Instruction.KindVisitor { - - private final ClassFileParser parser; - private final LineNumberTable_attribute lineNumTable; - - InstructorVisitor(ClassFileParser parser, LineNumberTable_attribute lineNumTable) { - this.parser = parser; - this.lineNumTable = lineNumTable; - } - - int getLineNumber(int pc) { - if (lineNumTable != null) { - int start_pc = 0; - int lineno = 0; - for (int i = 0; i < lineNumTable.line_number_table_length; i++) { - int cur_start_pc = lineNumTable.line_number_table[i].start_pc; - if (pc == 0 && cur_start_pc == 0) { - return lineNumTable.line_number_table[i].line_number; - } else if (pc >= start_pc && pc < cur_start_pc) { - return lineno; - } - start_pc = cur_start_pc; - lineno = lineNumTable.line_number_table[i].line_number; - } - } - return 0; - } - - void addConstantPoolRef(int index, Klass.Method m, int pc) { - try { - CPInfo cpInfo = parser.classfile.constant_pool.get(index); - String name = cpInfo.accept(typeFinder, null); - if (name != null) { - trace(" %s %s at line %d%n", parser.constantPoolParser.tagName(index), name, getLineNumber(pc)); - } - } catch (InvalidIndex ex) { - throw new RuntimeException(ex); - } - } - - public Void visitNoOperands(Instruction instr, Klass.Method m) { - return null; - } - - public Void visitArrayType(Instruction instr, TypeKind kind, Klass.Method m) { - return null; - } - - public Void visitBranch(Instruction instr, int offset, Klass.Method m) { - return null; - } - - public Void visitConstantPoolRef(Instruction instr, int index, Klass.Method m) { - addConstantPoolRef(index, m, instr.getPC()); - return null; - } - - public Void visitConstantPoolRefAndValue(Instruction instr, int index, int value, Klass.Method m) { - addConstantPoolRef(index, m, instr.getPC()); - return null; - } - - public Void visitLocal(Instruction instr, int index, Klass.Method m) { - return null; - } - - public Void visitLocalAndValue(Instruction instr, int index, int value, Klass.Method m) { - return null; - } - - public Void visitLookupSwitch(Instruction instr, int default_, int npairs, int[] matches, int[] offsets, Klass.Method m) { - return null; - } - - public Void visitTableSwitch(Instruction instr, int default_, int low, int high, int[] offsets, Klass.Method m) { - return null; - } - - public Void visitValue(Instruction instr, int value, Klass.Method m) { - return null; - } - - public Void visitUnknown(Instruction instr, Klass.Method m) { - return null; - } - private ConstantPool.Visitor typeFinder = new ConstantPool.Visitor() { - - String getClassName(CPRefInfo info, Void p) { - try { - return parser.checkClassName(info.getClassName()).replace('/', '.'); - } catch (ConstantPoolException ex) { - throw new RuntimeException(ex); - } - } - - boolean addReferencedClass(String name) { - if (Klass.findKlass(name) == null) { - MethodDescriptor md = new MethodDescriptor(name); - if (!methods.contains(md) && !pending.contains(md)) { - pending.add(md); - } - return true; - } - return false; - } - private String privilegedActionClass = ""; - - void cachePrivilegedAction(String classname) { - trace(" found PrivilegedAction %s%n", classname); - privilegedActionClass = classname; - } - - void doPrivilegedCall(String method) { - if (privilegedActionClass.length() > 0) { - MethodDescriptor md = new MethodDescriptor(privilegedActionClass + ".run", "*", false); - if (!methods.contains(md) && !pending.contains(md)) { - trace(" doPrivileged %s%n", md); - pending.add(md); - } - } - } - - private String addMethodDescriptor(CPRefInfo info, Void p) { - try { - String classname = getClassName(info, null); - String method = classname + "." + info.getNameAndTypeInfo().getName(); - String descriptor = info.getNameAndTypeInfo().getType(); - - if (method.endsWith(".") && isPrivilegedAction(classname)) { - cachePrivilegedAction(classname); - } - if (method.equals("java.security.AccessController.doPrivileged")) { - doPrivilegedCall(method); - return method; - } - - boolean interfaceMethodRef = info instanceof CONSTANT_InterfaceMethodref_info; - MethodDescriptor md = new MethodDescriptor(method, descriptor, interfaceMethodRef); - if (!methods.contains(md) && !pending.contains(md)) { - pending.add(md); - } - return method; - } catch (ConstantPoolException e) { - throw new RuntimeException(e); - } - } - - public String visitClass(CONSTANT_Class_info info, Void p) { - try { - String classname = parser.checkClassName(info.getName()).replace('/', '.'); - if (classname.length() > 0) { - addReferencedClass(classname); - } - return classname; - } catch (ConstantPoolException ex) { - throw new RuntimeException(ex); - } - } - - public String visitDouble(CONSTANT_Double_info info, Void p) { - // skip - return null; - } - - public String visitFieldref(CONSTANT_Fieldref_info info, Void p) { - try { - String classname = getClassName(info, p); - if (classname.length() > 0) { - addReferencedClass(classname); - } - - String type = info.getNameAndTypeInfo().getType(); - String fieldType = parser.checkClassName(type).replace('/', '.'); - if (fieldType.length() > 0) { - addReferencedClass(classname); - } - return parser.constantPoolParser.stringValue(info); - } catch (ConstantPoolException e) { - throw new RuntimeException(e); - } - } - - public String visitFloat(CONSTANT_Float_info info, Void p) { - // skip - return null; - } - - public String visitInteger(CONSTANT_Integer_info info, Void p) { - // skip - return null; - } - - public String visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) { - return addMethodDescriptor(info, p); - } - - public String visitLong(CONSTANT_Long_info info, Void p) { - // skip - return null; - } - - public String visitNameAndType(CONSTANT_NameAndType_info info, Void p) { - // skip - return null; - } - - public String visitMethodref(CONSTANT_Methodref_info info, Void p) { - return addMethodDescriptor(info, p); - } - - public String visitString(CONSTANT_String_info info, Void p) { - // skip - return null; - } - - public String visitUtf8(CONSTANT_Utf8_info info, Void p) { - return null; - } - }; - } - static boolean traceOn = System.getProperty("classanalyzer.debug") != null; - - private static void trace(String format, Object... args) { - if (traceOn) { - System.out.format(format, args); - } - } - - private static void usage() { - System.out.println("Usage: BootAnalyzer "); - System.out.println("Options: "); - System.out.println("\t-jdkhome where all jars will be parsed"); - System.out.println("\t-config "); - System.out.println("\t-output "); - System.out.println("\t-classlist print class list and summary"); - System.exit(-1); - } -}