Add (failing) tests for nest based access control

Tests exhaustively check for each case where a private
member can be accessed from nest mates.

Bug: 130529338
Change-Id: Ia14a8e78079db26585bb4ea5a1ca7b64606dcebd
diff --git a/build.gradle b/build.gradle
index b448ea2..158dc7b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1282,6 +1282,22 @@
     }
 }
 
+task buildExampleJava11Jars {
+    def examplesDir = file("src/test/examplesJava11")
+    examplesDir.eachDir { dir ->
+        def name = dir.getName();
+        def exampleOutputDir = file("build/test/examplesJava11");
+        def jarName = "${name}.jar"
+        dependsOn "jar_examplesJava11_${name}"
+        task "jar_examplesJava11_${name}"(type: Jar) {
+            archiveName = jarName
+            destinationDir = exampleOutputDir
+            from "src/test/examplesJava11"  // Java 11 classes
+            include "**/" + name + "/**/*.class"
+        }
+    }
+}
+
 task buildExamplesKotlin {
     if (OperatingSystem.current().isMacOsX() || OperatingSystem.current().isWindows()) {
         logger.lifecycle("WARNING: Testing (including building kotlin examples) is only partially" +
@@ -1362,6 +1378,7 @@
     dependsOn buildExampleAndroidOJars
     dependsOn buildExampleAndroidPJars
     dependsOn buildExampleJava9Jars
+    dependsOn buildExampleJava11Jars
     dependsOn buildExampleAndroidApi
     def examplesDir = file("src/test/examples")
     def noDexTests = [
@@ -2093,3 +2110,45 @@
 
     dependsOn compile_Java9examples
 }
+
+// Same as above for Java 11
+// ./tools/gradle.py -Pjava11Home=<java 11 home> buildJava11Tests
+task buildJava11Tests {
+    def javacOutputFolder = getTemporaryDir();
+    def examplesDir = file("src/test/examplesJava11")
+
+    task "compile_Java11examples"(type: JavaCompile) {
+        doFirst {
+            if (!project.hasProperty('java11Home') || project.property('java11Home').isEmpty()) {
+                throw new GradleException("Set java11Home property.")
+            }
+        }
+
+        source = fileTree(dir: examplesDir, include: '**/*.java')
+        destinationDir = javacOutputFolder
+        classpath = sourceSets.main.compileClasspath
+        options.compilerArgs += ["-Xlint:-options"]
+        sourceCompatibility = JavaVersion.VERSION_11
+        targetCompatibility = JavaVersion.VERSION_11
+        options.fork = true
+
+        if (project.hasProperty('java11Home')) {
+            options.forkOptions.javaHome = file(getProperty('java11Home'))
+        }
+
+        doLast {
+            def classfileFrom = copySpec {
+                from javacOutputFolder
+                include "**/*.class"
+            }
+            copy {
+                into examplesDir
+                with classfileFrom
+            }
+            delete javacOutputFolder
+            println "Warning: in my cases nested classes were not showed on IntelliJ"
+        }
+    }
+
+    dependsOn compile_Java11examples
+}
diff --git a/src/test/examplesJava11/nestHostExample/NestHostExample$NestMemberInner$NestMemberInnerInner.class b/src/test/examplesJava11/nestHostExample/NestHostExample$NestMemberInner$NestMemberInnerInner.class
new file mode 100644
index 0000000..0259744
--- /dev/null
+++ b/src/test/examplesJava11/nestHostExample/NestHostExample$NestMemberInner$NestMemberInnerInner.class
Binary files differ
diff --git a/src/test/examplesJava11/nestHostExample/NestHostExample$NestMemberInner.class b/src/test/examplesJava11/nestHostExample/NestHostExample$NestMemberInner.class
new file mode 100644
index 0000000..b9ef04a
--- /dev/null
+++ b/src/test/examplesJava11/nestHostExample/NestHostExample$NestMemberInner.class
Binary files differ
diff --git a/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestInterfaceInner.class b/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestInterfaceInner.class
new file mode 100644
index 0000000..c158f44
--- /dev/null
+++ b/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestInterfaceInner.class
Binary files differ
diff --git a/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestMemberInner$StaticNestMemberInnerInner.class b/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestMemberInner$StaticNestMemberInnerInner.class
new file mode 100644
index 0000000..13d5b4c
--- /dev/null
+++ b/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestMemberInner$StaticNestMemberInnerInner.class
Binary files differ
diff --git a/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestMemberInner.class b/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestMemberInner.class
new file mode 100644
index 0000000..c75b0d3
--- /dev/null
+++ b/src/test/examplesJava11/nestHostExample/NestHostExample$StaticNestMemberInner.class
Binary files differ
diff --git a/src/test/examplesJava11/nestHostExample/NestHostExample.class b/src/test/examplesJava11/nestHostExample/NestHostExample.class
new file mode 100644
index 0000000..3d7c788
--- /dev/null
+++ b/src/test/examplesJava11/nestHostExample/NestHostExample.class
Binary files differ
diff --git a/src/test/examplesJava11/nestHostExample/NestHostExample.java b/src/test/examplesJava11/nestHostExample/NestHostExample.java
new file mode 100644
index 0000000..1fdfbaf
--- /dev/null
+++ b/src/test/examplesJava11/nestHostExample/NestHostExample.java
@@ -0,0 +1,296 @@
+// 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.
+
+// Warning: This requires Java 9+ to be compiled (private interface methods)
+// This file builds all the possible combinations of private members:
+// - static VS non-static
+// - fields VS methods
+// - multiple nesting levels
+// - nested interfaces
+// Note: Inner interfaces have to be static.
+// Then the accessPrivate methods generate all possibles calls.
+// Each instance is created through a private constructor to test them too.
+@SuppressWarnings("WeakerAccess")
+public class NestHostExample {
+
+  private String method() {
+    return "hostMethod";
+  }
+
+  private static String staticMethod() {
+    return "staticHostMethod";
+  }
+
+  private String field;
+  private static String staticField = "staticField";
+
+  private NestHostExample(String field) {
+    this.field = field;
+  }
+
+  public StaticNestMemberInner createStaticNestMemberInner(String field) {
+    return new StaticNestMemberInner(field);
+  }
+
+  public NestMemberInner createNestMemberInner(String field) {
+    return new NestMemberInner(field);
+  }
+
+  @SuppressWarnings("static-access") // we want to test that too.
+  public String accessPrivate(
+      NestHostExample o0,
+      StaticNestMemberInner o1,
+      StaticNestMemberInner.StaticNestMemberInnerInner o2,
+      NestMemberInner o3,
+      NestMemberInner.NestMemberInnerInner o4) {
+    return o0.field
+        + o0.staticField
+        + NestHostExample.staticField
+        + o0.method()
+        + o0.staticMethod()
+        + NestHostExample.staticMethod()
+        + o1.field
+        + o1.staticField
+        + StaticNestMemberInner.staticField
+        + o1.method()
+        + o1.staticMethod()
+        + StaticNestMemberInner.staticMethod()
+        + o2.field
+        + o2.staticField
+        + StaticNestMemberInner.StaticNestMemberInnerInner.staticField
+        + o2.method()
+        + o2.staticMethod()
+        + StaticNestMemberInner.StaticNestMemberInnerInner.staticMethod()
+        + o3.field
+        + o3.method()
+        + o4.field
+        + o4.method();
+  }
+
+  public String accessPrivateInterface(StaticNestInterfaceInner i1) {
+    return i1.interfaceMethod() + StaticNestInterfaceInner.staticInterfaceMethod();
+  }
+
+  // Nested interface (has to be static)
+  public interface StaticNestInterfaceInner {
+
+    private String interfaceMethod() {
+      return "staticInterfaceMethod";
+    }
+
+    private static String staticInterfaceMethod() {
+      return "staticStaticInterfaceMethod";
+    }
+
+    public static NestHostExample createNestHostExample(String field) {
+      return new NestHostExample(field);
+    }
+
+    @SuppressWarnings("static-access") // we want to test that too.
+    default String accessPrivate(
+        NestHostExample o0,
+        StaticNestMemberInner o1,
+        StaticNestMemberInner.StaticNestMemberInnerInner o2,
+        NestMemberInner o3,
+        NestMemberInner.NestMemberInnerInner o4) {
+      return o0.field
+          + o0.staticField
+          + NestHostExample.staticField
+          + o0.method()
+          + o0.staticMethod()
+          + NestHostExample.staticMethod()
+          + o1.field
+          + o1.staticField
+          + StaticNestMemberInner.staticField
+          + o1.method()
+          + o1.staticMethod()
+          + StaticNestMemberInner.staticMethod()
+          + o2.field
+          + o2.staticField
+          + StaticNestMemberInner.StaticNestMemberInnerInner.staticField
+          + o2.method()
+          + o2.staticMethod()
+          + StaticNestMemberInner.StaticNestMemberInnerInner.staticMethod()
+          + o3.field
+          + o3.method()
+          + o4.field
+          + o4.method();
+    }
+
+    default String accessPrivateInterface(StaticNestInterfaceInner i1) {
+      return i1.interfaceMethod() + StaticNestInterfaceInner.staticInterfaceMethod();
+    }
+  }
+
+  // Static Nest mates
+  public static class StaticNestMemberInner {
+
+    private String method() {
+      return "nest1SMethod";
+    }
+
+    private static String staticMethod() {
+      return "staticNest1SMethod";
+    }
+
+    private String field;
+    private static String staticField = "staticNest1SField";
+
+    private StaticNestMemberInner(String field) {
+      this.field = field;
+    }
+
+    public static StaticNestMemberInnerInner createStaticNestMemberInnerInner(String field) {
+      return new StaticNestMemberInnerInner(field);
+    }
+
+    public static class StaticNestMemberInnerInner implements StaticNestInterfaceInner {
+
+      private String method() {
+        return "nest2SMethod";
+      }
+
+      private static String staticMethod() {
+        return "staticNest2SMethod";
+      }
+
+      private String field;
+      private static String staticField = "staticNest2SField";
+
+      private StaticNestMemberInnerInner(String field) {
+        this.field = field;
+      }
+
+      @SuppressWarnings("static-access") // we want to test that too.
+      public String accessPrivate(
+          NestHostExample o0,
+          StaticNestMemberInner o1,
+          StaticNestMemberInner.StaticNestMemberInnerInner o2,
+          NestMemberInner o3,
+          NestMemberInner.NestMemberInnerInner o4) {
+        return o0.field
+            + o0.staticField
+            + NestHostExample.staticField
+            + o0.method()
+            + o0.staticMethod()
+            + NestHostExample.staticMethod()
+            + o1.field
+            + o1.staticField
+            + StaticNestMemberInner.staticField
+            + o1.method()
+            + o1.staticMethod()
+            + StaticNestMemberInner.staticMethod()
+            + o2.field
+            + o2.staticField
+            + StaticNestMemberInner.StaticNestMemberInnerInner.staticField
+            + o2.method()
+            + o2.staticMethod()
+            + StaticNestMemberInner.StaticNestMemberInnerInner.staticMethod()
+            + o3.field
+            + o3.method()
+            + o4.field
+            + o4.method();
+      }
+
+      public String accessPrivateInterface(StaticNestInterfaceInner i1) {
+        return i1.interfaceMethod() + StaticNestInterfaceInner.staticInterfaceMethod();
+      }
+    }
+  }
+
+  // Non static Nest mates
+  public class NestMemberInner {
+
+    private String method() {
+      return "nest1Method";
+    }
+
+    private String field;
+
+    private NestMemberInner(String field) {
+      this.field = field;
+    }
+
+    public NestMemberInnerInner createNestMemberInnerInner(String field) {
+      return new NestMemberInnerInner(field);
+    }
+
+    public class NestMemberInnerInner {
+
+      private String method() {
+        return "nest2Method";
+      }
+
+      private String field;
+
+      private NestMemberInnerInner(String field) {
+        this.field = field;
+      }
+
+      @SuppressWarnings("static-access") // we want to test that too.
+      public String accessPrivate(
+          NestHostExample o0,
+          StaticNestMemberInner o1,
+          StaticNestMemberInner.StaticNestMemberInnerInner o2,
+          NestMemberInner o3,
+          NestMemberInner.NestMemberInnerInner o4) {
+        return o0.field
+            + o0.staticField
+            + NestHostExample.staticField
+            + o0.method()
+            + o0.staticMethod()
+            + NestHostExample.staticMethod()
+            + o1.field
+            + o1.staticField
+            + StaticNestMemberInner.staticField
+            + o1.method()
+            + o1.staticMethod()
+            + StaticNestMemberInner.staticMethod()
+            + o2.field
+            + o2.staticField
+            + StaticNestMemberInner.StaticNestMemberInnerInner.staticField
+            + o2.method()
+            + o2.staticMethod()
+            + StaticNestMemberInner.StaticNestMemberInnerInner.staticMethod()
+            + o3.field
+            + o3.method()
+            + o4.field
+            + o4.method();
+      }
+
+      public String accessPrivateInterface(StaticNestInterfaceInner i1) {
+        return i1.interfaceMethod() + StaticNestInterfaceInner.staticInterfaceMethod();
+      }
+    }
+  }
+
+  @SuppressWarnings("all") // do not know how to remove the redundant i1 variable
+  public static void main(String[] args) {
+    NestHostExample o0 = StaticNestInterfaceInner.createNestHostExample("field");
+    StaticNestMemberInner o1 = o0.createStaticNestMemberInner("nest1SField");
+    StaticNestMemberInner.StaticNestMemberInnerInner o2 =
+        o1.createStaticNestMemberInnerInner("nest2SField");
+    NestMemberInner o3 = o0.createNestMemberInner("nest1Field");
+    NestMemberInner.NestMemberInnerInner o4 = o3.createNestMemberInnerInner("nest2Field");
+
+    StaticNestInterfaceInner i1 = o2;
+
+    System.out.println(o0.accessPrivate(o0, o1, o2, o3, o4));
+    System.out.println(o2.accessPrivate(o0, o1, o2, o3, o4));
+    System.out.println(o4.accessPrivate(o0, o1, o2, o3, o4));
+    System.out.println(i1.accessPrivate(o0, o1, o2, o3, o4));
+
+    System.out.println(o0.accessPrivateInterface(i1));
+    System.out.println(o2.accessPrivateInterface(i1));
+    System.out.println(o4.accessPrivateInterface(i1));
+    System.out.println(i1.accessPrivateInterface(i1));
+
+    // Expected results: 4x
+    // fieldstaticFieldstaticFieldhostMethodstaticHostMethodstaticHostMethodnest1SField
+    //  staticNest1SFieldstaticNest1SFieldnest1SMethodstaticNest1SMethodstaticNest1SMethod
+    //  nest2SFieldstaticNest2SFieldstaticNest2SFieldnest2SMethodstaticNest2SMethod
+    //  staticNest2SMethodnest1Fieldnest1Methodnest2Fieldnest2Method
+    // staticInterfaceMethodstaticStaticInterfaceMethod
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index b65a8fe..2f6e870 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -95,6 +95,7 @@
   public static final String EXAMPLES_ANDROID_O_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidO/";
   public static final String EXAMPLES_ANDROID_P_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidP/";
   public static final String EXAMPLES_JAVA9_BUILD_DIR = TESTS_BUILD_DIR + "examplesJava9/";
+  public static final String EXAMPLES_JAVA11_BUILD_DIR = TESTS_BUILD_DIR + "examplesJava11/";
   public static final String SMALI_DIR = TESTS_DIR + "smali/";
   public static final String SMALI_BUILD_DIR = TESTS_BUILD_DIR + "smali/";
 
diff --git a/src/test/java/com/android/tools/r8/desugar/NestAccessControl/NestAccessControlTest.java b/src/test/java/com/android/tools/r8/desugar/NestAccessControl/NestAccessControlTest.java
new file mode 100644
index 0000000..41d7876
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/NestAccessControl/NestAccessControlTest.java
@@ -0,0 +1,45 @@
+// 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 org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Paths;
+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 NestAccessControlTest extends TestBase {
+
+  static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_JAVA11_BUILD_DIR;
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().build();
+  }
+
+  public NestAccessControlTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testNestAccessControl() throws Exception {
+    testForD8()
+        .addProgramFiles(Paths.get(EXAMPLE_DIR, "nestHostExample" + JAR_EXTENSION))
+        .setMinApi(parameters.getRuntime())
+        .compile()
+        .run(parameters.getRuntime(), "NestHostExample")
+        .assertFailureWithErrorThatMatches(containsString("IllegalAccessError"));
+  }
+}