ByteCodeTypePrinter.java revision 0156e0d255709355b1e8f5303bd8256ab6967b0a
1// Copyright 2017 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.android.desugar;
15
16import com.google.common.base.Preconditions;
17import com.google.common.collect.ImmutableList;
18import java.io.IOException;
19import java.io.InputStream;
20import java.io.PrintWriter;
21import java.nio.file.Files;
22import java.nio.file.Path;
23import java.util.Comparator;
24import java.util.zip.ZipEntry;
25import java.util.zip.ZipFile;
26import org.objectweb.asm.ClassReader;
27import org.objectweb.asm.ClassVisitor;
28import org.objectweb.asm.Handle;
29import org.objectweb.asm.Label;
30import org.objectweb.asm.MethodVisitor;
31import org.objectweb.asm.Opcodes;
32import org.objectweb.asm.util.Textifier;
33
34/** Print the types of the operand stack for each method. */
35public class ByteCodeTypePrinter {
36
37  public static void printClassesWithTypes(Path inputJarFile, PrintWriter printWriter)
38      throws IOException {
39    Preconditions.checkState(
40        Files.exists(inputJarFile), "The input jar file %s does not exist.", inputJarFile);
41    try (ZipFile jarFile = new ZipFile(inputJarFile.toFile())) {
42      for (ZipEntry entry : getSortedClassEntriess(jarFile)) {
43        try (InputStream classStream = jarFile.getInputStream(entry)) {
44          printWriter.println("\nClass: " + entry.getName());
45          ClassReader classReader = new ClassReader(classStream);
46          ClassVisitor visitor = new ClassWithTypeDumper(printWriter);
47          classReader.accept(visitor, 0);
48        }
49        printWriter.println("\n");
50      }
51    }
52  }
53
54  private static ImmutableList<ZipEntry> getSortedClassEntriess(ZipFile jar) {
55    return jar.stream()
56        .filter(entry -> entry.getName().endsWith(".class"))
57        .sorted(Comparator.comparing(ZipEntry::getName))
58        .collect(ImmutableList.toImmutableList());
59  }
60
61  private static class ClassWithTypeDumper extends ClassVisitor {
62
63    private String internalName;
64    private final PrintWriter printWriter;
65
66    public ClassWithTypeDumper(PrintWriter printWriter) {
67      super(Opcodes.ASM5);
68      this.printWriter = printWriter;
69    }
70
71    @Override
72    public void visit(
73        int version,
74        int access,
75        String name,
76        String signature,
77        String superName,
78        String[] interfaces) {
79      internalName = name;
80      super.visit(version, access, name, signature, superName, interfaces);
81    }
82
83    @Override
84    public MethodVisitor visitMethod(
85        int access, String name, String desc, String signature, String[] exceptions) {
86      printWriter.println("Method " + name);
87      MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
88      BytecodeTypeInference inference = new BytecodeTypeInference(access, internalName, name, desc);
89      mv = new MethodIrTypeDumper(mv, inference, printWriter);
90      inference.setDelegateMethodVisitor(mv);
91      // Let the type inference runs first.
92      return inference;
93    }
94  }
95
96  private static final class TextifierExt extends Textifier {
97
98    public TextifierExt() {
99      super(Opcodes.ASM5);
100    }
101
102    public void print(String string) {
103      text.add(tab2 + string);
104    }
105  }
106
107  private static class MethodIrTypeDumper extends MethodVisitor {
108
109    private final BytecodeTypeInference inference;
110    private final TextifierExt printer = new TextifierExt();
111    private final PrintWriter printWriter;
112
113    public MethodIrTypeDumper(
114        MethodVisitor visitor, BytecodeTypeInference inference, PrintWriter printWriter) {
115      super(Opcodes.ASM5, visitor);
116      this.inference = inference;
117      this.printWriter = printWriter;
118    }
119
120    private void printTypeOfOperandStack() {
121      printer.print("    |__STACK: " + inference.getOperandStackAsString() + "\n");
122      printer.print("    |__LOCAL: " + inference.getLocalsAsString() + "\n");
123    }
124
125    @Override
126    public void visitIntInsn(int opcode, int operand) {
127      printer.visitIntInsn(opcode, operand);
128      printTypeOfOperandStack();
129      super.visitIntInsn(opcode, operand);
130    }
131
132    @Override
133    public void visitInsn(int opcode) {
134      printer.visitInsn(opcode);
135      printTypeOfOperandStack();
136      super.visitInsn(opcode);
137    }
138
139    @Override
140    public void visitMultiANewArrayInsn(String desc, int dims) {
141      printer.visitMultiANewArrayInsn(desc, dims);
142      printTypeOfOperandStack();
143      super.visitMultiANewArrayInsn(desc, dims);
144    }
145
146    @Override
147    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
148      printer.visitLookupSwitchInsn(dflt, keys, labels);
149      printTypeOfOperandStack();
150      super.visitLookupSwitchInsn(dflt, keys, labels);
151    }
152
153    @Override
154    public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
155      printer.visitTableSwitchInsn(min, max, dflt, labels);
156      printTypeOfOperandStack();
157      super.visitTableSwitchInsn(min, max, dflt, labels);
158    }
159
160    @Override
161    public void visitIincInsn(int var, int increment) {
162      printer.visitIincInsn(var, increment);
163      printTypeOfOperandStack();
164      super.visitIincInsn(var, increment);
165    }
166
167    @Override
168    public void visitLdcInsn(Object cst) {
169      printer.visitLdcInsn(cst);
170      printTypeOfOperandStack();
171      super.visitLdcInsn(cst);
172    }
173
174    @Override
175    public void visitJumpInsn(int opcode, Label label) {
176      printer.visitJumpInsn(opcode, label);
177      printTypeOfOperandStack();
178      super.visitJumpInsn(opcode, label);
179    }
180
181    @Override
182    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
183      printer.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
184      printTypeOfOperandStack();
185      super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
186    }
187
188    @Override
189    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
190      printer.visitMethodInsn(opcode, owner, name, desc, itf);
191      printTypeOfOperandStack();
192      super.visitMethodInsn(opcode, owner, name, desc, itf);
193    }
194
195    @Override
196    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
197      printer.visitMethodInsn(opcode, owner, name, desc);
198      printTypeOfOperandStack();
199      super.visitMethodInsn(opcode, owner, name, desc);
200    }
201
202    @Override
203    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
204      printer.visitFieldInsn(opcode, owner, name, desc);
205      printTypeOfOperandStack();
206      super.visitFieldInsn(opcode, owner, name, desc);
207    }
208
209    @Override
210    public void visitTypeInsn(int opcode, String type) {
211      printer.visitTypeInsn(opcode, type);
212      printTypeOfOperandStack();
213      super.visitTypeInsn(opcode, type);
214    }
215
216    @Override
217    public void visitVarInsn(int opcode, int var) {
218      printer.visitVarInsn(opcode, var);
219      printTypeOfOperandStack();
220      super.visitVarInsn(opcode, var);
221    }
222
223    @Override
224    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
225      printer.visitFrame(type, nLocal, local, nStack, stack);
226      super.visitFrame(type, nLocal, local, nStack, stack);
227    }
228
229    @Override
230    public void visitLabel(Label label) {
231      printer.visitLabel(label);
232      super.visitLabel(label);
233    }
234
235    @Override
236    public void visitEnd() {
237      printer.print(printWriter);
238      printWriter.flush();
239      super.visitEnd();
240    }
241  }
242}
243