[CF] Add printer for Java bytecode.
Change-Id: Ia989bdf08fc1ee95eb5e3c363793894e8edd7372
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
new file mode 100644
index 0000000..af51fdc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -0,0 +1,306 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf;
+
+import com.android.tools.r8.cf.code.CfArrayLoad;
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfConstString;
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfGoto;
+import com.android.tools.r8.cf.code.CfIf;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfPop;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStaticGet;
+import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.ValueType;
+import java.util.HashMap;
+import java.util.Map;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Utility to print CF code and instructions.
+ *
+ * <p>This implementation prints the code formatted according to the Jasmin syntax.
+ */
+public class CfPrinter {
+
+ private final String indent;
+ private final Map<CfLabel, String> labels;
+
+ private final StringBuilder builder = new StringBuilder();
+
+ /** Entry for printing single instructions without global knowledge (eg, label numbers). */
+ public CfPrinter() {
+ indent = "";
+ labels = null;
+ }
+
+ /** Entry for printing a complete code object. */
+ public CfPrinter(CfCode code) {
+ indent = " ";
+ labels = new HashMap<>();
+ int nextLabelNumber = 0;
+ for (CfInstruction instruction : code.getInstructions()) {
+ if (instruction instanceof CfLabel) {
+ labels.put((CfLabel) instruction, "L" + nextLabelNumber++);
+ }
+ }
+ builder.append(".method ");
+ appendMethod(code.getMethod());
+ newline();
+ builder.append(".limit stack ").append(code.getMaxStack());
+ newline();
+ builder.append(".limit locals ").append(code.getMaxLocals());
+ for (CfInstruction instruction : code.getInstructions()) {
+ instruction.print(this);
+ }
+ newline();
+ builder.append(".end method");
+ newline();
+ }
+
+ private void print(String name) {
+ indent();
+ builder.append(name);
+ }
+
+ public void print(CfConstNull constNull) {
+ print("aconst_null");
+ }
+
+ public void print(CfConstNumber constNumber) {
+ indent();
+ // TODO(zerny): Determine when the number matches the tconst_X instructions.
+ switch (constNumber.getType()) {
+ case INT:
+ builder.append("ldc ").append(constNumber.getIntValue());
+ break;
+ case FLOAT:
+ builder.append("ldc ").append(constNumber.getFloatValue());
+ break;
+ case LONG:
+ builder.append("ldc_w ").append(constNumber.getLongValue());
+ break;
+ case DOUBLE:
+ builder.append("ldc_w ").append(constNumber.getDoubleValue());
+ break;
+ default:
+ throw new Unreachable("Unexpected const-number type: " + constNumber.getType());
+ }
+ }
+
+ public void print(CfReturn ret) {
+ print("return");
+ }
+
+ public void print(CfPop pop) {
+ print("pop");
+ }
+
+ public void print(CfConstString constString) {
+ indent();
+ builder.append("ldc ").append(constString.getString());
+ }
+
+ public void print(CfArrayLoad arrayLoad) {
+ switch (arrayLoad.getType()) {
+ case OBJECT:
+ print("aaload");
+ break;
+ case BOOLEAN:
+ case BYTE:
+ print("baload");
+ break;
+ case CHAR:
+ print("caload");
+ break;
+ case SHORT:
+ print("saload");
+ break;
+ case INT:
+ print("iaload");
+ break;
+ case FLOAT:
+ print("faload");
+ break;
+ case LONG:
+ print("laload");
+ break;
+ case DOUBLE:
+ print("daload");
+ break;
+ default:
+ throw new Unreachable("Unexpected array-load type: " + arrayLoad.getType());
+ }
+ }
+
+ public void print(CfInvoke invoke) {
+ indent();
+ switch (invoke.getOpcode()) {
+ case Opcodes.INVOKEVIRTUAL:
+ builder.append("invokevirtual");
+ break;
+ case Opcodes.INVOKESPECIAL:
+ builder.append("invokespecial");
+ break;
+ case Opcodes.INVOKESTATIC:
+ builder.append("invokestatic");
+ break;
+ case Opcodes.INVOKEINTERFACE:
+ builder.append("invokeinterface");
+ break;
+ case Opcodes.INVOKEDYNAMIC:
+ builder.append("invokedynamic");
+ break;
+ default:
+ throw new Unreachable("Unexpected invoke opcode: " + invoke.getOpcode());
+ }
+ builder.append(' ');
+ appendMethod(invoke.getMethod());
+ }
+
+ public void print(CfFrame frame) {
+ comment("frame");
+ }
+
+ public void print(CfStaticGet staticGet) {
+ indent();
+ builder.append("getstatic ");
+ appendField(staticGet.getField());
+ appendDescriptor(staticGet.getField().type);
+ }
+
+ public void print(CfNew newInstance) {
+ indent();
+ builder.append("new ");
+ appendClass(newInstance.getType());
+ }
+
+ public void print(CfLabel label) {
+ newline();
+ builder.append(getLabel(label)).append(':');
+ }
+
+ public void print(CfGoto jump) {
+ indent();
+ builder.append("goto ").append(getLabel(jump.getTarget()));
+ }
+
+ public void print(CfIf conditional) {
+ indent();
+ switch (conditional.getOpcode()) {
+ case Opcodes.IFNULL:
+ builder.append("ifnull");
+ break;
+ case Opcodes.IFNONNULL:
+ builder.append("ifnonnull");
+ break;
+ case Opcodes.IFEQ:
+ builder.append("ifeq");
+ break;
+ case Opcodes.IFNE:
+ builder.append("ifne");
+ break;
+ case Opcodes.IFLT:
+ builder.append("iflt");
+ break;
+ case Opcodes.IFGE:
+ builder.append("ifge");
+ break;
+ case Opcodes.IFGT:
+ builder.append("ifgt");
+ break;
+ case Opcodes.IFLE:
+ builder.append("ifle");
+ break;
+ default:
+ throw new Unreachable();
+ }
+ builder.append(' ').append(getLabel(conditional.getTarget()));
+ }
+
+ public void print(CfLoad load) {
+ print(load.getType(), "load", load.getLocalIndex());
+ }
+
+ public void print(CfStore store) {
+ print(store.getType(), "store", store.getLocalIndex());
+ }
+
+ private void print(ValueType type, String instruction, int local) {
+ indent();
+ builder.append(typePrefix(type)).append(instruction).append(' ').append(local);
+ }
+
+ private char typePrefix(ValueType type) {
+ switch (type) {
+ case OBJECT:
+ return 'a';
+ case INT:
+ return 'i';
+ case FLOAT:
+ return 'f';
+ case LONG:
+ return 'l';
+ case DOUBLE:
+ return 'd';
+ default:
+ throw new Unreachable("Unexpected type for prefix: " + type);
+ }
+ }
+
+ private String getLabel(CfLabel label) {
+ return labels != null ? labels.get(label) : "L?";
+ }
+
+ private void newline() {
+ if (builder.length() > 0) {
+ builder.append('\n');
+ }
+ }
+
+ private void indent() {
+ newline();
+ builder.append(indent);
+ }
+
+ private void comment(String comment) {
+ indent();
+ builder.append("; ").append(comment);
+ }
+
+ private void appendDescriptor(DexType type) {
+ builder.append(type.toDescriptorString());
+ }
+
+ private void appendClass(DexType type) {
+ builder.append(type.getInternalName());
+ }
+
+ private void appendField(DexField field) {
+ appendClass(field.getHolder());
+ builder.append('/').append(field.name);
+ }
+
+ private void appendMethod(DexMethod method) {
+ builder.append(method.qualifiedName());
+ builder.append(method.proto.toDescriptorString());
+ }
+
+ @Override
+ public String toString() {
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
index 9043ab6..3ff24b9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.MemberType;
import org.objectweb.asm.MethodVisitor;
@@ -16,6 +17,10 @@
this.type = type;
}
+ public MemberType getType() {
+ return type;
+ }
+
private int getLoadType() {
switch (type) {
case OBJECT:
@@ -44,4 +49,9 @@
public void write(MethodVisitor visitor) {
visitor.visitInsn(getLoadType());
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
index f022f78..a788a3b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -12,4 +13,9 @@
public void write(MethodVisitor visitor) {
visitor.visitInsn(Opcodes.ACONST_NULL);
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
index 1d51bab..c3b4490 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -3,9 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.ValueType;
import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
public class CfConstNumber extends CfInstruction {
@@ -17,23 +19,84 @@
this.type = type;
}
+ public ValueType getType() {
+ return type;
+ }
+
+ public long getRawValue() {
+ return value;
+ }
+
+ public int getIntValue() {
+ assert type == ValueType.INT;
+ return (int) value;
+ }
+
+ public long getLongValue() {
+ assert type == ValueType.LONG;
+ return value;
+ }
+
+ public float getFloatValue() {
+ assert type == ValueType.FLOAT;
+ return Float.intBitsToFloat((int) value);
+ }
+
+ public double getDoubleValue() {
+ assert type == ValueType.DOUBLE;
+ return Double.longBitsToDouble(value);
+ }
+
@Override
public void write(MethodVisitor visitor) {
switch (type) {
case INT:
- visitor.visitLdcInsn((int)value);
- break;
+ {
+ int value = getIntValue();
+ if (-1 <= value && value <= 5) {
+ visitor.visitInsn(Opcodes.ICONST_0 + value);
+ } else {
+ visitor.visitLdcInsn(value);
+ }
+ break;
+ }
case LONG:
- visitor.visitLdcInsn(value);
- break;
+ {
+ long value = getLongValue();
+ if (value == 0 || value == 1) {
+ visitor.visitInsn(Opcodes.LCONST_0 + (int) value);
+ } else {
+ visitor.visitLdcInsn(value);
+ }
+ break;
+ }
case FLOAT:
- visitor.visitLdcInsn(Float.intBitsToFloat((int)value));
- break;
+ {
+ float value = getFloatValue();
+ if (value == 0 || value == 1 || value == 2) {
+ visitor.visitInsn(Opcodes.FCONST_0 + (int) value);
+ } else {
+ visitor.visitLdcInsn(value);
+ }
+ break;
+ }
case DOUBLE:
- visitor.visitLdcInsn(Double.longBitsToDouble(value));
- break;
+ {
+ double value = getDoubleValue();
+ if (value == 0 || value == 1) {
+ visitor.visitInsn(Opcodes.DCONST_0 + (int) value);
+ } else {
+ visitor.visitLdcInsn(value);
+ }
+ break;
+ }
default:
throw new Unreachable("Non supported type in cf backend: " + type);
}
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index 62f1537..0f7f9f7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexString;
import org.objectweb.asm.MethodVisitor;
@@ -14,8 +15,17 @@
this.string = string;
}
+ public DexString getString() {
+ return string;
+ }
+
@Override
public void write(MethodVisitor visitor) {
visitor.visitLdcInsn(string.toString());
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index 78a350d..1d946b2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -5,6 +5,7 @@
import static org.objectweb.asm.Opcodes.F_NEW;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -12,7 +13,6 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
public class CfFrame extends CfInstruction {
@@ -45,7 +45,7 @@
}
switch (type.toShorty()) {
case 'L':
- return Type.getType(type.toDescriptorString()).getInternalName();
+ return type.getInternalName();
case 'I':
return Opcodes.INTEGER;
case 'F':
@@ -63,4 +63,9 @@
public String toString() {
return getClass().getSimpleName();
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index 2207e6f..c881e7b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -14,8 +15,17 @@
this.target = target;
}
+ public CfLabel getTarget() {
+ return target;
+ }
+
@Override
public void write(MethodVisitor visitor) {
visitor.visitJumpInsn(Opcodes.GOTO, target.getLabel());
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index b8397e5..5c7a2c7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
@@ -21,7 +22,11 @@
this.target = target;
}
- private int getOpcode() {
+ public CfLabel getTarget() {
+ return target;
+ }
+
+ public int getOpcode() {
switch (kind) {
case EQ:
return type.isObject() ? Opcodes.IFNULL : Opcodes.IFEQ;
@@ -41,6 +46,11 @@
}
@Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ @Override
public void write(MethodVisitor visitor) {
visitor.visitJumpInsn(getOpcode(), target.getLabel());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 915a491..76806f6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -3,14 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import org.objectweb.asm.MethodVisitor;
public abstract class CfInstruction {
public abstract void write(MethodVisitor visitor);
+ public abstract void print(CfPrinter printer);
+
@Override
public String toString() {
- return getClass().getSimpleName();
+ CfPrinter printer = new CfPrinter();
+ print(printer);
+ return printer.toString();
}
+
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 0e927ac..d90fb92 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexMethod;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
public class CfInvoke extends CfInstruction {
@@ -19,12 +19,25 @@
this.method = method;
}
+ public DexMethod getMethod() {
+ return method;
+ }
+
+ public int getOpcode() {
+ return opcode;
+ }
+
@Override
public void write(MethodVisitor visitor) {
- String owner = Type.getType(method.getHolder().toDescriptorString()).getInternalName();
+ String owner = method.getHolder().getInternalName();
String name = method.name.toString();
String desc = method.proto.toDescriptorString();
boolean iface = method.holder.isInterface();
visitor.visitMethodInsn(opcode, owner, name, desc, iface);
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index 9c6560d..13d9078 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -18,6 +19,11 @@
}
@Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ @Override
public void write(MethodVisitor visitor) {
visitor.visitLabel(getLabel());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
index af053a8..7d6be37 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.ValueType;
import org.objectweb.asm.MethodVisitor;
@@ -39,4 +40,17 @@
public void write(MethodVisitor visitor) {
visitor.visitVarInsn(getLoadType(), var);
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ public ValueType getType() {
+ return type;
+ }
+
+ public int getLocalIndex() {
+ return var;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index b528ea5..48b2ca3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
public class CfNew extends CfInstruction {
@@ -16,8 +16,17 @@
this.type = type;
}
+ public DexType getType() {
+ return type;
+ }
+
@Override
public void write(MethodVisitor visitor) {
- visitor.visitTypeInsn(Opcodes.NEW, Type.getType(type.toDescriptorString()).getInternalName());
+ visitor.visitTypeInsn(Opcodes.NEW, type.getInternalName());
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
}
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPop.java b/src/main/java/com/android/tools/r8/cf/code/CfPop.java
index e058df6..68cedb9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPop.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.ir.code.ValueType;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -19,4 +20,9 @@
public void write(MethodVisitor visitor) {
visitor.visitInsn(type.isWide() ? Opcodes.POP2 : Opcodes.POP);
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index 4cf2908..1a20d29 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -12,4 +13,9 @@
public void write(MethodVisitor visitor) {
visitor.visitInsn(Opcodes.RETURN);
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticGet.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticGet.java
index 6e71cb5..c5a68b6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStaticGet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticGet.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexField;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
public class CfStaticGet extends CfInstruction {
@@ -16,11 +16,20 @@
this.field = field;
}
+ public DexField getField() {
+ return field;
+ }
+
@Override
public void write(MethodVisitor visitor) {
- String owner = Type.getType(field.getHolder().toDescriptorString()).getInternalName();
+ String owner = field.getHolder().getInternalName();
String name = field.name.toString();
String desc = field.type.toDescriptorString();
visitor.visitFieldInsn(Opcodes.GETSTATIC, owner, name, desc);
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
index 0074abc..2526a58 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.code;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.ValueType;
import org.objectweb.asm.MethodVisitor;
@@ -39,4 +40,17 @@
public void write(MethodVisitor visitor) {
visitor.visitVarInsn(getStoreType(), var);
}
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ public ValueType getType() {
+ return type;
+ }
+
+ public int getLocalIndex() {
+ return var;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 7c76969..b0d0827 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -4,35 +4,55 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.Collections;
import java.util.List;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Type;
public class CfCode extends Code {
+ private final DexMethod method;
private final int maxStack;
private final int maxLocals;
private final List<CfInstruction> instructions;
private final List<CfTryCatch> tryCatchRanges;
public CfCode(
+ DexMethod method,
int maxStack,
int maxLocals,
List<CfInstruction> instructions,
List<CfTryCatch> tryCatchRanges) {
+ this.method = method;
this.maxStack = maxStack;
this.maxLocals = maxLocals;
this.instructions = instructions;
this.tryCatchRanges = tryCatchRanges;
}
+ public DexMethod getMethod() {
+ return method;
+ }
+
+ public int getMaxStack() {
+ return maxStack;
+ }
+
+ public int getMaxLocals() {
+ return maxLocals;
+ }
+
+ public List<CfInstruction> getInstructions() {
+ return Collections.unmodifiableList(instructions);
+ }
+
@Override
public boolean isCfCode() {
return true;
@@ -59,9 +79,7 @@
start,
end,
target,
- guard == DexItemFactory.catchAllType
- ? null
- : Type.getType(guard.toDescriptorString()).getInternalName());
+ guard == DexItemFactory.catchAllType ? null : guard.getInternalName());
}
}
}
@@ -89,11 +107,7 @@
@Override
public String toString() {
- StringBuilder builder = new StringBuilder();
- for (CfInstruction instruction : instructions) {
- builder.append(instruction.toString()).append('\n');
- }
- return builder.toString();
+ return new CfPrinter(this).toString();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 69a71f9..bae6fba 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -457,6 +457,13 @@
return getPackageOrName(false);
}
+ /** Get the fully qualified name using '/' in place of '.', ala the "internal type name" in ASM */
+ public String getInternalName() {
+ assert isClassType();
+ String descriptor = toDescriptorString();
+ return descriptor.substring(1, descriptor.length() - 1);
+ }
+
public boolean isImmediateSubtypeOf(DexType type) {
assert hierarchyLevel != UNKNOWN_LEVEL;
return type.directSubtypes.contains(this);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 0ad6c6c..f1c3e32 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -37,10 +37,12 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import java.util.Set;
public class CfBuilder {
@@ -50,6 +52,7 @@
private Map<Value, DexType> types;
private Map<BasicBlock, CfLabel> labels;
+ private Set<CfLabel> emittedLabels;
private List<CfInstruction> instructions;
private CfRegisterAllocator registerAllocator;
@@ -170,6 +173,7 @@
Stack stack = new Stack();
List<CfTryCatch> tryCatchRanges = new ArrayList<>();
labels = new HashMap<>(code.blocks.size());
+ emittedLabels = new HashSet<>(code.blocks.size());
instructions = new ArrayList<>();
Iterator<BasicBlock> blockIterator = code.listIterator();
BasicBlock block = blockIterator.next();
@@ -182,17 +186,12 @@
// Close try-catch and save the range.
CfLabel tryCatchEnd = getLabel(block);
tryCatchRanges.add(new CfTryCatch(tryCatchStart, tryCatchEnd, tryCatchHandlers, this));
- if (instructions.get(instructions.size() - 1) != tryCatchEnd) {
- instructions.add(tryCatchEnd);
- }
+ emitLabel(tryCatchEnd);
}
if (!handlers.isEmpty()) {
// Open a try-catch.
tryCatchStart = getLabel(block);
- if (instructions.isEmpty()
- || instructions.get(instructions.size() - 1) != tryCatchStart) {
- instructions.add(tryCatchStart);
- }
+ emitLabel(tryCatchStart);
}
tryCatchHandlers = handlers;
}
@@ -201,7 +200,7 @@
buildCfInstructions(block, fallthrough, stack);
if (nextBlock != null && (!fallthrough || nextBlock.getPredecessors().size() > 1)) {
assert stack.isEmpty();
- instructions.add(getLabel(nextBlock));
+ emitLabel(getLabel(nextBlock));
if (nextBlock.getPredecessors().size() > 1) {
addFrame(nextBlock, Collections.emptyList());
}
@@ -210,7 +209,11 @@
} while (block != null);
assert stack.isEmpty();
return new CfCode(
- stack.maxHeight, registerAllocator.registersUsed(), instructions, tryCatchRanges);
+ method.method,
+ stack.maxHeight,
+ registerAllocator.registersUsed(),
+ instructions,
+ tryCatchRanges);
}
private void buildCfInstructions(BasicBlock block, boolean fallthrough, Stack stack) {
@@ -268,9 +271,16 @@
instructions.add(new CfFrame(mapping));
}
+ private void emitLabel(CfLabel label) {
+ if (!emittedLabels.contains(label)) {
+ emittedLabels.add(label);
+ instructions.add(label);
+ }
+ }
+
// Callbacks
- public CfLabel getLabel(BasicBlock target){
+ public CfLabel getLabel(BasicBlock target) {
return labels.computeIfAbsent(target, (block) -> new CfLabel());
}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index b2760e4..d2b2f5e 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import java.io.IOException;
import java.io.PrintWriter;
@@ -21,7 +20,6 @@
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.CheckClassAdapter;
@@ -68,13 +66,13 @@
int version = downgrade(clazz.getClassFileVersion());
int access = clazz.accessFlags.getAsCfAccessFlags();
String desc = clazz.type.toDescriptorString();
- String name = internalName(clazz.type);
+ String name = clazz.type.getInternalName();
String signature = null; // TODO(zerny): Support generic signatures.
String superName =
- clazz.type == options.itemFactory.objectType ? null : internalName(clazz.superType);
+ clazz.type == options.itemFactory.objectType ? null : clazz.superType.getInternalName();
String[] interfaces = new String[clazz.interfaces.values.length];
for (int i = 0; i < clazz.interfaces.values.length; i++) {
- interfaces[i] = internalName(clazz.interfaces.values[i]);
+ interfaces[i] = clazz.interfaces.values[i].getInternalName();
}
writer.visit(version, access, name, signature, superName, interfaces);
// TODO(zerny): Methods and fields.
@@ -107,10 +105,6 @@
}
}
- private static String internalName(DexType type) {
- return Type.getType(type.toDescriptorString()).getInternalName();
- }
-
private String printCf(byte[] result) {
ClassReader reader = new ClassReader(result);
ClassNode node = new ClassNode(ASM6);