Nest private member access clean-up

- in D8, private constructor calls inside
   a class should not require bridge
   (Add test for this case)
- in r8, no bridges are needed for private
   field access within a class
   (Also move nest access desugar logic from
   LensCodeRewriter to corresponding Lens)
- in R8, nest based desugaring should work
   when other lens are present.
- in R8, the lens can be ignored only if none
   of the map are used.
   (Add test for this case)

Bug:132147078
Change-Id: I2aaa95a906709e4e930eddfdacec0994d7e6d6d0
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 5b6ef08..fc24fd2 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -458,19 +458,19 @@
 
   public abstract DexField lookupField(DexField field);
 
-  public DexMethod lookupStaticGetFieldForMethod(DexField field) {
+  public DexMethod lookupStaticGetFieldForMethod(DexField field, DexMethod context) {
     return null;
   }
 
-  public DexMethod lookupStaticPutFieldForMethod(DexField field) {
+  public DexMethod lookupStaticPutFieldForMethod(DexField field, DexMethod context) {
     return null;
   }
 
-  public DexMethod lookupInstanceGetFieldForMethod(DexField field) {
+  public DexMethod lookupInstanceGetFieldForMethod(DexField field, DexMethod context) {
     return null;
   }
 
-  public DexMethod lookupInstancePutFieldForMethod(DexField field) {
+  public DexMethod lookupInstancePutFieldForMethod(DexField field, DexMethod context) {
     return null;
   }
 
@@ -970,6 +970,26 @@
       return previousLense.lookupPrototypeChanges(method);
     }
 
+    @Override
+    public DexMethod lookupStaticGetFieldForMethod(DexField field, DexMethod context) {
+      return previousLense.lookupStaticGetFieldForMethod(field, context);
+    }
+
+    @Override
+    public DexMethod lookupStaticPutFieldForMethod(DexField field, DexMethod context) {
+      return previousLense.lookupStaticPutFieldForMethod(field, context);
+    }
+
+    @Override
+    public DexMethod lookupInstanceGetFieldForMethod(DexField field, DexMethod context) {
+      return previousLense.lookupInstanceGetFieldForMethod(field, context);
+    }
+
+    @Override
+    public DexMethod lookupInstancePutFieldForMethod(DexField field, DexMethod context) {
+      return previousLense.lookupInstancePutFieldForMethod(field, context);
+    }
+
     /**
      * Default invocation type mapping.
      *
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 2203096..cb56a09 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -278,8 +278,8 @@
           DexField field = instanceGet.getField();
           DexField actualField = graphLense.lookupField(field);
           DexMethod replacementMethod =
-              graphLense.lookupInstanceGetFieldForMethod(actualField);
-          if (replacementMethod != null && method.method != replacementMethod) {
+              graphLense.lookupInstanceGetFieldForMethod(actualField, method.method);
+          if (replacementMethod != null) {
             iterator.replaceCurrentInstruction(
                 new InvokeStatic(replacementMethod, current.outValue(), current.inValues()));
           } else if (actualField != field) {
@@ -295,8 +295,8 @@
           DexField field = instancePut.getField();
           DexField actualField = graphLense.lookupField(field);
           DexMethod replacementMethod =
-              graphLense.lookupInstancePutFieldForMethod(actualField);
-          if (replacementMethod != null && method.method != replacementMethod) {
+              graphLense.lookupInstancePutFieldForMethod(actualField, method.method);
+          if (replacementMethod != null) {
             iterator.replaceCurrentInstruction(
                 new InvokeStatic(replacementMethod, current.outValue(), current.inValues()));
           } else if (actualField != field) {
@@ -309,8 +309,8 @@
           DexField field = staticGet.getField();
           DexField actualField = graphLense.lookupField(field);
           DexMethod replacementMethod =
-              graphLense.lookupStaticGetFieldForMethod(actualField);
-          if (replacementMethod != null && method.method != replacementMethod) {
+              graphLense.lookupStaticGetFieldForMethod(actualField, method.method);
+          if (replacementMethod != null) {
             iterator.replaceCurrentInstruction(
                 new InvokeStatic(replacementMethod, current.outValue(), current.inValues()));
           } else if (actualField != field) {
@@ -323,8 +323,8 @@
           DexField field = staticPut.getField();
           DexField actualField = graphLense.lookupField(field);
           DexMethod replacementMethod =
-              graphLense.lookupStaticPutFieldForMethod(actualField);
-          if (replacementMethod != null && method.method != replacementMethod) {
+              graphLense.lookupStaticPutFieldForMethod(actualField, method.method);
+          if (replacementMethod != null) {
             iterator.replaceCurrentInstruction(
                 new InvokeStatic(replacementMethod, current.outValue(), current.inValues()));
           } else if (actualField != field) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
index 16ae320..288b6a0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaringRewriter.java
@@ -123,7 +123,7 @@
           DexMethod methodCalled = invokeMethod.getInvokedMethod();
           registerInvoke(methodCalled, nest, currentClass);
           DexMethod newTarget = methodMap.get(methodCalled);
-          if (newTarget != null && encodedMethod.method != newTarget) {
+          if (newTarget != null && encodedMethod.method.holder != newTarget.holder) {
             instructions.replaceCurrentInstruction(
                 new InvokeStatic(newTarget, invokeMethod.outValue(), invokeMethod.arguments()));
           } else {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
index 0411ea4..6878f86 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLense.java
@@ -48,7 +48,12 @@
     }
 
     public GraphLense build(GraphLense previousLense, DexType nestConstructorType) {
-      if (methodMap.isEmpty() && fieldMap.isEmpty()) {
+      if (methodMap.isEmpty()
+          && staticGetToMethodMap.isEmpty()
+          && staticPutToMethodMap.isEmpty()
+          && instanceGetToMethodMap.isEmpty()
+          && instancePutToMethodMap.isEmpty()
+          && initializerMap.isEmpty()) {
         return previousLense;
       }
       return new NestedPrivateMethodLense(
@@ -103,23 +108,33 @@
   }
 
   @Override
-  public DexMethod lookupStaticGetFieldForMethod(DexField field) {
-    return staticGetToMethodMap.get(field);
+  public DexMethod lookupStaticGetFieldForMethod(DexField field, DexMethod context) {
+    return lookupFieldAccessForMethod(field, context, staticGetToMethodMap);
   }
 
   @Override
-  public DexMethod lookupStaticPutFieldForMethod(DexField field) {
-    return staticPutToMethodMap.get(field);
+  public DexMethod lookupStaticPutFieldForMethod(DexField field, DexMethod context) {
+    return lookupFieldAccessForMethod(field, context, staticPutToMethodMap);
   }
 
   @Override
-  public DexMethod lookupInstanceGetFieldForMethod(DexField field) {
-    return instanceGetToMethodMap.get(field);
+  public DexMethod lookupInstanceGetFieldForMethod(DexField field, DexMethod context) {
+    return lookupFieldAccessForMethod(field, context, instanceGetToMethodMap);
   }
 
   @Override
-  public DexMethod lookupInstancePutFieldForMethod(DexField field) {
-    return instancePutToMethodMap.get(field);
+  public DexMethod lookupInstancePutFieldForMethod(DexField field, DexMethod context) {
+    return lookupFieldAccessForMethod(field, context, instancePutToMethodMap);
+  }
+
+  private DexMethod lookupFieldAccessForMethod(
+      DexField field, DexMethod context, Map<DexField, DexMethod> map) {
+    DexMethod replacementMethod = map.get(field);
+    // We replace the field access by the bridge only if on a different class than context.
+    if (replacementMethod != null && context.holder != replacementMethod.holder) {
+      return replacementMethod;
+    }
+    return null;
   }
 
   @Override
@@ -127,6 +142,12 @@
     return false;
   }
 
+  // This is true because mappings specific to this class can be filled.
+  @Override
+  protected boolean isLegitimateToHaveEmptyMappings() {
+    return true;
+  }
+
   @Override
   public RewrittenPrototypeDescription lookupPrototypeChanges(DexMethod method) {
     DexType[] parameters = method.proto.parameters.values;
diff --git a/src/test/examplesJava11/nestHostExample/BasicNestHostWithInnerClassConstructors.java b/src/test/examplesJava11/nestHostExample/BasicNestHostWithInnerClassConstructors.java
index 8b8f6ce..d61d351 100644
--- a/src/test/examplesJava11/nestHostExample/BasicNestHostWithInnerClassConstructors.java
+++ b/src/test/examplesJava11/nestHostExample/BasicNestHostWithInnerClassConstructors.java
@@ -12,6 +12,10 @@
     this.field = field;
   }
 
+  private BasicNestHostWithInnerClassConstructors(int intVal) {
+    this.field = String.valueOf(intVal);
+  }
+
   public static BasicNestedClass createNestedInstance(String field) {
     return new BasicNestedClass(field);
   }
@@ -33,8 +37,11 @@
     BasicNestHostWithInnerClassConstructors outer = BasicNestedClass.createOuterInstance("field");
     BasicNestedClass inner =
         BasicNestHostWithInnerClassConstructors.createNestedInstance("nest1SField");
+    BasicNestHostWithInnerClassConstructors noBridge =
+        new BasicNestHostWithInnerClassConstructors(1);
 
     System.out.println(outer.field);
     System.out.println(inner.field);
+    System.out.println(noBridge.field);
   }
 }
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
index 0d6bd56..c62c4bd 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTest.java
@@ -36,6 +36,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
+import java.util.Map;
 import java.util.function.BiFunction;
 import java.util.function.Function;
 import org.hamcrest.Matcher;
@@ -99,7 +100,7 @@
               StringUtils.lines(
                   "nestMethodstaticNestMethodstaticNestMethodnoBridge",
                   "hostMethodstaticHostMethodstaticNestMethod"),
-          "constructors", StringUtils.lines("field", "nest1SField"),
+          "constructors", StringUtils.lines("field", "nest1SField", "1"),
           "anonymous",
               StringUtils.lines(
                   "fieldstaticFieldstaticFieldhostMethodstaticHostMethodstaticHostMethod"),
@@ -216,6 +217,13 @@
     assertEquals(
         methodNumBridges, methodMainClass.allMethods(FoundMethodSubject::isSynthetic).size());
 
+    // Two bridges for method and staticMethod.
+    int constructorNumBridges = parameters.isCfRuntime() ? 0 : 1;
+    ClassSubject constructorMainClass = inspector.clazz(getMainClass("constructors"));
+    assertEquals(
+        constructorNumBridges,
+        constructorMainClass.allMethods(FoundMethodSubject::isSynthetic).size());
+
     // Four bridges for field and staticField, both get & set.
     int fieldNumBridges = parameters.isCfRuntime() ? 0 : 4;
     ClassSubject fieldMainClass = inspector.clazz(getMainClass("fields"));
@@ -314,6 +322,27 @@
     testIncompleteNestError(false);
   }
 
+  @Test
+  public void testR8SingleNest() throws Exception {
+    for (Map.Entry<String, String> entry : MAIN_CLASSES.entrySet()) {
+      List<Path> matchingClasses =
+          CLASS_NAMES.stream()
+              .filter(name -> containsString(entry.getValue()).matches(name))
+              .map(name -> CLASSES_PATH.resolve(name + CLASS_EXTENSION))
+              .collect(toList());
+      testForR8(parameters.getBackend())
+          .noTreeShaking()
+          .noMinification()
+          .addKeepAllAttributes()
+          .setMinApi(parameters.getApiLevel())
+          .addProgramFiles(matchingClasses)
+          .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true)
+          .compile()
+          .run(parameters.getRuntime(), getMainClass(entry.getKey()))
+          .assertSuccessWithOutput(getExpectedResult(entry.getKey()));
+    }
+  }
+
   private D8TestCompileResult compileClassesWithD8ProgramClassesMatching(Matcher<String> matcher)
       throws Exception {
     List<Path> matchingClasses =