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
26package com.sun.tools.javap;
27
28import java.util.ArrayList;
29import java.util.List;
30
31import com.sun.tools.classfile.AccessFlags;
32import com.sun.tools.classfile.Code_attribute;
33import com.sun.tools.classfile.ConstantPool;
34import com.sun.tools.classfile.ConstantPoolException;
35import com.sun.tools.classfile.DescriptorException;
36import com.sun.tools.classfile.Instruction;
37import com.sun.tools.classfile.Instruction.TypeKind;
38import 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 */
48public class CodeWriter extends BasicWriter {
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
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
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
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
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
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
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
233    private void printConstant(int index) {
234        constantWriter.write(index);
235    }
236
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