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