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')