Nest Access Control application tests
- Add flag to force nest desugaring
- Compile Hello/R8 with production R8,
with R8 compiled with Java 11 (R811),
with R8 11 shrunk without desugaring,
with R8 11 shrunk with desugaring
and ensure all the outputs are identical.
Bug: 133608609
Change-Id: I12f9d82ebb2545ae404e32b248a2cd09d2a0510e
diff --git a/build.gradle b/build.gradle
index b9a6506..52abc60 100644
--- a/build.gradle
+++ b/build.gradle
@@ -689,7 +689,7 @@
if (!project.hasProperty('lib_no_relocate')) {
configureRelocations(it)
}
- baseName 'sources'
+ baseName 'sources11'
}
task r8WithRelocatedDeps(type: ShadowJar) {
@@ -827,46 +827,6 @@
}
}
-def baseR8CommandLine11(args = []) {
- // Execute r8 commands against a stable r8 with relocated dependencies.
- def jdkDir = 'third_party/openjdk/jdk-11/'
- def javaExecutable
- if (OperatingSystem.current().isLinux()) {
- javaExecutable = file(jdkDir + 'Linux/bin/java')
- } else if (OperatingSystem.current().isMacOsX()) {
- javaExecutable = file(jdkDir + 'Mac/bin/java')
- } else {
- javaExecutable = file(jdkDir + 'Windows/bin/java')
- }
- return [javaExecutable,
- "-ea", "-jar", r8WithRelocatedDeps11.outputs.files[0]] + args
-}
-
-
-def r8CfCommandLine11(input, output, pgconf, args = [], libs = []) {
- return baseR8CommandLine11([
- "--classfile", "--release",
- input,
- "--output", output,
- "--pg-conf", pgconf,
- "--pg-map-output", output + ".map",
- "--lib", "third_party/openjdk/openjdk-rt-1.8/rt.jar"
- ] + args + libs.collectMany { ["--lib", it] })
-}
-
-def r8LibCreateTask11(name, pgConf, r8Task, output, args = [], libs = []) {
- // Execute r8 11 commands against a stable r8 11 with relocated dependencies.
- return tasks.create("r8Lib${name}", Exec) {
- inputs.files ([pgConf, r8WithRelocatedDeps.outputs, r8Task.outputs])
- outputs.file output
- dependsOn downloadOpenJDKrt
- dependsOn r8WithRelocatedDeps11
- dependsOn r8Task
- commandLine r8CfCommandLine11(r8Task.outputs.files[0], output, pgConf, args, libs)
- workingDir = projectDir
- }
-}
-
task testJar(type: ShadowJar, dependsOn: testClasses) {
baseName = "r8tests"
from sourceSets.test.output
@@ -919,22 +879,6 @@
outputs.file r8LibPath
}
-task R8Lib11 {
- def genRulesTask = generateR8LibKeepRules(
- "Main11",
- r8WithRelocatedDeps11,
- testJar,
- r8LibGeneratedKeepRulesPath)
- dependsOn r8LibCreateTask(
- "Main11",
- "src/main/keep.txt",
- r8WithRelocatedDeps11,
- r8LibPath11,
- ["--pg-conf", genRulesTask.outputs.files[0]]
- ).dependsOn(genRulesTask)
- outputs.file r8LibPath11
-}
-
task R8LibNoDeps {
def genRulesTask = generateR8LibKeepRules(
"NoDeps",
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index a1ca852..18e9055 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -441,7 +441,8 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
appView.setGraphLense(new MemberRebindingAnalysis(appViewWithLiveness).run());
- if (options.enableNestBasedAccessDesugaring && !options.canUseNestBasedAccess()) {
+ if (options.testing.enableForceNestBasedAccessDesugaringForTest
+ || (options.enableNestBasedAccessDesugaring && !options.canUseNestBasedAccess())) {
timing.begin("NestBasedAccessDesugaring");
R8NestBasedAccessDesugaring analyzer = new R8NestBasedAccessDesugaring(appViewWithLiveness);
boolean changed =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index ddef220..449f404 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -336,6 +336,11 @@
}
private boolean registerInvoke(DexMethod method, Invoke.Type invokeType) {
+ // Calls to non class type are not done through nest based access control.
+ // Work-around for calls to enum.clone().
+ if (!method.holder.isClassType()) {
+ return false;
+ }
DexEncodedMethod encodedMethod = definitionFor(method, context, invokeType);
if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, currentClass)) {
ensureInvokeBridge(encodedMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
index d2a06b9..9ec39ed 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
@@ -41,7 +41,8 @@
public GraphLense run(ExecutorService executorService, DexApplication.Builder<?> appBuilder)
throws ExecutionException {
- assert !appView.options().canUseNestBasedAccess();
+ assert !appView.options().canUseNestBasedAccess()
+ || appView.options().testing.enableForceNestBasedAccessDesugaringForTest;
computeAndProcessNestsConcurrently(executorService);
addDeferredBridgesAndMapMethods();
clearNestAttributes();
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 f08f81a..0fe7579 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -204,13 +204,23 @@
options.reporter, handler -> consumer.accept(ByteDataView.of(result), desc, handler));
}
+ private int getClassFileVersion(DexEncodedMethod method) {
+ if (!method.hasClassFileVersion()) {
+ // In this case bridges have been introduced for the Cf back-end,
+ // which do not have class file version.
+ assert options.testing.enableForceNestBasedAccessDesugaringForTest;
+ return 0;
+ }
+ return method.getClassFileVersion();
+ }
+
private int getClassFileVersion(DexProgramClass clazz) {
int version = clazz.hasClassFileVersion() ? clazz.getInitialClassFileVersion() : 50;
for (DexEncodedMethod method : clazz.directMethods()) {
- version = Math.max(version, method.getClassFileVersion());
+ version = Math.max(version, getClassFileVersion(method));
}
for (DexEncodedMethod method : clazz.virtualMethods()) {
- version = Math.max(version, method.getClassFileVersion());
+ version = Math.max(version, getClassFileVersion(method));
}
return version;
}
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 a65dd20..0a8f820 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -820,6 +820,9 @@
// TODO(b/129458850) When fixed, remove this and change all usages to "true".
public boolean enableStatefulLambdaCreateInstanceMethod = false;
+ // Flag to turn on/off JDK11+ nest-access control even when not required (Cf backend)
+ public boolean enableForceNestBasedAccessDesugaringForTest = false;
+
public boolean desugarLambdasThroughLensCodeRewriter() {
return enableStatefulLambdaCreateInstanceMethod;
}
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 3584faa..9b14ac5 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
@@ -51,6 +52,9 @@
// Additional Proguard configuration files.
private List<Path> proguardConfigFiles = new ArrayList<>();
+ // External JDK to use to run R8
+ private CfVm externalJDK = null;
+
private boolean addR8ExternalDeps = false;
private ExternalR8TestBuilder(TestState state, Builder builder, Backend backend) {
@@ -66,6 +70,18 @@
return this;
}
+ public ExternalR8TestBuilder useExternalJDK(CfVm externalJDK) {
+ this.externalJDK = externalJDK;
+ return this;
+ }
+
+ private String getJDKToRun() {
+ if (externalJDK == null) {
+ return getJavaExecutable();
+ }
+ return getJavaExecutable(externalJDK);
+ }
+
@Override
ExternalR8TestCompileResult internalCompile(
Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
@@ -85,7 +101,7 @@
List<String> command = new ArrayList<>();
Collections.addAll(
command,
- getJavaExecutable(),
+ getJDKToRun(),
"-ea",
"-cp",
classPath,
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 9fef6f1..448d698 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -128,6 +128,8 @@
public static final Path R8_JAR = Paths.get(LIBS_DIR, "r8.jar");
public static final Path R8_WITH_RELOCATED_DEPS_JAR =
Paths.get(LIBS_DIR, "r8_with_relocated_deps.jar");
+ public static final Path R8_WITH_RELOCATED_DEPS_JAR_11 =
+ Paths.get(LIBS_DIR, "r8_with_relocated_deps_11.jar");
public static final Path R8LIB_JAR = Paths.get(LIBS_DIR, "r8lib.jar");
public static final Path R8LIB_EXCLUDE_DEPS_JAR = Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar");
public static final Path DEPS_NOT_RELOCATED = Paths.get(LIBS_DIR, "deps-not-relocated.jar");
diff --git a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
index 7503774..a30d0bd 100644
--- a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
@@ -27,7 +27,6 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.junit.BeforeClass;
@@ -195,7 +194,7 @@
assertProgramsEqual(result.outputJar(), runR8R8.outputJar());
}
- private static void assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
+ public static void assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
if (filesAreEqual(expectedJar, actualJar)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11BootstrapTest.java
new file mode 100644
index 0000000..730fd28
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11BootstrapTest.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2019, 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.desugar.nestaccesscontrol;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.cf.BootstrapCurrentEqualityTest;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * This test relies on a freshly built build/libs/r8_with_relocated_deps_11.jar.
+ *
+ * <p>The test compiles Hello/R8 with the same settings using R8 compiled with Java 11 (non shrunk),
+ * and the same but shrunk with/without nest desugaring. All generated jars should be run correctly
+ * and identical.
+ */
+@RunWith(Parameterized.class)
+public class Java11BootstrapTest extends TestBase {
+
+ private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
+ private static final String[] HELLO_KEEP = {
+ "-keep class hello.Hello { public static void main(...);}"
+ };
+
+ private static Path r8Lib11NoDesugar;
+ private static Path r8Lib11Desugar;
+
+ public Java11BootstrapTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withCfRuntime(CfVm.JDK11).build();
+ }
+
+ @BeforeClass
+ public static void beforeAll() throws Exception {
+ r8Lib11NoDesugar = compileR8(false);
+ r8Lib11Desugar = compileR8(true);
+ }
+
+ private static Path compileR8(boolean desugar) throws Exception {
+ // Shrink R8 11 with R8
+ return testForR8(TestBase.getStaticTemp(), Backend.CF)
+ .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11)
+ .addKeepRuleFiles(MAIN_KEEP)
+ .addOptionsModification(
+ options -> options.testing.enableForceNestBasedAccessDesugaringForTest = desugar)
+ .compile()
+ .inspect(inspector -> assertNests(inspector, desugar))
+ .writeToZip();
+ }
+
+ private static void assertNests(CodeInspector inspector, boolean desugar) {
+ if (desugar) {
+ assertTrue(inspector.allClasses().stream().noneMatch(subj -> subj.getDexClass().isInANest()));
+ } else {
+ assertTrue(inspector.allClasses().stream().anyMatch(subj -> subj.getDexClass().isInANest()));
+ }
+ }
+
+ private Path[] jarsToCompare() {
+ return new Path[] {
+ ToolHelper.R8_WITH_RELOCATED_DEPS_JAR,
+ ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11,
+ r8Lib11NoDesugar,
+ r8Lib11Desugar
+ };
+ }
+
+ @Test
+ public void testHello() throws Exception {
+ Assume.assumeTrue(!ToolHelper.isWindows());
+ Path prevGeneratedJar = null;
+ String prevRunResult = null;
+ for (Path jar : jarsToCompare()) {
+ Path generatedJar =
+ testForExternalR8(Backend.CF)
+ .useProvidedR8(jar)
+ .useExternalJDK(jar == ToolHelper.R8_WITH_RELOCATED_DEPS_JAR ? null : CfVm.JDK11)
+ .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION))
+ .addKeepRules(HELLO_KEEP)
+ .compile()
+ .outputJar();
+ String runResult =
+ ToolHelper.runJava(
+ parameters.getRuntime().asCf().getVm(),
+ ImmutableList.of(generatedJar),
+ "hello.Hello")
+ .toString();
+ if (prevRunResult != null) {
+ assertEquals(prevRunResult, runResult);
+ }
+ prevRunResult = runResult;
+ if (prevGeneratedJar != null) {
+ BootstrapCurrentEqualityTest.assertProgramsEqual(prevGeneratedJar, generatedJar);
+ }
+ prevGeneratedJar = generatedJar;
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Assume.assumeTrue(!ToolHelper.isWindows());
+ Path prevGeneratedJar = null;
+ for (Path jar : jarsToCompare()) {
+ Path generatedJar =
+ testForExternalR8(Backend.CF)
+ .useProvidedR8(jar)
+ .useExternalJDK(CfVm.JDK11)
+ .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION))
+ .addKeepRuleFiles(MAIN_KEEP)
+ .compile()
+ .outputJar();
+ if (prevGeneratedJar != null) {
+ BootstrapCurrentEqualityTest.assertProgramsEqual(prevGeneratedJar, generatedJar);
+ }
+ prevGeneratedJar = generatedJar;
+ }
+ }
+}
diff --git a/tools/test.py b/tools/test.py
index 015e53e..be271f2 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -212,6 +212,7 @@
# Build an R8 with dependencies for bootstrapping tests before adding test sources.
gradle_args.append('r8WithRelocatedDeps')
+ gradle_args.append('r8WithRelocatedDeps11')
# Add Gradle tasks
gradle_args.append('cleanTest')