// Copyright 2017 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.android.desugar; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.util.Comparator; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.util.Textifier; /** Print the types of the operand stack for each method. */ public class ByteCodeTypePrinter { public static void printClassesWithTypes(Path inputJarFile, PrintWriter printWriter) throws IOException { Preconditions.checkState( Files.exists(inputJarFile), "The input jar file %s does not exist.", inputJarFile); try (ZipFile jarFile = new ZipFile(inputJarFile.toFile())) { for (ZipEntry entry : getSortedClassEntriess(jarFile)) { try (InputStream classStream = jarFile.getInputStream(entry)) { printWriter.println("\nClass: " + entry.getName()); ClassReader classReader = new ClassReader(classStream); ClassVisitor visitor = new ClassWithTypeDumper(printWriter); classReader.accept(visitor, 0); } printWriter.println("\n"); } } } private static ImmutableList getSortedClassEntriess(ZipFile jar) { return jar.stream() .filter(entry -> entry.getName().endsWith(".class")) .sorted(Comparator.comparing(ZipEntry::getName)) .collect(ImmutableList.toImmutableList()); } private static class ClassWithTypeDumper extends ClassVisitor { private String internalName; private final PrintWriter printWriter; public ClassWithTypeDumper(PrintWriter printWriter) { super(Opcodes.ASM5); this.printWriter = printWriter; } @Override public void visit( int version, int access, String name, String signature, String superName, String[] interfaces) { internalName = name; super.visit(version, access, name, signature, superName, interfaces); } @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions) { printWriter.println("Method " + name); MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); BytecodeTypeInference inference = new BytecodeTypeInference(access, internalName, name, desc); mv = new MethodIrTypeDumper(mv, inference, printWriter); inference.setDelegateMethodVisitor(mv); // Let the type inference runs first. return inference; } } private static final class TextifierExt extends Textifier { public TextifierExt() { super(Opcodes.ASM5); } public void print(String string) { text.add(tab2 + string); } } private static class MethodIrTypeDumper extends MethodVisitor { private final BytecodeTypeInference inference; private final TextifierExt printer = new TextifierExt(); private final PrintWriter printWriter; public MethodIrTypeDumper( MethodVisitor visitor, BytecodeTypeInference inference, PrintWriter printWriter) { super(Opcodes.ASM5, visitor); this.inference = inference; this.printWriter = printWriter; } private void printTypeOfOperandStack() { printer.print(" |__STACK: " + inference.getOperandStackAsString() + "\n"); printer.print(" |__LOCAL: " + inference.getLocalsAsString() + "\n"); } @Override public void visitIntInsn(int opcode, int operand) { printer.visitIntInsn(opcode, operand); printTypeOfOperandStack(); super.visitIntInsn(opcode, operand); } @Override public void visitInsn(int opcode) { printer.visitInsn(opcode); printTypeOfOperandStack(); super.visitInsn(opcode); } @Override public void visitMultiANewArrayInsn(String desc, int dims) { printer.visitMultiANewArrayInsn(desc, dims); printTypeOfOperandStack(); super.visitMultiANewArrayInsn(desc, dims); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { printer.visitLookupSwitchInsn(dflt, keys, labels); printTypeOfOperandStack(); super.visitLookupSwitchInsn(dflt, keys, labels); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { printer.visitTableSwitchInsn(min, max, dflt, labels); printTypeOfOperandStack(); super.visitTableSwitchInsn(min, max, dflt, labels); } @Override public void visitIincInsn(int var, int increment) { printer.visitIincInsn(var, increment); printTypeOfOperandStack(); super.visitIincInsn(var, increment); } @Override public void visitLdcInsn(Object cst) { printer.visitLdcInsn(cst); printTypeOfOperandStack(); super.visitLdcInsn(cst); } @Override public void visitJumpInsn(int opcode, Label label) { printer.visitJumpInsn(opcode, label); printTypeOfOperandStack(); super.visitJumpInsn(opcode, label); } @Override public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { printer.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); printTypeOfOperandStack(); super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { printer.visitMethodInsn(opcode, owner, name, desc, itf); printTypeOfOperandStack(); super.visitMethodInsn(opcode, owner, name, desc, itf); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { printer.visitMethodInsn(opcode, owner, name, desc); printTypeOfOperandStack(); super.visitMethodInsn(opcode, owner, name, desc); } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { printer.visitFieldInsn(opcode, owner, name, desc); printTypeOfOperandStack(); super.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitTypeInsn(int opcode, String type) { printer.visitTypeInsn(opcode, type); printTypeOfOperandStack(); super.visitTypeInsn(opcode, type); } @Override public void visitVarInsn(int opcode, int var) { printer.visitVarInsn(opcode, var); printTypeOfOperandStack(); super.visitVarInsn(opcode, var); } @Override public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { printer.visitFrame(type, nLocal, local, nStack, stack); super.visitFrame(type, nLocal, local, nStack, stack); } @Override public void visitLabel(Label label) { printer.visitLabel(label); super.visitLabel(label); } @Override public void visitEnd() { printer.print(printWriter); printWriter.flush(); super.visitEnd(); } } }