aoqi@0: /* aoqi@0: * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javap; aoqi@0: aoqi@0: import java.io.BufferedReader; aoqi@0: import java.io.IOException; aoqi@0: import java.io.StringReader; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.List; aoqi@0: import java.util.Set; aoqi@0: import java.util.SortedMap; aoqi@0: import java.util.SortedSet; aoqi@0: import java.util.TreeMap; aoqi@0: import java.util.TreeSet; aoqi@0: import javax.tools.JavaFileManager; aoqi@0: import javax.tools.JavaFileManager.Location; aoqi@0: import javax.tools.JavaFileObject; aoqi@0: import javax.tools.StandardLocation; aoqi@0: aoqi@0: import com.sun.tools.classfile.Attribute; aoqi@0: import com.sun.tools.classfile.ClassFile; aoqi@0: import com.sun.tools.classfile.Code_attribute; aoqi@0: import com.sun.tools.classfile.ConstantPoolException; aoqi@0: import com.sun.tools.classfile.Instruction; aoqi@0: import com.sun.tools.classfile.LineNumberTable_attribute; aoqi@0: import com.sun.tools.classfile.SourceFile_attribute; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Annotate instructions with source code. aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: */ aoqi@0: public class SourceWriter extends InstructionDetailWriter { aoqi@0: static SourceWriter instance(Context context) { aoqi@0: SourceWriter instance = context.get(SourceWriter.class); aoqi@0: if (instance == null) aoqi@0: instance = new SourceWriter(context); aoqi@0: return instance; aoqi@0: } aoqi@0: aoqi@0: protected SourceWriter(Context context) { aoqi@0: super(context); aoqi@0: context.put(SourceWriter.class, this); aoqi@0: } aoqi@0: aoqi@0: void setFileManager(JavaFileManager fileManager) { aoqi@0: this.fileManager = fileManager; aoqi@0: } aoqi@0: aoqi@0: public void reset(ClassFile cf, Code_attribute attr) { aoqi@0: setSource(cf); aoqi@0: setLineMap(attr); aoqi@0: } aoqi@0: aoqi@0: public void writeDetails(Instruction instr) { aoqi@0: String indent = space(40); // could get from Options? aoqi@0: Set lines = lineMap.get(instr.getPC()); aoqi@0: if (lines != null) { aoqi@0: for (int line: lines) { aoqi@0: print(indent); aoqi@0: print(String.format(" %4d ", line)); aoqi@0: if (line < sourceLines.length) aoqi@0: print(sourceLines[line]); aoqi@0: println(); aoqi@0: int nextLine = nextLine(line); aoqi@0: for (int i = line + 1; i < nextLine; i++) { aoqi@0: print(indent); aoqi@0: print(String.format("(%4d)", i)); aoqi@0: if (i < sourceLines.length) aoqi@0: print(sourceLines[i]); aoqi@0: println(); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public boolean hasSource() { aoqi@0: return (sourceLines.length > 0); aoqi@0: } aoqi@0: aoqi@0: private void setLineMap(Code_attribute attr) { aoqi@0: SortedMap> map = aoqi@0: new TreeMap>(); aoqi@0: SortedSet allLines = new TreeSet(); aoqi@0: for (Attribute a: attr.attributes) { aoqi@0: if (a instanceof LineNumberTable_attribute) { aoqi@0: LineNumberTable_attribute t = (LineNumberTable_attribute) a; aoqi@0: for (LineNumberTable_attribute.Entry e: t.line_number_table) { aoqi@0: int start_pc = e.start_pc; aoqi@0: int line = e.line_number; aoqi@0: SortedSet pcLines = map.get(start_pc); aoqi@0: if (pcLines == null) { aoqi@0: pcLines = new TreeSet(); aoqi@0: map.put(start_pc, pcLines); aoqi@0: } aoqi@0: pcLines.add(line); aoqi@0: allLines.add(line); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: lineMap = map; aoqi@0: lineList = new ArrayList(allLines); aoqi@0: } aoqi@0: aoqi@0: private void setSource(ClassFile cf) { aoqi@0: if (cf != classFile) { aoqi@0: classFile = cf; aoqi@0: sourceLines = splitLines(readSource(cf)); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private String readSource(ClassFile cf) { aoqi@0: if (fileManager == null) aoqi@0: return null; aoqi@0: aoqi@0: Location location; aoqi@0: if (fileManager.hasLocation((StandardLocation.SOURCE_PATH))) aoqi@0: location = StandardLocation.SOURCE_PATH; aoqi@0: else aoqi@0: location = StandardLocation.CLASS_PATH; aoqi@0: aoqi@0: // Guess the source file for a class from the package name for this aoqi@0: // class and the base of the source file. This avoids having to read aoqi@0: // additional classes to determine the outmost class from any aoqi@0: // InnerClasses and EnclosingMethod attributes. aoqi@0: try { aoqi@0: String className = cf.getName(); aoqi@0: SourceFile_attribute sf = aoqi@0: (SourceFile_attribute) cf.attributes.get(Attribute.SourceFile); aoqi@0: if (sf == null) { aoqi@0: report(messages.getMessage("err.no.SourceFile.attribute")); aoqi@0: return null; aoqi@0: } aoqi@0: String sourceFile = sf.getSourceFile(cf.constant_pool); aoqi@0: String fileBase = sourceFile.endsWith(".java") aoqi@0: ? sourceFile.substring(0, sourceFile.length() - 5) : sourceFile; aoqi@0: int sep = className.lastIndexOf("/"); aoqi@0: String pkgName = (sep == -1 ? "" : className.substring(0, sep+1)); aoqi@0: String topClassName = (pkgName + fileBase).replace('/', '.'); aoqi@0: JavaFileObject fo = aoqi@0: fileManager.getJavaFileForInput(location, aoqi@0: topClassName, aoqi@0: JavaFileObject.Kind.SOURCE); aoqi@0: if (fo == null) { aoqi@0: report(messages.getMessage("err.source.file.not.found")); aoqi@0: return null; aoqi@0: } aoqi@0: return fo.getCharContent(true).toString(); aoqi@0: } catch (ConstantPoolException e) { aoqi@0: report(e); aoqi@0: return null; aoqi@0: } catch (IOException e) { aoqi@0: report(e.getLocalizedMessage()); aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private static String[] splitLines(String text) { aoqi@0: if (text == null) aoqi@0: return new String[0]; aoqi@0: aoqi@0: List lines = new ArrayList(); aoqi@0: lines.add(""); // dummy line 0 aoqi@0: try { aoqi@0: BufferedReader r = new BufferedReader(new StringReader(text)); aoqi@0: String line; aoqi@0: while ((line = r.readLine()) != null) aoqi@0: lines.add(line); aoqi@0: } catch (IOException ignore) { aoqi@0: } aoqi@0: return lines.toArray(new String[lines.size()]); aoqi@0: } aoqi@0: aoqi@0: private int nextLine(int line) { aoqi@0: int i = lineList.indexOf(line); aoqi@0: if (i == -1 || i == lineList.size() - 1) aoqi@0: return - 1; aoqi@0: return lineList.get(i + 1); aoqi@0: } aoqi@0: aoqi@0: private JavaFileManager fileManager; aoqi@0: private ClassFile classFile; aoqi@0: private SortedMap> lineMap; aoqi@0: private List lineList; aoqi@0: private String[] sourceLines; aoqi@0: }