Merge "Add more ifz tests using Kotlin !! and ?. operators."
diff --git a/build.gradle b/build.gradle
index 759e475..33605a1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -63,7 +63,6 @@
}
}
dependencies {
- classpath 'com.cookpad.android.licensetools:license-tools-plugin:0.23.0'
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
classpath "net.ltgt.gradle:gradle-apt-plugin:0.12"
@@ -80,7 +79,6 @@
apply plugin: 'java'
apply plugin: 'idea'
-apply plugin: 'com.cookpad.android.licensetools'
apply plugin: 'net.ltgt.errorprone-base'
apply plugin: "net.ltgt.apt"
@@ -210,24 +208,24 @@
}
dependencies {
- compile "net.sf.jopt-simple:jopt-simple:$joptSimpleVersion"
- compile "com.google.code.gson:gson:$gsonVersion"
+ implementation "net.sf.jopt-simple:jopt-simple:$joptSimpleVersion"
+ implementation "com.google.code.gson:gson:$gsonVersion"
// Include all of guava when compiling the code, but exclude annotations that we don't
// need from the packaging.
compileOnly("com.google.guava:guava:$guavaVersion")
- compile("com.google.guava:guava:$guavaVersion", {
+ implementation("com.google.guava:guava:$guavaVersion", {
exclude group: 'com.google.errorprone'
exclude group: 'com.google.code.findbugs'
exclude group: 'com.google.j2objc'
exclude group: 'org.codehaus.mojo'
})
- compile group: 'it.unimi.dsi', name: 'fastutil', version: fastutilVersion
- compile "org.jetbrains.kotlinx:kotlinx-metadata-jvm:$kotlinExtMetadataJVMVersion"
- compile group: 'org.ow2.asm', name: 'asm', version: asmVersion
- compile group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion
- compile group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
- compile group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
- compile group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
+ implementation group: 'it.unimi.dsi', name: 'fastutil', version: fastutilVersion
+ implementation "org.jetbrains.kotlinx:kotlinx-metadata-jvm:$kotlinExtMetadataJVMVersion"
+ implementation group: 'org.ow2.asm', name: 'asm', version: asmVersion
+ implementation group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion
+ implementation group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
+ implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
+ implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
testCompile sourceSets.examples.output
testCompile "junit:junit:$junitVersion"
testCompile group: 'org.smali', name: 'smali', version: smaliVersion
@@ -253,10 +251,6 @@
apt "com.google.auto.value:auto-value:$autoValueVersion"
}
-licenseTools {
- licensesYaml = file('LIBRARY-LICENSE')
-}
-
def osString = OperatingSystem.current().isLinux() ? "linux" :
OperatingSystem.current().isMacOsX() ? "mac" : "windows"
@@ -458,13 +452,27 @@
}
task consolidatedLicense {
- // checkLicenses verifies that the list of libraries referenced in 'LIBRARY-LICENSE' is
- // corresponding to the effective list of embedded libraries.
- dependsOn 'checkLicenses'
def license = new File(new File(buildDir, 'generatedLicense'), 'LICENSE')
+
inputs.files files('LICENSE', 'LIBRARY-LICENSE') + fileTree(dir: 'library-licensing')
+ def runtimeClasspath = configurations.findByName("runtimeClasspath")
+ inputs.files { runtimeClasspath.getResolvedConfiguration().files }
+
outputs.files license
+
doLast {
+ def dependencies = []
+ runtimeClasspath.resolvedConfiguration.resolvedArtifacts.each {
+ def identifier = (ModuleComponentIdentifier) it.id.componentIdentifier
+ dependencies.add("${identifier.group}:${identifier.module}")
+ }
+ def libraryLicenses = file('LIBRARY-LICENSE').text
+ dependencies.each {
+ if (!libraryLicenses.contains("- artifact: $it")) {
+ throw new GradleException("No license for $it in LIBRARY_LICENSE")
+ }
+ }
+
license.getParentFile().mkdirs()
license.createNewFile()
license.text = "This file lists all licenses for code distributed.\n"
@@ -476,7 +484,7 @@
license.text += "\n"
license.text += "Summary of distributed libraries:\n"
license.text += "\n"
- license.text += file('LIBRARY-LICENSE').text
+ license.text += libraryLicenses
license.text += "\n"
license.text += "\n"
license.text += "Licenses details:\n"
@@ -511,7 +519,7 @@
}
task repackageDeps(type: ShadowJar) {
- configurations = [project.configurations.compile]
+ configurations = [project.configurations.runtimeClasspath]
mergeServiceFiles(it)
if (!project.hasProperty('lib_no_relocate')) {
configureRelocations(it)
@@ -522,7 +530,7 @@
}
task repackageDepsForLib(type: ShadowJar) {
- configurations = [project.configurations.compile]
+ configurations = [project.configurations.runtimeClasspath]
mergeServiceFiles(it)
configureRelocations(it)
exclude { it.getRelativePath().getPathString() == "module-info.class" }
@@ -569,6 +577,21 @@
}
}
+task R8NoManifest(type: ShadowJar) {
+ from consolidatedLicense.outputs.files
+ baseName 'r8nomanifest'
+ classifier = null
+ version = null
+ // In order to build without dependencies, pass the exclude_deps property using:
+ // gradle -Pexclude_deps R8
+ if (!project.hasProperty('exclude_deps')) {
+ from repackageSources.outputs.files
+ from repackageDeps.outputs.files
+ } else {
+ from sourceSets.main.output
+ }
+}
+
task D8(type: ShadowJar) {
from R8.outputs.files
baseName 'd8'
@@ -599,16 +622,17 @@
input,
"--lib", "third_party/openjdk/openjdk-rt-1.8/rt.jar",
"--output", output,
- "--pg-conf", pgconf]
+ "--pg-conf", pgconf,
+ "--pg-map-output", output + ".map"]
}
task R8Lib(type: Exec) {
def pgconf = "src/main/keep.txt"
def output = "build/libs/r8lib.jar"
- inputs.files files(pgconf, R8.outputs)
+ inputs.files files(pgconf, R8.outputs, R8NoManifest.outputs)
outputs.file output
dependsOn downloadOpenJDKrt
- commandLine r8CfCommandLine(R8.outputs.files[0], output, pgconf)
+ commandLine r8CfCommandLine(R8NoManifest.outputs.files[0], output, pgconf)
workingDir = projectDir
}
@@ -618,7 +642,7 @@
inputs.files files(pgconf, CompatDx.outputs, R8.outputs)
outputs.file output
dependsOn downloadOpenJDKrt
- commandLine r8CfCommandLine(R8.outputs.files[0], output, pgconf)
+ commandLine r8CfCommandLine(CompatDx.outputs.files[0], output, pgconf)
workingDir = projectDir
}
@@ -628,7 +652,7 @@
inputs.files files(pgconf, CompatProguard.outputs, R8.outputs)
outputs.file output
dependsOn downloadOpenJDKrt
- commandLine r8CfCommandLine(R8.outputs.files[0], output, pgconf)
+ commandLine r8CfCommandLine(CompatProguard.outputs.files[0], output, pgconf)
workingDir = projectDir
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 40d31d2..be3c93b 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRCode.LiveAtEntrySets;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.Move;
@@ -222,6 +223,15 @@
// and we do not actually want locals information in the output.
if (options.debug) {
computeDebugInfo(blocks);
+ } else if (code.method.getOptimizationInfo().isReachabilitySensitive()) {
+ InstructionIterator it = code.instructionIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ if (instruction.isDebugLocalRead()) {
+ instruction.clearDebugValues();
+ it.remove();
+ }
+ }
}
clearUserInfo();
clearState();
@@ -372,11 +382,11 @@
}
// Remove the end markers now that local liveness is computed.
instruction.clearDebugValues();
- if (instruction.isDebugLocalRead()) {
- Instruction prev = instructionIterator.previous();
- assert prev == instruction;
- instructionIterator.remove();
- }
+ }
+ if (instruction.isDebugLocalRead()) {
+ Instruction prev = instructionIterator.previous();
+ assert prev == instruction;
+ instructionIterator.remove();
}
Instruction nextInstruction = instructionIterator.peekNext();
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index f95e666..9c9a977 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.debug.CfDebugTestConfig;
import com.android.tools.r8.debug.DebugTestConfig;
@@ -124,20 +125,7 @@
@Override
public JvmTestBuilder addProgramClasses(Collection<Class<?>> classes) {
- // Adding a collection of classes will build a jar of exactly those classes so that no other
- // classes are made available via a too broad classpath directory.
- List<ProgramResource> resources = ListUtils.map(classes, ClassFileResource::new);
- AndroidApp build = AndroidApp.builder()
- .addProgramResourceProvider(new ClassFileResourceProvider(resources)).build();
- Path out;
- try {
- out = getState().getNewTempFolder().resolve("out.zip");
- build.writeToZip(out, OutputMode.ClassFile);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- classpath.add(out);
- builder.addProgramFiles(out);
+ addProgramResources(ListUtils.map(classes, ClassFileResource::new));
return self();
}
@@ -149,8 +137,14 @@
@Override
public JvmTestBuilder addProgramClassFileData(Collection<byte[]> files) {
- throw new Unimplemented(
- "No support for adding classfile data directly (we need to compute the descriptor)");
+ addProgramResources(
+ ListUtils.map(files, data ->
+ ProgramResource.fromBytes(
+ Origin.unknown(),
+ Kind.CF,
+ data,
+ Collections.singleton(TestBase.extractClassDescriptor(data)))));
+ return self();
}
public JvmTestBuilder addClasspath(Path... paths) {
@@ -168,4 +162,22 @@
public JvmTestBuilder addTestClasspath() {
return addClasspath(ToolHelper.getClassPathForTests());
}
+
+ // Adding a collection of resources will build a jar of exactly those classes so that no other
+ // classes are made available via a too broad classpath directory.
+ private void addProgramResources(List<ProgramResource> resources) {
+ AndroidApp build =
+ AndroidApp.builder()
+ .addProgramResourceProvider(new ClassFileResourceProvider(resources))
+ .build();
+ Path out;
+ try {
+ out = getState().getNewTempFolder().resolve("out.zip");
+ build.writeToZip(out, OutputMode.ClassFile);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ classpath.add(out);
+ builder.addProgramFiles(out);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index fa4c41c..109b42d 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -158,6 +158,18 @@
}
@Override
+ public ProguardTestBuilder noTreeShaking() {
+ addKeepRules("-dontshrink");
+ return self();
+ }
+
+ @Override
+ public ProguardTestBuilder noMinification() {
+ addKeepRules("-dontobfuscate");
+ return self();
+ }
+
+ @Override
public ProguardTestBuilder addProgramClasses(Collection<Class<?>> classes) {
// Adding a collection of classes will build a jar of exactly those classes so that no other
// classes are made available via a too broad classpath directory.
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 4404bae..07eea98 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -55,6 +55,18 @@
return new R8TestCompileResult(getState(), backend, app.get(), proguardMapBuilder.toString());
}
+ @Override
+ public R8TestBuilder noTreeShaking() {
+ builder.setDisableTreeShaking(true);
+ return self();
+ }
+
+ @Override
+ public R8TestBuilder noMinification() {
+ builder.setDisableMinification(true);
+ return self();
+ }
+
public R8TestBuilder addDataResources(List<DataEntryResource> resources) {
resources.forEach(builder.getAppBuilder()::addDataResource);
return self();
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index a1a4f9d..2fd4034 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -797,7 +797,15 @@
}
}
- public String extractClassName(byte[] ccc) {
+ public static String extractClassName(byte[] ccc) {
+ return DescriptorUtils.descriptorToJavaType(extractClassDescriptor(ccc));
+ }
+
+ public static String extractClassDescriptor(byte[] ccc) {
+ return "L" + extractClassInternalType(ccc) + ";";
+ }
+
+ private static String extractClassInternalType(byte[] ccc) {
class ClassNameExtractor extends ClassVisitor {
private String className;
@@ -813,13 +821,10 @@
String signature,
String superName,
String[] interfaces) {
- className =
- name.replace(
- DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR,
- DescriptorUtils.JAVA_PACKAGE_SEPARATOR);
+ className = name;
}
- String getClassName() {
+ String getClassInternalType() {
return className;
}
}
@@ -828,7 +833,7 @@
ClassNameExtractor extractor = new ClassNameExtractor();
reader.accept(
extractor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
- return extractor.getClassName();
+ return extractor.getClassInternalType();
}
protected Path writeToJar(List<byte[]> classes) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 2704039..46ae905 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -21,6 +21,10 @@
super(state, builder, backend);
}
+ public abstract T noTreeShaking();
+
+ public abstract T noMinification();
+
public abstract T addKeepRules(Collection<String> rules);
public T addKeepRules(String... rules) {
@@ -53,4 +57,5 @@
" public static void main(java.lang.String[]);",
"}"));
}
+
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTest.java
new file mode 100644
index 0000000..1406a48
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTest.java
@@ -0,0 +1,19 @@
+// 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.ir.optimize.reflection;
+// NOTE: This file is just used to create the initial dump in InnerClassNameTestDump.
+class OuterClass {
+ static class InnerClass {}
+}
+
+public class InnerClassNameTest {
+
+ public static void main(String[] args) {
+ Class<?> test = OuterClass.InnerClass.class;
+ System.out.println("getName: " + test.getName());
+ System.out.println("getTypeName: " + test.getTypeName());
+ System.out.println("getCanonicalName: " + test.getCanonicalName());
+ System.out.println("getSimpleName: " + test.getSimpleName());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestDump.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestDump.java
new file mode 100644
index 0000000..cf1dd95
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestDump.java
@@ -0,0 +1,276 @@
+// 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.ir.optimize.reflection;
+
+import com.android.tools.r8.ir.optimize.reflection.InnerClassNameTestRunner.TestNamingConfig;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class InnerClassNameTestDump implements Opcodes {
+
+ private static void writeInnerClass(TestNamingConfig config, ClassWriter classWriter) {
+ classWriter.visitInnerClass(
+ config.getInnerInternalType(),
+ config.getOuterInternalType(),
+ config.getInnerClassName(),
+ ACC_STATIC);
+ }
+
+ public static Collection<byte[]> dump(TestNamingConfig config) {
+ return ImmutableList.of(
+ dumpMainClass(config),
+ dumpOuterClass(config),
+ dumpInnerClass(config));
+ }
+
+ public static byte[] dumpOuterClass(TestNamingConfig config) {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_8, ACC_SUPER, config.getOuterInternalType(), null, "java/lang/Object", null);
+
+ classWriter.visitSource("InnerClassNameTest.java", null);
+
+ writeInnerClass(config, classWriter);
+
+ {
+ methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(6, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable(
+ "this", config.getOuterDescriptor(), null, label0, label1, 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+
+ public static byte[] dumpInnerClass(TestNamingConfig config) {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_8, ACC_SUPER, config.getInnerInternalType(), null, "java/lang/Object", null);
+
+ classWriter.visitSource("InnerClassNameTest.java", null);
+
+ writeInnerClass(config, classWriter);
+
+ {
+ methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(7, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable(
+ "this", config.getInnerDescriptor(), null, label0, label1, 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+
+ public static byte[] dumpMainClass(TestNamingConfig config) {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_8,
+ ACC_PUBLIC | ACC_SUPER,
+ "com/android/tools/r8/ir/optimize/reflection/InnerClassNameTest",
+ null,
+ "java/lang/Object",
+ null);
+
+ classWriter.visitSource("InnerClassNameTest.java", null);
+
+ writeInnerClass(config, classWriter);
+
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(11, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable(
+ "this",
+ "Lcom/android/tools/r8/ir/optimize/reflection/InnerClassNameTest;",
+ null,
+ label0,
+ label1,
+ 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(14, label0);
+ methodVisitor.visitLdcInsn(Type.getType(config.getInnerDescriptor()));
+ methodVisitor.visitVarInsn(ASTORE, 1);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLineNumber(15, label1);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+ methodVisitor.visitLdcInsn("getName: ");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+ if (config.isGetTypeNameSupported()) {
+ Label label2 = new Label();
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitLineNumber(16, label2);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+ methodVisitor.visitLdcInsn("getTypeName: ");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/Class", "getTypeName", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+ }
+ Label label3 = new Label();
+ methodVisitor.visitLabel(label3);
+ methodVisitor.visitLineNumber(17, label3);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+ methodVisitor.visitLdcInsn("getCanonicalName: ");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/Class", "getCanonicalName", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+ Label label4 = new Label();
+ methodVisitor.visitLabel(label4);
+ methodVisitor.visitLineNumber(18, label4);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(
+ INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
+ methodVisitor.visitLdcInsn("getSimpleName: ");
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/Class", "getSimpleName", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/StringBuilder",
+ "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+ Label label5 = new Label();
+ methodVisitor.visitLabel(label5);
+ methodVisitor.visitLineNumber(19, label5);
+ methodVisitor.visitInsn(RETURN);
+ Label label6 = new Label();
+ methodVisitor.visitLabel(label6);
+ methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label0, label6, 0);
+ methodVisitor.visitLocalVariable(
+ "test", "Ljava/lang/Class;", "Ljava/lang/Class<*>;", label1, label6, 1);
+ methodVisitor.visitMaxs(3, 2);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
new file mode 100644
index 0000000..8063a7f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
@@ -0,0 +1,240 @@
+// 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.ir.optimize.reflection;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.JvmTestRunResult;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.concurrent.ExecutionException;
+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 InnerClassNameTestRunner extends TestBase {
+
+ private static final Class<?> MAIN_CLASS = InnerClassNameTest.class;
+ public static final String PACKAGE = "com/android/tools/r8/ir/optimize/reflection/";
+
+ public enum TestNamingConfig {
+ DEFAULT,
+ DOLLAR2_SEPARATOR,
+ EMTPY_SEPARATOR,
+ UNDERBAR_SEPARATOR,
+ NON_NESTED_INNER,
+ OUTER_ENDS_WITH_DOLLAR,
+ $_$_$;
+
+ public String getOuterTypeRaw() {
+ switch (this) {
+ case DEFAULT:
+ case DOLLAR2_SEPARATOR:
+ case EMTPY_SEPARATOR:
+ case UNDERBAR_SEPARATOR:
+ case NON_NESTED_INNER:
+ return "OuterClass";
+ case OUTER_ENDS_WITH_DOLLAR:
+ return "OuterClass$";
+ case $_$_$:
+ return "$";
+ }
+ throw new Unreachable();
+ }
+
+ public String getSeparator() {
+ switch (this) {
+ case DEFAULT:
+ case OUTER_ENDS_WITH_DOLLAR:
+ case $_$_$:
+ return "$";
+ case DOLLAR2_SEPARATOR:
+ return "$$";
+ case UNDERBAR_SEPARATOR:
+ return "_";
+ case NON_NESTED_INNER:
+ case EMTPY_SEPARATOR:
+ return "";
+ }
+ throw new Unreachable();
+ }
+
+ public String getInnerClassName() {
+ return this == $_$_$ ? "$" : "InnerClass";
+ }
+
+ public String getInnerTypeRaw() {
+ switch (this) {
+ case DEFAULT:
+ case OUTER_ENDS_WITH_DOLLAR:
+ case DOLLAR2_SEPARATOR:
+ case EMTPY_SEPARATOR:
+ case UNDERBAR_SEPARATOR:
+ case $_$_$:
+ return getOuterTypeRaw() + getSeparator() + getInnerClassName();
+ case NON_NESTED_INNER:
+ return getInnerClassName();
+ }
+ throw new Unreachable();
+ }
+
+ public String getInnerInternalType() {
+ return PACKAGE + getInnerTypeRaw();
+ }
+
+ public String getOuterInternalType() {
+ return PACKAGE + getOuterTypeRaw();
+ }
+
+ public String getInnerDescriptor() {
+ return "L" + getInnerInternalType() + ";";
+ }
+
+ public String getOuterDescriptor() {
+ return "L" + getOuterInternalType() + ";";
+ }
+
+ public static boolean isGetTypeNameSupported() {
+ return ToolHelper.getDexVm().isNewerThan(DexVm.ART_7_0_0_HOST);
+ }
+ }
+
+ @Parameters(name = "{0} minify:{1} {2}")
+ public static Collection<Object[]> parameters() {
+ return buildParameters(Backend.values(), BooleanUtils.values(), TestNamingConfig.values());
+ }
+
+ private final Backend backend;
+ private final boolean minify;
+ private final TestNamingConfig config;
+
+ public InnerClassNameTestRunner(Backend backend, boolean minify, TestNamingConfig config) {
+ this.backend = backend;
+ this.minify = minify;
+ this.config = config;
+ }
+
+ @Test
+ public void test() throws IOException, CompilationFailedException, ExecutionException {
+ assumeTrue(
+ "b/120185045 The DEX backend does not minify inner-class names",
+ backend != Backend.DEX || !minify);
+
+ JvmTestRunResult runResult =
+ testForJvm().addProgramClassFileData(InnerClassNameTestDump.dump(config)).run(MAIN_CLASS);
+
+ R8TestBuilder r8TestBuilder =
+ testForR8(backend)
+ .addKeepMainRule(MAIN_CLASS)
+ .addKeepRules("-keep,allowobfuscation class * { *; }")
+ .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
+ .addProgramClassFileData(InnerClassNameTestDump.dump(config));
+
+ if (!minify) {
+ r8TestBuilder.noMinification();
+ }
+
+ R8TestCompileResult r8CompileResult = r8TestBuilder.compile();
+ CodeInspector inspector = r8CompileResult.inspector();
+ R8TestRunResult r8RunResult = r8CompileResult.run(MAIN_CLASS);
+ switch (config) {
+ case DEFAULT:
+ case OUTER_ENDS_WITH_DOLLAR:
+ case $_$_$:
+ if (backend == Backend.CF) {
+ runResult.assertSuccessWithOutput(getExpectedNonMinified(config.getInnerClassName()));
+ }
+ if (backend == Backend.CF && minify) {
+ // TODO(b/120639028) R8 does not keep the structure of inner classes.
+ r8RunResult.assertFailureWithErrorThatMatches(containsString("Malformed class name"));
+ } else {
+ r8RunResult.assertSuccessWithOutput(getExpectedMinified(inspector));
+ }
+ break;
+ case DOLLAR2_SEPARATOR:
+ if (backend == Backend.CF && minify) {
+ // TODO(b/120639028) R8 does not keep the structure of inner classes.
+ r8RunResult.assertFailureWithErrorThatMatches(containsString("Malformed class name"));
+ } else if (backend == Backend.CF) {
+ // $$ as separator and InnerClass as name, results in $InnerClass from getSimpleName...
+ String expectedWithDollarOnInnerName =
+ getExpectedNonMinified("$" + config.getInnerClassName());
+ runResult.assertSuccessWithOutput(expectedWithDollarOnInnerName);
+ // When minifying with R8 the classname $InnerName will map to, eg, 'a' and thus the
+ // dollar will not appear. This is coincidental and could be seen as an error, in which
+ // case R8 should map it to 'a' but with '$$' kept as the separator.
+ r8RunResult.assertSuccessWithOutput(
+ minify ? getExpectedMinified(inspector) : expectedWithDollarOnInnerName);
+ } else {
+ // $$ in DEX will not change the InnerName/getSimpleName.
+ r8RunResult.assertSuccessWithOutput(getExpectedMinified(inspector));
+ }
+ break;
+ case EMTPY_SEPARATOR:
+ case UNDERBAR_SEPARATOR:
+ case NON_NESTED_INNER:
+ if (backend == Backend.CF) {
+ // NOTE(b/120597515): These cases should fail, but if they succeed, we have recovered via
+ // minification, likely by not using the same separator from output in input.
+ // Any non-$ separator results in a runtime exception in getCanonicalName.
+ r8RunResult.assertFailureWithErrorThatMatches(containsString("Malformed class name"));
+ } else {
+ assert backend == Backend.DEX;
+ r8RunResult.assertSuccessWithOutput(getExpectedMinified(inspector));
+ }
+ break;
+ default:
+ throw new Unreachable("Unexpected test configuration: " + config);
+ }
+ }
+
+ private static String getExpected(String typeName, String canonicalName, String simpleName) {
+ if (TestNamingConfig.isGetTypeNameSupported()) {
+ return StringUtils.lines(
+ "getName: " + typeName,
+ "getTypeName: " + typeName,
+ "getCanonicalName: " + canonicalName,
+ "getSimpleName: " + simpleName);
+ } else {
+ return StringUtils.lines(
+ "getName: " + typeName,
+ "getCanonicalName: " + canonicalName,
+ "getSimpleName: " + simpleName);
+ }
+ }
+
+ private String getExpectedNonMinified(String innerName) {
+ String outerClassType = DescriptorUtils.descriptorToJavaType(config.getOuterDescriptor());
+ String innerClassType = DescriptorUtils.descriptorToJavaType(config.getInnerDescriptor());
+ return getExpected(innerClassType, outerClassType + "." + innerName, innerName);
+ }
+
+ private String getExpectedMinified(CodeInspector inspector) {
+ String outerClassType = DescriptorUtils.descriptorToJavaType(config.getOuterDescriptor());
+ String innerClassType = DescriptorUtils.descriptorToJavaType(config.getInnerDescriptor());
+ String outerClassTypeFinal = inspector.clazz(outerClassType).getFinalName();
+ String innerClassTypeFinal = inspector.clazz(innerClassType).getFinalName();
+ String innerNameFinal =
+ !minify
+ ? config.getInnerClassName()
+ : innerClassTypeFinal.substring(innerClassTypeFinal.length() - 1);
+ return getExpected(
+ innerClassTypeFinal, outerClassTypeFinal + "." + innerNameFinal, innerNameFinal);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveAndDebugLocalReads.java b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveAndDebugLocalReads.java
new file mode 100644
index 0000000..ff125be
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveAndDebugLocalReads.java
@@ -0,0 +1,180 @@
+// 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.reachabilitysensitive;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class ReachabilitySensitiveAndDebugLocalReads extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ byte[] classdata = Dump.dump();
+ String mainClass = "test.Test";
+ String expected = StringUtils.lines("5");
+ testForJvm()
+ .addProgramClassFileData(classdata)
+ .run(mainClass)
+ .assertSuccessWithOutput(expected);
+
+ testForD8()
+ .release()
+ .addProgramClassFileData(classdata)
+ .run(mainClass)
+ .assertSuccessWithOutput(expected);
+ }
+}
+
+/* ASM Dump of ReachabilitySensitive/TestClassWithAnnotatedMethod + main method:
+<pre>
+ package test;
+ public class Test {
+
+ @ReachabilitySensitive
+ public void unrelatedAnnotatedMethod() {}
+
+ public void method() {
+ int i = 2;
+ int j = i + 1;
+ int k = j + 2;
+ System.out.println(k);
+ }
+
+ public static void main(String[] args) {
+ new Test().method();
+ }
+}
+</pre>
+ */
+class Dump implements Opcodes {
+
+ public static byte[] dump() {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ MethodVisitor methodVisitor;
+ AnnotationVisitor annotationVisitor0;
+
+ classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "test/Test", null, "java/lang/Object", null);
+
+ classWriter.visitSource("Test.java", null);
+
+ {
+ methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(53, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable("this", "Ltest/Test;", null, label0, label1, 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+
+ {
+ methodVisitor =
+ classWriter.visitMethod(ACC_PUBLIC, "unrelatedAnnotatedMethod", "()V", null, null);
+ {
+ annotationVisitor0 =
+ methodVisitor.visitAnnotation(
+ "Ldalvik/annotation/optimization/ReachabilitySensitive;", true);
+ annotationVisitor0.visitEnd();
+ }
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(56, label0);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable("this", "Ltest/Test;", null, label0, label1, 0);
+ methodVisitor.visitMaxs(0, 1);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "method", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(59, label0);
+ methodVisitor.visitInsn(ICONST_2);
+ methodVisitor.visitVarInsn(ISTORE, 1);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLineNumber(60, label1);
+ methodVisitor.visitVarInsn(ILOAD, 1);
+ methodVisitor.visitInsn(ICONST_1);
+ methodVisitor.visitInsn(IADD);
+ methodVisitor.visitVarInsn(ISTORE, 2);
+ Label label2 = new Label();
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitLineNumber(61, label2);
+ methodVisitor.visitVarInsn(ILOAD, 2);
+ methodVisitor.visitInsn(ICONST_2);
+ methodVisitor.visitInsn(IADD);
+ methodVisitor.visitVarInsn(ISTORE, 3);
+ Label label3 = new Label();
+ methodVisitor.visitLabel(label3);
+ methodVisitor.visitLineNumber(62, label3);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitVarInsn(ILOAD, 3);
+ methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
+ Label label4 = new Label();
+ methodVisitor.visitLabel(label4);
+ methodVisitor.visitLineNumber(63, label4);
+ // Insert some unneeded code which we know will be removed.
+ methodVisitor.visitLdcInsn(1);
+ methodVisitor.visitLdcInsn(2);
+ methodVisitor.visitInsn(IADD);
+ // Insert an label that will end some local variables so removing will create local reads.
+ Label labelForEndingLocals = new Label();
+ methodVisitor.visitLabel(labelForEndingLocals);
+ methodVisitor.visitInsn(POP);
+ // Pop the unneeded value and continue as usual.
+ methodVisitor.visitInsn(RETURN);
+ Label label5 = new Label();
+ methodVisitor.visitLabel(label5);
+ methodVisitor.visitLocalVariable("this", "Ltest/Test;", null, label0, label5, 0);
+ methodVisitor.visitLocalVariable("i", "I", null, label1, labelForEndingLocals, 1);
+ methodVisitor.visitLocalVariable("j", "I", null, label2, labelForEndingLocals, 2);
+ methodVisitor.visitLocalVariable("k", "I", null, label3, labelForEndingLocals, 3);
+ methodVisitor.visitMaxs(2, 4);
+ methodVisitor.visitEnd();
+ }
+ {
+ methodVisitor =
+ classWriter.visitMethod(
+ ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(66, label0);
+ methodVisitor.visitTypeInsn(NEW, "test/Test");
+ methodVisitor.visitInsn(DUP);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "test/Test", "<init>", "()V", false);
+ methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "test/Test", "method", "()V", false);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLineNumber(67, label1);
+ methodVisitor.visitInsn(RETURN);
+ Label label2 = new Label();
+ methodVisitor.visitLabel(label2);
+ methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label0, label2, 0);
+ methodVisitor.visitMaxs(2, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}
diff --git a/tools/archive.py b/tools/archive.py
index 302b0ff..78a8189 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -5,6 +5,7 @@
import create_maven_release
import gradle
+import optparse
import os
import shutil
import subprocess
@@ -16,6 +17,13 @@
ARCHIVE_BUCKET = 'r8-releases'
+def ParseOptions():
+ result = optparse.OptionParser()
+ result.add_option('--dry-run', '--dry_run',
+ help='Build only, no upload.',
+ default=False, action='store_true')
+ return result.parse_args()
+
def GetToolVersion(jar_path):
output = subprocess.check_output(['java', '-jar', jar_path, '--version'])
return output.splitlines()[0].strip()
@@ -79,12 +87,13 @@
return GetVersionDestination('http://storage.googleapis.com/', '', is_master)
def Main():
- if not utils.is_bot():
+ (options, args) = ParseOptions()
+ if not utils.is_bot() and not options.dry_run:
raise Exception('You are not a bot, don\'t archive builds')
# Generate an r8-ed build without dependencies.
# Note: build_r8lib does a gradle-clean, this must be the first command.
- build_r8lib('r8', True, True, utils.R8LIB_KEEP_RULES,
+ build_r8lib('r8nomanifest', True, True, utils.R8LIB_KEEP_RULES,
utils.R8LIB_EXCLUDE_DEPS_JAR)
# Create maven release which uses a build that exclude dependencies.
@@ -128,13 +137,17 @@
utils.D8_JAR,
utils.R8_JAR,
utils.R8LIB_JAR,
+ utils.R8LIB_JAR + '.map',
utils.R8_SRC_JAR,
utils.R8_FULL_EXCLUDE_DEPS_JAR,
utils.R8LIB_EXCLUDE_DEPS_JAR,
+ utils.R8LIB_EXCLUDE_DEPS_JAR + '.map',
utils.COMPATDX_JAR,
utils.COMPATDXLIB_JAR,
+ utils.COMPATDXLIB_JAR + '.map',
utils.COMPATPROGUARD_JAR,
utils.COMPATPROGUARDLIB_JAR,
+ utils.COMPATPROGUARDLIB_JAR + '.map',
utils.MAVEN_ZIP,
utils.GENERATED_LICENSE,
]:
@@ -146,14 +159,20 @@
zip.write(version_file, os.path.basename(version_file))
destination = GetUploadDestination(version, file_name, is_master)
print('Uploading %s to %s' % (tagged_jar, destination))
- utils.upload_file_to_cloud_storage(tagged_jar, destination)
- print('File available at: %s' % GetUrl(version, file_name, is_master))
+ if options.dry_run:
+ print('Dry run, not actually uploading')
+ else:
+ utils.upload_file_to_cloud_storage(tagged_jar, destination)
+ print('File available at: %s' % GetUrl(version, file_name, is_master))
if file == utils.R8_JAR:
# Upload R8 to a maven compatible location.
maven_dst = GetUploadDestination(utils.get_maven_path(version),
'r8-%s.jar' % version, is_master)
- utils.upload_file_to_cloud_storage(tagged_jar, maven_dst)
- print('Maven repo root available at: %s' % GetMavenUrl(is_master))
+ if options.dry_run:
+ print('Dry run, not actually creating maven repo')
+ else:
+ utils.upload_file_to_cloud_storage(tagged_jar, maven_dst)
+ print('Maven repo root available at: %s' % GetMavenUrl(is_master))
if __name__ == '__main__':
diff --git a/tools/archive_logs.py b/tools/archive_logs.py
deleted file mode 100755
index bdae32e..0000000
--- a/tools/archive_logs.py
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/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 achiving gradle test logs.
-
-import utils
-
-if __name__ == '__main__':
- utils.archive_failures()
diff --git a/tools/build_r8lib.py b/tools/build_r8lib.py
index 5af6d4d..5368f6f 100755
--- a/tools/build_r8lib.py
+++ b/tools/build_r8lib.py
@@ -56,7 +56,7 @@
# Produce R8 for compiling lib
if output_path is None:
output_path = target + 'lib.jar'
- output_map_path = os.path.splitext(output_path)[0] + '.map'
+ output_map_path = output_path + '.map'
toolhelper.run(
'r8',
('--release',
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index ae64322..e5ab66c 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -22,7 +22,8 @@
TYPES = ['dex', 'deploy', 'proguarded']
APPS = ['gmscore', 'nest', 'youtube', 'gmail', 'chrome']
-COMPILERS = ['d8', 'r8']
+COMPILERS = ['d8', 'r8', 'r8lib-r8', 'r8lib-d8']
+R8_COMPILERS = ['r8', 'r8lib-r8']
def ParseOptions(argv):
result = optparse.OptionParser()
@@ -136,14 +137,18 @@
for version in data.VERSIONS:
for type in data.VERSIONS[version]:
if (app, version, type) not in DISABLED_PERMUTATIONS:
- yield app, version, type
+ for use_r8lib in [False, True]:
+ yield app, version, type, use_r8lib
def run_all(options, args):
# Args will be destroyed
assert len(args) == 0
- for name, version, type in get_permutations():
- print('Execution %s %s %s' % (name, version, type))
+ for name, version, type, use_r8lib in get_permutations():
compiler = 'r8' if type == 'deploy' else 'd8'
+ if use_r8lib:
+ compiler = 'r8lib-' + compiler
+ print('Executing %s with %s %s %s' % (compiler, name, version, type))
+
fixed_options = copy.copy(options)
fixed_options.app = name
fixed_options.version = version
@@ -201,7 +206,7 @@
version = data.VERSIONS[options.version]
if not options.type:
- options.type = 'deploy' if options.compiler == 'r8' \
+ options.type = 'deploy' if options.compiler in R8_COMPILERS \
else 'proguarded'
if options.type not in version:
@@ -213,7 +218,7 @@
# For R8 'deploy' the JAR is located using the Proguard configuration
# -injars option. For chrome and nest we don't have the injars in the
# proguard files.
- if 'inputs' in values and (options.compiler != 'r8'
+ if 'inputs' in values and (options.compiler not in R8_COMPILERS
or options.type != 'deploy'
or options.app == 'chrome'
or options.app == 'nest'):
@@ -226,7 +231,7 @@
if 'main-dex-list' in values:
args.extend(['--main-dex-list', values['main-dex-list']])
- if options.compiler == 'r8':
+ if options.compiler in R8_COMPILERS:
if 'pgconf' in values and not options.k:
for pgconf in values['pgconf']:
args.extend(['--pg-conf', pgconf])
@@ -248,7 +253,7 @@
# Additional flags for the compiler from the configuration file.
if 'flags' in values:
args.extend(values['flags'].split(' '))
- if options.compiler == 'r8':
+ if options.compiler in R8_COMPILERS:
if 'r8-flags' in values:
args.extend(values['r8-flags'].split(' '))
@@ -270,7 +275,7 @@
if options.print_memoryuse and not options.track_memory_to_file:
options.track_memory_to_file = os.path.join(temp,
utils.MEMORY_USE_TMP_FILE)
- if options.compiler == 'r8' and app_provided_pg_conf:
+ if options.compiler in R8_COMPILERS and app_provided_pg_conf:
# Ensure that output of -printmapping and -printseeds go to the output
# location and not where the app Proguard configuration places them.
if outdir.endswith('.zip') or outdir.endswith('.jar'):
diff --git a/tools/test.py b/tools/test.py
index e451699..0e4625f 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -15,6 +15,7 @@
import thread
import time
import utils
+import uuid
import notify
ALL_ART_VMS = [
@@ -35,6 +36,8 @@
# is not a problem, no harm done except some logging in the stdout.
TIMEOUT_HANDLER_PERIOD = 60 * 18
+BUCKET = 'r8-test-results'
+
def ParseOptions():
result = optparse.OptionParser()
result.add_option('--no-internal', '--no_internal',
@@ -103,6 +106,15 @@
return result.parse_args()
+def archive_failures():
+ upload_dir = os.path.join(utils.REPO_ROOT, 'build', 'reports', 'tests')
+ u_dir = uuid.uuid4()
+ destination = 'gs://%s/%s' % (BUCKET, u_dir)
+ utils.upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
+ url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, u_dir)
+ print 'Test results available at: %s' % url
+ print '@@@STEP_LINK@Test failures@%s@@@' % url
+
def Main():
(options, args) = ParseOptions()
if utils.is_bot():
@@ -204,7 +216,7 @@
if return_code != 0:
if options.archive_failures and os.name != 'nt':
- utils.archive_failures()
+ archive_failures()
return return_code
return 0
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 56cd5a7..d9c96f1 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -13,7 +13,7 @@
if build is None:
build, args = extract_build_from_args(args)
if build:
- gradle.RunGradle(['r8'])
+ gradle.RunGradle(['r8lib' if tool.startswith('r8lib') else 'r8'])
cmd = []
if track_memory_file:
cmd.extend(['tools/track_memory.sh', track_memory_file])
@@ -24,7 +24,13 @@
cmd.append('-ea')
if profile:
cmd.append('-agentlib:hprof=cpu=samples,interval=1,depth=8')
- cmd.extend(['-jar', utils.R8_JAR, tool])
+ if tool in ['r8', 'd8']:
+ cmd.extend(['-jar', utils.R8_JAR, tool])
+ elif tool == 'r8lib-r8':
+ cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.R8'])
+ else:
+ assert(tool == 'r8lib-d8')
+ cmd.extend(['-cp', utils.R8LIB_JAR, 'com.android.tools.r8.D8'])
lib, args = extract_lib_from_args(args)
if lib:
cmd.extend(["--lib", lib])
diff --git a/tools/utils.py b/tools/utils.py
index 83a6478..9278459 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -12,7 +12,6 @@
import sys
import tarfile
import tempfile
-import uuid
ANDROID_JAR = 'third_party/android_jar/lib-v{api}/android.jar'
TOOLS_DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..')))
@@ -52,8 +51,6 @@
RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
R8LIB_KEEP_RULES = os.path.join(REPO_ROOT, 'src/main/keep.txt')
-TEST_RESULT_BUCKET = 'r8-test-results'
-
def PrintCmd(s):
if type(s) is list:
s = ' '.join(s)
@@ -190,15 +187,6 @@
def __exit__(self, *_):
shutil.rmtree(self._temp_dir, ignore_errors=True)
-def archive_failures():
- upload_dir = os.path.join(REPO_ROOT, 'build', 'reports', 'tests')
- u_dir = uuid.uuid4()
- destination = 'gs://%s/%s' % (TEST_RESULT_BUCKET, u_dir)
- upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
- url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (TEST_RESULT_BUCKET, u_dir)
- print 'Test results available at: %s' % url
- print '@@@STEP_LINK@Test failures@%s@@@' % url
-
class ChangedWorkingDirectory(object):
def __init__(self, working_directory):
self._working_directory = working_directory