Merge "Improve the error messages for invalid locals information"
diff --git a/.gitignore b/.gitignore
index 86e3098..cdc19a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,8 @@
third_party/android_jar/lib-v[0-9][0-9].tar.gz
third_party/benchmarks/santa-tracker
third_party/benchmarks/santa-tracker.tar.gz
+third_party/desugar/desugar_*/
+third_party/desugar/desugar_*.tar.gz
third_party/gmail/*
!third_party/gmail/*.sha1
third_party/gmscore/*
@@ -66,6 +68,8 @@
third_party/ddmlib.tar.gz
third_party/core-lambda-stubs
third_party/core-lambda-stubs.tar.gz
+third_party/openjdk/openjdk-rt-1.8
+third_party/openjdk/openjdk-rt-1.8.tar.gz
src/test/jack/ub-jack
gradle-app.setting
gradlew
diff --git a/build.gradle b/build.gradle
index e0c50ee..2d9e9b4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -310,6 +310,7 @@
"shadow",
"ddmlib",
"core-lambda-stubs",
+ "openjdk/openjdk-rt-1.8",
],
// All dex-vms have a fixed OS of Linux, as they are only supported on Linux, and will be run in a Docker
// container on other platforms where supported.
@@ -366,6 +367,7 @@
"proguard/proguard_internal_159423826",
"framework",
"goyt",
+ "desugar/desugar_20180308"
],
]
@@ -1337,6 +1339,10 @@
}
task buildPreNJdwpTestsDex(type: Exec, dependsOn: "buildPreNJdwpTestsJar") {
+ onlyIf {
+ // TODO(b/76135355): Update dx.bat on Windows to something that can build this.
+ !OperatingSystem.current().isWindows()
+ }
def inFile = buildPreNJdwpTestsJar.archivePath
def outFile = new File(buildPreNJdwpTestsJar.destinationDir, buildPreNJdwpTestsJar.baseName + '-dex.jar')
inputs.file inFile
@@ -1451,6 +1457,7 @@
dependsOn jctfTestsClasses
dependsOn buildDebugInfoExamplesDex
dependsOn buildPreNJdwpTestsJar
+ dependsOn buildPreNJdwpTestsDex
} else {
logger.lifecycle("WARNING: Testing in not supported on your platform. Testing is only fully supported on " +
"Linux and partially supported on Mac OS and Windows. Art does not run on other platforms.")
diff --git a/src/main/java/com/android/tools/r8/ResourceShrinker.java b/src/main/java/com/android/tools/r8/ResourceShrinker.java
index 1df78be..d2969d6 100644
--- a/src/main/java/com/android/tools/r8/ResourceShrinker.java
+++ b/src/main/java/com/android/tools/r8/ResourceShrinker.java
@@ -91,7 +91,7 @@
@Deprecated
final public class ResourceShrinker {
- final static class Command extends BaseCommand {
+ public final static class Command extends BaseCommand {
Command(AndroidApp app) {
super(app);
@@ -103,7 +103,7 @@
}
}
- final public static class Builder extends BaseCommand.Builder<Command, Builder> {
+ public final static class Builder extends BaseCommand.Builder<Command, Builder> {
@Override
Builder self() {
@@ -120,7 +120,7 @@
* Classes that would like to process data relevant to resource shrinking should implement this
* interface.
*/
- interface ReferenceChecker {
+ public interface ReferenceChecker {
/**
* Returns if the class with specified internal name should be processed. Typically,
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java
index 9232e08..23883e9 100644
--- a/src/main/java/com/android/tools/r8/StringConsumer.java
+++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -147,6 +147,7 @@
new BufferedWriter(new OutputStreamWriter(outputStream, encoding.newEncoder()));
try {
writer.write(string);
+ writer.flush();
} catch (IOException e) {
handler.error(new IOExceptionDiagnostic(e, origin));
}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index bf46660..6a78145 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "v1.2.2-dev";
+ public static final String LABEL = "v1.2.3-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
index 0b5e678..dfe7c1b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -4,13 +4,14 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class CfArrayLength extends CfInstruction {
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(Opcodes.ARRAYLENGTH);
}
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 3ff24b9..85405e4 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -46,7 +47,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(getLoadType());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
index 6d7ca1a..4b13af4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -46,7 +47,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(getStoreType());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfBinop.java
index af78290..6f8e868 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfBinop.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
public class CfBinop extends CfInstruction {
@@ -19,7 +20,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(opcode);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index 2ff7625..e46d475 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -5,6 +5,8 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -21,12 +23,17 @@
}
@Override
- public void write(MethodVisitor visitor) {
- visitor.visitTypeInsn(Opcodes.CHECKCAST, type.getInternalName());
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitTypeInsn(Opcodes.CHECKCAST, lens.lookupInternalName(type));
}
@Override
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ registry.registerCheckCast(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index 5218a02..e4037e4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
@@ -22,8 +24,8 @@
}
@Override
- public void write(MethodVisitor visitor) {
- visitor.visitLdcInsn(Type.getObjectType(getInternalName()));
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitLdcInsn(Type.getObjectType(getInternalName(lens)));
}
@Override
@@ -31,11 +33,11 @@
printer.print(this);
}
- private String getInternalName() {
+ private String getInternalName(NamingLens lens) {
switch (type.toShorty()) {
case '[':
case 'L':
- return type.getInternalName();
+ return lens.lookupInternalName(type);
case 'Z':
return "java/lang/Boolean/TYPE";
case 'B':
@@ -56,4 +58,9 @@
throw new Unreachable("Unexpected type in const-class: " + type);
}
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ registry.registerConstClass(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index b63b4d5..ca34be7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -5,6 +5,9 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
public class CfConstMethodHandle extends CfInstruction {
@@ -20,12 +23,17 @@
}
@Override
- public void write(MethodVisitor visitor) {
- visitor.visitLdcInsn(handle.toAsmHandle());
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitLdcInsn(handle.toAsmHandle(lens));
}
@Override
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ registry.registerMethodHandle(handle);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index 8ec1545..7e06a88 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -5,6 +5,9 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
@@ -21,12 +24,17 @@
}
@Override
- public void write(MethodVisitor visitor) {
- visitor.visitLdcInsn(Type.getType(type.toDescriptorString()));
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitLdcInsn(Type.getType(type.toDescriptorString(lens)));
}
@Override
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ registry.registerProto(type);
+ }
}
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 a788a3b..385669d 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
@@ -4,13 +4,14 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class CfConstNull extends CfInstruction {
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(Opcodes.ACONST_NULL);
}
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 246cf0b..07fd664 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -48,7 +49,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
switch (type) {
case INT:
{
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 0f7f9f7..a22f279 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
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
public class CfConstString extends CfInstruction {
@@ -20,7 +21,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitLdcInsn(string.toString());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index dc8f827..e30d8ce 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -4,8 +4,13 @@
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.graph.DexField;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
public class CfFieldInstruction extends CfInstruction {
@@ -26,10 +31,10 @@
}
@Override
- public void write(MethodVisitor visitor) {
- String owner = field.getHolder().getInternalName();
- String name = field.name.toString();
- String desc = field.type.toDescriptorString();
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ String owner = lens.lookupInternalName(field.getHolder());
+ String name = lens.lookupName(field).toString();
+ String desc = lens.lookupDescriptor(field.type).toString();
visitor.visitFieldInsn(opcode, owner, name, desc);
}
@@ -37,4 +42,24 @@
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ switch (opcode) {
+ case Opcodes.GETFIELD:
+ registry.registerInstanceFieldRead(field);
+ break;
+ case Opcodes.PUTFIELD:
+ registry.registerInstanceFieldWrite(field);
+ break;
+ case Opcodes.GETSTATIC:
+ registry.registerStaticFieldRead(field);
+ break;
+ case Opcodes.PUTSTATIC:
+ registry.registerStaticFieldWrite(field);
+ break;
+ default:
+ throw new Unreachable("Unexpected opcode " + opcode);
+ }
+ }
}
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 b589a78..2818e44 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
@@ -9,6 +9,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import java.util.List;
import org.objectweb.asm.MethodVisitor;
@@ -33,11 +34,11 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
int stackCount = computeStackCount();
- Object[] stackTypes = computeStackTypes(stackCount);
+ Object[] stackTypes = computeStackTypes(stackCount, lens);
int localsCount = computeLocalsCount();
- Object[] localsTypes = computeLocalsTypes(localsCount);
+ Object[] localsTypes = computeLocalsTypes(localsCount, lens);
visitor.visitFrame(F_NEW, localsCount, localsTypes, stackCount, stackTypes);
}
@@ -49,14 +50,14 @@
return stack.size();
}
- private Object[] computeStackTypes(int stackCount) {
+ private Object[] computeStackTypes(int stackCount, NamingLens lens) {
assert stackCount == stack.size();
if (stackCount == 0) {
return null;
}
Object[] stackTypes = new Object[stackCount];
for (int i = 0; i < stackCount; i++) {
- stackTypes[i] = getType(stack.get(i));
+ stackTypes[i] = getType(stack.get(i), lens);
}
return stackTypes;
}
@@ -78,7 +79,7 @@
return localsCount;
}
- private Object[] computeLocalsTypes(int localsCount) {
+ private Object[] computeLocalsTypes(int localsCount, NamingLens lens) {
if (localsCount == 0) {
return null;
}
@@ -87,7 +88,7 @@
int localIndex = 0;
for (int i = 0; i <= maxRegister; i++) {
DexType type = locals.get(i);
- Object typeOpcode = getType(type);
+ Object typeOpcode = getType(type, lens);
localsTypes[localIndex++] = typeOpcode;
if (type != null && isWide(type)) {
i++;
@@ -96,7 +97,7 @@
return localsTypes;
}
- private Object getType(DexType type) {
+ private Object getType(DexType type, NamingLens lens) {
if (type == null) {
return Opcodes.TOP;
}
@@ -105,7 +106,7 @@
}
switch (type.toShorty()) {
case 'L':
- return type.getInternalName();
+ return lens.lookupInternalName(type);
case 'I':
return Opcodes.INTEGER;
case 'F':
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 c881e7b..47cfed6 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -20,7 +21,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitJumpInsn(Opcodes.GOTO, target.getLabel());
}
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 d928ff7..d39c358 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
@@ -8,6 +8,7 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.If.Type;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -60,7 +61,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitJumpInsn(getOpcode(), target.getLabel());
}
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index dc8b63f..2a076e4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.If.Type;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -60,7 +61,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitJumpInsn(getOpcode(), target.getLabel());
}
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index 43b6966..deba4ad 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -5,6 +5,8 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -21,12 +23,17 @@
}
@Override
- public void write(MethodVisitor visitor) {
- visitor.visitTypeInsn(Opcodes.INSTANCEOF, type.getInternalName());
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitTypeInsn(Opcodes.INSTANCEOF, lens.lookupInternalName(type));
}
@Override
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ registry.registerTypeReference(type);
+ }
}
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 76806f6..04ab380 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
@@ -4,11 +4,14 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
public abstract class CfInstruction {
- public abstract void write(MethodVisitor visitor);
+ public abstract void write(MethodVisitor visitor, NamingLens lens);
public abstract void print(CfPrinter printer);
@@ -19,4 +22,7 @@
return printer.toString();
}
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ // Intentionally empty.
+ }
}
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 86903fe..82f2438 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
@@ -4,7 +4,12 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -12,11 +17,15 @@
private final DexMethod method;
private final int opcode;
+ private final boolean itf;
- public CfInvoke(int opcode, DexMethod method) {
+ public CfInvoke(int opcode, DexMethod method, boolean itf) {
assert Opcodes.INVOKEVIRTUAL <= opcode && opcode <= Opcodes.INVOKEINTERFACE;
+ assert !(opcode == Opcodes.INVOKEVIRTUAL && itf) : "InvokeVirtual on interface type";
+ assert !(opcode == Opcodes.INVOKEINTERFACE && !itf) : "InvokeInterface on class type";
this.opcode = opcode;
this.method = method;
+ this.itf = itf;
}
public DexMethod getMethod() {
@@ -28,16 +37,41 @@
}
@Override
- public void write(MethodVisitor visitor) {
- String owner = method.getHolder().getInternalName();
- String name = method.name.toString();
- String desc = method.proto.toDescriptorString();
- boolean isInterface = opcode == Opcodes.INVOKEINTERFACE;
- visitor.visitMethodInsn(opcode, owner, name, desc, isInterface);
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ String owner = lens.lookupInternalName(method.getHolder());
+ String name = lens.lookupName(method).toString();
+ String desc = method.proto.toDescriptorString(lens);
+ visitor.visitMethodInsn(opcode, owner, name, desc, itf);
}
@Override
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ switch (opcode) {
+ case Opcodes.INVOKEINTERFACE:
+ registry.registerInvokeInterface(method);
+ break;
+ case Opcodes.INVOKEVIRTUAL:
+ registry.registerInvokeVirtual(method);
+ break;
+ case Opcodes.INVOKESPECIAL:
+ if (method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME)) {
+ registry.registerInvokeDirect(method);
+ } else if (method.holder == clazz) {
+ registry.registerInvokeDirect(method);
+ } else {
+ registry.registerInvokeSuper(method);
+ }
+ break;
+ case Opcodes.INVOKESTATIC:
+ registry.registerInvokeStatic(method);
+ break;
+ default:
+ throw new Unreachable("unknown CfInvoke opcode " + opcode);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index 43e6133..ca8f7cb 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueDouble;
import com.android.tools.r8.graph.DexValue.DexValueFloat;
@@ -16,6 +17,8 @@
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import java.util.List;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
@@ -30,14 +33,14 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
Object[] bsmArgs = new Object[bootstrapArgs.size()];
for (int i = 0; i < bootstrapArgs.size(); i++) {
- bsmArgs[i] = decodeBootstrapArgument(bootstrapArgs.get(i));
+ bsmArgs[i] = decodeBootstrapArgument(bootstrapArgs.get(i), lens);
}
- Handle bsmHandle = bootstrapMethod.toAsmHandle();
+ Handle bsmHandle = bootstrapMethod.toAsmHandle(lens);
visitor.visitInvokeDynamicInsn(
callSite.methodName.toString(),
callSite.methodProto.toDescriptorString(),
@@ -45,7 +48,7 @@
bsmArgs);
}
- private Object decodeBootstrapArgument(DexValue dexValue) {
+ private Object decodeBootstrapArgument(DexValue dexValue, NamingLens lens) {
if (dexValue instanceof DexValueInt) {
return ((DexValueInt) dexValue).getValue();
} else if (dexValue instanceof DexValueLong) {
@@ -61,7 +64,7 @@
} else if (dexValue instanceof DexValueMethodType) {
return Type.getMethodType(((DexValueMethodType) dexValue).value.toDescriptorString());
} else if (dexValue instanceof DexValueMethodHandle) {
- return ((DexValueMethodHandle) dexValue).value.toAsmHandle();
+ return ((DexValueMethodHandle) dexValue).value.toAsmHandle(lens);
} else {
throw new Unreachable(
"Unsupported bootstrap argument of type " + dexValue.getClass().getSimpleName());
@@ -76,4 +79,9 @@
public DexCallSite getCallSite() {
return callSite;
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ registry.registerCallSite(callSite);
+ }
}
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 13d9078..401e4e8 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -24,7 +25,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
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 7d6be37..58c59a5 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -37,7 +38,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitVarInsn(getLoadType(), var);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
index 76052c5..b38ef12 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.ir.code.Monitor.Type;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -21,7 +22,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(type == Type.ENTER ? Opcodes.MONITORENTER : Opcodes.MONITOREXIT);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 6fecd01..a2f3da2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -5,6 +5,8 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
public class CfMultiANewArray extends CfInstruction {
@@ -26,12 +28,17 @@
}
@Override
- public void write(MethodVisitor visitor) {
- visitor.visitMultiANewArrayInsn(type.getInternalName(), dimensions);
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitMultiANewArrayInsn(lens.lookupInternalName(type), dimensions);
}
@Override
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ registry.registerTypeReference(type);
+ }
}
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 48b2ca3..b7261b6 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
@@ -5,6 +5,8 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -21,12 +23,17 @@
}
@Override
- public void write(MethodVisitor visitor) {
- visitor.visitTypeInsn(Opcodes.NEW, type.getInternalName());
+ public void write(MethodVisitor visitor, NamingLens lens) {
+ visitor.visitTypeInsn(Opcodes.NEW, lens.lookupInternalName(type));
}
@Override
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ registry.registerNewInstance(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 2271cfa..708a414 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -6,9 +6,11 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.DescriptorUtils;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
public class CfNewArray extends CfInstruction {
@@ -46,17 +48,20 @@
}
}
- private String getElementInternalName() {
+ private String getElementInternalName(NamingLens lens) {
assert !type.isPrimitiveArrayType();
- return Type.getType(type.toDescriptorString().substring(1)).getInternalName();
+ String renamedArrayType = lens.lookupDescriptor(type).toString();
+ assert renamedArrayType.charAt(0) == '[';
+ String elementType = renamedArrayType.substring(1);
+ return DescriptorUtils.descriptorToInternalName(elementType);
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
if (type.isPrimitiveArrayType()) {
visitor.visitIntInsn(Opcodes.NEWARRAY, getPrimitiveTypeCode());
} else {
- visitor.visitTypeInsn(Opcodes.ANEWARRAY, getElementInternalName());
+ visitor.visitTypeInsn(Opcodes.ANEWARRAY, getElementInternalName(lens));
}
}
@@ -64,4 +69,11 @@
public void print(CfPrinter printer) {
printer.print(this);
}
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType clazz) {
+ if (!type.isPrimitiveArrayType()) {
+ registry.registerTypeReference(type);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
index e8eb664..2cdd37c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -4,13 +4,14 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class CfNop extends CfInstruction {
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(Opcodes.NOP);
}
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 68cedb9..9f485d2 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
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -17,7 +18,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(type.isWide() ? Opcodes.POP2 : Opcodes.POP);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index 8004ad8..169d252 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
public class CfPosition extends CfInstruction {
@@ -18,7 +19,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitLineNumber(position.line, label.getLabel());
}
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 c426dbd..7993919 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -39,7 +40,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(getOpcode());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index b8bfc7d..7d98073 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -4,13 +4,14 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class CfReturnVoid extends CfInstruction {
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(Opcodes.RETURN);
}
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 2526a58..817045c 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -37,7 +38,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitVarInsn(getStoreType(), var);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index 6d84c85..f27064c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.List;
@@ -44,7 +45,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
Label[] labels = new Label[targets.size()];
for (int i = 0; i < targets.size(); i++) {
labels[i] = targets.get(i).getLabel();
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index cff3a8c..ef73d10 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -4,13 +4,14 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class CfThrow extends CfInstruction {
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(Opcodes.ATHROW);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfUnop.java b/src/main/java/com/android/tools/r8/cf/code/CfUnop.java
index 6e50bb7..6b4bf76 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfUnop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfUnop.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.MethodVisitor;
public class CfUnop extends CfInstruction {
@@ -15,7 +16,7 @@
}
@Override
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens lens) {
visitor.visitInsn(this.opcode);
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
index 42acdbe..171aef2 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -4,9 +4,6 @@
package com.android.tools.r8.code;
import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
-import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.UseRegistry;
@@ -43,7 +40,7 @@
@Override
public void registerUse(UseRegistry registry) {
- registerCallSite(registry, getCallSite());
+ registry.registerCallSite(getCallSite());
}
@Override
@@ -60,17 +57,4 @@
public boolean canThrow() {
return true;
}
-
- static void registerCallSite(UseRegistry registry, DexCallSite callSite) {
- registry.registerMethodHandle(callSite.bootstrapMethod);
-
- // Register bootstrap method arguments, only Type and MethodHandle need to be register.
- for (DexValue arg : callSite.bootstrapArgs) {
- if (arg instanceof DexValueType) {
- registry.registerTypeReference(((DexValueType) arg).value);
- } else if (arg instanceof DexValueMethodHandle) {
- registry.registerMethodHandle(((DexValueMethodHandle) arg).value);
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
index c98a930..3aa60fc 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
@@ -44,7 +44,7 @@
@Override
public void registerUse(UseRegistry registry) {
- InvokeCustom.registerCallSite(registry, getCallSite());
+ registry.registerCallSite(getCallSite());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 0ccd724..958deb4 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -689,21 +689,23 @@
AttributesAndAnnotations attrs =
new AttributesAndAnnotations(type, annotationsDirectory.clazz, dexItemFactory);
- DexClass clazz = classKind.create(
- type,
- Kind.DEX,
- origin,
- flags,
- superclass,
- typeListAt(interfacesOffsets[i]),
- source,
- attrs.getEnclosingMethodAttribute(),
- attrs.getInnerClasses(),
- attrs.getAnnotations(),
- staticFields,
- instanceFields,
- directMethods,
- virtualMethods);
+ DexClass clazz =
+ classKind.create(
+ type,
+ Kind.DEX,
+ origin,
+ flags,
+ superclass,
+ typeListAt(interfacesOffsets[i]),
+ source,
+ attrs.getEnclosingMethodAttribute(),
+ attrs.getInnerClasses(),
+ attrs.getAnnotations(),
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
+ dexItemFactory.getSkipNameValidationForTesting());
classCollection.accept(clazz); // Update the application object.
}
}
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 947f77e..61cd5a7 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -5,18 +5,29 @@
import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfPosition;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
-import java.util.function.Consumer;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -113,9 +124,9 @@
return this;
}
- public void write(MethodVisitor visitor) {
+ public void write(MethodVisitor visitor, NamingLens namingLens) {
for (CfInstruction instruction : instructions) {
- instruction.write(visitor);
+ instruction.write(visitor, namingLens);
}
visitor.visitEnd();
visitor.visitMaxs(maxStack, maxLocals);
@@ -129,14 +140,14 @@
start,
end,
target,
- guard == DexItemFactory.catchAllType ? null : guard.getInternalName());
+ guard == DexItemFactory.catchAllType ? null : namingLens.lookupInternalName(guard));
}
}
for (LocalVariableInfo localVariable : localVariables) {
DebugLocalInfo info = localVariable.local;
visitor.visitLocalVariable(
info.name.toString(),
- info.type.toDescriptorString(),
+ namingLens.lookupDescriptor(info.type).toString(),
info.signature == null ? null : info.signature.toString(),
localVariable.start.getLabel(),
localVariable.end.getLabel(),
@@ -155,8 +166,35 @@
}
@Override
+ public boolean isEmptyVoidMethod() {
+ for (CfInstruction insn : instructions) {
+ if (!(insn instanceof CfReturnVoid)
+ && !(insn instanceof CfLabel)
+ && !(insn instanceof CfPosition)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
+ if (instructions.size() == 2
+ && instructions.get(0) instanceof CfConstNull
+ && instructions.get(1) instanceof CfThrow) {
+ BasicBlock block = new BasicBlock();
+ block.setNumber(1);
+ Value nullValue = new Value(0, ValueType.OBJECT, null);
+ block.add(new ConstNumber(nullValue, 0L));
+ block.add(new Throw(nullValue));
+ block.close(null);
+ for (Instruction insn : block.getInstructions()) {
+ insn.setPosition(Position.none());
+ }
+ LinkedList<BasicBlock> blocks = new LinkedList<>(Collections.singleton(block));
+ return new IRCode(options, encodedMethod, blocks, null, false);
+ }
throw new Unimplemented("Converting Java class-file bytecode to IR not yet supported");
}
@@ -171,13 +209,17 @@
}
@Override
- public void registerInstructionsReferences(UseRegistry registry) {
- throw new Unimplemented("Inspecting Java class-file bytecode not yet supported");
- }
-
- @Override
- public void registerCaughtTypes(Consumer<DexType> dexTypeConsumer) {
- throw new Unimplemented("Inspecting Java class-file bytecode not yet supported");
+ public void registerCodeReferences(UseRegistry registry) {
+ for (CfInstruction instruction : instructions) {
+ instruction.registerUse(registry, method.holder);
+ }
+ for (CfTryCatch tryCatch : tryCatchRanges) {
+ for (DexType guard : tryCatch.guards) {
+ if (guard != DexItemFactory.catchAllType) {
+ registry.registerTypeReference(guard);
+ }
+ }
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index 755cb7b..94b2076 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -27,7 +27,8 @@
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
- DexEncodedMethod[] virtualMethods);
+ DexEncodedMethod[] virtualMethods,
+ boolean skipNameValidationForTesting);
}
private final Factory factory;
@@ -52,7 +53,8 @@
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
- DexEncodedMethod[] virtualMethods) {
+ DexEncodedMethod[] virtualMethods,
+ boolean skipNameValidationForTesting) {
return factory.create(
type,
kind,
@@ -67,7 +69,8 @@
staticFields,
instanceFields,
directMethods,
- virtualMethods);
+ virtualMethods,
+ skipNameValidationForTesting);
}
public boolean isOfKind(DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index b6c1583..52217a3 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.ir.optimize.Outliner.OutlineCode;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.utils.InternalOptions;
-import java.util.function.Consumer;
public abstract class Code extends CachedHashValueDexItem {
@@ -30,9 +29,7 @@
+ getClass().getCanonicalName());
}
- public abstract void registerInstructionsReferences(UseRegistry registry);
-
- public abstract void registerCaughtTypes(Consumer<DexType> dexTypeConsumer);
+ public abstract void registerCodeReferences(UseRegistry registry);
@Override
public abstract String toString();
@@ -84,4 +81,6 @@
void collectMixedSectionItems(MixedSectionCollection collection) {
throw new Unreachable();
}
+
+ public abstract boolean isEmptyVoidMethod();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index b228098..0c32750 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -15,7 +15,11 @@
import java.io.ObjectOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
import java.util.List;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.InvokeDynamicInsnNode;
public final class DexCallSite extends IndexedDexItem {
@@ -40,6 +44,37 @@
this.bootstrapArgs = bootstrapArgs;
}
+ public static DexCallSite fromAsmInvokeDynamic(
+ InvokeDynamicInsnNode insn, JarApplicationReader application, DexType clazz) {
+ return fromAsmInvokeDynamic(application, clazz, insn.name, insn.desc, insn.bsm, insn.bsmArgs);
+ }
+
+ public static DexCallSite fromAsmInvokeDynamic(
+ JarApplicationReader application,
+ DexType clazz,
+ String name,
+ String desc,
+ Handle bsmHandle,
+ Object[] bsmArgs) {
+ // Bootstrap method
+ if (bsmHandle.getTag() != Opcodes.H_INVOKESTATIC
+ && bsmHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
+ // JVM9 §4.7.23 note: Tag must be InvokeStatic or NewInvokeSpecial.
+ throw new Unreachable("Bootstrap handle invalid: tag == " + bsmHandle.getTag());
+ }
+ // Resolve the bootstrap method.
+ DexMethodHandle bootstrapMethod = DexMethodHandle.fromAsmHandle(bsmHandle, application, clazz);
+
+ // Decode static bootstrap arguments
+ List<DexValue> bootstrapArgs = new ArrayList<>();
+ for (Object arg : bsmArgs) {
+ bootstrapArgs.add(DexValue.fromAsmBootstrapArgument(arg, application, clazz));
+ }
+
+ // Construct call site
+ return application.getCallSite(name, desc, bootstrapMethod, bootstrapArgs);
+ }
+
@Override
public int computeHashCode() {
return methodName.hashCode()
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index d6b15eb..e9b9255 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -64,7 +64,8 @@
EnclosingMethodAttribute enclosingMethod,
List<InnerClassAttribute> innerClasses,
DexAnnotationSet annotations,
- Origin origin) {
+ Origin origin,
+ boolean skipNameValidationForTesting) {
assert origin != null;
this.origin = origin;
this.sourceFile = sourceFile;
@@ -87,7 +88,7 @@
throw new CompilationError("Interface " + type.toString() + " cannot implement itself");
}
}
- if (!type.descriptor.isValidClassDescriptor()) {
+ if (!skipNameValidationForTesting && !type.descriptor.isValidClassDescriptor()) {
throw new CompilationError(
"Class descriptor '"
+ type.descriptor.toString()
@@ -338,9 +339,7 @@
return superType == null;
}
DexEncodedMethod clinit = getClassInitializer();
- return clinit != null
- && clinit.getCode() != null
- && clinit.getCode().asDexCode().isEmptyVoidMethod();
+ return clinit != null && clinit.getCode() != null && clinit.getCode().isEmptyVoidMethod();
}
public boolean hasNonTrivialClassInitializer() {
@@ -352,11 +351,7 @@
if (clinit == null || clinit.getCode() == null) {
return false;
}
- if (clinit.getCode().isDexCode()) {
- return !clinit.getCode().asDexCode().isEmptyVoidMethod();
- }
- // For non-dex code we don't try to check the code.
- return true;
+ return !clinit.getCode().isEmptyVoidMethod();
}
public boolean hasDefaultInitializer() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 6148b00..4308dd4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -29,7 +29,8 @@
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
- DexEncodedMethod[] virtualMethods) {
+ DexEncodedMethod[] virtualMethods,
+ boolean skipNameValidationForTesting) {
super(
sourceFile,
interfaces,
@@ -43,7 +44,8 @@
enclosingMember,
innerClasses,
annotations,
- origin);
+ origin,
+ skipNameValidationForTesting);
assert kind == Kind.CF : "Invalid kind " + kind + " for class-path class " + type;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 1517dfb..50117c7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -28,7 +28,6 @@
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
-import java.util.function.Consumer;
// DexCode corresponds to code item in dalvik/dex-format.html
public class DexCode extends Code {
@@ -158,6 +157,7 @@
return false;
}
+ @Override
public boolean isEmptyVoidMethod() {
return instructions.length == 1 && instructions[0] instanceof ReturnVoid;
}
@@ -190,17 +190,15 @@
}
@Override
- public void registerInstructionsReferences(UseRegistry registry) {
+ public void registerCodeReferences(UseRegistry registry) {
for (Instruction insn : instructions) {
insn.registerUse(registry);
}
- }
-
- @Override
- public void registerCaughtTypes(Consumer<DexType> dexTypeConsumer) {
- for (TryHandler handler : handlers) {
- for (TypeAddrPair pair : handler.pairs) {
- dexTypeConsumer.accept(pair.type);
+ if (handlers != null) {
+ for (TryHandler handler : handlers) {
+ for (TypeAddrPair pair : handler.pairs) {
+ registry.registerTypeReference(pair.type);
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index b7f1802..e18bf8a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -10,6 +10,9 @@
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfThrow;
import com.android.tools.r8.code.Const;
import com.android.tools.r8.code.ConstString;
import com.android.tools.r8.code.ConstStringJumbo;
@@ -38,7 +41,9 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -296,13 +301,17 @@
}
public String descriptor() {
+ return descriptor(NamingLens.getIdentityLens());
+ }
+
+ public String descriptor(NamingLens namingLens) {
StringBuilder builder = new StringBuilder();
builder.append("(");
for (DexType type : method.proto.parameters.values) {
- builder.append(type.descriptor.toString());
+ builder.append(namingLens.lookupDescriptor(type).toString());
}
builder.append(")");
- builder.append(method.proto.returnType.descriptor.toString());
+ builder.append(namingLens.lookupDescriptor(method.proto.returnType).toString());
return builder.toString();
}
@@ -360,12 +369,28 @@
outRegisters, instructions, new DexCode.Try[0], new DexCode.TryHandler[0], null, null);
}
- public DexEncodedMethod toEmptyThrowingMethod() {
- Instruction insn[] = {new Const(0, 0), new Throw(0)};
- DexCode code = generateCodeFromTemplate(1, 0, insn);
- assert !accessFlags.isAbstract();
+ public DexEncodedMethod toEmptyThrowingMethodDex() {
+ assert !accessFlags.isAbstract() && !accessFlags.isNative();
Builder builder = builder(this);
- builder.setCode(code);
+ Instruction insn[] = {new Const(0, 0), new Throw(0)};
+ DexCode emptyThrowingCode = generateCodeFromTemplate(1, 0, insn);
+ builder.setCode(emptyThrowingCode);
+ return builder.build();
+ }
+
+ public DexEncodedMethod toEmptyThrowingMethodCf() {
+ assert !accessFlags.isAbstract() && !accessFlags.isNative();
+ Builder builder = builder(this);
+ CfInstruction insn[] = {new CfConstNull(), new CfThrow()};
+ CfCode emptyThrowingCode =
+ new CfCode(
+ method,
+ 1,
+ method.proto.parameters.size() + 1,
+ Arrays.asList(insn),
+ Collections.emptyList(),
+ Collections.emptyList());
+ builder.setCode(emptyThrowingCode);
return builder.build();
}
@@ -506,21 +531,12 @@
return !annotations.isEmpty() || !parameterAnnotations.isEmpty();
}
- public void registerInstructionsReferences(UseRegistry registry) {
+ public void registerCodeReferences(UseRegistry registry) {
if (code != null) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Registering definitions reachable from `%s`.", method);
}
- code.registerInstructionsReferences(registry);
- }
- }
-
- public void registerCatchedTypes(Consumer<DexType> dexTypeConsumer) {
- if (code != null) {
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Visiting catched types `%s`.", method);
- }
- code.registerCaughtTypes(dexTypeConsumer);
+ code.registerCodeReferences(registry);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index fa3d6ad..ab0d654 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -14,11 +14,11 @@
public final DexType type;
public final DexString name;
- DexField(DexType clazz, DexType type, DexString name) {
+ DexField(DexType clazz, DexType type, DexString name, boolean skipNameValidationForTesting) {
this.clazz = clazz;
this.type = type;
this.name = name;
- if (!name.isValidFieldName()) {
+ if (!skipNameValidationForTesting && !name.isValidFieldName()) {
throw new CompilationError(
"Field name '" + name.toString() + "' cannot be represented in dex format.");
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index ff3e30d..30d0dbe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -227,6 +227,16 @@
public final DexType annotationSynthesizedClassMap =
createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
+ private boolean skipNameValidationForTesting = false;
+
+ public void setSkipNameValidationForTesting(boolean skipNameValidationForTesting) {
+ this.skipNameValidationForTesting = skipNameValidationForTesting;
+ }
+
+ public boolean getSkipNameValidationForTesting() {
+ return skipNameValidationForTesting;
+ }
+
public synchronized void clearSubtypeInformation() {
types.values().forEach(DexType::clearSubtypeInformation);
}
@@ -479,7 +489,7 @@
public DexField createField(DexType clazz, DexType type, DexString name) {
assert !sorted;
- DexField field = new DexField(clazz, type, name);
+ DexField field = new DexField(clazz, type, name, skipNameValidationForTesting);
return canonicalize(fields, field);
}
@@ -510,7 +520,7 @@
public DexMethod createMethod(DexType holder, DexProto proto, DexString name) {
assert !sorted;
- DexMethod method = new DexMethod(holder, proto, name);
+ DexMethod method = new DexMethod(holder, proto, name, skipNameValidationForTesting);
return canonicalize(methods, method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 4700553..9644558 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -29,7 +29,8 @@
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
- DexEncodedMethod[] virtualMethods) {
+ DexEncodedMethod[] virtualMethods,
+ boolean skipNameValidationForTesting) {
super(
sourceFile,
interfaces,
@@ -43,7 +44,8 @@
enclosingMember,
innerClasses,
annotations,
- origin);
+ origin,
+ skipNameValidationForTesting);
// Set all static field values to unknown. We don't want to use the value from the library
// at compile time, as it can be different at runtime.
for (DexEncodedField staticField : staticFields) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 621878f..c0c2e27 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -19,11 +19,11 @@
// Caches used during processing.
private Map<DexType, DexEncodedMethod> singleTargetCache;
- DexMethod(DexType holder, DexProto proto, DexString name) {
+ DexMethod(DexType holder, DexProto proto, DexString name, boolean skipNameValidationForTesting) {
this.holder = holder;
this.proto = proto;
this.name = name;
- if (!name.isValidMethodName()) {
+ if (!skipNameValidationForTesting && !name.isValidMethodName()) {
throw new CompilationError(
"Method name '" + name.toASCIIString() + "' in class '" + holder.toSourceString() +
"' cannot be represented in dex format.");
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index ae8251d..a9e3b8e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.NamingLens;
@@ -77,6 +78,39 @@
return kind;
}
+ public static MethodHandleType fromAsmHandle(
+ Handle handle, JarApplicationReader application, DexType clazz) {
+ switch (handle.getTag()) {
+ case Opcodes.H_GETFIELD:
+ return MethodHandleType.INSTANCE_GET;
+ case Opcodes.H_GETSTATIC:
+ return MethodHandleType.STATIC_GET;
+ case Opcodes.H_PUTFIELD:
+ return MethodHandleType.INSTANCE_PUT;
+ case Opcodes.H_PUTSTATIC:
+ return MethodHandleType.STATIC_PUT;
+ case Opcodes.H_INVOKESPECIAL:
+ assert !handle.getName().equals(Constants.INSTANCE_INITIALIZER_NAME);
+ assert !handle.getName().equals(Constants.CLASS_INITIALIZER_NAME);
+ DexType owner = application.getTypeFromName(handle.getOwner());
+ if (owner == clazz) {
+ return MethodHandleType.INVOKE_DIRECT;
+ } else {
+ return MethodHandleType.INVOKE_SUPER;
+ }
+ case Opcodes.H_INVOKEVIRTUAL:
+ return MethodHandleType.INVOKE_INSTANCE;
+ case Opcodes.H_INVOKEINTERFACE:
+ return MethodHandleType.INVOKE_INTERFACE;
+ case Opcodes.H_INVOKESTATIC:
+ return MethodHandleType.INVOKE_STATIC;
+ case Opcodes.H_NEWINVOKESPECIAL:
+ return MethodHandleType.INVOKE_CONSTRUCTOR;
+ default:
+ throw new Unreachable("MethodHandle tag is not supported: " + handle.getTag());
+ }
+ }
+
public boolean isFieldType() {
return isStaticPut() || isStaticGet() || isInstancePut() || isInstanceGet();
}
@@ -137,6 +171,16 @@
this.fieldOrMethod = fieldOrMethod;
}
+ public static DexMethodHandle fromAsmHandle(
+ Handle handle, JarApplicationReader application, DexType clazz) {
+ MethodHandleType methodHandleType = MethodHandleType.fromAsmHandle(handle, application, clazz);
+ Descriptor<? extends DexItem, ? extends Descriptor<?, ?>> descriptor =
+ methodHandleType.isFieldType()
+ ? application.getField(handle.getOwner(), handle.getName(), handle.getDesc())
+ : application.getMethod(handle.getOwner(), handle.getName(), handle.getDesc());
+ return application.getMethodHandle(methodHandleType, descriptor);
+ }
+
@Override
public int computeHashCode() {
return type.hashCode() + fieldOrMethod.computeHashCode() * 7;
@@ -248,16 +292,16 @@
return sortedCompareTo(other.getSortedIndex());
}
- public Handle toAsmHandle() {
+ public Handle toAsmHandle(NamingLens lens) {
String owner;
String name;
String desc;
boolean itf;
if (isMethodHandle()) {
DexMethod method = asMethod();
- owner = method.holder.getInternalName();
- name = method.name.toString();
- desc = method.proto.toDescriptorString();
+ owner = lens.lookupInternalName(method.holder);
+ name = lens.lookupName(method).toString();
+ desc = method.proto.toDescriptorString(lens);
if (method.holder.toDescriptorString().equals("Ljava/lang/invoke/LambdaMetafactory;")) {
itf = false;
} else {
@@ -266,9 +310,9 @@
} else {
assert isFieldHandle();
DexField field = asField();
- owner = field.clazz.getInternalName();
- name = field.name.toString();
- desc = field.type.toDescriptorString();
+ owner = lens.lookupInternalName(field.clazz);
+ name = lens.lookupName(field).toString();
+ desc = lens.lookupDescriptor(field.type).toString();
itf = field.clazz.isInterface();
}
return new Handle(getAsmTag(), owner, name, desc, itf);
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index ffff10f..90db1d2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -44,7 +44,8 @@
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
- DexEncodedMethod[] virtualMethods) {
+ DexEncodedMethod[] virtualMethods,
+ boolean skipNameValidationForTesting) {
this(
type,
originKind,
@@ -60,6 +61,7 @@
instanceFields,
directMethods,
virtualMethods,
+ skipNameValidationForTesting,
Collections.emptyList());
}
@@ -78,6 +80,7 @@
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
DexEncodedMethod[] virtualMethods,
+ boolean skipNameValidationForTesting,
Collection<DexProgramClass> synthesizedDirectlyFrom) {
super(
sourceFile,
@@ -92,7 +95,8 @@
enclosingMember,
innerClasses,
classAnnotations,
- origin);
+ origin,
+ skipNameValidationForTesting);
assert classAnnotations != null;
this.originKind = originKind;
this.synthesizedFrom = accumulateSynthesizedFrom(new HashSet<>(), synthesizedDirectlyFrom);
@@ -108,8 +112,6 @@
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
- assert getEnclosingMethod() == null;
- assert getInnerClasses().isEmpty();
if (indexedItems.addClass(this)) {
type.collectIndexedItems(indexedItems);
if (superType != null) {
@@ -126,6 +128,12 @@
if (interfaces != null) {
interfaces.collectIndexedItems(indexedItems);
}
+ if (getEnclosingMethod() != null) {
+ getEnclosingMethod().collectIndexedItems(indexedItems);
+ }
+ for (InnerClassAttribute attribute : getInnerClasses()) {
+ attribute.collectIndexedItems(indexedItems);
+ }
synchronizedCollectAll(indexedItems, staticFields);
synchronizedCollectAll(indexedItems, instanceFields);
synchronizedCollectAll(indexedItems, directMethods);
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index 69eaeb5..7acab73 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -93,13 +93,17 @@
}
public String toDescriptorString() {
+ return toDescriptorString(NamingLens.getIdentityLens());
+ }
+
+ public String toDescriptorString(NamingLens lens) {
StringBuilder builder = new StringBuilder();
builder.append("(");
for (int i = 0; i < parameters.values.length; i++) {
- builder.append(parameters.values[i].toDescriptorString());
+ builder.append(lens.lookupDescriptor(parameters.values[i]));
}
builder.append(")");
- builder.append(returnType.toDescriptorString());
+ builder.append(lens.lookupDescriptor(returnType));
return builder.toString();
}
}
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 cc1f8e7..820e20b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -463,8 +463,7 @@
/** Get the fully qualified name using '/' in place of '.', ala the "internal type name" in ASM */
public String getInternalName() {
assert isClassType() || isArrayType();
- String descriptor = toDescriptorString();
- return isArrayType() ? descriptor : descriptor.substring(1, descriptor.length() - 1);
+ return DescriptorUtils.descriptorToInternalName(toDescriptorString());
}
public boolean isImmediateSubtypeOf(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index dd08190..f1a0f4d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.utils.EncodedValueUtils;
import java.util.Arrays;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Type;
public abstract class DexValue extends DexItem {
@@ -37,6 +39,40 @@
public static final byte VALUE_NULL = 0x1e;
public static final byte VALUE_BOOLEAN = 0x1f;
+ public static DexValue fromAsmBootstrapArgument(
+ Object value, JarApplicationReader application, DexType clazz) {
+ if (value instanceof Integer) {
+ return DexValue.DexValueInt.create((Integer) value);
+ } else if (value instanceof Long) {
+ return DexValue.DexValueLong.create((Long) value);
+ } else if (value instanceof Float) {
+ return DexValue.DexValueFloat.create((Float) value);
+ } else if (value instanceof Double) {
+ return DexValue.DexValueDouble.create((Double) value);
+ } else if (value instanceof String) {
+ return new DexValue.DexValueString(application.getString((String) value));
+
+ } else if (value instanceof Type) {
+ Type type = (Type) value;
+ switch (type.getSort()) {
+ case Type.OBJECT:
+ return new DexValue.DexValueType(
+ application.getTypeFromDescriptor(((Type) value).getDescriptor()));
+ case Type.METHOD:
+ return new DexValue.DexValueMethodType(
+ application.getProto(((Type) value).getDescriptor()));
+ default:
+ throw new Unreachable("Type sort is not supported: " + type.getSort());
+ }
+ } else if (value instanceof Handle) {
+ return new DexValue.DexValueMethodHandle(
+ DexMethodHandle.fromAsmHandle((Handle) value, application, clazz));
+ } else {
+ throw new Unreachable(
+ "Unsupported bootstrap static argument of type " + value.getClass().getSimpleName());
+ }
+ }
+
private static void writeHeader(byte type, int arg, DexOutputBuffer dest) {
dest.putByte((byte) ((arg << 5) | type));
}
diff --git a/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java b/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
index ce741a3..57bc3c4 100644
--- a/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.ClassWriter;
/**
@@ -30,14 +32,14 @@
this.enclosingMethod = enclosingMethod;
}
- public void write(ClassWriter writer) {
+ public void write(ClassWriter writer, NamingLens lens) {
if (enclosingMethod != null) {
writer.visitOuterClass(
- enclosingMethod.getHolder().getInternalName(),
- enclosingMethod.name.toString(),
- enclosingMethod.proto.toDescriptorString());
+ lens.lookupInternalName(enclosingMethod.getHolder()),
+ lens.lookupName(enclosingMethod).toString(),
+ enclosingMethod.proto.toDescriptorString(lens));
} else {
- writer.visitOuterClass(enclosingClass.getInternalName(), null, null);
+ writer.visitOuterClass(lens.lookupInternalName(enclosingClass), null, null);
}
}
@@ -61,4 +63,13 @@
enclosingClass == ((EnclosingMethodAttribute) obj).enclosingClass &&
enclosingMethod == ((EnclosingMethodAttribute) obj).enclosingMethod;
}
+
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ if (enclosingClass != null) {
+ enclosingClass.collectIndexedItems(indexedItems);
+ }
+ if (enclosingMethod != null) {
+ enclosingMethod.collectIndexedItems(indexedItems);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java b/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
index 8a158de..cd62ef3 100644
--- a/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.ClassWriter;
/** Representation of an entry in the Java InnerClasses attribute table. */
@@ -58,11 +60,23 @@
return innerName;
}
- public void write(ClassWriter writer) {
+ public void write(ClassWriter writer, NamingLens lens) {
+ String internalName = lens.lookupInternalName(inner);
+ String simpleName = lens.lookupSimpleName(inner, innerName);
writer.visitInnerClass(
- inner.getInternalName(),
- outer == null ? null : outer.getInternalName(),
- innerName == null ? null : innerName.toString(),
+ internalName,
+ outer == null ? null : lens.lookupInternalName(outer),
+ innerName == null ? null : simpleName,
access);
}
+
+ public void collectIndexedItems(IndexedItemCollection indexedItems) {
+ inner.collectIndexedItems(indexedItems);
+ if (outer != null) {
+ outer.collectIndexedItems(indexedItems);
+ }
+ if (innerName != null) {
+ innerName.collectIndexedItems(indexedItems);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 945da10..32cc10b 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -251,7 +251,8 @@
staticFields.toArray(new DexEncodedField[staticFields.size()]),
instanceFields.toArray(new DexEncodedField[instanceFields.size()]),
directMethods.toArray(new DexEncodedMethod[directMethods.size()]),
- virtualMethods.toArray(new DexEncodedMethod[virtualMethods.size()]));
+ virtualMethods.toArray(new DexEncodedMethod[virtualMethods.size()]),
+ application.getFactory().getSkipNameValidationForTesting());
if (clazz.isProgramClass()) {
context.owner = clazz.asProgramClass();
clazz.asProgramClass().setClassFileVersion(version);
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 8933353..8c1a6c5 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -18,13 +18,16 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
-import java.util.function.Consumer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.JSRInlinerAdapter;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
@@ -99,6 +102,19 @@
}
@Override
+ public boolean isEmptyVoidMethod() {
+ for (Iterator<AbstractInsnNode> it = getNode().instructions.iterator(); it.hasNext(); ) {
+ AbstractInsnNode insn = it.next();
+ if (insn.getType() != Opcodes.RETURN
+ && !(insn instanceof LabelNode)
+ && !(insn instanceof LineNumberNode)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
triggerDelayedParsingIfNeccessary();
@@ -155,16 +171,12 @@
}
@Override
- public void registerInstructionsReferences(UseRegistry registry) {
+ public void registerCodeReferences(UseRegistry registry) {
triggerDelayedParsingIfNeccessary();
node.instructions.accept(
new JarRegisterEffectsVisitor(method.getHolder(), registry, application));
- }
-
- @Override
- public void registerCaughtTypes(Consumer<DexType> dexTypeConsumer) {
node.tryCatchBlocks.forEach(tryCatchBlockNode ->
- dexTypeConsumer.accept(application.getTypeFromDescriptor(
+ registry.registerTypeReference(application.getTypeFromDescriptor(
DescriptorUtils.getDescriptorFromClassBinaryName(tryCatchBlockNode.type))));
}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 48ef91d..49145fa 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -3,6 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.graph.DexValue.DexValueDouble;
+import com.android.tools.r8.graph.DexValue.DexValueFloat;
+import com.android.tools.r8.graph.DexValue.DexValueInt;
+import com.android.tools.r8.graph.DexValue.DexValueLong;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+
public abstract class UseRegistry {
public abstract boolean registerInvokeVirtual(DexMethod method);
@@ -73,4 +82,33 @@
throw new AssertionError();
}
}
+
+ public void registerCallSite(DexCallSite callSite) {
+ registerMethodHandle(callSite.bootstrapMethod);
+
+ // Register bootstrap method arguments.
+ // Only Type, MethodHandle, and MethodType need to be registered.
+ for (DexValue arg : callSite.bootstrapArgs) {
+ if (arg instanceof DexValueType) {
+ registerTypeReference(((DexValueType) arg).value);
+ } else if (arg instanceof DexValueMethodHandle) {
+ registerMethodHandle(((DexValueMethodHandle) arg).value);
+ } else if (arg instanceof DexValueMethodType) {
+ registerProto(((DexValueMethodType) arg).value);
+ } else {
+ assert (arg instanceof DexValueInt)
+ || (arg instanceof DexValueLong)
+ || (arg instanceof DexValueFloat)
+ || (arg instanceof DexValueDouble)
+ || (arg instanceof DexValueString);
+ }
+ }
+ }
+
+ public void registerProto(DexProto proto) {
+ registerTypeReference(proto.returnType);
+ for (DexType type : proto.parameters.values) {
+ registerTypeReference(type);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 58bc675..ee2c6ab 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -242,7 +242,20 @@
if (indexOfNewBlock >= successors.size() - 2 && indexOfOldBlock >= successors.size() - 2) {
// New and old are true target and fallthrough, replace last instruction with a goto.
Instruction instruction = getInstructions().removeLast();
- for (Value value : instruction.inValues()) {
+ // Iterate in reverse order to ensure that POP instructions are inserted in correct order.
+ for (int i = instruction.inValues().size() - 1; i >= 0; i--) {
+ Value value = instruction.inValues().get(i);
+ if (value instanceof StackValue) {
+ if (value.definition.isLoad()) {
+ assert value.definition.getBlock() == this;
+ removeInstruction(value.definition);
+ } else {
+ Pop pop = new Pop((StackValue) value);
+ pop.setBlock(this);
+ pop.setPosition(instruction.getPosition());
+ getInstructions().addLast(pop);
+ }
+ }
if (value.hasUsersInfo()) {
value.removeUser(instruction);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index ff8bd02..bf62fed 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -45,7 +45,7 @@
public final boolean hasDebugPositions;
- private final InternalOptions options;
+ public final InternalOptions options;
public IRCode(
InternalOptions options,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index cdca04e..04e7bb6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -78,7 +78,13 @@
protected void addInValue(Value value) {
if (value != null) {
inValues.add(value);
- value.addUser(this);
+ // TODO(mathiasr): We insert Pop instructions in BasicBlock.replaceSuccessor() after clearing
+ // user info. If we change the CF-specific IR to avoid having StackValues as in/out-values,
+ // we can remove this 'if'.
+ assert value.hasUsersInfo() || isPop();
+ if (value.hasUsersInfo()) {
+ value.addUser(this);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 7f24908..af6d5a3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -39,15 +39,20 @@
public static Invoke create(
Type type, DexItem target, DexProto proto, Value result, List<Value> arguments) {
+ return create(type, target, proto, result, arguments, false);
+ }
+
+ public static Invoke create(
+ Type type, DexItem target, DexProto proto, Value result, List<Value> arguments, boolean itf) {
switch (type) {
case DIRECT:
- return new InvokeDirect((DexMethod) target, result, arguments);
+ return new InvokeDirect((DexMethod) target, result, arguments, itf);
case INTERFACE:
return new InvokeInterface((DexMethod) target, result, arguments);
case STATIC:
- return new InvokeStatic((DexMethod) target, result, arguments);
+ return new InvokeStatic((DexMethod) target, result, arguments, itf);
case SUPER:
- return new InvokeSuper((DexMethod) target, result, arguments);
+ return new InvokeSuper((DexMethod) target, result, arguments, itf);
case VIRTUAL:
return new InvokeVirtual((DexMethod) target, result, arguments);
case NEW_ARRAY:
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 7338a2b..fa85845 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -21,8 +21,15 @@
public class InvokeDirect extends InvokeMethodWithReceiver {
+ private final boolean itf;
+
public InvokeDirect(DexMethod target, Value result, List<Value> arguments) {
+ this(target, result, arguments, false);
+ }
+
+ public InvokeDirect(DexMethod target, Value result, List<Value> arguments, boolean itf) {
super(target, result, arguments);
+ this.itf = itf;
}
@Override
@@ -114,6 +121,6 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(Opcodes.INVOKESPECIAL, getInvokedMethod()));
+ builder.add(new CfInvoke(Opcodes.INVOKESPECIAL, getInvokedMethod(), itf));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 60aee95..d29e3eb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -103,6 +103,6 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(Opcodes.INVOKEINTERFACE, getInvokedMethod()));
+ builder.add(new CfInvoke(Opcodes.INVOKEINTERFACE, getInvokedMethod(), true));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 01cd5f5..0e63cc7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -90,7 +90,7 @@
// To translate InvokePolymorphic back into InvokeVirtual, use the original prototype
// that is stored in getProto().
DexMethod method = factory.createMethod(dexMethod.holder, getProto(), dexMethod.name);
- builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method));
+ builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index e628f39..896fca9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -22,8 +22,15 @@
public class InvokeStatic extends InvokeMethod {
+ private final boolean itf;
+
public InvokeStatic(DexMethod target, Value result, List<Value> arguments) {
+ this(target, result, arguments, false);
+ }
+
+ public InvokeStatic(DexMethod target, Value result, List<Value> arguments, boolean itf) {
super(target, result, arguments);
+ this.itf = itf;
}
@Override
@@ -109,7 +116,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(Opcodes.INVOKESTATIC, getInvokedMethod()));
+ builder.add(new CfInvoke(Opcodes.INVOKESTATIC, getInvokedMethod(), itf));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 9e9a096..c761184 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -22,8 +22,11 @@
public class InvokeSuper extends InvokeMethodWithReceiver {
- public InvokeSuper(DexMethod target, Value result, List<Value> arguments) {
+ public final boolean itf;
+
+ public InvokeSuper(DexMethod target, Value result, List<Value> arguments, boolean itf) {
super(target, result, arguments);
+ this.itf = itf;
}
@Override
@@ -76,7 +79,7 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(Opcodes.INVOKESPECIAL, getInvokedMethod()));
+ builder.add(new CfInvoke(Opcodes.INVOKESPECIAL, getInvokedMethod(), itf));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 8a41c4e..5ef74b8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -103,6 +103,6 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, getInvokedMethod()));
+ builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, getInvokedMethod(), false));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index f863778..8330e06 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -144,7 +144,7 @@
for (DexEncodedMethod method : clazz.allMethodsSorted()) {
Node node = graph.ensureMethodNode(method);
InvokeExtractor extractor = new InvokeExtractor(appInfo, graphLense, node, graph);
- method.registerInstructionsReferences(extractor);
+ method.registerCodeReferences(extractor);
}
}
assert allMethodsExists(application, graph);
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 60f9c79..933f66d 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
@@ -11,12 +11,10 @@
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfTryCatch;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
-import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -106,27 +104,23 @@
return factory;
}
- public Code build(CodeRewriter rewriter, InternalOptions options, AppInfoWithSubtyping appInfo) {
- try {
- types = new TypeVerificationHelper(code, factory, appInfo).computeVerificationTypes();
- splitExceptionalBlocks();
- LoadStoreHelper loadStoreHelper = new LoadStoreHelper(code, types);
- loadStoreHelper.insertLoadsAndStores();
- DeadCodeRemover.removeDeadCode(code, rewriter, options);
- removeUnneededLoadsAndStores();
- registerAllocator = new CfRegisterAllocator(code, options);
- registerAllocator.allocateRegisters();
- loadStoreHelper.insertPhiMoves(registerAllocator);
- CodeRewriter.collapsTrivialGotos(method, code);
- int instructionTableCount =
- DexBuilder.instructionNumberToIndex(code.numberRemainingInstructions());
- DexBuilder.removeRedundantDebugPositions(code, instructionTableCount);
- CfCode code = buildCfCode();
- return code;
- } catch (Unimplemented e) {
- System.out.println("Incomplete CF construction: " + e.getMessage());
- return method.getCode().asJarCode();
- }
+ public CfCode build(
+ CodeRewriter rewriter, InternalOptions options, AppInfoWithSubtyping appInfo) {
+ types = new TypeVerificationHelper(code, factory, appInfo).computeVerificationTypes();
+ splitExceptionalBlocks();
+ LoadStoreHelper loadStoreHelper = new LoadStoreHelper(code, types);
+ loadStoreHelper.insertLoadsAndStores();
+ DeadCodeRemover.removeDeadCode(code, rewriter, options);
+ removeUnneededLoadsAndStores();
+ registerAllocator = new CfRegisterAllocator(code, options);
+ registerAllocator.allocateRegisters();
+ loadStoreHelper.insertPhiMoves(registerAllocator);
+ CodeRewriter.collapsTrivialGotos(method, code);
+ int instructionTableCount =
+ DexBuilder.instructionNumberToIndex(code.numberRemainingInstructions());
+ DexBuilder.removeRedundantDebugPositions(code, instructionTableCount);
+ CfCode code = buildCfCode();
+ return code;
}
// Split all blocks with throwing instructions and exceptional edges such that any non-throwing
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 20051f9..5bea64e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -327,7 +327,8 @@
targets.put(INITIAL_BLOCK_OFFSET, new BlockInfo());
// Process reachable code paths starting from instruction 0.
- processedInstructions = new boolean[source.instructionCount()];
+ int instCount = source.instructionCount();
+ processedInstructions = new boolean[instCount];
traceBlocksWorklist.add(0);
while (!traceBlocksWorklist.isEmpty()) {
int startOfBlockOffset = traceBlocksWorklist.remove();
@@ -337,17 +338,17 @@
continue;
}
// Process each instruction until the block is closed.
- for (int index = startOfBlockIndex; index < source.instructionCount(); ++index) {
+ for (int index = startOfBlockIndex; index < instCount; ++index) {
markIndexProcessed(index);
int closedAt = source.traceInstruction(index, this);
if (closedAt != -1) {
- if (closedAt + 1 < source.instructionCount()) {
+ if (closedAt + 1 < instCount) {
ensureBlockWithoutEnqueuing(source.instructionOffset(closedAt + 1));
}
break;
}
// If the next instruction starts a block, fall through to it.
- if (index + 1 < source.instructionCount()) {
+ if (index + 1 < instCount) {
int nextOffset = source.instructionOffset(index + 1);
if (targets.get(nextOffset) != null) {
ensureNormalSuccessorBlock(startOfBlockOffset, nextOffset);
@@ -491,7 +492,8 @@
continue;
}
// Build IR for each dex instruction in the block.
- for (int i = item.firstInstructionIndex; i < source.instructionCount(); ++i) {
+ int instCount = source.instructionCount();
+ for (int i = item.firstInstructionIndex; i < instCount; ++i) {
if (currentBlock == null) {
break;
}
@@ -997,7 +999,8 @@
add(instruction);
}
- public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments)
+ public void addInvoke(
+ Type type, DexItem item, DexProto callSiteProto, List<Value> arguments, boolean itf)
throws ApiLevelException {
if (type == Invoke.Type.POLYMORPHIC) {
assert item instanceof DexMethod;
@@ -1014,7 +1017,12 @@
null /* sourceString */);
}
}
- add(Invoke.create(type, item, callSiteProto, null, arguments));
+ add(Invoke.create(type, item, callSiteProto, null, arguments, itf));
+ }
+
+ public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments)
+ throws ApiLevelException {
+ addInvoke(type, item, callSiteProto, arguments, false);
}
public void addInvoke(
@@ -1024,12 +1032,23 @@
List<ValueType> types,
List<Integer> registers)
throws ApiLevelException {
+ addInvoke(type, item, callSiteProto, types, registers, false);
+ }
+
+ public void addInvoke(
+ Invoke.Type type,
+ DexItem item,
+ DexProto callSiteProto,
+ List<ValueType> types,
+ List<Integer> registers,
+ boolean itf)
+ throws ApiLevelException {
assert types.size() == registers.size();
List<Value> arguments = new ArrayList<>(types.size());
for (int i = 0; i < types.size(); i++) {
arguments.add(readRegister(registers.get(i), types.get(i)));
}
- addInvoke(type, item, callSiteProto, arguments);
+ addInvoke(type, item, callSiteProto, arguments, itf);
}
public void addInvokeCustomRegisters(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 0f0b44a..2b6ad47 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexApplication;
@@ -711,9 +712,7 @@
private void finalizeToCf(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
assert !method.getCode().isDexCode();
CfBuilder builder = new CfBuilder(method, code, options.itemFactory);
- // TODO(zerny): Change the return type of CfBuilder::build CfCode once complete.
- Code result = builder.build(codeRewriter, options, appInfo.withSubtyping());
- assert result.isCfCode() || result.isJarCode();
+ CfCode result = builder.build(codeRewriter, options, appInfo.withSubtyping());
method.setCode(result);
markProcessed(method, code, feedback);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index b584448..2298bc6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -8,17 +8,13 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.Descriptor;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.Cmp.Bias;
@@ -428,11 +424,12 @@
for (JarStateWorklistItem item = worklist.poll(); item != null; item = worklist.poll()) {
state.restoreState(item.instructionIndex);
// Iterate each of the instructions in the block to compute the outgoing JarState.
- for (int i = item.instructionIndex; i <= instructionCount(); ++i) {
+ int instCount = instructionCount();
+ for (int i = item.instructionIndex; i <= instCount; ++i) {
// If we are at the end of the instruction stream or if we have reached the start
// of a new block, propagate the state to all successors and add the ones
// that changed to the worklist.
- if (i == instructionCount() || (i != item.instructionIndex && CFG.containsKey(i))) {
+ if (i == instCount || (i != item.instructionIndex && CFG.containsKey(i))) {
item.blockInfo.normalSuccessors.iterator().forEachRemaining(offset -> {
if (state.recordStateForTarget(offset)) {
if (offset >= 0) {
@@ -2606,7 +2603,7 @@
}
callSiteProto = application.getProto(insn.desc);
}
- builder.addInvoke(invokeType, targetMethod, callSiteProto, types, registers);
+ builder.addInvoke(invokeType, targetMethod, callSiteProto, types, registers, insn.itf);
});
}
@@ -2654,104 +2651,13 @@
}
private void build(InvokeDynamicInsnNode insn, IRBuilder builder) throws ApiLevelException {
- // Bootstrap method
- Handle bsmHandle = insn.bsm;
- if (bsmHandle.getTag() != Opcodes.H_INVOKESTATIC &&
- bsmHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
- // JVM9 §4.7.23 note: Tag must be InvokeStatic or NewInvokeSpecial.
- throw new Unreachable("Bootstrap handle invalid: tag == " + bsmHandle.getTag());
- }
- // Resolve the bootstrap method.
- DexMethodHandle bootstrapMethod = getMethodHandle(application, bsmHandle);
-
- // Decode static bootstrap arguments
- List<DexValue> bootstrapArgs = new ArrayList<>();
- for (Object arg : insn.bsmArgs) {
- bootstrapArgs.add(decodeBootstrapArgument(arg));
- }
-
- // Construct call site
- DexCallSite callSite = application
- .getCallSite(insn.name, insn.desc, bootstrapMethod, bootstrapArgs);
+ DexCallSite callSite = DexCallSite.fromAsmInvokeDynamic(insn, application, clazz);
buildInvoke(insn.desc, null /* Not needed */,
false /* Receiver is passed explicitly */, builder,
(types, registers) -> builder.addInvokeCustom(callSite, types, registers));
}
- private DexValue decodeBootstrapArgument(Object value) {
- if (value instanceof Integer) {
- return DexValue.DexValueInt.create((Integer) value);
- } else if (value instanceof Long) {
- return DexValue.DexValueLong.create((Long) value);
- } else if (value instanceof Float) {
- return DexValue.DexValueFloat.create((Float) value);
- } else if (value instanceof Double) {
- return DexValue.DexValueDouble.create((Double) value);
- } else if (value instanceof String) {
- return new DexValue.DexValueString(application.getString((String) value));
-
- } else if (value instanceof Type) {
- Type type = (Type) value;
- switch (type.getSort()) {
- case Type.OBJECT:
- return new DexValue.DexValueType(
- application.getTypeFromDescriptor(((Type) value).getDescriptor()));
- case Type.METHOD:
- return new DexValue.DexValueMethodType(
- application.getProto(((Type) value).getDescriptor()));
- default:
- throw new Unreachable("Type sort is not supported: " + type.getSort());
- }
- } else if (value instanceof Handle) {
- return new DexValue.DexValueMethodHandle(getMethodHandle(application, (Handle) value));
- } else {
- throw new Unreachable(
- "Unsupported bootstrap static argument of type " + value.getClass().getSimpleName());
- }
- }
-
- private DexMethodHandle getMethodHandle(JarApplicationReader application, Handle handle) {
- MethodHandleType methodHandleType = getMethodHandleType(handle);
- Descriptor<? extends DexItem, ? extends Descriptor<?,?>> descriptor =
- methodHandleType.isFieldType()
- ? application.getField(handle.getOwner(), handle.getName(), handle.getDesc())
- : application.getMethod(handle.getOwner(), handle.getName(), handle.getDesc());
- return application.getMethodHandle(methodHandleType, descriptor);
- }
-
- private MethodHandleType getMethodHandleType(Handle handle) {
- switch (handle.getTag()) {
- case Opcodes.H_GETFIELD:
- return MethodHandleType.INSTANCE_GET;
- case Opcodes.H_GETSTATIC:
- return MethodHandleType.STATIC_GET;
- case Opcodes.H_PUTFIELD:
- return MethodHandleType.INSTANCE_PUT;
- case Opcodes.H_PUTSTATIC:
- return MethodHandleType.STATIC_PUT;
- case Opcodes.H_INVOKESPECIAL:
- assert !handle.getName().equals(Constants.INSTANCE_INITIALIZER_NAME);
- assert !handle.getName().equals(Constants.CLASS_INITIALIZER_NAME);
- DexType owner = application.getTypeFromName(handle.getOwner());
- if (owner == clazz) {
- return MethodHandleType.INVOKE_DIRECT;
- } else {
- return MethodHandleType.INVOKE_SUPER;
- }
- case Opcodes.H_INVOKEVIRTUAL:
- return MethodHandleType.INVOKE_INSTANCE;
- case Opcodes.H_INVOKEINTERFACE:
- return MethodHandleType.INVOKE_INTERFACE;
- case Opcodes.H_INVOKESTATIC:
- return MethodHandleType.INVOKE_STATIC;
- case Opcodes.H_NEWINVOKESPECIAL:
- return MethodHandleType.INVOKE_CONSTRUCTOR;
- default:
- throw new Unreachable("MethodHandle tag is not supported: " + handle.getTag());
- }
- }
-
private void build(JumpInsnNode insn, IRBuilder builder) {
processLocalVariablesAtControlEdge(insn, builder);
int[] targets = getTargets(insn);
@@ -2844,7 +2750,7 @@
} else if (insn.cst instanceof Handle) {
Handle handle = (Handle) insn.cst;
int dest = state.push(METHOD_HANDLE_TYPE);
- builder.addConstMethodHandle(dest, getMethodHandle(application, handle));
+ builder.addConstMethodHandle(dest, DexMethodHandle.fromAsmHandle(handle, application, clazz));
} else {
throw new CompilationError("Unsupported constant: " + insn.cst.toString());
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 8c17d91..15a90e9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -62,8 +62,10 @@
newFlags.unsetBridge();
newFlags.setStatic();
DexCode dexCode = code.asDexCode();
- // TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
- dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null));
+ // We cannot name the parameter "this" because the debugger may omit it due to the method
+ // actually being static. Instead we prepend it with a special character.
+ dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(
+ rewriter.factory.createString("-this")));
assert (dexCode.getDebugInfo() == null)
|| (companionMethod.getArity() == dexCode.getDebugInfo().parameters.length);
@@ -172,6 +174,7 @@
DexEncodedField.EMPTY_ARRAY,
companionMethods.toArray(new DexEncodedMethod[companionMethods.size()]),
DexEncodedMethod.EMPTY_ARRAY,
+ rewriter.factory.getSkipNameValidationForTesting(),
Collections.singletonList(iface));
companionClasses.put(iface, companionClass);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 05a4d56..82958da 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -139,6 +139,7 @@
synthesizeInstanceFields(),
synthesizeDirectMethods(),
synthesizeVirtualMethods(),
+ rewriter.factory.getSkipNameValidationForTesting(),
synthesizedFrom);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 30e9bae4..3feb59a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1838,8 +1838,20 @@
private static class CSEExpressionEquivalence extends Equivalence<Instruction> {
+ private final IRCode code;
+
+ private CSEExpressionEquivalence(IRCode code) {
+ this.code = code;
+ }
+
@Override
protected boolean doEquivalent(Instruction a, Instruction b) {
+ // Some Dalvik VMs incorrectly handle Cmp instructions which leads to a requirement
+ // that we do not perform common subexpression elimination for them. See comment on
+ // canHaveCmpLongBug for details.
+ if (a.isCmp() && code.options.canHaveCmpLongBug()) {
+ return false;
+ }
// Note that we don't consider positions because CSE can at most remove an instruction.
if (a.getClass() != b.getClass() || !a.identicalNonValueNonPositionParts(b)) {
return false;
@@ -1923,7 +1935,7 @@
public void commonSubexpressionElimination(IRCode code) {
final ListMultimap<Wrapper<Instruction>, Value> instructionToValue = ArrayListMultimap.create();
final DominatorTree dominatorTree = new DominatorTree(code);
- final CSEExpressionEquivalence equivalence = new CSEExpressionEquivalence();
+ final CSEExpressionEquivalence equivalence = new CSEExpressionEquivalence(code);
for (int i = 0; i < dominatorTree.getSortedBlocks().length; i++) {
BasicBlock block = dominatorTree.getSortedBlocks()[i];
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 536e4b7..dba6183 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -62,7 +62,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.function.Consumer;
public class Outliner {
@@ -997,6 +996,11 @@
}
@Override
+ public boolean isEmptyVoidMethod() {
+ return false;
+ }
+
+ @Override
public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
OutlineSourceCode source = new OutlineSourceCode(outline);
@@ -1010,12 +1014,7 @@
}
@Override
- public void registerInstructionsReferences(UseRegistry registry) {
- throw new Unreachable();
- }
-
- @Override
- public void registerCaughtTypes(Consumer<DexType> dexTypeConsumer) {
+ public void registerCodeReferences(UseRegistry registry) {
throw new Unreachable();
}
@@ -1083,8 +1082,8 @@
DexEncodedField.EMPTY_ARRAY, // Static fields.
DexEncodedField.EMPTY_ARRAY, // Instance fields.
direct,
- DexEncodedMethod.EMPTY_ARRAY // Virtual methods.
- );
+ DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+ options.itemFactory.getSkipNameValidationForTesting());
return clazz;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
index 5127632..10dc013 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
@@ -48,7 +48,8 @@
buildStaticFields(),
buildInstanceFields(),
buildDirectMethods(),
- buildVirtualMethods());
+ buildVirtualMethods(),
+ factory.getSkipNameValidationForTesting());
}
protected abstract DexType getSuperClassType();
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
index 62238c3..53bc57e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -33,6 +32,11 @@
}
@Override
+ public boolean isEmptyVoidMethod() {
+ return false;
+ }
+
+ @Override
public final IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
throws ApiLevelException {
return new IRBuilder(encodedMethod, sourceCode, options).build();
@@ -48,18 +52,11 @@
}
@Override
- public void registerInstructionsReferences(UseRegistry registry) {
+ public void registerCodeReferences(UseRegistry registry) {
registryCallback.accept(registry);
}
@Override
- public void registerCaughtTypes(Consumer<DexType> dexTypeConsumer) {
- // Support for synthesized code with catch handler is not implemented.
- // Let's check that we're in a well known where no catch handler is possible.
- assert sourceCode.instructionCount() == 1 || sourceCode instanceof SyntheticSourceCode;
- }
-
- @Override
protected final int computeHashCode() {
return sourceCode.hashCode();
}
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 5eb2bb7..c8be00e 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -108,25 +108,27 @@
writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, null);
int version = clazz.getClassFileVersion();
int access = clazz.accessFlags.getAsCfAccessFlags();
- String desc = clazz.type.toDescriptorString();
- String name = clazz.type.getInternalName();
+ String desc = namingLens.lookupDescriptor(clazz.type).toString();
+ String name = namingLens.lookupInternalName(clazz.type);
String signature = getSignature(clazz.annotations);
String superName =
- clazz.type == options.itemFactory.objectType ? null : clazz.superType.getInternalName();
+ clazz.type == options.itemFactory.objectType
+ ? null
+ : namingLens.lookupInternalName(clazz.superType);
String[] interfaces = new String[clazz.interfaces.values.length];
for (int i = 0; i < clazz.interfaces.values.length; i++) {
- interfaces[i] = clazz.interfaces.values[i].getInternalName();
+ interfaces[i] = namingLens.lookupInternalName(clazz.interfaces.values[i]);
}
writer.visit(version, access, name, signature, superName, interfaces);
writeAnnotations(writer::visitAnnotation, clazz.annotations.annotations);
ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations);
if (clazz.getEnclosingMethod() != null) {
- clazz.getEnclosingMethod().write(writer);
+ clazz.getEnclosingMethod().write(writer, namingLens);
}
for (InnerClassAttribute entry : clazz.getInnerClasses()) {
- entry.write(writer);
+ entry.write(writer, namingLens);
}
for (DexEncodedField field : clazz.staticFields()) {
@@ -175,6 +177,7 @@
if (value == null) {
return null;
}
+ // Signature has already been minified by ClassNameMinifier.renameTypesInGenericSignatures().
DexValue[] parts = value.getValues();
StringBuilder res = new StringBuilder();
for (DexValue part : parts) {
@@ -208,7 +211,7 @@
DexValue[] values = value.getValues();
String[] res = new String[values.length];
for (int i = 0; i < values.length; i++) {
- res[i] = ((DexValueType) values[i]).value.getInternalName();
+ res[i] = namingLens.lookupInternalName(((DexValueType) values[i]).value);
}
return res;
}
@@ -222,8 +225,8 @@
private void writeField(DexEncodedField field, ClassWriter writer) {
int access = field.accessFlags.getAsCfAccessFlags();
- String name = field.field.name.toString();
- String desc = field.field.type.toDescriptorString();
+ String name = namingLens.lookupName(field.field).toString();
+ String desc = namingLens.lookupDescriptor(field.field.type).toString();
String signature = getSignature(field.annotations);
Object value = getStaticValue(field);
FieldVisitor visitor = writer.visitField(access, name, desc, signature, value);
@@ -234,8 +237,8 @@
private void writeMethod(
DexEncodedMethod method, ClassWriter writer, ImmutableMap<DexString, DexValue> defaults) {
int access = method.accessFlags.getAsCfAccessFlags();
- String name = method.method.name.toString();
- String desc = method.descriptor();
+ String name = namingLens.lookupName(method.method).toString();
+ String desc = method.descriptor(namingLens);
String signature = getSignature(method.annotations);
String[] exceptions = getExceptions(method.annotations);
MethodVisitor visitor = writer.visitMethod(access, name, desc, signature, exceptions);
@@ -332,10 +335,11 @@
private void writeCode(Code code, MethodVisitor visitor) {
if (code.isJarCode()) {
+ assert namingLens.isIdentityLens();
code.asJarCode().writeTo(visitor);
} else {
assert code.isCfCode();
- code.asCfCode().write(visitor);
+ code.asCfCode().write(visitor, namingLens);
}
}
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
index 37c9aea..0b67945 100644
--- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -7,8 +7,10 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.UseRegistry;
@@ -50,13 +52,16 @@
@Override
public void visitLdcInsn(Object cst) {
if (cst instanceof Type) {
- // Nothing to register for method type, it represents only a prototype not associated with a
- // method name.
- if (((Type) cst).getSort() != Type.METHOD) {
+ if (((Type) cst).getSort() == Type.METHOD) {
+ String descriptor = ((Type) cst).getDescriptor();
+ assert descriptor.charAt(0) == '(';
+ registry.registerProto(application.getProto(descriptor));
+ } else {
registry.registerConstClass(application.getType((Type) cst));
}
} else if (cst instanceof Handle) {
- registerMethodHandleType((Handle) cst);
+ registry.registerMethodHandle(
+ DexMethodHandle.fromAsmHandle((Handle) cst, application, clazz));
}
}
@@ -109,55 +114,8 @@
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
- registerMethodHandleType(bsm);
-
- // Register bootstrap method arguments, only Type and MethodHandle need to be register.
- for (Object arg : bsmArgs) {
- if (arg instanceof Type && ((Type) arg).getSort() == Type.OBJECT) {
- registry.registerTypeReference(application.getType((Type) arg));
- } else if (arg instanceof Handle) {
- registerMethodHandleType((Handle) arg);
- }
- }
+ registry.registerCallSite(
+ DexCallSite.fromAsmInvokeDynamic(application, clazz, name, desc, bsm, bsmArgs));
}
- private void registerMethodHandleType(Handle handle) {
- switch (handle.getTag()) {
- case Opcodes.H_GETFIELD:
- visitFieldInsn(Opcodes.GETFIELD, handle.getOwner(), handle.getName(), handle.getDesc());
- break;
- case Opcodes.H_GETSTATIC:
- visitFieldInsn(Opcodes.GETSTATIC, handle.getOwner(), handle.getName(), handle.getDesc());
- break;
- case Opcodes.H_PUTFIELD:
- visitFieldInsn(Opcodes.PUTFIELD, handle.getOwner(), handle.getName(), handle.getDesc());
- break;
- case Opcodes.H_PUTSTATIC:
- visitFieldInsn(Opcodes.PUTSTATIC, handle.getOwner(), handle.getName(), handle.getDesc());
- break;
- case Opcodes.H_INVOKEVIRTUAL:
- visitMethodInsn(
- Opcodes.INVOKEVIRTUAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
- break;
- case Opcodes.H_INVOKEINTERFACE:
- visitMethodInsn(
- Opcodes.INVOKEINTERFACE, handle.getOwner(), handle.getName(), handle.getDesc(), true);
- break;
- case Opcodes.H_INVOKESPECIAL:
- visitMethodInsn(
- Opcodes.INVOKESPECIAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
- break;
- case Opcodes.H_INVOKESTATIC:
- visitMethodInsn(
- Opcodes.INVOKESTATIC, handle.getOwner(), handle.getName(), handle.getDesc(), false);
- break;
- case Opcodes.H_NEWINVOKESPECIAL:
- registry.registerNewInstance(application.getTypeFromName(handle.getOwner()));
- visitMethodInsn(
- Opcodes.INVOKESPECIAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
- break;
- default:
- throw new Unreachable("MethodHandle tag is not supported: " + handle.getTag());
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index dd00254..4f49fbe 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -438,14 +438,15 @@
appInfo.dexItemFactory.createType(
getDescriptorFromClassBinaryName(
getClassBinaryNameFromDescriptor(enclosingDescriptor)
- + '$' + name));
+ + Minifier.INNER_CLASS_SEPARATOR
+ + name));
String enclosingRenamedBinaryName =
getClassBinaryNameFromDescriptor(
renaming.getOrDefault(enclosingType, enclosingType.descriptor).toString());
String renamed =
getClassBinaryNameFromDescriptor(
renaming.getOrDefault(type, type.descriptor).toString());
- assert renamed.startsWith(enclosingRenamedBinaryName + '$');
+ assert renamed.startsWith(enclosingRenamedBinaryName + Minifier.INNER_CLASS_SEPARATOR);
String outName = renamed.substring(enclosingRenamedBinaryName.length() + 1);
renamedSignature.append(outName);
return type;
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 82eb242..6bd0c6d 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -25,6 +25,8 @@
public class Minifier {
+ static final char INNER_CLASS_SEPARATOR = '$';
+
private final AppInfoWithLiveness appInfo;
private final RootSet rootSet;
private final InternalOptions options;
@@ -77,6 +79,12 @@
}
@Override
+ public String lookupSimpleName(DexType inner, DexString innerName) {
+ String internalName = lookupInternalName(inner);
+ return internalName.substring(internalName.lastIndexOf(INNER_CLASS_SEPARATOR) + 1);
+ }
+
+ @Override
public DexString lookupName(DexMethod method) {
return renaming.getOrDefault(method, method.name);
}
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index ca6c1c8..fe73dc5 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.function.Consumer;
@@ -30,6 +31,8 @@
public abstract DexString lookupDescriptor(DexType type);
+ public abstract String lookupSimpleName(DexType inner, DexString innerName);
+
public abstract DexString lookupName(DexMethod method);
public abstract DexString lookupName(DexField field);
@@ -42,6 +45,11 @@
return this instanceof IdentityLens;
}
+ public String lookupInternalName(DexType type) {
+ assert type.isClassType() || type.isArrayType();
+ return DescriptorUtils.descriptorToInternalName(lookupDescriptor(type).toString());
+ }
+
abstract void forAllRenamedTypes(Consumer<DexType> consumer);
abstract <T extends DexItem> Map<String, T> getRenamedItems(
@@ -68,6 +76,11 @@
}
@Override
+ public String lookupSimpleName(DexType inner, DexString innerName) {
+ return innerName == null ? null : innerName.toString();
+ }
+
+ @Override
public DexString lookupName(DexMethod method) {
return method.name;
}
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index 3736008..1dd6e31 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -50,16 +50,20 @@
if (code == null) {
return;
}
- assert code.isDexCode();
- DexDebugInfo dexDebugInfo = code.asDexCode().getDebugInfo();
- if (dexDebugInfo == null) {
- return;
+ if (code.isDexCode()) {
+ DexDebugInfo dexDebugInfo = code.asDexCode().getDebugInfo();
+ if (dexDebugInfo == null) {
+ return;
+ }
+ // Thanks to a single global source file, we can safely remove DBG_SET_FILE entirely.
+ dexDebugInfo.events =
+ Arrays.stream(dexDebugInfo.events)
+ .filter(dexDebugEvent -> !(dexDebugEvent instanceof SetFile))
+ .toArray(DexDebugEvent[]::new);
+ } else {
+ assert code.isCfCode();
+ // CF has nothing equivalent to SetFile, so there is nothing to remove.
}
- // Thanks to a single global source file, we can safely remove DBG_SET_FILE entirely.
- dexDebugInfo.events =
- Arrays.stream(dexDebugInfo.events)
- .filter(dexDebugEvent -> !(dexDebugEvent instanceof SetFile))
- .toArray(DexDebugEvent[]::new);
});
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
index 321001e..a6a9a11 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -38,7 +38,7 @@
// be removed.
if (method.accessFlags.isBridge() && !method.accessFlags.isAbstract()) {
InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
- method.getCode().registerInstructionsReferences(targetExtractor);
+ method.getCode().registerCodeReferences(targetExtractor);
DexMethod target = targetExtractor.getTarget();
InvokeKind kind = targetExtractor.getKind();
if (target != null && target.getArity() == method.method.getArity()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index 685e733..c815cf8 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -30,7 +30,7 @@
private void identifyBridgeMethod(DexEncodedMethod method) {
if (method.accessFlags.isBridge()) {
InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
- method.getCode().registerInstructionsReferences(targetExtractor);
+ method.getCode().registerCodeReferences(targetExtractor);
DexMethod target = targetExtractor.getTarget();
InvokeKind kind = targetExtractor.getKind();
if (target != null &&
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 9e00a64..b11b999 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1179,7 +1179,7 @@
if (protoLiteExtension != null && protoLiteExtension.appliesTo(method)) {
protoLiteExtension.processMethod(method, new UseRegistry(method), protoLiteFields);
} else {
- method.registerInstructionsReferences(new UseRegistry(method));
+ method.registerCodeReferences(new UseRegistry(method));
}
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(method));
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index 5c51786..8e5e12a 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -151,8 +151,7 @@
clazz.forEachField(field -> addMainDexType(field.field.type));
clazz.forEachMethod(method -> {
traceMethodDirectDependencies(method.method);
- method.registerInstructionsReferences(codeDirectReferenceCollector);
- method.registerCatchedTypes(this::addMainDexType);
+ method.registerCodeReferences(codeDirectReferenceCollector);
});
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
index 63475b7..a80e758 100644
--- a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
@@ -371,7 +371,7 @@
return existing;
} else if (existing.accessFlags.isBridge()) {
InvokeSingleTargetExtractor extractor = new InvokeSingleTargetExtractor();
- existing.getCode().registerInstructionsReferences(extractor);
+ existing.getCode().registerCodeReferences(extractor);
if (extractor.getTarget() != method.method) {
abortMerge = true;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 27a6405..962247e 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -151,9 +151,12 @@
// this can only happen as the result of an invalid invoke. They will not actually be
// called at runtime but we have to keep them as non-abstract (see above) to produce the
// same failure mode.
- reachableMethods.add(allowAbstract
- ? method.toAbstractMethod()
- : method.toEmptyThrowingMethod());
+ reachableMethods.add(
+ allowAbstract
+ ? method.toAbstractMethod()
+ : (options.isGeneratingClassFiles()
+ ? method.toEmptyThrowingMethodCf()
+ : method.toEmptyThrowingMethodDex()));
} else {
if (Log.ENABLED) {
Log.debug(getClass(), "Removing method %s.", method.method);
diff --git a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLiteExtension.java b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLiteExtension.java
index c2ba7e5..c4dee58 100644
--- a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLiteExtension.java
+++ b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLiteExtension.java
@@ -116,13 +116,13 @@
// serialized stream for this proto. As we mask all reads in the writing code and normally
// remove fields that are only written but never read, we have to mark fields used in setters
// as read and written.
- method.registerInstructionsReferences(
+ method.registerCodeReferences(
new FieldWriteImpliesReadUseRegistry(registry, method.method.holder));
} else {
// Filter all getters and field accesses in these methods. We do not want fields to become
// live just due to being referenced in a special method. The pruning phase will remove
// all references to dead fields in the code later.
- method.registerInstructionsReferences(new FilteringUseRegistry(registry, method.method.holder,
+ method.registerCodeReferences(new FilteringUseRegistry(registry, method.method.holder,
protoLiteFields));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index b79e8e6..fa045e6 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -138,6 +138,23 @@
}
/**
+ * Convert a class type descriptor to an ASM internal name.
+ *
+ * @param descriptor type descriptor
+ * @return Java type name
+ */
+ public static String descriptorToInternalName(String descriptor) {
+ switch (descriptor.charAt(0)) {
+ case '[':
+ return descriptor;
+ case 'L':
+ return descriptor.substring(1, descriptor.length() - 1);
+ default:
+ throw new Unreachable("Not array or class type");
+ }
+ }
+
+ /**
* Convert a type descriptor to a Java type name. Will also deobfuscate class names if a
* class mapper is provided.
*
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 8fdf68d..dc0cd0f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -518,6 +518,32 @@
// Some dalvik versions found in the wild perform invalid JIT compilation of cmp-long
// instructions where the result register overlaps with the input registers.
// See b/74084493.
+ //
+ // The same dalvik versions also have a bug where the JIT compilation of code such as:
+ //
+ // void method(long l) {
+ // if (l < 0) throw new RuntimeException("less than");
+ // if (l == 0) throw new RuntimeException("equal");
+ // }
+ //
+ // Will enter the case for l==0 even when l is non-zero. The code generated for this is of
+ // the form:
+ //
+ // 0: 0x00: ConstWide16 v0, 0x0000000000000000 (0)
+ // 1: 0x02: CmpLong v2, v4, v0
+ // 2: 0x04: IfLtz v2, 0x0c (+8)
+ // 3: 0x06: IfNez v2, 0x0a (+4)
+ //
+ // However, the jit apparently clobbers the input register in the IfLtz instruction. Therefore,
+ // for dalvik VMs we have to instead generate the following code:
+ //
+ // 0: 0x00: ConstWide16 v0, 0x0000000000000000 (0)
+ // 1: 0x02: CmpLong v2, v4, v0
+ // 2: 0x04: IfLtz v2, 0x0e (+10)
+ // 3: 0x06: CmpLong v2, v4, v0
+ // 4: 0x08: IfNez v2, 0x0c (+4)
+ //
+ // See b/75408029.
public boolean canHaveCmpLongBug() {
return minApiLevel < AndroidApiLevel.L.getLevel();
}
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index c208cea..258aa43 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1907,11 +1907,6 @@
// 1) t03
// java.lang.AssertionError: expected null, but was:<[I@e2603b4>
- .put("lang.ref.SoftReference.enqueue.SoftReference_enqueue_A01",
- match(runtimesUpTo(Version.V4_4_4)))
- // 1) t03(com.google.jctf.test.lib.java.lang.ref.SoftReference.enqueue.SoftReference_enqueue_A01)
- // java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
-
.put("lang.ref.ReferenceQueue.poll.ReferenceQueue_poll_A01",
match(runtimes(Version.DEFAULT, Version.V7_0_0, Version.V6_0_1, Version.V5_1_1)))
// 1) t03
@@ -4963,12 +4958,24 @@
.put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
match(runtimesUpTo(Version.V4_4_4)))
+
.put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01",
match(runtimesUpTo(Version.V4_4_4)))
+
+ .put("lang.ref.WeakReference.enqueue.WeakReference_enqueue_A01",
+ match(runtimesUpTo(Version.V4_4_4)))
+ // 1) t03
+ // java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
+
.put("lang.ref.SoftReference.isEnqueued.SoftReference_isEnqueued_A01",
match(runtimesUpTo(Version.V4_4_4)))
// Passes or fails randomly. Check that something is enqueued after 2 seconds.
+ .put("lang.ref.SoftReference.enqueue.SoftReference_enqueue_A01",
+ match(runtimesUpTo(Version.V4_4_4)))
+ // 1) t03(com.google.jctf.test.lib.java.lang.ref.SoftReference.enqueue.SoftReference_enqueue_A01)
+ // java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
+
.put("lang.ref.ReferenceQueue.poll.ReferenceQueue_poll_A01",
match(runtimesUpTo(Version.V4_4_4)))
// Passes or fails randomly.
diff --git a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
index 28fe5dc..79017bc 100644
--- a/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
+++ b/src/test/java/com/android/tools/r8/R8ApiBinaryCompatibilityTests.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
@@ -13,7 +16,6 @@
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
-import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -31,13 +33,15 @@
List<Path> inputs =
ImmutableList.of(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "arithmetic.jar"));
+ String keepMain = "-keep public class arithmetic.Arithmetic {\n"
+ + " public static void main(java.lang.String[]);\n"
+ + "}";
+
Path pgConf = temp.getRoot().toPath().resolve("pg.conf");
- FileUtils.writeTextFile(
- pgConf, TestBase.keepMainProguardConfiguration("arithmetic.Arithmetic"));
+ FileUtils.writeTextFile(pgConf, keepMain);
Path mainDexRules = temp.getRoot().toPath().resolve("maindex.rules");
- FileUtils.writeTextFile(
- mainDexRules, TestBase.keepMainProguardConfiguration("arithmetic.Arithmetic"));
+ FileUtils.writeTextFile(mainDexRules, keepMain);
Path mainDexList = temp.getRoot().toPath().resolve("maindexlist.txt");
FileUtils.writeTextFile(mainDexList, "arithmetic/Arithmetic.class");
@@ -68,8 +72,8 @@
ProcessBuilder builder = new ProcessBuilder(command);
ProcessResult result = ToolHelper.runProcess(builder);
- Assert.assertEquals(result.stderr + "\n" + result.stdout, 0, result.exitCode);
- Assert.assertTrue(result.stdout, result.stdout.isEmpty());
- Assert.assertTrue(result.stderr, result.stderr.isEmpty());
+ assertEquals(result.stderr + "\n" + result.stdout, 0, result.exitCode);
+ assertTrue(result.stdout, result.stdout.isEmpty());
+ assertTrue(result.stderr, result.stderr.isEmpty());
}
}
diff --git a/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
index f4d3da5..b3950b4 100644
--- a/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
@@ -107,8 +107,6 @@
private static List<String> expectedFailures =
ImmutableList.of(
- "native-private-interface-methods",
- "desugared-private-interface-methods"
);
private boolean expectedToFailCf(String testName) {
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index c6ab4e2..5d7bec0 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -361,6 +361,47 @@
.build());
}
+ private ProcessResult runR8OnShaking1(Path additionalProguardConfiguration) throws Throwable {
+ Path input = Paths.get(EXAMPLES_BUILD_DIR, "shaking1.jar").toAbsolutePath();
+ Path proguardConfiguration =
+ Paths.get(ToolHelper.EXAMPLES_DIR, "shaking1", "keep-rules.txt").toAbsolutePath();
+ return ToolHelper.forkR8(temp.getRoot().toPath(),
+ "--pg-conf", proguardConfiguration.toString(),
+ "--pg-conf", additionalProguardConfiguration.toString(),
+ "--lib", ToolHelper.getDefaultAndroidJar().toAbsolutePath().toString(),
+ input.toString());
+ }
+
+ @Test
+ public void printsPrintSeedsOnStdout() throws Throwable {
+ Path proguardPrintSeedsConfiguration = temp.newFile("printseeds.txt").toPath().toAbsolutePath();
+ FileUtils.writeTextFile(proguardPrintSeedsConfiguration, ImmutableList.of("-printseeds"));
+ ProcessResult result = runR8OnShaking1(proguardPrintSeedsConfiguration);
+ assertEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+ assertTrue(result.stdout.contains("void main(java.lang.String[])"));
+ }
+
+ @Test
+ public void printsPrintUsageOnStdout() throws Throwable {
+ Path proguardPrintUsageConfiguration = temp.newFile("printusage.txt").toPath().toAbsolutePath();
+ FileUtils.writeTextFile(proguardPrintUsageConfiguration, ImmutableList.of("-printusage"));
+ ProcessResult result = runR8OnShaking1(proguardPrintUsageConfiguration);
+ assertEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+ assertTrue(result.stdout.contains("shaking1.Unused"));
+ }
+
+ @Test
+ public void printsPrintSeedsAndPrintUsageOnStdout() throws Throwable {
+ Path proguardPrintSeedsConfiguration =
+ temp.newFile("printseedsandprintusage.txt").toPath().toAbsolutePath();
+ FileUtils.writeTextFile(
+ proguardPrintSeedsConfiguration, ImmutableList.of("-printseeds", "-printusage"));
+ ProcessResult result = runR8OnShaking1(proguardPrintSeedsConfiguration);
+ assertEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+ assertTrue(result.stdout.contains("void main(java.lang.String[])"));
+ assertTrue(result.stdout.contains("shaking1.Unused"));
+ }
+
private R8Command parse(String... args) throws CompilationFailedException {
return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 024be59..d7d8b99 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -54,7 +54,7 @@
public class TestBase {
// Actually running Proguard should only be during development.
- private boolean runProguard = System.getProperty("run_proguard") != null;
+ private static final boolean RUN_PROGUARD = System.getProperty("run_proguard") != null;
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -63,7 +63,7 @@
* Check if tests should also run Proguard when applicable.
*/
protected boolean isRunProguard() {
- return runProguard;
+ return RUN_PROGUARD;
}
/**
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 6ffe54d..bd66301 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -86,6 +86,7 @@
public static final String DEFAULT_DEX_FILENAME = "classes.dex";
public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
+ public static final String JAVA_8_RUNTIME = "third_party/openjdk/openjdk-rt-1.8/rt.jar";
private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I;
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleDump.java b/src/test/java/com/android/tools/r8/cf/MethodHandleDump.java
index c80047a..a213a91 100644
--- a/src/test/java/com/android/tools/r8/cf/MethodHandleDump.java
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleDump.java
@@ -22,14 +22,18 @@
// so this LDC instruction must be in a subclass of C, and not directly on MethodHandleTest.
public class MethodHandleDump implements Opcodes {
+ private static final String cDesc = "com/android/tools/r8/cf/MethodHandleTest$C";
+ private static final String eDesc = "com/android/tools/r8/cf/MethodHandleTest$E";
+ private static final String fDesc = "com/android/tools/r8/cf/MethodHandleTest$F";
+ private static final String iDesc = "com/android/tools/r8/cf/MethodHandleTest$I";
private static final Type viType = Type.getMethodType(Type.VOID_TYPE, Type.INT_TYPE);
private static final Type jiType = Type.getMethodType(Type.LONG_TYPE, Type.INT_TYPE);
private static final Type vicType =
Type.getMethodType(Type.VOID_TYPE, Type.INT_TYPE, Type.CHAR_TYPE);
private static final Type jicType =
Type.getMethodType(Type.LONG_TYPE, Type.INT_TYPE, Type.CHAR_TYPE);
- private static final String cDesc = "com/android/tools/r8/cf/MethodHandleTest$C";
- private static final String iDesc = "com/android/tools/r8/cf/MethodHandleTest$I";
+ private static final Type veType = Type.getMethodType(Type.VOID_TYPE, Type.getObjectType(eDesc));
+ private static final Type fType = Type.getMethodType(Type.getObjectType(fDesc));
private static final String viDesc = viType.getDescriptor();
private static final String jiDesc = jiType.getDescriptor();
private static final String vicDesc = vicType.getDescriptor();
@@ -44,6 +48,8 @@
.put("jiType", jiType)
.put("vicType", vicType)
.put("jicType", jicType)
+ .put("veType", veType)
+ .put("fType", fType)
.build();
Builder<String, Handle> methodsBuilder = ImmutableMap.builder();
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleTest.java b/src/test/java/com/android/tools/r8/cf/MethodHandleTest.java
index d42d443..844ae99 100644
--- a/src/test/java/com/android/tools/r8/cf/MethodHandleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleTest.java
@@ -73,6 +73,14 @@
}
}
+ public static class E {
+ // Class that is only mentioned in parameter list of LDC(MethodType)-instruction.
+ }
+
+ public static class F {
+ // Class that is only mentioned in return value of LDC(MethodType)-instruction.
+ }
+
public interface I {
int ii = 42;
@@ -166,6 +174,8 @@
MethodHandle methodHandle = D.vcviSpecialMethod();
methodHandle.invoke(new D(), 20);
constructorMethod().invoke(21);
+ System.out.println(veType().parameterType(0).getName().lastIndexOf('.'));
+ System.out.println(fType().returnType().getName().lastIndexOf('.'));
} catch (Throwable e) {
throw new RuntimeException(e);
}
@@ -199,6 +209,14 @@
return MethodType.methodType(long.class, int.class, char.class);
}
+ public static MethodType veType() {
+ return MethodType.methodType(void.class, E.class);
+ }
+
+ public static MethodType fType() {
+ return MethodType.methodType(F.class);
+ }
+
public static MethodHandle scviMethod() {
try {
return MethodHandles.lookup().findStatic(C.class, "svi", viType());
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
index d92ff90..ae3b95e 100644
--- a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import java.nio.file.Path;
+import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -27,8 +28,10 @@
static final Class<?> CLASS = MethodHandleTest.class;
private boolean ldc = false;
+ private boolean minify = false;
- @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@Test
public void testMethodHandlesLookup() throws Exception {
@@ -44,20 +47,35 @@
test();
}
+ @Test
+ public void testMinify() throws Exception {
+ // Run test with LDC methods, i.e. without java.lang.invoke.MethodHandles
+ ldc = true;
+ ProcessResult runInput = runInput();
+ assertEquals(0, runInput.exitCode);
+ Path outCf = temp.getRoot().toPath().resolve("cf.jar");
+ build(new ClassFileConsumer.ArchiveConsumer(outCf), true);
+ ProcessResult runCf =
+ ToolHelper.runJava(outCf, CLASS.getCanonicalName(), ldc ? "error" : "exception");
+ assertEquals(runInput.toString(), runCf.toString());
+ }
+
private final Class[] inputClasses = {
MethodHandleTest.class,
MethodHandleTest.C.class,
MethodHandleTest.I.class,
MethodHandleTest.Impl.class,
MethodHandleTest.D.class,
+ MethodHandleTest.E.class,
+ MethodHandleTest.F.class,
};
private void test() throws Exception {
ProcessResult runInput = runInput();
Path outCf = temp.getRoot().toPath().resolve("cf.jar");
- build(new ClassFileConsumer.ArchiveConsumer(outCf));
+ build(new ClassFileConsumer.ArchiveConsumer(outCf), false);
Path outDex = temp.getRoot().toPath().resolve("dex.zip");
- build(new DexIndexedConsumer.ArchiveConsumer(outDex));
+ build(new DexIndexedConsumer.ArchiveConsumer(outDex), false);
ProcessResult runCf =
ToolHelper.runJava(outCf, CLASS.getCanonicalName(), ldc ? "error" : "exception");
@@ -79,7 +97,7 @@
assertEquals(runInput.exitCode, runDex.exitCode);
}
- private void build(ProgramConsumer programConsumer) throws Exception {
+ private void build(ProgramConsumer programConsumer, boolean minify) throws Exception {
// MethodHandle.invoke() only supported from Android O
// ConstMethodHandle only supported from Android P
AndroidApiLevel apiLevel = AndroidApiLevel.P;
@@ -95,6 +113,14 @@
byte[] classAsBytes = getClassAsBytes(c);
cfBuilder.addClassProgramData(classAsBytes, Origin.unknown());
}
+ if (minify) {
+ cfBuilder.addProguardConfiguration(
+ Arrays.asList(
+ "-keep public class com.android.tools.r8.cf.MethodHandleTest {",
+ " public static void main(...);",
+ "}"),
+ Origin.unknown());
+ }
R8.run(cfBuilder.build());
}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index cbfcca6..685e8f7 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -139,6 +139,9 @@
Assume.assumeTrue("Skipping test " + testName.getMethodName()
+ " because debug tests are not yet supported on Windows",
!ToolHelper.isWindows());
+ Assume.assumeTrue("Skipping test " + testName.getMethodName()
+ + " because debug tests are not yet supported on device",
+ ToolHelper.getDexVm().getKind() == ToolHelper.DexVm.Kind.HOST);
ClassNameMapper classNameMapper =
config.getProguardMap() == null
diff --git a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
index c5c21a7..26d3d5c 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -58,6 +58,8 @@
// TODO(shertz) we should see the local variable this even when desugaring.
if (supportsDefaultMethod(config)) {
commands.add(checkLocal("this"));
+ } else {
+ commands.add(checkLocal("-this"));
}
commands.add(checkLocal(parameterName));
commands.add(stepOver(INTELLIJ_FILTER));
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index 5f63038..414fbc3 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -3,13 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.debug.DebugTestConfig.RuntimeKind;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
@@ -28,13 +35,15 @@
private static final String SOURCE_FILE = "Minified.java";
- @Parameterized.Parameters(name = "minification: {0}, proguardMap: {1}")
+ @Parameterized.Parameters(name = "backend:{0} minification:{1} proguardMap:{2}")
public static Collection minificationControl() {
ImmutableList.Builder<Object> builder = ImmutableList.builder();
- for (MinifyMode mode : MinifyMode.values()) {
- builder.add((Object) new Object[]{mode, false});
- if (mode.isMinify()) {
- builder.add((Object) new Object[]{mode, true});
+ for (RuntimeKind kind : RuntimeKind.values()) {
+ for (MinifyMode mode : MinifyMode.values()) {
+ builder.add((Object) new Object[] {kind, mode, false});
+ if (mode.isMinify()) {
+ builder.add((Object) new Object[] {kind, mode, true});
+ }
}
}
return builder.build();
@@ -43,10 +52,13 @@
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+ private final RuntimeKind runtimeKind;
private final MinifyMode minificationMode;
private final boolean writeProguardMap;
- public MinificationTest(MinifyMode minificationMode, boolean writeProguardMap) throws Exception {
+ public MinificationTest(
+ RuntimeKind runtimeKind, MinifyMode minificationMode, boolean writeProguardMap) {
+ this.runtimeKind = runtimeKind;
this.minificationMode = minificationMode;
this.writeProguardMap = writeProguardMap;
}
@@ -69,15 +81,19 @@
}
AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
- Path dexOutputDir = temp.newFolder().toPath();
- Path proguardMap = writeProguardMap ? dexOutputDir.resolve("proguard.map") : null;
+ Path outputPath = temp.getRoot().toPath().resolve("classes.zip");
+ Path proguardMap = writeProguardMap ? temp.getRoot().toPath().resolve("proguard.map") : null;
+ OutputMode outputMode =
+ runtimeKind == RuntimeKind.CF ? OutputMode.ClassFile : OutputMode.DexIndexed;
R8Command.Builder builder =
R8Command.builder()
.addProgramFiles(DEBUGGEE_JAR)
- .setOutput(dexOutputDir, OutputMode.DexIndexed)
- .setMinApiLevel(minSdk.getLevel())
+ .setOutput(outputPath, outputMode)
.setMode(CompilationMode.DEBUG)
.addLibraryFiles(ToolHelper.getAndroidJar(minSdk));
+ if (runtimeKind != RuntimeKind.CF) {
+ builder.setMinApiLevel(minSdk.getLevel());
+ }
if (proguardMap != null) {
builder.setProguardMapOutputPath(proguardMap);
}
@@ -91,9 +107,22 @@
? (oc -> oc.lineNumberOptimization = LineNumberOptimization.OFF)
: null);
- DexDebugTestConfig config = new DexDebugTestConfig(dexOutputDir.resolve("classes.dex"));
- config.setProguardMap(proguardMap);
- return config;
+ switch (runtimeKind) {
+ case CF:
+ {
+ CfDebugTestConfig config = new CfDebugTestConfig(outputPath);
+ config.setProguardMap(proguardMap);
+ return config;
+ }
+ case DEX:
+ {
+ DexDebugTestConfig config = new DexDebugTestConfig(outputPath);
+ config.setProguardMap(proguardMap);
+ return config;
+ }
+ default:
+ throw new Unreachable();
+ }
}
@Test
@@ -104,8 +133,15 @@
final String innerClassName = minifiedNames() ? "a" : "Minified$Inner";
final String innerMethodName = minifiedNames() ? "a" : "innerTest";
final String innerSignature = "()I";
+ DebugTestConfig config = getTestConfig();
+ checkStructure(
+ config,
+ className,
+ MethodSignature.fromSignature(methodName, signature),
+ innerClassName,
+ MethodSignature.fromSignature(innerMethodName, innerSignature));
runDebugTest(
- getTestConfig(),
+ config,
className,
breakpoint(className, methodName, signature),
run(),
@@ -126,8 +162,14 @@
final String innerClassName = minifiedNames() ? "a" : "Minified$Inner";
final String innerMethodName = minifiedNames() ? "a" : "innerTest";
final String innerSignature = "()I";
+ DebugTestConfig config = getTestConfig();
+ checkStructure(
+ config,
+ className,
+ innerClassName,
+ MethodSignature.fromSignature(innerMethodName, innerSignature));
runDebugTest(
- getTestConfig(),
+ config,
className,
breakpoint(innerClassName, innerMethodName, innerSignature),
run(),
@@ -135,4 +177,35 @@
checkLine(SOURCE_FILE, 8),
run());
}
+
+ private void checkStructure(
+ DebugTestConfig config,
+ String className,
+ MethodSignature method,
+ String innerClassName,
+ MethodSignature innerMethod)
+ throws Throwable {
+ if (ToolHelper.isWindows()) {
+ // TODO(b/76135355): Update dx.bat on Windows to something that can build
+ // jdwp-tests-preN-dex.jar.
+ return;
+ }
+ Path proguardMap = config.getProguardMap();
+ String mappingFile = proguardMap == null ? null : proguardMap.toString();
+ DexInspector inspector = new DexInspector(config.getPaths(), mappingFile);
+ ClassSubject clazz = inspector.clazz(className);
+ assertTrue(clazz.isPresent());
+ if (method != null) {
+ assertTrue(clazz.method(method).isPresent());
+ }
+ ClassSubject innerClass = inspector.clazz(innerClassName);
+ assertTrue(innerClass.isPresent());
+ assertTrue(innerClass.method(innerMethod).isPresent());
+ }
+
+ private void checkStructure(
+ DebugTestConfig config, String className, String innerClassName, MethodSignature innerMethod)
+ throws Throwable {
+ checkStructure(config, className, null, innerClassName, innerMethod);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index 40d8ad0..e07f319 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -4,11 +4,14 @@
package com.android.tools.r8.desugaring.interfacemethods;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.AsmTestBase;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.VmTestRunner;
-import com.android.tools.r8.desugaring.interfacemethods.test0.InterfaceWithDefaults;
-import com.android.tools.r8.desugaring.interfacemethods.test0.TestMain;
+import com.android.tools.r8.VmTestRunner.IgnoreForRangeOfVmVersions;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -25,13 +28,35 @@
@Test
public void testInvokeSpecialToDefaultMethod() throws Exception {
- ensureSameOutput(TestMain.class.getCanonicalName(),
+ ensureSameOutput(
+ com.android.tools.r8.desugaring.interfacemethods.test0.TestMain.class.getCanonicalName(),
ToolHelper.getMinApiLevelForDexVm(),
- ToolHelper.getClassAsBytes(TestMain.class),
- introduceInvokeSpecial(ToolHelper.getClassAsBytes(InterfaceWithDefaults.class)));
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test0.TestMain.class),
+ introduceInvokeSpecial(ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test0.InterfaceWithDefaults.class)));
+ }
+
+ // NOTE: this particular test is working on pre-N devices since
+ // it's fixed by interface default method desugaring.
+ @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.DEFAULT)
+ @Test
+ public void testInvokeSpecialToDefaultMethodFromStatic() throws Exception {
+ ensureSameOutput(
+ com.android.tools.r8.desugaring.interfacemethods.test1.TestMain.class.getCanonicalName(),
+ ToolHelper.getMinApiLevelForDexVm(),
+ ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test1.TestMain.class),
+ introduceInvokeSpecial(ToolHelper.getClassAsBytes(
+ com.android.tools.r8.desugaring.interfacemethods.test1.InterfaceWithDefaults.class)));
+ }
+
+ private class MutableBoolean {
+ boolean value;
}
private byte[] introduceInvokeSpecial(byte[] classBytes) throws IOException {
+ MutableBoolean patched = new MutableBoolean();
try (InputStream input = new ByteArrayInputStream(classBytes)) {
ClassReader cr = new ClassReader(input);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
@@ -46,9 +71,11 @@
public void visitMethodInsn(
int opcode, String owner, String name, String desc, boolean itf) {
if (opcode == Opcodes.INVOKEINTERFACE &&
- owner.endsWith("test0/InterfaceWithDefaults") &&
+ owner.endsWith("InterfaceWithDefaults") &&
name.equals("foo")) {
+ assertFalse(patched.value);
super.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, itf);
+ patched.value = true;
} else {
super.visitMethodInsn(opcode, owner, name, desc, itf);
@@ -57,6 +84,7 @@
};
}
}, 0);
+ assertTrue(patched.value);
return cw.toByteArray();
}
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/InterfaceWithDefaults.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/InterfaceWithDefaults.java
index fc5ab9e..dc671dc 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/InterfaceWithDefaults.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/InterfaceWithDefaults.java
@@ -1,3 +1,7 @@
+// Copyright (c) 2018, 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.desugaring.interfacemethods.test0;
public interface InterfaceWithDefaults {
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/TestMain.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/TestMain.java
index b57cbf3..79ac7b0 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/TestMain.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test0/TestMain.java
@@ -1,3 +1,7 @@
+// Copyright (c) 2018, 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.desugaring.interfacemethods.test0;
public class TestMain implements InterfaceWithDefaults {
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/InterfaceWithDefaults.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/InterfaceWithDefaults.java
new file mode 100644
index 0000000..6f79c96
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/InterfaceWithDefaults.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2018, 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.desugaring.interfacemethods.test1;
+
+public interface InterfaceWithDefaults {
+ default void foo() {
+ System.out.println("InterfaceWithDefaults::foo()");
+ }
+
+ static void bar(InterfaceWithDefaults iface) {
+ System.out.println("InterfaceWithDefaults::bar()");
+ iface.foo();
+ }
+
+ void test();
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/TestMain.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/TestMain.java
new file mode 100644
index 0000000..e38a17d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/test1/TestMain.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2018, 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.desugaring.interfacemethods.test1;
+
+public class TestMain implements InterfaceWithDefaults {
+ @Override
+ public void test() {
+ System.out.println("TestMain::test()");
+ this.foo();
+ InterfaceWithDefaults.bar(this);
+ }
+
+ @Override
+ public void foo() {
+ System.out.println("TestMain::foo()");
+ }
+
+ public static void main(String[] args) {
+ new TestMain().test();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index b99bf6d..9edc735 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -103,6 +103,7 @@
DexEncodedField.EMPTY_ARRAY,
DexEncodedMethod.EMPTY_ARRAY,
new DexEncodedMethod[] {makeMethod(type, stringCount, startOffset)},
+ false,
synthesizedFrom);
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java
index 6e9efa6..bd462b7 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java
@@ -3,80 +3,88 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.jasmin;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.errors.CompilationError;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class InvalidClassNames extends JasminTestBase {
+public class InvalidClassNames extends NameTestBase {
- public boolean runsOnJVM;
- public String name;
+ private static final String RESULT = "MAIN";
+ private static final String MAIN_CLASS = "Main";
- public InvalidClassNames(String name, boolean runsOnJVM) {
- this.name = name;
- this.runsOnJVM = runsOnJVM;
- }
-
- private void runTest(JasminBuilder builder, String main, String expected) throws Exception {
- if (runsOnJVM) {
- String javaResult = runOnJava(builder, main);
- assertEquals(expected, javaResult);
- }
- String artResult = null;
- try {
- artResult = runOnArtD8(builder, main);
- fail();
- } catch (CompilationError t) {
- // Intentionally left empty.
- } catch (Throwable t) {
- t.printStackTrace(System.out);
- fail("Invalid dex class names should be compilation errors.");
- }
- assertNull("Invalid dex class names should be rejected.", artResult);
- }
-
- @Parameters
+ @Parameters(name = "\"{0}\", jvm: {1}, art: {2}")
public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][]{
- {"\u00a0", !ToolHelper.isJava9Runtime()},
- {"\u2000", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"\u200f", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"\u2028", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"\u202f", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"\ud800", false},
- {"\udfff", false},
- {"\ufff0", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"\uffff", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"a/b/c/a/D/", true},
- {"a<b", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"a>b", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"<a>b", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()},
- {"<a>", !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime()}
- });
+ Collection<Object[]> data = new ArrayList<>();
+ data.addAll(NameTestBase.getCommonNameTestData(true));
+ data.addAll(
+ Arrays.asList(
+ new Object[][] {
+ {new TestString("a/b/c/a/D/"), true, false},
+ {
+ new TestString("a<b"),
+ !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime(),
+ false
+ },
+ {
+ new TestString("a>b"),
+ !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime(),
+ false
+ },
+ {
+ new TestString("<a>b"),
+ !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime(),
+ false
+ },
+ {
+ new TestString("<a>"),
+ !ToolHelper.isWindows() && !ToolHelper.isJava9Runtime(),
+ false
+ }
+ }));
+ return data;
+ }
+
+ private String name;
+ private boolean validForJVM;
+ private boolean validForArt;
+
+ public InvalidClassNames(TestString name, boolean validForJVM, boolean validForArt) {
+ this.name = name.getValue();
+ this.validForJVM = validForJVM;
+ this.validForArt = validForArt;
+ }
+
+ private JasminBuilder createJasminBuilder() {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass(name);
+ clazz.addStaticMethod(
+ "run",
+ Collections.emptyList(),
+ "V",
+ ".limit stack 2",
+ ".limit locals 0",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc \"" + RESULT + "\"",
+ " invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+ " return");
+
+ clazz = builder.addClass(MAIN_CLASS);
+ clazz.addMainMethod(
+ ".limit stack 0", ".limit locals 1", "invokestatic " + name + "/run()V", " return");
+
+ return builder;
}
@Test
public void invalidClassName() throws Exception {
- JasminBuilder builder = new JasminBuilder();
- JasminBuilder.ClassBuilder clazz = builder.addClass(name);
- clazz.addMainMethod(
- ".limit stack 2",
- ".limit locals 1",
- " getstatic java/lang/System/out Ljava/io/PrintStream;",
- " ldc \"MAIN\"",
- " invokevirtual java/io/PrintStream.print(Ljava/lang/String;)V",
- " return");
-
- runTest(builder, clazz.name, "MAIN");
+ runNameTesting(validForJVM, createJasminBuilder(), MAIN_CLASS, RESULT, validForArt, name);
}
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java
index bad64ac..8f680ee 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java
@@ -3,117 +3,48 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.jasmin;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.smali.SmaliBuilder;
-import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.base.Strings;
-import com.google.common.primitives.Bytes;
-import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.zip.Adler32;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class InvalidFieldNames extends JasminTestBase {
+public class InvalidFieldNames extends NameTestBase {
private static final String CLASS_NAME = "Test";
private static String FIELD_VALUE = "42";
@Parameters(name = "\"{0}\", jvm: {1}, art: {2}")
public static Collection<Object[]> data() {
- return Arrays.asList(
- new Object[][] {
- {new TestStringParameter("azAZ09$_"), true, true},
- {new TestStringParameter("_"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("a-b"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\u00a0"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\u00a1"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\u1fff"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\u2000"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\u200f"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\u2010"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\u2027"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\u2028"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\u202f"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\u2030"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\ud7ff"), !ToolHelper.isJava9Runtime(), true},
-
- // Standalone high and low surrogates.
- {new TestStringParameter("\ud800"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\udbff"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\udc00"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\udfff"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\ue000"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\uffef"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\ufff0"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("\uffff"), !ToolHelper.isJava9Runtime(), false},
-
- // Single and double code points above 0x10000.
- {new TestStringParameter("\ud800\udc00"), true, true},
- {new TestStringParameter("\ud800\udcfa"), true, true},
- {new TestStringParameter("\ud800\udcfb"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\udbff\udfff"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("\ud800\udc00\ud800\udcfa"), true, true},
- {new TestStringParameter("\ud800\udc00\udbff\udfff"), !ToolHelper.isJava9Runtime(), true},
- {new TestStringParameter("a/b"), false, false},
- {new TestStringParameter("<a"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("a>"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("a<b>"), !ToolHelper.isJava9Runtime(), false},
- {new TestStringParameter("<a>b"), !ToolHelper.isJava9Runtime(), false}
- });
- }
-
- // TestStringParameter is a String with modified toString() which prints \\uXXXX for
- // characters outside 0x20..0x7e.
- static class TestStringParameter {
- private final String value;
-
- TestStringParameter(String value) {
- this.value = value;
- }
-
- String getValue() {
- return value;
- }
-
- @Override
- public String toString() {
- return StringUtils.toASCIIString(value);
- }
+ Collection<Object[]> data = new ArrayList<>();
+ data.addAll(NameTestBase.getCommonNameTestData(false));
+ data.addAll(
+ Arrays.asList(
+ new Object[][] {
+ {new TestString("a/b"), false, false},
+ {new TestString("<a"), !ToolHelper.isJava9Runtime(), false},
+ {new TestString("a>"), !ToolHelper.isJava9Runtime(), false},
+ {new TestString("a<b>"), !ToolHelper.isJava9Runtime(), false},
+ {new TestString("<a>b"), !ToolHelper.isJava9Runtime(), false},
+ {new TestString("<a>"), false, true}
+ }));
+ return data;
}
private String name;
private boolean validForJVM;
private boolean validForArt;
- public InvalidFieldNames(TestStringParameter name, boolean validForJVM, boolean validForArt) {
+ public InvalidFieldNames(TestString name, boolean validForJVM, boolean validForArt) {
this.name = name.getValue();
this.validForJVM = validForJVM;
this.validForArt = validForArt;
}
- private byte[] trimLastZeroByte(byte[] bytes) {
- assert bytes.length > 0 && bytes[bytes.length - 1] == 0;
- byte[] result = new byte[bytes.length - 1];
- System.arraycopy(bytes, 0, result, 0, result.length);
- return result;
- }
-
private JasminBuilder createJasminBuilder() {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass(CLASS_NAME);
@@ -130,84 +61,8 @@
return builder;
}
- private AndroidApp createAppWithSmali() throws Exception {
-
- SmaliBuilder smaliBuilder = new SmaliBuilder(CLASS_NAME);
- String originalSourceFile = CLASS_NAME + FileUtils.JAVA_EXTENSION;
- smaliBuilder.setSourceFile(originalSourceFile);
-
- // We're using a valid placeholder string which will be replaced by the actual name.
- byte[] nameMutf8 = trimLastZeroByte(DexString.encodeToMutf8(name));
-
- String placeholderString = Strings.repeat("A", nameMutf8.length);
-
- smaliBuilder.addStaticField(placeholderString, "I", FIELD_VALUE);
- MethodSignature mainSignature =
- smaliBuilder.addMainMethod(
- 1,
- "sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
- "sget v0, LTest;->" + placeholderString + ":I",
- "invoke-virtual {p0, v0}, Ljava/io/PrintStream;->print(I)V",
- "return-void");
- byte[] dexCode = smaliBuilder.compile();
-
- // Replace placeholder by mutf8-encoded name
- byte[] placeholderBytes = trimLastZeroByte(DexString.encodeToMutf8(placeholderString));
- assert placeholderBytes.length == nameMutf8.length;
- int index = Bytes.indexOf(dexCode, placeholderBytes);
- if (index >= 0) {
- System.arraycopy(nameMutf8, 0, dexCode, index, nameMutf8.length);
- }
- assert Bytes.indexOf(dexCode, placeholderBytes) < 0;
-
- // Update checksum
- Adler32 adler = new Adler32();
- adler.update(dexCode, Constants.SIGNATURE_OFFSET, dexCode.length - Constants.SIGNATURE_OFFSET);
- int checksum = (int) adler.getValue();
- for (int i = 0; i < 4; ++i) {
- dexCode[Constants.CHECKSUM_OFFSET + i] = (byte) (checksum >> (8 * i) & 0xff);
- }
-
- return AndroidApp.builder()
- .addDexProgramData(dexCode, new PathOrigin(Paths.get(originalSourceFile)))
- .build();
- }
-
@Test
public void invalidFieldNames() throws Exception {
- JasminBuilder jasminBuilder = createJasminBuilder();
-
- if (validForJVM) {
- String javaResult = runOnJava(jasminBuilder, CLASS_NAME);
- assertEquals(FIELD_VALUE, javaResult);
- } else {
- try {
- runOnJava(jasminBuilder, CLASS_NAME);
- fail("Should have failed on JVM.");
- } catch (AssertionError e) {
- // Silent on expected failure.
- }
- }
-
- if (validForArt) {
- String artResult = runOnArtD8(jasminBuilder, CLASS_NAME);
- assertEquals(FIELD_VALUE, artResult);
- } else {
- // Make sure the compiler fails.
- try {
- runOnArtD8(jasminBuilder, CLASS_NAME);
- fail("D8 should have rejected this case.");
- } catch (CompilationError t) {
- assertTrue(t.getMessage().contains(name));
- }
-
- // Make sure ART also fail, if D8 rejects it.
- try {
- runOnArt(createAppWithSmali(), CLASS_NAME);
- fail("Art should have failed.");
- } catch (AssertionError e) {
- // Silent on expected failure.
- }
- }
+ runNameTesting(validForJVM, createJasminBuilder(), CLASS_NAME, FIELD_VALUE, validForArt, name);
}
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java
index 5f83016..fe95845 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java
@@ -3,15 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.jasmin;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
@@ -20,73 +16,79 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class InvalidMethodNames extends JasminTestBase {
+public class InvalidMethodNames extends NameTestBase {
- public boolean runsOnJVM;
- public String name;
+ private final String CLASS_NAME = "Test";
+ private final String RESULT = "CALLED";
- public InvalidMethodNames(String name, boolean runsOnJVM) {
- this.name = name;
- this.runsOnJVM = runsOnJVM;
- }
+ private String name;
+ private boolean validForJVM;
+ private boolean validForArt;
- private void runTest(JasminBuilder builder, String main, String expected) throws Exception {
- if (runsOnJVM) {
- String javaResult = runOnJava(builder, main);
- assertEquals(expected, javaResult);
- }
- String artResult = null;
- try {
- artResult = runOnArtD8(builder, main);
- fail();
- } catch (CompilationError t) {
- String asciiString = new DexString(name).toASCIIString();
- assertTrue(t.getMessage().contains(asciiString));
- } catch (Throwable t) {
- t.printStackTrace(System.out);
- fail("Invalid dex method names should be compilation errors.");
- }
- assertNull("Invalid dex method names should be rejected.", artResult);
- }
-
- @Parameters
+ @Parameters(name = "\"{0}\", jvm: {1}, art: {2}")
public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][]{
- {"\u00a0", !ToolHelper.isJava9Runtime()},
- {"\u2000", !ToolHelper.isJava9Runtime()},
- {"\u200f", !ToolHelper.isJava9Runtime()},
- {"\u2028", !ToolHelper.isJava9Runtime()},
- {"\u202f", !ToolHelper.isJava9Runtime()},
- {"\ud800", !ToolHelper.isJava9Runtime()},
- {"\udfff", !ToolHelper.isJava9Runtime()},
- {"\ufff0", !ToolHelper.isJava9Runtime()},
- {"\uffff", !ToolHelper.isJava9Runtime()},
- {"a/b", false},
- {"<a", false},
- {"a>", !ToolHelper.isJava9Runtime()},
- {"<a>", false}
- });
+ Collection<Object[]> data = new ArrayList<>();
+ data.addAll(NameTestBase.getCommonNameTestData(false));
+ data.addAll(
+ Arrays.asList(
+ new Object[][] {
+ {new TestString("a/b"), false, false},
+ {new TestString("<a"), false, false},
+ {new TestString("a>"), !ToolHelper.isJava9Runtime(), false},
+ {new TestString("<a>"), false, false}
+ }));
+ return data;
}
- @Test
- public void invalidMethodName() throws Exception {
- JasminBuilder builder = new JasminBuilder();
- JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+ public InvalidMethodNames(TestString name, boolean validForJVM, boolean validForArt) {
+ this.name = name.getValue();
+ this.validForJVM = validForJVM;
+ this.validForArt = validForArt;
+ }
- clazz.addStaticMethod(name, ImmutableList.of(), "V",
+ JasminBuilder createJasminBuilder() {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass(CLASS_NAME);
+
+ clazz.addStaticMethod(
+ name,
+ ImmutableList.of(),
+ "V",
".limit stack 2",
".limit locals 0",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
- " ldc \"CALLED\"",
+ " ldc \"" + RESULT + "\"",
" invokevirtual java/io/PrintStream.print(Ljava/lang/String;)V",
" return");
clazz.addMainMethod(
".limit stack 0",
".limit locals 1",
- " invokestatic Test/" + name + "()V",
+ " invokestatic " + CLASS_NAME + "/" + name + "()V",
" return");
+ return builder;
+ }
- runTest(builder, clazz.name, "CALLED");
+ static class StringReplaceData {
+ final byte[] inputMutf8;
+ final String placeholderString;
+ final byte[] placeholderBytes;
+
+ StringReplaceData(byte[] inputMutf8, String placeholderString, byte[] placeholderBytes) {
+ this.inputMutf8 = inputMutf8;
+ this.placeholderString = placeholderString;
+ this.placeholderBytes = placeholderBytes;
+ }
+ }
+
+ @Test
+ public void invalidMethodName() throws Exception {
+ runNameTesting(
+ validForJVM,
+ createJasminBuilder(),
+ CLASS_NAME,
+ RESULT,
+ validForArt,
+ StringUtils.toASCIIString(name));
}
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index ff0a177..c75ea7d 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -75,7 +75,13 @@
}
protected String runOnArtD8(JasminBuilder builder, String main) throws Exception {
- return runOnArt(compileWithD8(builder), main);
+ return runOnArtD8(builder, main, null);
+ }
+
+ protected String runOnArtD8(
+ JasminBuilder builder, String main, Consumer<InternalOptions> optionsConsumer)
+ throws Exception {
+ return runOnArt(compileWithD8(builder, optionsConsumer), main);
}
protected ProcessResult runOnArtD8Raw(JasminBuilder builder, String main) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java b/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
new file mode 100644
index 0000000..1104e54
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2018, 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.jasmin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.InvalidPathException;
+import java.util.Arrays;
+import java.util.Collection;
+
+class NameTestBase extends JasminTestBase {
+ // TestString is a String with modified toString() which prints \\uXXXX for
+ // characters outside 0x20..0x7e.
+ static class TestString {
+ private final String value;
+
+ TestString(String value) {
+ this.value = value;
+ }
+
+ String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return StringUtils.toASCIIString(value);
+ }
+ }
+
+ // The return value is a collection of rows with the following fields:
+ // - Name (String) to test (can be class name, field name, method name).
+ // - boolean, whether it runs on the JVM.
+ // - boolean, whether it runs on the ART.
+ static Collection<Object[]> getCommonNameTestData(boolean classNames) {
+
+ boolean windowsSensitive = classNames && ToolHelper.isWindows();
+
+ return Arrays.asList(
+ new Object[][] {
+ {new TestString("azAZ09$_"), true, true},
+ {new TestString("_"), !ToolHelper.isJava9Runtime(), true},
+ {new TestString("a-b"), !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\u00a0"), !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\u00a1"), !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\u1fff"), !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\u2000"), !windowsSensitive && !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\u200f"), !windowsSensitive && !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\u2010"), !windowsSensitive && !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\u2027"), !windowsSensitive && !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\u2028"), !windowsSensitive && !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\u202f"), !windowsSensitive && !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\u2030"), !windowsSensitive && !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\ud7ff"), !windowsSensitive && !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\ue000"), !windowsSensitive && !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\uffef"), !windowsSensitive && !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\ufff0"), !windowsSensitive && !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\uffff"), !windowsSensitive && !ToolHelper.isJava9Runtime(), false},
+
+ // Standalone high and low surrogates.
+ {new TestString("\ud800"), !classNames && !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\udbff"), !classNames && !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\udc00"), !classNames && !ToolHelper.isJava9Runtime(), false},
+ {new TestString("\udfff"), !classNames && !ToolHelper.isJava9Runtime(), false},
+
+ // Single and double code points above 0x10000.
+ {new TestString("\ud800\udc00"), true, true},
+ {new TestString("\ud800\udcfa"), true, true},
+ {new TestString("\ud800\udcfb"), !windowsSensitive && !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\udbff\udfff"), !windowsSensitive && !ToolHelper.isJava9Runtime(), true},
+ {new TestString("\ud800\udc00\ud800\udcfa"), true, true},
+ {
+ new TestString("\ud800\udc00\udbff\udfff"),
+ !windowsSensitive && !ToolHelper.isJava9Runtime(),
+ true
+ }
+ });
+ }
+
+ void runNameTesting(
+ boolean validForJVM,
+ JasminBuilder jasminBuilder,
+ String mainClassName,
+ String expectedResult,
+ boolean validForArt,
+ String expectedNameInFailingD8Message)
+ throws Exception {
+
+ if (validForJVM) {
+ String javaResult = runOnJava(jasminBuilder, mainClassName);
+ assertEquals(expectedResult, javaResult);
+ } else {
+ try {
+ runOnJava(jasminBuilder, mainClassName);
+ fail("Should have failed on JVM.");
+ } catch (AssertionError | InvalidPathException e) {
+ // Silent on expected failure.
+ }
+ }
+
+ if (validForArt) {
+ String artResult = runOnArtD8(jasminBuilder, mainClassName);
+ assertEquals(expectedResult, artResult);
+ } else {
+ // Make sure the compiler fails.
+ try {
+ runOnArtD8(jasminBuilder, mainClassName);
+ fail("D8 should have rejected this case.");
+ } catch (CompilationError t) {
+ assertTrue(t.getMessage().contains(expectedNameInFailingD8Message));
+ }
+
+ // Make sure ART also fail, if D8 rejects it.
+ try {
+ runOnArtD8(
+ jasminBuilder,
+ mainClassName,
+ options -> {
+ options.itemFactory.setSkipNameValidationForTesting(true);
+ });
+ fail("Art should have failed.");
+ } catch (AssertionError e) {
+ // Silent on expected failure.
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index 65dc98f..315e980 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -251,10 +251,10 @@
@Test
public void testTrivialKs() throws Exception {
- final String mainClassName = "lambdas.kstyle.trivial.MainKt";
+ final String mainClassName = "lambdas_kstyle_trivial.MainKt";
runTest("lambdas_kstyle_trivial", mainClassName, null, (app) -> {
Verifier verifier = new Verifier(app);
- String pkg = "lambdas/kstyle/trivial";
+ String pkg = "lambdas_kstyle_trivial";
verifier.assertLambdaGroups(
allowAccessModification ?
@@ -292,10 +292,10 @@
@Test
public void testCapturesKs() throws Exception {
- final String mainClassName = "lambdas.kstyle.captures.MainKt";
+ final String mainClassName = "lambdas_kstyle_captures.MainKt";
runTest("lambdas_kstyle_captures", mainClassName, null, (app) -> {
Verifier verifier = new Verifier(app);
- String pkg = "lambdas/kstyle/captures";
+ String pkg = "lambdas_kstyle_captures";
String grpPkg = allowAccessModification ? "" : pkg;
verifier.assertLambdaGroups(
@@ -317,10 +317,10 @@
@Test
public void testGenericsNoSignatureKs() throws Exception {
- final String mainClassName = "lambdas.kstyle.generics.MainKt";
+ final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest("lambdas_kstyle_generics", mainClassName, null, (app) -> {
Verifier verifier = new Verifier(app);
- String pkg = "lambdas/kstyle/generics";
+ String pkg = "lambdas_kstyle_generics";
String grpPkg = allowAccessModification ? "" : pkg;
verifier.assertLambdaGroups(
@@ -338,10 +338,10 @@
@Test
public void testInnerClassesAndEnclosingMethodsKs() throws Exception {
- final String mainClassName = "lambdas.kstyle.generics.MainKt";
+ final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest("lambdas_kstyle_generics", mainClassName, KEEP_INNER_AND_ENCLOSING, (app) -> {
Verifier verifier = new Verifier(app);
- String pkg = "lambdas/kstyle/generics";
+ String pkg = "lambdas_kstyle_generics";
String grpPkg = allowAccessModification ? "" : pkg;
verifier.assertLambdaGroups(
@@ -361,10 +361,10 @@
@Test
public void testGenericsSignatureInnerEnclosingKs() throws Exception {
- final String mainClassName = "lambdas.kstyle.generics.MainKt";
+ final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest("lambdas_kstyle_generics", mainClassName, KEEP_SIGNATURE_INNER_ENCLOSING, (app) -> {
Verifier verifier = new Verifier(app);
- String pkg = "lambdas/kstyle/generics";
+ String pkg = "lambdas_kstyle_generics";
String grpPkg = allowAccessModification ? "" : pkg;
verifier.assertLambdaGroups(
@@ -386,17 +386,17 @@
@Test
public void testTrivialJs() throws Exception {
- final String mainClassName = "lambdas.jstyle.trivial.MainKt";
+ final String mainClassName = "lambdas_jstyle_trivial.MainKt";
runTest("lambdas_jstyle_trivial", mainClassName, null, (app) -> {
Verifier verifier = new Verifier(app);
- String pkg = "lambdas/jstyle/trivial";
+ String pkg = "lambdas_jstyle_trivial";
String grp = allowAccessModification ? "" : pkg;
- String supplier = "Lambdas$Supplier";
- String intSupplier = "Lambdas$IntSupplier";
- String consumer = "Lambdas$Consumer";
- String intConsumer = "Lambdas$IntConsumer";
- String multiFunction = "Lambdas$MultiFunction";
+ String supplier = "lambdas_jstyle_trivial.Lambdas$Supplier";
+ String intSupplier = "lambdas_jstyle_trivial.Lambdas$IntSupplier";
+ String consumer = "lambdas_jstyle_trivial.Lambdas$Consumer";
+ String intConsumer = "lambdas_jstyle_trivial.Lambdas$IntConsumer";
+ String multiFunction = "lambdas_jstyle_trivial.Lambdas$MultiFunction";
verifier.assertLambdaGroups(
jstyle(grp, 0, intSupplier, 2),
@@ -434,10 +434,10 @@
@Test
public void testSingleton() throws Exception {
- final String mainClassName = "lambdas.singleton.MainKt";
+ final String mainClassName = "lambdas_singleton.MainKt";
runTest("lambdas_singleton", mainClassName, null, (app) -> {
Verifier verifier = new Verifier(app);
- String pkg = "lambdas/singleton";
+ String pkg = "lambdas_singleton";
String grp = allowAccessModification ? "" : pkg;
verifier.assertLambdaGroups(
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 5df6bd5..35df33e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -639,7 +639,8 @@
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
directMethods,
- DexEncodedMethod.EMPTY_ARRAY));
+ DexEncodedMethod.EMPTY_ARRAY,
+ false));
}
DirectMappedDexApplication application = builder.build().toDirect();
ApplicationWriter writer =
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index fe1f1eb..ae2a432 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.TestBase.MinifyMode;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -57,8 +59,7 @@
public class TreeShakingTest {
private static final Path ANDROID_JAR = ToolHelper.getDefaultAndroidJar();
- private static final List<Path> JAR_LIBRARIES = ImmutableList
- .of(ANDROID_JAR, Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar"));
+ private final List<Path> JAR_LIBRARIES;
private static final String EMPTY_FLAGS = "src/test/proguard/valid/empty.flags";
private static final Set<String> IGNORED_FLAGS = ImmutableSet.of(
"examples/minification:conflict-mapping.txt",
@@ -66,33 +67,35 @@
);
private static final Set<String> IGNORED = ImmutableSet.of(
// there's no point in running those without obfuscation
- "examples/shaking1:keep-rules-repackaging.txt:DEX:NONE",
- "examples/shaking1:keep-rules-repackaging.txt:JAR:NONE",
- "examples/shaking16:keep-rules-1.txt:DEX:NONE",
- "examples/shaking16:keep-rules-1.txt:JAR:NONE",
- "examples/shaking16:keep-rules-2.txt:DEX:NONE",
- "examples/shaking16:keep-rules-2.txt:JAR:NONE",
- "examples/shaking15:keep-rules.txt:DEX:NONE",
- "examples/shaking15:keep-rules.txt:JAR:NONE",
- "examples/minifygeneric:keep-rules.txt:DEX:NONE",
- "examples/minifygeneric:keep-rules.txt:JAR:NONE",
- "examples/minifygenericwithinner:keep-rules.txt:DEX:NONE",
- "examples/minifygenericwithinner:keep-rules.txt:JAR:NONE",
+ "examples/shaking1:keep-rules-repackaging.txt:*:NONE",
+ "examples/shaking16:keep-rules-1.txt:*:NONE",
+ "examples/shaking16:keep-rules-2.txt:*:NONE",
+ "examples/shaking15:keep-rules.txt:*:NONE",
+ "examples/minifygeneric:keep-rules.txt:*:NONE",
+ "examples/minifygenericwithinner:keep-rules.txt:*:NONE",
// No prebuild DEX files for AndroidN
- "examplesAndroidN/shaking:keep-rules.txt:DEX:NONE",
- "examplesAndroidN/shaking:keep-rules.txt:DEX:JAVA",
- "examplesAndroidN/shaking:keep-rules.txt:DEX:AGGRESSIVE"
+ "examplesAndroidN/shaking:keep-rules.txt:DEX:DEX:*",
+ // TODO(b/75997473): No inlining in R8/CF yet
+ "examples/inlining:keep-rules-discard.txt:JAR:CF:*",
+ "examples/inlining:keep-rules.txt:JAR:CF:*"
);
private static Set<String> SKIPPED = Collections.emptySet();
private final MinifyMode minify;
+ private Path proguardMap;
+ private Path out;
private enum Frontend {
DEX, JAR
-
}
- private final Frontend kind;
+
+ private enum Backend {
+ DEX, CF
+ }
+
+ private final Frontend frontend;
+ private final Backend backend;
private final String originalDex;
private final String programFile;
private final String mainClass;
@@ -105,13 +108,14 @@
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
- public TreeShakingTest(String test, Frontend kind, String mainClass, List<String> keepRulesFiles,
- MinifyMode minify, Consumer<DexInspector> inspection,
+ public TreeShakingTest(String test, Frontend frontend, Backend backend, String mainClass,
+ List<String> keepRulesFiles, MinifyMode minify, Consumer<DexInspector> inspection,
BiConsumer<String, String> outputComparator,
BiConsumer<DexInspector, DexInspector> dexComparator) {
- this.kind = kind;
+ this.frontend = frontend;
+ this.backend = backend;
originalDex = ToolHelper.TESTS_BUILD_DIR + test + "/classes.dex";
- if (kind == Frontend.DEX) {
+ if (frontend == Frontend.DEX) {
this.programFile = originalDex;
} else {
this.programFile = ToolHelper.TESTS_BUILD_DIR + test + ".jar";
@@ -122,12 +126,23 @@
this.minify = minify;
this.outputComparator = outputComparator;
this.dexComparator = dexComparator;
+ if (backend == Backend.CF) {
+ JAR_LIBRARIES =
+ ImmutableList.of(
+ Paths.get(ToolHelper.JAVA_8_RUNTIME),
+ Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar"));
+ } else {
+ JAR_LIBRARIES =
+ ImmutableList.of(
+ ANDROID_JAR, Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar"));
+ }
}
@Before
public void generateTreeShakedVersion() throws Exception {
+ out = temp.getRoot().toPath().resolve("out.zip");
+ proguardMap = temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE);
// Generate R8 processed version without library option.
- Path out = temp.getRoot().toPath();
boolean inline = programFile.contains("inlining");
R8Command.Builder builder =
@@ -135,15 +150,24 @@
R8Command.builder(),
pgConfig -> {
pgConfig.setPrintMapping(true);
- pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE));
+ pgConfig.setPrintMappingFile(proguardMap);
pgConfig.setOverloadAggressively(minify == MinifyMode.AGGRESSIVE);
if (!minify.isMinify()) {
pgConfig.disableObfuscation();
}
})
- .setOutput(out, OutputMode.DexIndexed)
.addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
.addLibraryFiles(JAR_LIBRARIES);
+ switch (backend) {
+ case CF:
+ builder.setOutput(out, OutputMode.ClassFile);
+ break;
+ case DEX:
+ builder.setOutput(out, OutputMode.DexIndexed);
+ break;
+ default:
+ throw new Unreachable();
+ }
ToolHelper.getAppBuilder(builder).addProgramFiles(Paths.get(programFile));
ToolHelper.runR8(builder.build(), options -> options.enableInlining = inline);
}
@@ -364,6 +388,10 @@
ClassSubject mainClass = inspector.clazz("shaking13.Shaking");
MethodSubject testMethod = mainClass.method("void", "fieldTest", Collections.emptyList());
Assert.assertTrue(testMethod.isPresent());
+ if (testMethod.getMethod().getCode().isJarCode()) {
+ // TODO(mathiasr): Implement iterateInstructions() for JarCode/CfCode
+ return;
+ }
Iterator<FieldAccessInstructionSubject> iterator =
testMethod.iterateInstructions(InstructionSubject::isFieldAccess);
Assert.assertTrue(iterator.hasNext() && iterator.next().holder().is("shakinglib.LibraryClass"));
@@ -617,7 +645,7 @@
clazz.field(signature.type, signature.name).isPresent());
}
- @Parameters(name = "dex: {0} frontend: {1} keep: {3} minify: {4}")
+ @Parameters(name = "dex:{0} mode:{1}-{2} keep:{4} minify:{5}")
public static Collection<Object[]> data() {
List<String> tests = Arrays
.asList(
@@ -837,21 +865,38 @@
return;
}
for (MinifyMode mode : MinifyMode.values()) {
- addTestCase(testCases, test, Frontend.JAR, mainClass, keepName, keepList, mode, inspection,
- outputComparator, dexComparator);
- addTestCase(testCases, test, Frontend.DEX, mainClass, keepName, keepList, mode, inspection,
- outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.JAR, Backend.CF, mainClass, keepName, keepList, mode,
+ inspection, outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.JAR, Backend.DEX, mainClass, keepName, keepList, mode,
+ inspection, outputComparator, dexComparator);
+ addTestCase(testCases, test, Frontend.DEX, Backend.DEX, mainClass, keepName, keepList, mode,
+ inspection, outputComparator, dexComparator);
}
}
- private static void addTestCase(List<Object[]> testCases, String test, Frontend kind,
- String mainClass, String keepName, List<String> keepList, MinifyMode minify,
+ private static void addTestCase(List<Object[]> testCases, String test, Frontend frontend,
+ Backend backend, String mainClass, String keepName, List<String> keepList, MinifyMode minify,
Consumer<DexInspector> inspection, BiConsumer<String, String> outputComparator,
BiConsumer<DexInspector, DexInspector> dexComparator) {
+ String full = test + ":" + keepName + ":" + frontend + ":" + backend + ":" + minify;
+ String anyKind = test + ":" + keepName + ":*:" + minify;
+ String anyMinify = test + ":" + keepName + ":" + frontend + ":" + backend + ":*";
if (!IGNORED_FLAGS.contains(test + ":" + keepName)
- && !IGNORED.contains(test + ":" + keepName + ":" + kind + ":" + minify)) {
- testCases.add(new Object[]{
- test, kind, mainClass, keepList, minify, inspection, outputComparator, dexComparator});
+ && !IGNORED.contains(full)
+ && !IGNORED.contains(anyKind)
+ && !IGNORED.contains(anyMinify)) {
+ testCases.add(
+ new Object[] {
+ test,
+ frontend,
+ backend,
+ mainClass,
+ keepList,
+ minify,
+ inspection,
+ outputComparator,
+ dexComparator
+ });
}
}
@@ -888,11 +933,32 @@
@Test
public void treeShakingTest() throws IOException, InterruptedException, ExecutionException {
+ if (backend == Backend.CF) {
+ Path shakinglib = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "shakinglib.jar");
+ ProcessResult resultInput =
+ ToolHelper.runJava(Arrays.asList(Paths.get(programFile), shakinglib), mainClass);
+ Assert.assertEquals(0, resultInput.exitCode);
+ ProcessResult resultOutput =
+ ToolHelper.runJava(Arrays.asList(out, shakinglib), mainClass);
+ if (outputComparator != null) {
+ outputComparator.accept(resultInput.stdout, resultOutput.stdout);
+ } else {
+ Assert.assertEquals(resultInput.toString(), resultOutput.toString());
+ }
+ if (inspection != null) {
+ DexInspector inspector =
+ new DexInspector(
+ out,
+ minify.isMinify()
+ ? proguardMap.toString()
+ : null);
+ inspection.accept(inspector);
+ }
+ return;
+ }
if (!ToolHelper.artSupported()) {
return;
}
- String out = temp.getRoot().getCanonicalPath();
- Path generated = Paths.get(out, "classes.dex");
Consumer<ArtCommandBuilder> extraArtArgs = builder -> {
builder.appendClasspath(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib/classes.dex");
};
@@ -902,19 +968,18 @@
String output1 = ToolHelper.runArtNoVerificationErrors(
Collections.singletonList(originalDex), mainClass, extraArtArgs, null);
String output2 = ToolHelper.runArtNoVerificationErrors(
- Collections.singletonList(generated.toString()), mainClass, extraArtArgs, null);
+ Collections.singletonList(out.toString()), mainClass, extraArtArgs, null);
outputComparator.accept(output1, output2);
} else {
ToolHelper.checkArtOutputIdentical(Collections.singletonList(originalDex),
- Collections.singletonList(generated.toString()), mainClass,
+ Collections.singletonList(out.toString()), mainClass,
extraArtArgs, null);
}
if (dexComparator != null) {
DexInspector ref = new DexInspector(Paths.get(originalDex));
- DexInspector inspector = new DexInspector(generated,
- minify.isMinify() ? temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE)
- .toString()
+ DexInspector inspector = new DexInspector(out,
+ minify.isMinify() ? proguardMap.toString()
: null);
dexComparator.accept(ref, inspector);
}
@@ -922,12 +987,12 @@
Assert.assertNull(outputComparator);
Assert.assertNull(dexComparator);
ToolHelper.runArtNoVerificationErrors(
- Collections.singletonList(generated.toString()), mainClass, extraArtArgs, null);
+ Collections.singletonList(out.toString()), mainClass, extraArtArgs, null);
}
if (inspection != null) {
- DexInspector inspector = new DexInspector(generated,
- minify.isMinify() ? temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE).toString()
+ DexInspector inspector = new DexInspector(out,
+ minify.isMinify() ? proguardMap.toString()
: null);
inspection.accept(inspector);
}
diff --git a/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/Lambdas.java b/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/Lambdas.java
index 69e9a78..2352efd 100644
--- a/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/Lambdas.java
+++ b/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/Lambdas.java
@@ -2,6 +2,8 @@
// 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 lambdas_jstyle_trivial;
+
public class Lambdas {
public interface IntConsumer {
void put(int value);
diff --git a/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/inner.kt b/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/inner/inner.kt
similarity index 85%
rename from src/test/kotlinR8TestResources/lambdas_jstyle_trivial/inner.kt
rename to src/test/kotlinR8TestResources/lambdas_jstyle_trivial/inner/inner.kt
index 5da8884..b0afcc3 100644
--- a/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/inner.kt
+++ b/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/inner/inner.kt
@@ -2,11 +2,11 @@
// 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 lambdas.jstyle.trivial.inner
+package lambdas_jstyle_trivial.inner
-import Lambdas
-import lambdas.jstyle.trivial.next
-import lambdas.jstyle.trivial.nextInt
+import lambdas_jstyle_trivial.Lambdas
+import lambdas_jstyle_trivial.next
+import lambdas_jstyle_trivial.nextInt
fun testInner() {
testInner1(nextInt(), nextInt(), nextInt(), nextInt())
diff --git a/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/main.kt b/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/main.kt
index 39673af..6cdae60 100644
--- a/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/main.kt
+++ b/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/main.kt
@@ -2,10 +2,10 @@
// 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 lambdas.jstyle.trivial
+package lambdas_jstyle_trivial
-import Lambdas
-import lambdas.jstyle.trivial.inner.testInner
+import lambdas_jstyle_trivial.Lambdas
+import lambdas_jstyle_trivial.inner.testInner
private var COUNT = 0
diff --git a/src/test/kotlinR8TestResources/lambdas_kstyle_captures/main.kt b/src/test/kotlinR8TestResources/lambdas_kstyle_captures/main.kt
index 3ee0bf8..62727bd 100644
--- a/src/test/kotlinR8TestResources/lambdas_kstyle_captures/main.kt
+++ b/src/test/kotlinR8TestResources/lambdas_kstyle_captures/main.kt
@@ -2,7 +2,7 @@
// 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 lambdas.kstyle.captures
+package lambdas_kstyle_captures
fun consume(l: () -> String) = l()
diff --git a/src/test/kotlinR8TestResources/lambdas_kstyle_generics/main.kt b/src/test/kotlinR8TestResources/lambdas_kstyle_generics/main.kt
index 7fb22eb..e2d044d 100644
--- a/src/test/kotlinR8TestResources/lambdas_kstyle_generics/main.kt
+++ b/src/test/kotlinR8TestResources/lambdas_kstyle_generics/main.kt
@@ -2,7 +2,7 @@
// 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 lambdas.kstyle.generics
+package lambdas_kstyle_generics
private var COUNT = 11
diff --git a/src/test/kotlinR8TestResources/lambdas_kstyle_trivial/inner.kt b/src/test/kotlinR8TestResources/lambdas_kstyle_trivial/inner/inner.kt
similarity index 79%
rename from src/test/kotlinR8TestResources/lambdas_kstyle_trivial/inner.kt
rename to src/test/kotlinR8TestResources/lambdas_kstyle_trivial/inner/inner.kt
index 00587a9..c22a3b8 100644
--- a/src/test/kotlinR8TestResources/lambdas_kstyle_trivial/inner.kt
+++ b/src/test/kotlinR8TestResources/lambdas_kstyle_trivial/inner/inner.kt
@@ -2,11 +2,11 @@
// 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 lambdas.kstyle.trivial.inner
+package lambdas_kstyle_trivial.inner
-import lambdas.kstyle.trivial.consumeEmpty
-import lambdas.kstyle.trivial.consumeOne
-import lambdas.kstyle.trivial.consumeTwo
+import lambdas_kstyle_trivial.consumeEmpty
+import lambdas_kstyle_trivial.consumeOne
+import lambdas_kstyle_trivial.consumeTwo
fun testInner() {
testInnerStateless()
diff --git a/src/test/kotlinR8TestResources/lambdas_kstyle_trivial/main.kt b/src/test/kotlinR8TestResources/lambdas_kstyle_trivial/main.kt
index 34ba194..42cce37 100644
--- a/src/test/kotlinR8TestResources/lambdas_kstyle_trivial/main.kt
+++ b/src/test/kotlinR8TestResources/lambdas_kstyle_trivial/main.kt
@@ -2,9 +2,9 @@
// 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 lambdas.kstyle.trivial
+package lambdas_kstyle_trivial
-import lambdas.kstyle.trivial.inner.testInner
+import lambdas_kstyle_trivial.inner.testInner
private var COUNT = 11
diff --git a/src/test/kotlinR8TestResources/lambdas_singleton/Lambdas.java b/src/test/kotlinR8TestResources/lambdas_singleton/Lambdas.java
deleted file mode 100644
index 4140293..0000000
--- a/src/test/kotlinR8TestResources/lambdas_singleton/Lambdas.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2018, 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.
-
-public class Lambdas {
- public interface Function<R, P1, P2> {
- R get(P1 a, P2 b);
- }
-
- public synchronized static <R, P1, P2> void accept(Function<R, P1, P2> s, P1 a, P2 b) {
- System.out.println(s.get(a, b));
- }
-}
diff --git a/src/test/kotlinR8TestResources/lambdas_singleton/main.kt b/src/test/kotlinR8TestResources/lambdas_singleton/main.kt
index 8e34a0c..f06904a 100644
--- a/src/test/kotlinR8TestResources/lambdas_singleton/main.kt
+++ b/src/test/kotlinR8TestResources/lambdas_singleton/main.kt
@@ -2,7 +2,7 @@
// 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 lambdas.singleton
+package lambdas_singleton
private var COUNT = 0
diff --git a/src/test/sampleApks/simple/AndroidManifest.xml b/src/test/sampleApks/simple/AndroidManifest.xml
new file mode 100644
index 0000000..f959dbd
--- /dev/null
+++ b/src/test/sampleApks/simple/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2018, 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tools.r8.sample.simple"
+ android:versionCode="1"
+ android:versionName="0.1" >
+
+ <uses-sdk android:minSdkVersion="21" />
+
+ <application
+ android:icon="@drawable/icon"
+ android:label="@string/app_name" >
+ <activity
+ android:name=".R8Activity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/src/test/sampleApks/simple/assets/README.txt b/src/test/sampleApks/simple/assets/README.txt
new file mode 100644
index 0000000..fdf73af
--- /dev/null
+++ b/src/test/sampleApks/simple/assets/README.txt
@@ -0,0 +1 @@
+Sample app from R8 project
diff --git a/src/test/sampleApks/simple/res/drawable-mdpi/icon.png b/src/test/sampleApks/simple/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..0799d58
--- /dev/null
+++ b/src/test/sampleApks/simple/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/src/test/sampleApks/simple/res/layout/main.xml b/src/test/sampleApks/simple/res/layout/main.xml
new file mode 100644
index 0000000..7859435
--- /dev/null
+++ b/src/test/sampleApks/simple/res/layout/main.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2018, 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="fill_parent"
+ android:layout_height="fill_parent" android:id="@+id/MainLayout"
+ android:background="@android:color/background_light">
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent" android:id="@+id/PressButton"
+ android:layout_margin="2dip"
+ android:text="Do something"/>
+</LinearLayout>
diff --git a/src/test/sampleApks/simple/res/values/strings.xml b/src/test/sampleApks/simple/res/values/strings.xml
new file mode 100644
index 0000000..8bae26c
--- /dev/null
+++ b/src/test/sampleApks/simple/res/values/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2018, 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.
+-->
+<resources>
+ <string name="app_name">R8 simple app</string>
+</resources>
diff --git a/src/test/sampleApks/simple/src/com/android/tools/r8/sample/simple/R8Activity.java b/src/test/sampleApks/simple/src/com/android/tools/r8/sample/simple/R8Activity.java
new file mode 100644
index 0000000..4a09fd4
--- /dev/null
+++ b/src/test/sampleApks/simple/src/com/android/tools/r8/sample/simple/R8Activity.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2018, 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.sample.simple;
+
+import android.app.Activity;
+import android.os.Bundle;
+import com.android.tools.r8.sample.simple.R;
+
+public class R8Activity extends Activity {
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTheme(android.R.style.Theme_Light);
+ setContentView(R.layout.main);
+ }
+}
diff --git a/third_party/desugar/desugar_20180308.tar.gz.sha1 b/third_party/desugar/desugar_20180308.tar.gz.sha1
new file mode 100644
index 0000000..bc15c22
--- /dev/null
+++ b/third_party/desugar/desugar_20180308.tar.gz.sha1
@@ -0,0 +1 @@
+e31fb39c34a94592056e878462f086ffb922d1d0
\ No newline at end of file
diff --git a/third_party/openjdk/openjdk-rt-1.8.tar.gz.sha1 b/third_party/openjdk/openjdk-rt-1.8.tar.gz.sha1
new file mode 100644
index 0000000..47ebc7c
--- /dev/null
+++ b/third_party/openjdk/openjdk-rt-1.8.tar.gz.sha1
@@ -0,0 +1 @@
+f2a21bbd0efa0d78617b8f4f4ae42ac0bcc4ec4c
\ No newline at end of file
diff --git a/tools/apk-masseur.py b/tools/apk-masseur.py
index 8e98e47..4f24f0f 100755
--- a/tools/apk-masseur.py
+++ b/tools/apk-masseur.py
@@ -3,6 +3,7 @@
# 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.
+import apk_utils
import glob
import optparse
import os
@@ -68,23 +69,8 @@
return processed_apk
def sign(unsigned_apk, keystore, temp):
- print 'Signing (ignore the warnings)'
- cmd = ['zip', '-d', unsigned_apk, 'META-INF/*']
- utils.PrintCmd(cmd)
- subprocess.call(cmd)
signed_apk = os.path.join(temp, 'unaligned.apk')
- cmd = [
- 'jarsigner',
- '-sigalg', 'SHA1withRSA',
- '-digestalg', 'SHA1',
- '-keystore', keystore,
- '-storepass', 'android',
- '-signedjar', signed_apk,
- unsigned_apk,
- 'androiddebugkey'
- ]
- utils.PrintCmd(cmd)
- subprocess.check_call(cmd)
+ apk_utils.sign(unsigned_apk, signed_apk, keystore)
return signed_apk
def align(signed_apk, temp):
diff --git a/tools/apk_utils.py b/tools/apk_utils.py
new file mode 100644
index 0000000..a4c0471
--- /dev/null
+++ b/tools/apk_utils.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, 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.
+
+import subprocess
+import utils
+
+def sign(unsigned_apk, signed_apk, keystore):
+ print 'Signing (ignore the warnings)'
+ cmd = ['zip', '-d', unsigned_apk, 'META-INF/*']
+ utils.PrintCmd(cmd)
+ subprocess.call(cmd)
+ cmd = [
+ 'jarsigner',
+ '-sigalg', 'SHA1withRSA',
+ '-digestalg', 'SHA1',
+ '-keystore', keystore,
+ '-storepass', 'android',
+ '-signedjar', signed_apk,
+ unsigned_apk,
+ 'androiddebugkey'
+ ]
+ utils.PrintCmd(cmd)
+ subprocess.check_call(cmd)
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
new file mode 100755
index 0000000..5d691d9
--- /dev/null
+++ b/tools/build_sample_apk.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, 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.
+
+# Script for building sample apks using the sdk tools directly.
+
+import apk_utils
+import fnmatch
+import glob
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import utils
+
+ANDROID_JAR = 'third_party/android_jar/lib-v{api}/android.jar'
+DEFAULT_AAPT = 'aapt' # Assume in path.
+DEFAULT_D8 = os.path.join(utils.REPO_ROOT, 'tools', 'd8.py')
+DEFAULT_JAVAC = 'javac'
+SRC_LOCATION = 'src/com/android/tools/r8/sample/{app}/*.java'
+DEFAULT_KEYSTORE = os.path.join(os.getenv('HOME'), '.android', 'debug.keystore')
+
+SAMPLE_APKS = [
+ 'simple'
+]
+
+def parse_options():
+ result = optparse.OptionParser()
+ result.add_option('--aapt',
+ help='aapt executable to use',
+ default=DEFAULT_AAPT)
+ result.add_option('--api',
+ help='Android api level',
+ default=21,
+ choices=[14, 15, 19, 21, 22, 23, 24, 25, 26])
+ result.add_option('--keystore',
+ help='Keystore used for signing',
+ default=DEFAULT_KEYSTORE)
+ result.add_option('--app',
+ help='Which app to build',
+ default='simple',
+ choices=SAMPLE_APKS)
+ return result.parse_args()
+
+def run_aapt(aapt, args):
+ command = [aapt]
+ command.extend(args)
+ utils.PrintCmd(command)
+ subprocess.check_call(command)
+
+def get_build_dir(app):
+ return os.path.join(utils.BUILD, 'sampleApks', app)
+
+def get_gen_path(app):
+ gen_path = os.path.join(get_build_dir(app), 'gen')
+ utils.makedirs_if_needed(gen_path)
+ return gen_path
+
+def get_bin_path(app):
+ bin_path = os.path.join(get_build_dir(app), 'bin')
+ utils.makedirs_if_needed(bin_path)
+ return bin_path
+
+def get_android_jar(api):
+ return os.path.join(utils.REPO_ROOT, ANDROID_JAR.format(api=api))
+
+def get_sample_dir(app):
+ return os.path.join(utils.REPO_ROOT, 'src', 'test', 'sampleApks', app)
+
+def get_src_path(app):
+ return os.path.join(get_sample_dir(app), 'src')
+
+def run_aapt_pack(aapt, api, app):
+ with utils.ChangedWorkingDirectory(get_sample_dir(app)):
+ args = ['package',
+ '-v', '-f',
+ '-I', get_android_jar(api),
+ '-M', 'AndroidManifest.xml',
+ '-A', 'assets',
+ '-S', 'res',
+ '-m',
+ '-J', get_gen_path(app),
+ '-F', os.path.join(get_bin_path(app), 'resources.ap_')]
+ run_aapt(aapt, args)
+
+def compile_with_javac(api, app):
+ with utils.ChangedWorkingDirectory(get_sample_dir(app)):
+ files = glob.glob(SRC_LOCATION.format(app=app))
+ command = [DEFAULT_JAVAC,
+ '-classpath', get_android_jar(api),
+ '-sourcepath', '%s:%s' % (get_src_path(app), get_gen_path(app)),
+ '-d', get_bin_path(app)]
+ command.extend(files)
+ utils.PrintCmd(command)
+ subprocess.check_call(command)
+
+def dex(app, api):
+ files = []
+ for root, dirnames, filenames in os.walk(get_bin_path(app)):
+ for filename in fnmatch.filter(filenames, '*.class'):
+ files.append(os.path.join(root, filename))
+ command = [DEFAULT_D8,
+ '--output', get_bin_path(app),
+ '--classpath', get_android_jar(api),
+ '--min-api', str(api)]
+ command.extend(files)
+ utils.PrintCmd(command)
+ subprocess.check_call(command)
+
+def create_temp_apk(app):
+ temp_apk_path = os.path.join(get_bin_path(app), '%s.ap_' % app)
+ shutil.move(os.path.join(get_bin_path(app), 'resources.ap_'),
+ temp_apk_path)
+ return temp_apk_path
+
+def aapt_add_dex(aapt, app, temp_apk_path):
+ args = ['add',
+ '-k', temp_apk_path,
+ os.path.join(get_bin_path(app), 'classes.dex')]
+ run_aapt(aapt, args)
+
+def Main():
+ (options, args) = parse_options()
+ run_aapt_pack(options.aapt, options.api, options.app)
+ compile_with_javac(options.api, options.app)
+ dex(options.app, options.api)
+ temp_apk_path = create_temp_apk(options.app)
+ aapt_add_dex(options.aapt, options.app, temp_apk_path)
+ apk_path = os.path.join(get_bin_path(options.app), '%s.apk' % options.app)
+ apk_utils.sign(temp_apk_path, apk_path, options.keystore)
+ print('Apk available at: %s' % apk_path)
+
+if __name__ == '__main__':
+ sys.exit(Main())