# HG changeset patch # User jjg # Date 1242759054 25200 # Node ID cd0630109de5e5c02fa16cbaa11e028d42fbbdd6 # Parent fc634a59381280e16e6faa130aa438daee0e66ec 6824493: experimental support for additional info for instructions Reviewed-by: mcimadamore diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java --- a/src/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java Tue May 19 11:43:50 2009 -0700 +++ b/src/share/classes/com/sun/tools/classfile/StackMapTable_attribute.java Tue May 19 11:50:54 2009 -0700 @@ -107,6 +107,8 @@ return 1; } + public abstract int getOffsetDelta(); + public abstract R accept(Visitor visitor, D data); public final int frame_type; @@ -130,6 +132,10 @@ public R accept(Visitor visitor, D data) { return visitor.visit_same_frame(this, data); } + + public int getOffsetDelta() { + return frame_type; + } } public static class same_locals_1_stack_item_frame extends stack_map_frame { @@ -149,6 +155,10 @@ return visitor.visit_same_locals_1_stack_item_frame(this, data); } + public int getOffsetDelta() { + return frame_type - 64; + } + public final verification_type_info[] stack; } @@ -170,6 +180,10 @@ return visitor.visit_same_locals_1_stack_item_frame_extended(this, data); } + public int getOffsetDelta() { + return offset_delta; + } + public final int offset_delta; public final verification_type_info[] stack; } @@ -189,6 +203,10 @@ return visitor.visit_chop_frame(this, data); } + public int getOffsetDelta() { + return offset_delta; + } + public final int offset_delta; } @@ -207,6 +225,10 @@ return visitor.visit_same_frame_extended(this, data); } + public int getOffsetDelta() { + return offset_delta; + } + public final int offset_delta; } @@ -232,6 +254,10 @@ return visitor.visit_append_frame(this, data); } + public int getOffsetDelta() { + return offset_delta; + } + public final int offset_delta; public final verification_type_info[] locals; } @@ -266,6 +292,10 @@ return visitor.visit_full_frame(this, data); } + public int getOffsetDelta() { + return offset_delta; + } + public final int offset_delta; public final int number_of_locals; public final verification_type_info[] locals; @@ -308,7 +338,7 @@ } } - verification_type_info(int tag) { + protected verification_type_info(int tag) { this.tag = tag; } diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/BasicWriter.java --- a/src/share/classes/com/sun/tools/javap/BasicWriter.java Tue May 19 11:43:50 2009 -0700 +++ b/src/share/classes/com/sun/tools/javap/BasicWriter.java Tue May 19 11:50:54 2009 -0700 @@ -44,6 +44,9 @@ protected BasicWriter(Context context) { lineWriter = LineWriter.instance(context); out = context.get(PrintWriter.class); + messages = context.get(Messages.class); + if (messages == null) + throw new AssertionError(); } protected void print(String s) { @@ -88,8 +91,26 @@ return "???"; } + protected String space(int w) { + if (w < spaces.length && spaces[w] != null) + return spaces[w]; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < w; i++) + sb.append(" "); + + String s = sb.toString(); + if (w < spaces.length) + spaces[w] = s; + + return s; + } + + private String[] spaces = new String[80]; + private LineWriter lineWriter; private PrintWriter out; + protected Messages messages; private static class LineWriter { static LineWriter instance(Context context) { diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/ClassWriter.java --- a/src/share/classes/com/sun/tools/javap/ClassWriter.java Tue May 19 11:43:50 2009 -0700 +++ b/src/share/classes/com/sun/tools/javap/ClassWriter.java Tue May 19 11:50:54 2009 -0700 @@ -26,7 +26,9 @@ package com.sun.tools.javap; import java.net.URI; +import java.text.DateFormat; import java.util.Collection; +import java.util.Date; import java.util.List; import com.sun.tools.classfile.AccessFlags; @@ -47,8 +49,6 @@ import com.sun.tools.classfile.SourceFile_attribute; import com.sun.tools.classfile.Type; -import java.text.DateFormat; -import java.util.Date; import static com.sun.tools.classfile.AccessFlags.*; /* diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/CodeWriter.java --- a/src/share/classes/com/sun/tools/javap/CodeWriter.java Tue May 19 11:43:50 2009 -0700 +++ b/src/share/classes/com/sun/tools/javap/CodeWriter.java Tue May 19 11:50:54 2009 -0700 @@ -25,6 +25,9 @@ package com.sun.tools.javap; +import java.util.ArrayList; +import java.util.List; + import com.sun.tools.classfile.AccessFlags; import com.sun.tools.classfile.Code_attribute; import com.sun.tools.classfile.ConstantPool; @@ -33,9 +36,6 @@ import com.sun.tools.classfile.Instruction; import com.sun.tools.classfile.Instruction.TypeKind; import com.sun.tools.classfile.Method; -import com.sun.tools.classfile.Opcode; - -//import static com.sun.tools.classfile.OpCodes.*; /* * Write the contents of a Code attribute. @@ -59,6 +59,12 @@ attrWriter = AttributeWriter.instance(context); classWriter = ClassWriter.instance(context); constantWriter = ConstantWriter.instance(context); + sourceWriter = SourceWriter.instance(context); + tryBlockWriter = TryBlockWriter.instance(context); + stackMapWriter = StackMapWriter.instance(context); + localVariableTableWriter = LocalVariableTableWriter.instance(context); + localVariableTypeTableWriter = LocalVariableTypeTableWriter.instance(context); + options = Options.instance(context); } void write(Code_attribute attr, ConstantPool constant_pool) { @@ -90,14 +96,21 @@ } public void writeInstrs(Code_attribute attr) { + List detailWriters = getDetailWriters(attr); + for (Instruction instr: attr.getInstructions()) { try { + for (InstructionDetailWriter w: detailWriters) + w.writeDetails(instr); writeInstr(instr); } catch (ArrayIndexOutOfBoundsException e) { println(report("error at or after byte " + instr.getPC())); break; } } + + for (InstructionDetailWriter w: detailWriters) + w.flush(); } public void writeInstr(Instruction instr) { @@ -211,11 +224,45 @@ print(s); } - private static int align(int n) { - return (n + 3) & ~3; + private List getDetailWriters(Code_attribute attr) { + List detailWriters = + new ArrayList(); + if (options.details.contains(InstructionDetailWriter.Kind.SOURCE)) { + sourceWriter.reset(classWriter.getClassFile(), attr); + detailWriters.add(sourceWriter); + } + + if (options.details.contains(InstructionDetailWriter.Kind.LOCAL_VARS)) { + localVariableTableWriter.reset(attr); + detailWriters.add(localVariableTableWriter); + } + + if (options.details.contains(InstructionDetailWriter.Kind.LOCAL_VAR_TYPES)) { + localVariableTypeTableWriter.reset(attr); + detailWriters.add(localVariableTypeTableWriter); + } + + if (options.details.contains(InstructionDetailWriter.Kind.STACKMAPS)) { + stackMapWriter.reset(attr); + stackMapWriter.writeInitialDetails(); + detailWriters.add(stackMapWriter); + } + + if (options.details.contains(InstructionDetailWriter.Kind.TRY_BLOCKS)) { + tryBlockWriter.reset(attr); + detailWriters.add(tryBlockWriter); + } + + return detailWriters; } private AttributeWriter attrWriter; private ClassWriter classWriter; private ConstantWriter constantWriter; + private LocalVariableTableWriter localVariableTableWriter; + private LocalVariableTypeTableWriter localVariableTypeTableWriter; + private SourceWriter sourceWriter; + private StackMapWriter stackMapWriter; + private TryBlockWriter tryBlockWriter; + private Options options; } diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/InstructionDetailWriter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/tools/javap/InstructionDetailWriter.java Tue May 19 11:50:54 2009 -0700 @@ -0,0 +1,57 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.tools.javap; + +import com.sun.tools.classfile.Instruction; + + +/* + * Write additional details for an instruction. + * + *

This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public abstract class InstructionDetailWriter extends BasicWriter { + public enum Kind { + LOCAL_VARS("localVariables"), + LOCAL_VAR_TYPES("localVariableTypes"), + SOURCE("source"), + STACKMAPS("stackMaps"), + TRY_BLOCKS("tryBlocks"); + Kind(String option) { + this.option = option; + } + final String option; + } + InstructionDetailWriter(Context context) { + super(context); + } + + abstract void writeDetails(Instruction instr); + void flush() { } +} diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/JavapTask.java --- a/src/share/classes/com/sun/tools/javap/JavapTask.java Tue May 19 11:43:50 2009 -0700 +++ b/src/share/classes/com/sun/tools/javap/JavapTask.java Tue May 19 11:50:54 2009 -0700 @@ -39,6 +39,7 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -65,7 +66,7 @@ * This code and its internal interfaces are subject to change or * deletion without notice. */ -public class JavapTask implements DisassemblerTool.DisassemblerTask { +public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages { public class BadArgs extends Exception { static final long serialVersionUID = 8765093759964640721L; BadArgs(String key, Object... args) { @@ -241,6 +242,56 @@ } }, + new Option(false, "-XDdetails") { + void process(JavapTask task, String opt, String arg) { + task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class); + } + + }, + + new Option(false, "-XDdetails:") { + @Override + boolean matches(String opt) { + int sep = opt.indexOf(":"); + return sep != -1 && super.matches(opt.substring(0, sep + 1)); + } + + void process(JavapTask task, String opt, String arg) throws BadArgs { + int sep = opt.indexOf(":"); + for (String v: opt.substring(sep + 1).split("[,: ]+")) { + if (!handleArg(task, v)) + throw task.new BadArgs("err.invalid.arg.for.option", v); + } + } + + boolean handleArg(JavapTask task, String arg) { + if (arg.length() == 0) + return true; + + if (arg.equals("all")) { + task.options.details = EnumSet.allOf(InstructionDetailWriter.Kind.class); + return true; + } + + boolean on = true; + if (arg.startsWith("-")) { + on = false; + arg = arg.substring(1); + } + + for (InstructionDetailWriter.Kind k: InstructionDetailWriter.Kind.values()) { + if (arg.equalsIgnoreCase(k.option)) { + if (on) + task.options.details.add(k); + else + task.options.details.remove(k); + return true; + } + } + return false; + } + }, + new Option(false, "-constants") { void process(JavapTask task, String opt, String arg) { task.options.showConstants = true; @@ -251,6 +302,7 @@ JavapTask() { context = new Context(); + context.put(Messages.class, this); options = Options.instance(context); } @@ -469,6 +521,8 @@ context.put(PrintWriter.class, log); ClassWriter classWriter = ClassWriter.instance(context); + SourceWriter sourceWriter = SourceWriter.instance(context); + sourceWriter.setFileManager(fileManager); boolean ok = true; @@ -651,11 +705,11 @@ } - private String getMessage(String key, Object... args) { + public String getMessage(String key, Object... args) { return getMessage(task_locale, key, args); } - private String getMessage(Locale locale, String key, Object... args) { + public String getMessage(Locale locale, String key, Object... args) { if (bundles == null) { // could make this a HashMap> // and for efficiency, keep a hard reference to the bundle for the task diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/Messages.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/tools/javap/Messages.java Tue May 19 11:50:54 2009 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright 2007-2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.tools.javap; + +import java.util.Locale; + +/** + * Access to javap messages. + * + *

This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public interface Messages { + String getMessage(String key, Object... args); + + String getMessage(Locale locale, String key, Object... args); +} diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/Options.java --- a/src/share/classes/com/sun/tools/javap/Options.java Tue May 19 11:43:50 2009 -0700 +++ b/src/share/classes/com/sun/tools/javap/Options.java Tue May 19 11:50:54 2009 -0700 @@ -25,8 +25,10 @@ package com.sun.tools.javap; +import java.util.EnumSet; import java.util.HashSet; import java.util.Set; + import com.sun.tools.classfile.AccessFlags; /* @@ -77,6 +79,7 @@ public boolean showLineAndLocalVariableTables; public int showAccess; public Set accessOptions = new HashSet(); + public Set details = EnumSet.noneOf(InstructionDetailWriter.Kind.class); public boolean showDisassembled; public boolean showInternalSignatures; public boolean showAllAttrs; diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/SourceWriter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/tools/javap/SourceWriter.java Tue May 19 11:50:54 2009 -0700 @@ -0,0 +1,207 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.tools.javap; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileManager.Location; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; + +import com.sun.tools.classfile.Attribute; +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.Code_attribute; +import com.sun.tools.classfile.ConstantPoolException; +import com.sun.tools.classfile.Instruction; +import com.sun.tools.classfile.LineNumberTable_attribute; +import com.sun.tools.classfile.SourceFile_attribute; + + +/** + * Annotate instructions with source code. + * + *

This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class SourceWriter extends InstructionDetailWriter { + static SourceWriter instance(Context context) { + SourceWriter instance = context.get(SourceWriter.class); + if (instance == null) + instance = new SourceWriter(context); + return instance; + } + + protected SourceWriter(Context context) { + super(context); + context.put(SourceWriter.class, this); + } + + void setFileManager(JavaFileManager fileManager) { + this.fileManager = fileManager; + } + + public void reset(ClassFile cf, Code_attribute attr) { + setSource(cf); + setLineMap(attr); + } + + public void writeDetails(Instruction instr) { + String indent = space(40); // could get from Options? + Set lines = lineMap.get(instr.getPC()); + if (lines != null) { + for (int line: lines) { + print(indent); + print(String.format(" %4d ", line)); + if (line < sourceLines.length) + print(sourceLines[line]); + println(); + int nextLine = nextLine(line); + for (int i = line + 1; i < nextLine; i++) { + print(indent); + print(String.format("(%4d)", i)); + if (i < sourceLines.length) + print(sourceLines[i]); + println(); + } + } + } + + } + + private void setLineMap(Code_attribute attr) { + SortedMap> map = + new TreeMap>(); + SortedSet allLines = new TreeSet(); + for (Attribute a: attr.attributes) { + if (a instanceof LineNumberTable_attribute) { + LineNumberTable_attribute t = (LineNumberTable_attribute) a; + for (LineNumberTable_attribute.Entry e: t.line_number_table) { + int start_pc = e.start_pc; + int line = e.line_number; + SortedSet pcLines = map.get(start_pc); + if (pcLines == null) { + pcLines = new TreeSet(); + map.put(start_pc, pcLines); + } + pcLines.add(line); + allLines.add(line); + } + } + } + lineMap = map; + lineList = new ArrayList(allLines); + } + + private void setSource(ClassFile cf) { + if (cf != classFile) { + classFile = cf; + sourceLines = splitLines(readSource(cf)); + } + } + + private String readSource(ClassFile cf) { + Location location; + if (fileManager.hasLocation((StandardLocation.SOURCE_PATH))) + location = StandardLocation.SOURCE_PATH; + else + location = StandardLocation.CLASS_PATH; + + // Guess the source file for a class from the package name for this + // class and the base of the source file. This avoids having to read + // additional classes to determine the outmost class from any + // InnerClasses and EnclosingMethod attributes. + try { + String className = cf.getName(); + SourceFile_attribute sf = + (SourceFile_attribute) cf.attributes.get(Attribute.SourceFile); + if (sf == null) { + report(messages.getMessage("err.no.SourceFile.attribute")); + return null; + } + String sourceFile = sf.getSourceFile(cf.constant_pool); + String fileBase = sourceFile.endsWith(".java") + ? sourceFile.substring(0, sourceFile.length() - 5) : sourceFile; + int sep = className.lastIndexOf("/"); + String pkgName = (sep == -1 ? "" : className.substring(0, sep+1)); + String topClassName = (pkgName + fileBase).replace('/', '.'); + JavaFileObject fo = + fileManager.getJavaFileForInput(location, + topClassName, + JavaFileObject.Kind.SOURCE); + if (fo == null) { + report(messages.getMessage("err.source.file.not.found")); + return null; + } + return fo.getCharContent(true).toString(); + } catch (ConstantPoolException e) { + report(e); + return null; + } catch (IOException e) { + report(e.getLocalizedMessage()); + return null; + } + } + + private static String[] splitLines(String text) { + if (text == null) + return new String[0]; + + List lines = new ArrayList(); + lines.add(""); // dummy line 0 + try { + BufferedReader r = new BufferedReader(new StringReader(text)); + String line; + while ((line = r.readLine()) != null) + lines.add(line); + } catch (IOException ignore) { + } + return lines.toArray(new String[lines.size()]); + } + + private int nextLine(int line) { + int i = lineList.indexOf(line); + if (i == -1 || i == lineList.size() - 1) + return - 1; + return lineList.get(i + 1); + } + + private JavaFileManager fileManager; + private ClassFile classFile; + private SortedMap> lineMap; + private List lineList; + private String[] sourceLines; +} diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/StackMapWriter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/tools/javap/StackMapWriter.java Tue May 19 11:50:54 2009 -0700 @@ -0,0 +1,291 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.tools.javap; + +import com.sun.tools.classfile.AccessFlags; +import java.util.HashMap; +import java.util.Map; + +import com.sun.tools.classfile.Attribute; +import com.sun.tools.classfile.Code_attribute; +import com.sun.tools.classfile.ConstantPool; +import com.sun.tools.classfile.ConstantPoolException; +import com.sun.tools.classfile.Descriptor; +import com.sun.tools.classfile.Descriptor.InvalidDescriptor; +import com.sun.tools.classfile.Instruction; +import com.sun.tools.classfile.Method; +import com.sun.tools.classfile.StackMapTable_attribute; +import com.sun.tools.classfile.StackMapTable_attribute.*; + +import static com.sun.tools.classfile.StackMapTable_attribute.verification_type_info.*; + +/** + * Annotate instructions with stack map. + * + *

This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class StackMapWriter extends InstructionDetailWriter { + static StackMapWriter instance(Context context) { + StackMapWriter instance = context.get(StackMapWriter.class); + if (instance == null) + instance = new StackMapWriter(context); + return instance; + } + + protected StackMapWriter(Context context) { + super(context); + context.put(StackMapWriter.class, this); + classWriter = ClassWriter.instance(context); + } + + public void reset(Code_attribute attr) { + setStackMap((StackMapTable_attribute) attr.attributes.get(Attribute.StackMapTable)); + } + + void setStackMap(StackMapTable_attribute attr) { + if (attr == null) { + map = null; + return; + } + + Method m = classWriter.getMethod(); + Descriptor d = m.descriptor; + String[] args; + try { + ConstantPool cp = classWriter.getClassFile().constant_pool; + String argString = d.getParameterTypes(cp); + args = argString.substring(1, argString.length() - 1).split("[, ]+"); + } catch (ConstantPoolException e) { + return; + } catch (InvalidDescriptor e) { + return; + } + boolean isStatic = m.access_flags.is(AccessFlags.ACC_STATIC); + + verification_type_info[] initialLocals = new verification_type_info[(isStatic ? 0 : 1) + args.length]; + if (!isStatic) + initialLocals[0] = new CustomVerificationTypeInfo("this"); + for (int i = 0; i < args.length; i++) { + initialLocals[(isStatic ? 0 : 1) + i] = + new CustomVerificationTypeInfo(args[i].replace(".", "/")); + } + + map = new HashMap(); + StackMapBuilder builder = new StackMapBuilder(); + + // using -1 as the pc for the initial frame effectively compensates for + // the difference in behavior for the first stack map frame (where the + // pc offset is just offset_delta) compared to subsequent frames (where + // the pc offset is always offset_delta+1). + int pc = -1; + + map.put(pc, new StackMap(initialLocals, empty)); + + for (int i = 0; i < attr.entries.length; i++) + pc = attr.entries[i].accept(builder, pc); + } + + public void writeInitialDetails() { + writeDetails(-1); + } + + public void writeDetails(Instruction instr) { + writeDetails(instr.getPC()); + } + + private void writeDetails(int pc) { + if (map == null) + return; + + StackMap m = map.get(pc); + if (m != null) { + print("StackMap locals: ", m.locals); + print("StackMap stack: ", m.stack); + } + + } + + void print(String label, verification_type_info[] entries) { + print(label); + for (int i = 0; i < entries.length; i++) { + print(" "); + print(entries[i]); + } + println(); + } + + void print(verification_type_info entry) { + if (entry == null) { + print("ERROR"); + return; + } + + switch (entry.tag) { + case -1: + print(((CustomVerificationTypeInfo) entry).text); + break; + + case ITEM_Top: + print("top"); + break; + + case ITEM_Integer: + print("int"); + break; + + case ITEM_Float: + print("float"); + break; + + case ITEM_Long: + print("long"); + break; + + case ITEM_Double: + print("double"); + break; + + case ITEM_Null: + print("null"); + break; + + case ITEM_UninitializedThis: + print("uninit_this"); + break; + + case ITEM_Object: + try { + ConstantPool cp = classWriter.getClassFile().constant_pool; + ConstantPool.CONSTANT_Class_info class_info = cp.getClassInfo(((Object_variable_info) entry).cpool_index); + print(cp.getUTF8Value(class_info.name_index)); + } catch (ConstantPoolException e) { + print("??"); + } + break; + + case ITEM_Uninitialized: + print(((Uninitialized_variable_info) entry).offset); + break; + } + + } + + private Map map; + private ClassWriter classWriter; + + class StackMapBuilder + implements StackMapTable_attribute.stack_map_frame.Visitor { + + public Integer visit_same_frame(same_frame frame, Integer pc) { + int new_pc = pc + frame.getOffsetDelta() + 1; + StackMap m = map.get(pc); + assert (m != null); + map.put(new_pc, m); + return new_pc; + } + + public Integer visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame frame, Integer pc) { + int new_pc = pc + frame.getOffsetDelta() + 1; + StackMap prev = map.get(pc); + assert (prev != null); + StackMap m = new StackMap(prev.locals, frame.stack); + map.put(new_pc, m); + return new_pc; + } + + public Integer visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended frame, Integer pc) { + int new_pc = pc + frame.getOffsetDelta() + 1; + StackMap prev = map.get(pc); + assert (prev != null); + StackMap m = new StackMap(prev.locals, frame.stack); + map.put(new_pc, m); + return new_pc; + } + + public Integer visit_chop_frame(chop_frame frame, Integer pc) { + int new_pc = pc + frame.getOffsetDelta() + 1; + StackMap prev = map.get(pc); + assert (prev != null); + int k = 251 - frame.frame_type; + verification_type_info[] new_locals = new verification_type_info[prev.locals.length - k]; + System.arraycopy(prev.locals, 0, new_locals, 0, new_locals.length); + StackMap m = new StackMap(new_locals, empty); + map.put(new_pc, m); + return new_pc; + } + + public Integer visit_same_frame_extended(same_frame_extended frame, Integer pc) { + int new_pc = pc + frame.getOffsetDelta(); + StackMap m = map.get(pc); + assert (m != null); + map.put(new_pc, m); + return new_pc; + } + + public Integer visit_append_frame(append_frame frame, Integer pc) { + int new_pc = pc + frame.getOffsetDelta() + 1; + StackMap prev = map.get(pc); + assert (prev != null); + verification_type_info[] new_locals = new verification_type_info[prev.locals.length + frame.locals.length]; + System.arraycopy(prev.locals, 0, new_locals, 0, prev.locals.length); + System.arraycopy(frame.locals, 0, new_locals, prev.locals.length, frame.locals.length); + StackMap m = new StackMap(new_locals, empty); + map.put(new_pc, m); + return new_pc; + } + + public Integer visit_full_frame(full_frame frame, Integer pc) { + int new_pc = pc + frame.getOffsetDelta() + 1; + StackMap m = new StackMap(frame.locals, frame.stack); + map.put(new_pc, m); + return new_pc; + } + + } + + class StackMap { + StackMap(verification_type_info[] locals, verification_type_info[] stack) { + this.locals = locals; + this.stack = stack; + } + + private final verification_type_info[] locals; + private final verification_type_info[] stack; + } + + class CustomVerificationTypeInfo extends verification_type_info { + public CustomVerificationTypeInfo(String text) { + super(-1); + this.text = text; + } + private String text; + } + + private final verification_type_info[] empty = { }; +} diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/TryBlockWriter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/tools/javap/TryBlockWriter.java Tue May 19 11:50:54 2009 -0700 @@ -0,0 +1,142 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.tools.javap; + +import com.sun.tools.classfile.Code_attribute; +import com.sun.tools.classfile.Code_attribute.Exception_data; +import com.sun.tools.classfile.Instruction; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +/** + * Annotate instructions with details about try blocks. + * + *

This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class TryBlockWriter extends InstructionDetailWriter { + public enum NoteKind { + START("try") { + public boolean match(Exception_data entry, int pc) { + return (pc == entry.start_pc); + } + }, + END("end try") { + public boolean match(Exception_data entry, int pc) { + return (pc == entry.end_pc); + } + }, + HANDLER("catch") { + public boolean match(Exception_data entry, int pc) { + return (pc == entry.handler_pc); + } + }; + NoteKind(String text) { + this.text = text; + } + public abstract boolean match(Exception_data entry, int pc); + public final String text; + }; + + static TryBlockWriter instance(Context context) { + TryBlockWriter instance = context.get(TryBlockWriter.class); + if (instance == null) + instance = new TryBlockWriter(context); + return instance; + } + + protected TryBlockWriter(Context context) { + super(context); + context.put(TryBlockWriter.class, this); + constantWriter = ConstantWriter.instance(context); + } + + public void reset(Code_attribute attr) { + indexMap = new HashMap(); + pcMap = new HashMap>(); + for (int i = 0; i < attr.exception_table.length; i++) { + Exception_data entry = attr.exception_table[i]; + indexMap.put(entry, i); + put(entry.start_pc, entry); + put(entry.end_pc, entry); + put(entry.handler_pc, entry); + } + } + + public void writeDetails(Instruction instr) { + writeTrys(instr, NoteKind.END); + writeTrys(instr, NoteKind.START); + writeTrys(instr, NoteKind.HANDLER); + } + + public void writeTrys(Instruction instr, NoteKind kind) { + String indent = space(2); // get from Options? + int pc = instr.getPC(); + List entries = pcMap.get(pc); + if (entries != null) { + for (ListIterator iter = + entries.listIterator(kind == NoteKind.END ? entries.size() : 0); + kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) { + Exception_data entry = + kind == NoteKind.END ? iter.previous() : iter.next(); + if (kind.match(entry, pc)) { + print(indent); + print(kind.text); + print("["); + print(indexMap.get(entry)); + print("] "); + if (entry.catch_type == 0) + print("finally"); + else { + print("#" + entry.catch_type); + print(" // "); + constantWriter.write(entry.catch_type); + } + println(); + } + } + } + } + + private void put(int pc, Exception_data entry) { + List list = pcMap.get(pc); + if (list == null) { + list = new ArrayList(); + pcMap.put(pc, list); + } + if (!list.contains(entry)) + list.add(entry); + } + + private Map> pcMap; + private Map indexMap; + private ConstantWriter constantWriter; +} diff -r fc634a593812 -r cd0630109de5 src/share/classes/com/sun/tools/javap/resources/javap.properties --- a/src/share/classes/com/sun/tools/javap/resources/javap.properties Tue May 19 11:43:50 2009 -0700 +++ b/src/share/classes/com/sun/tools/javap/resources/javap.properties Tue May 19 11:50:54 2009 -0700 @@ -9,6 +9,7 @@ err.h.not.supported=-h is no longer available - use the 'javah' program err.incompatible.options=bad combination of options: {0} err.internal.error=internal error: {0} {1} {2} +err.invalid.arg.for.option=invalid argument for option: {0} err.ioerror=IO error reading {0}: {1} err.missing.arg=no value given for {0} err.no.classes.specified=no classes specified @@ -16,6 +17,8 @@ err.unknown.option=unknown option: {0} err.verify.not.supported=-verify not supported err.Xold.not.supported.here=-Xold must be given as the first option +err.no.SourceFile.attribute=no SourceFile attribute +err.source.file.not.found=source file not found main.usage.summary=\ Usage: {0} \n\ diff -r fc634a593812 -r cd0630109de5 test/tools/javap/T6824493.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javap/T6824493.java Tue May 19 11:50:54 2009 -0700 @@ -0,0 +1,116 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.*; +import java.util.*; + +/* + * @test + * @bug 6824493 + * @summary experimental support for additional info for instructions + * @compile -g T6824493.java + * @run main T6824493 + */ +public class T6824493 { + public static void main(String... args) { + new T6824493().run(); + } + + void run() { + // for each of the options, we run javap and check for some + // marker strings in the output that generally indicate the + // presence of the expected output, without being as specific + // as a full golden file test. + test("-XDdetails:source", + "for (int i = 0; i < 10; i++) {", + "System.out.println(s + i);"); + + test("-XDdetails:tryBlocks", + "try[0]", + "end try[0]", + "catch[0]"); + + test("-XDdetails:stackMaps", + "StackMap locals: this java/lang/String int", + "StackMap stack: java/lang/Throwable"); + + test("-XDdetails:localVariables", + "start local 3 // java.util.List list", + "end local 3 // java.util.List list"); + + test("-XDdetails:localVariableTypes", + "start generic local 3 // java.util.List list", + "end generic local 3 // java.util.List list"); + + if (errors > 0) + throw new Error(errors + " errors found"); + } + + void test(String option, String... expect) { + String[] args = { + "-c", + "-classpath", + testSrc + File.pathSeparator + testClasses, + option, + "Test" + }; + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = com.sun.tools.javap.Main.run(args, pw); + if (rc != 0) { + error("unexpected return code from javap: " + rc); + return; + } + + String out = sw.toString(); + System.out.println(out); + for (String e: expect) { + if (!out.contains(e)) + error("Not found: " + e); + } + } + + void error(String msg) { + System.err.println("Error: " + msg); + errors++; + } + + private int errors; + private String testSrc = System.getProperty("test.src", "."); + private String testClasses = System.getProperty("test.classes", "."); +} + +class Test { + void m(String s) { + for (int i = 0; i < 10; i++) { + try { + List list = null; + System.out.println(s + i); + } catch (NullPointerException e) { + System.out.println("catch NPE"); + } finally { + System.out.println("finally"); + } + } + } +}