diff options
Diffstat (limited to 'test/java/com/google/devtools/build/android/desugar/ByteCodeTypePrinter.java')
-rw-r--r-- | test/java/com/google/devtools/build/android/desugar/ByteCodeTypePrinter.java | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/test/java/com/google/devtools/build/android/desugar/ByteCodeTypePrinter.java b/test/java/com/google/devtools/build/android/desugar/ByteCodeTypePrinter.java new file mode 100644 index 0000000..fefc599 --- /dev/null +++ b/test/java/com/google/devtools/build/android/desugar/ByteCodeTypePrinter.java @@ -0,0 +1,240 @@ +// 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.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<ZipEntry> getSortedClassEntriess(ZipFile jar) { + return jar.stream() + .filter(entry -> entry.getName().endsWith(".class")) + .sorted() + .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 printTypeOfOperandStackTop() { + printer.print(" |__STACK: " + inference.getOperandStackAsString() + "\n"); + } + + @Override + public void visitIntInsn(int opcode, int operand) { + printer.visitIntInsn(opcode, operand); + printTypeOfOperandStackTop(); + super.visitIntInsn(opcode, operand); + } + + @Override + public void visitInsn(int opcode) { + printer.visitInsn(opcode); + printTypeOfOperandStackTop(); + super.visitInsn(opcode); + } + + @Override + public void visitMultiANewArrayInsn(String desc, int dims) { + printer.visitMultiANewArrayInsn(desc, dims); + printTypeOfOperandStackTop(); + super.visitMultiANewArrayInsn(desc, dims); + } + + @Override + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + printer.visitLookupSwitchInsn(dflt, keys, labels); + printTypeOfOperandStackTop(); + super.visitLookupSwitchInsn(dflt, keys, labels); + } + + @Override + public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { + printer.visitTableSwitchInsn(min, max, dflt, labels); + printTypeOfOperandStackTop(); + super.visitTableSwitchInsn(min, max, dflt, labels); + } + + @Override + public void visitIincInsn(int var, int increment) { + printer.visitIincInsn(var, increment); + printTypeOfOperandStackTop(); + super.visitIincInsn(var, increment); + } + + @Override + public void visitLdcInsn(Object cst) { + printer.visitLdcInsn(cst); + printTypeOfOperandStackTop(); + super.visitLdcInsn(cst); + } + + @Override + public void visitJumpInsn(int opcode, Label label) { + printer.visitJumpInsn(opcode, label); + printTypeOfOperandStackTop(); + super.visitJumpInsn(opcode, label); + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { + printer.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + printTypeOfOperandStackTop(); + 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); + printTypeOfOperandStackTop(); + 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); + printTypeOfOperandStackTop(); + super.visitMethodInsn(opcode, owner, name, desc); + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + printer.visitFieldInsn(opcode, owner, name, desc); + printTypeOfOperandStackTop(); + super.visitFieldInsn(opcode, owner, name, desc); + } + + @Override + public void visitTypeInsn(int opcode, String type) { + printer.visitTypeInsn(opcode, type); + printTypeOfOperandStackTop(); + super.visitTypeInsn(opcode, type); + } + + @Override + public void visitVarInsn(int opcode, int var) { + printer.visitVarInsn(opcode, var); + printTypeOfOperandStackTop(); + 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(); + } + } +} |