1 /*
2  * Copyright (c) 2007, 2013, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.tools.javap;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 import com.sun.tools.classfile.AccessFlags;
32 import com.sun.tools.classfile.Code_attribute;
33 import com.sun.tools.classfile.ConstantPool;
34 import com.sun.tools.classfile.ConstantPoolException;
35 import com.sun.tools.classfile.DescriptorException;
36 import com.sun.tools.classfile.Instruction;
37 import com.sun.tools.classfile.Instruction.TypeKind;
38 import com.sun.tools.classfile.Method;
39 
40 /*
41  *  Write the contents of a Code attribute.
42  *
43  *  <p><b>This is NOT part of any supported API.
44  *  If you write code that depends on this, you do so at your own risk.
45  *  This code and its internal interfaces are subject to change or
46  *  deletion without notice.</b>
47  */
48 public class CodeWriter extends BasicWriter {
instance(Context context)49     public static CodeWriter instance(Context context) {
50         CodeWriter instance = context.get(CodeWriter.class);
51         if (instance == null)
52             instance = new CodeWriter(context);
53         return instance;
54     }
55 
CodeWriter(Context context)56     protected CodeWriter(Context context) {
57         super(context);
58         context.put(CodeWriter.class, this);
59         attrWriter = AttributeWriter.instance(context);
60         classWriter = ClassWriter.instance(context);
61         constantWriter = ConstantWriter.instance(context);
62         sourceWriter = SourceWriter.instance(context);
63         tryBlockWriter = TryBlockWriter.instance(context);
64         stackMapWriter = StackMapWriter.instance(context);
65         localVariableTableWriter = LocalVariableTableWriter.instance(context);
66         localVariableTypeTableWriter = LocalVariableTypeTableWriter.instance(context);
67         typeAnnotationWriter = TypeAnnotationWriter.instance(context);
68         options = Options.instance(context);
69     }
70 
write(Code_attribute attr, ConstantPool constant_pool)71     void write(Code_attribute attr, ConstantPool constant_pool) {
72         println("Code:");
73         indent(+1);
74         writeVerboseHeader(attr, constant_pool);
75         writeInstrs(attr);
76         writeExceptionTable(attr);
77         attrWriter.write(attr, attr.attributes, constant_pool);
78         indent(-1);
79     }
80 
writeVerboseHeader(Code_attribute attr, ConstantPool constant_pool)81     public void writeVerboseHeader(Code_attribute attr, ConstantPool constant_pool) {
82         Method method = classWriter.getMethod();
83         String argCount;
84         try {
85             int n = method.descriptor.getParameterCount(constant_pool);
86             if (!method.access_flags.is(AccessFlags.ACC_STATIC))
87                 ++n;  // for 'this'
88             argCount = Integer.toString(n);
89         } catch (ConstantPoolException e) {
90             argCount = report(e);
91         } catch (DescriptorException e) {
92             argCount = report(e);
93         }
94 
95         println("stack=" + attr.max_stack +
96                 ", locals=" + attr.max_locals +
97                 ", args_size=" + argCount);
98 
99     }
100 
writeInstrs(Code_attribute attr)101     public void writeInstrs(Code_attribute attr) {
102         List<InstructionDetailWriter> detailWriters = getDetailWriters(attr);
103 
104         for (Instruction instr: attr.getInstructions()) {
105             try {
106                 for (InstructionDetailWriter w: detailWriters)
107                     w.writeDetails(instr);
108                 writeInstr(instr);
109             } catch (ArrayIndexOutOfBoundsException e) {
110                 println(report("error at or after byte " + instr.getPC()));
111                 break;
112             }
113         }
114 
115         for (InstructionDetailWriter w: detailWriters)
116             w.flush();
117     }
118 
writeInstr(Instruction instr)119     public void writeInstr(Instruction instr) {
120         print(String.format("%4d: %-13s ", instr.getPC(), instr.getMnemonic()));
121         // compute the number of indentations for the body of multi-line instructions
122         // This is 6 (the width of "%4d: "), divided by the width of each indentation level,
123         // and rounded up to the next integer.
124         int indentWidth = options.indentWidth;
125         int indent = (6 + indentWidth - 1) / indentWidth;
126         instr.accept(instructionPrinter, indent);
127         println();
128     }
129     // where
130     Instruction.KindVisitor<Void,Integer> instructionPrinter =
131             new Instruction.KindVisitor<Void,Integer>() {
132 
133         public Void visitNoOperands(Instruction instr, Integer indent) {
134             return null;
135         }
136 
137         public Void visitArrayType(Instruction instr, TypeKind kind, Integer indent) {
138             print(" " + kind.name);
139             return null;
140         }
141 
142         public Void visitBranch(Instruction instr, int offset, Integer indent) {
143             print((instr.getPC() + offset));
144             return null;
145         }
146 
147         public Void visitConstantPoolRef(Instruction instr, int index, Integer indent) {
148             print("#" + index);
149             tab();
150             print("// ");
151             printConstant(index);
152             return null;
153         }
154 
155         public Void visitConstantPoolRefAndValue(Instruction instr, int index, int value, Integer indent) {
156             print("#" + index + ",  " + value);
157             tab();
158             print("// ");
159             printConstant(index);
160             return null;
161         }
162 
163         public Void visitLocal(Instruction instr, int index, Integer indent) {
164             print(index);
165             return null;
166         }
167 
168         public Void visitLocalAndValue(Instruction instr, int index, int value, Integer indent) {
169             print(index + ", " + value);
170             return null;
171         }
172 
173         public Void visitLookupSwitch(Instruction instr,
174                 int default_, int npairs, int[] matches, int[] offsets, Integer indent) {
175             int pc = instr.getPC();
176             print("{ // " + npairs);
177             indent(indent);
178             for (int i = 0; i < npairs; i++) {
179                 print(String.format("%n%12d: %d", matches[i], (pc + offsets[i])));
180             }
181             print("\n     default: " + (pc + default_) + "\n}");
182             indent(-indent);
183             return null;
184         }
185 
186         public Void visitTableSwitch(Instruction instr,
187                 int default_, int low, int high, int[] offsets, Integer indent) {
188             int pc = instr.getPC();
189             print("{ // " + low + " to " + high);
190             indent(indent);
191             for (int i = 0; i < offsets.length; i++) {
192                 print(String.format("%n%12d: %d", (low + i), (pc + offsets[i])));
193             }
194             print("\n     default: " + (pc + default_) + "\n}");
195             indent(-indent);
196             return null;
197         }
198 
199         public Void visitValue(Instruction instr, int value, Integer indent) {
200             print(value);
201             return null;
202         }
203 
204         public Void visitUnknown(Instruction instr, Integer indent) {
205             return null;
206         }
207     };
208 
209 
writeExceptionTable(Code_attribute attr)210     public void writeExceptionTable(Code_attribute attr) {
211         if (attr.exception_table_length > 0) {
212             println("Exception table:");
213             indent(+1);
214             println(" from    to  target type");
215             for (int i = 0; i < attr.exception_table.length; i++) {
216                 Code_attribute.Exception_data handler = attr.exception_table[i];
217                 print(String.format(" %5d %5d %5d",
218                         handler.start_pc, handler.end_pc, handler.handler_pc));
219                 print("   ");
220                 int catch_type = handler.catch_type;
221                 if (catch_type == 0) {
222                     println("any");
223                 } else {
224                     print("Class ");
225                     println(constantWriter.stringValue(catch_type));
226                 }
227             }
228             indent(-1);
229         }
230 
231     }
232 
printConstant(int index)233     private void printConstant(int index) {
234         constantWriter.write(index);
235     }
236 
getDetailWriters(Code_attribute attr)237     private List<InstructionDetailWriter> getDetailWriters(Code_attribute attr) {
238         List<InstructionDetailWriter> detailWriters = new ArrayList<>();
239         if (options.details.contains(InstructionDetailWriter.Kind.SOURCE)) {
240             sourceWriter.reset(classWriter.getClassFile(), attr);
241             if (sourceWriter.hasSource())
242                 detailWriters.add(sourceWriter);
243             else
244                 println("(Source code not available)");
245         }
246 
247         if (options.details.contains(InstructionDetailWriter.Kind.LOCAL_VARS)) {
248             localVariableTableWriter.reset(attr);
249             detailWriters.add(localVariableTableWriter);
250         }
251 
252         if (options.details.contains(InstructionDetailWriter.Kind.LOCAL_VAR_TYPES)) {
253             localVariableTypeTableWriter.reset(attr);
254             detailWriters.add(localVariableTypeTableWriter);
255         }
256 
257         if (options.details.contains(InstructionDetailWriter.Kind.STACKMAPS)) {
258             stackMapWriter.reset(attr);
259             stackMapWriter.writeInitialDetails();
260             detailWriters.add(stackMapWriter);
261         }
262 
263         if (options.details.contains(InstructionDetailWriter.Kind.TRY_BLOCKS)) {
264             tryBlockWriter.reset(attr);
265             detailWriters.add(tryBlockWriter);
266         }
267 
268         if (options.details.contains(InstructionDetailWriter.Kind.TYPE_ANNOS)) {
269             typeAnnotationWriter.reset(attr);
270             detailWriters.add(typeAnnotationWriter);
271         }
272 
273         return detailWriters;
274     }
275 
276     private AttributeWriter attrWriter;
277     private ClassWriter classWriter;
278     private ConstantWriter constantWriter;
279     private LocalVariableTableWriter localVariableTableWriter;
280     private LocalVariableTypeTableWriter localVariableTypeTableWriter;
281     private TypeAnnotationWriter typeAnnotationWriter;
282     private SourceWriter sourceWriter;
283     private StackMapWriter stackMapWriter;
284     private TryBlockWriter tryBlockWriter;
285     private Options options;
286 }
287