Reland "Fix inadequate detection of package private accesses"

This reverts commit b9028365137393aa8632b29dbabcbec522ebad8f.

Bug: b/243955191
Change-Id: If19f6bb899ced9d0dcb0598c06b3d7814542c0b1
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 2ff1bfb..42f4731 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -279,7 +279,7 @@
         new SameNestHost(appView),
         new SameParentClass(),
         new SyntheticItemsPolicy(appView, mode),
-        new RespectPackageBoundaries(appView),
+        new RespectPackageBoundaries(appView, mode),
         new NoDifferentApiReferenceLevel(appView),
         new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
         new NoWeakerAccessPrivileges(appView, immediateSubtypingInfo),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index e3986c8..ded7e1d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -7,11 +7,13 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.MergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.shaking.VerticalClassMerger.IllegalAccessDetector;
@@ -25,9 +27,11 @@
 public class RespectPackageBoundaries extends MultiClassPolicy {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
+  private final Mode mode;
 
-  public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView) {
+  public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
     this.appView = appView;
+    this.mode = mode;
   }
 
   boolean shouldRestrictMergingAcrossPackageBoundary(DexProgramClass clazz) {
@@ -83,7 +87,29 @@
             method -> {
               boolean foundIllegalAccess =
                   method.registerCodeReferencesWithResult(
-                      new IllegalAccessDetector(appView, method));
+                      new IllegalAccessDetector(appView, method) {
+
+                        @Override
+                        protected boolean checkRewrittenFieldType(DexClassAndField field) {
+                          if (mode.isInitial()) {
+                            // If the type of the field is package private, we need to keep the
+                            // current class in its package in case we end up synthesizing a
+                            // check-cast for the field type after relaxing the type of the field
+                            // after instance field merging.
+                            DexType fieldBaseType = field.getType().toBaseType(dexItemFactory());
+                            if (fieldBaseType.isClassType()) {
+                              DexClass fieldBaseClass = appView.definitionFor(fieldBaseType);
+                              if (fieldBaseClass == null || !fieldBaseClass.isPublic()) {
+                                return setFoundPackagePrivateAccess();
+                              }
+                            }
+                          } else {
+                            // No relaxing of field types, hence no insertion of casts where we need
+                            // to guarantee visibility.
+                          }
+                          return continueSearchForPackagePrivateAccess();
+                        }
+                      });
               if (foundIllegalAccess) {
                 return TraversalContinuation.doBreak();
               }
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 95ba96a..a875269 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
@@ -2106,8 +2107,8 @@
     }
   }
 
-  // Searches for a reference to a non-public class, field or method declared in the same package
-  // as [source].
+  // Searches for a reference to a non-private, non-public class, field or method declared in the
+  // same package as [source].
   public static class IllegalAccessDetector extends UseRegistryWithResult<Boolean, ProgramMethod> {
 
     private final AppView<? extends AppInfoWithClassHierarchy> appView;
@@ -2118,106 +2119,144 @@
       this.appView = appView;
     }
 
-    private boolean checkFieldReference(DexField field) {
-      DexType baseType =
-          appView.graphLens().lookupType(field.holder.toBaseType(appView.dexItemFactory()));
-      if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
-        if (checkTypeReference(field.holder) || checkTypeReference(field.type)) {
-          return true;
-        }
+    protected boolean checkFoundPackagePrivateAccess() {
+      assert getResult();
+      return true;
+    }
 
-        DexEncodedField definition = appView.appInfo().resolveField(field).getResolvedField();
-        if (definition == null || !definition.accessFlags.isPublic()) {
-          setResult(true);
-          return true;
-        }
-      }
+    protected boolean setFoundPackagePrivateAccess() {
+      setResult(true);
+      return true;
+    }
+
+    protected static boolean continueSearchForPackagePrivateAccess() {
       return false;
     }
 
-    private boolean checkMethodReference(DexMethod method, OptionalBool isInterface) {
-      DexType baseType =
-          appView.graphLens().lookupType(method.holder.toBaseType(appView.dexItemFactory()));
-      if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
-        if (checkTypeReference(method.holder)
-            || checkTypeReference(method.proto.returnType)
-            || Iterables.any(method.getParameters(), this::checkTypeReference)) {
-          return true;
-        }
+    private boolean checkFieldReference(DexField field) {
+      return checkRewrittenFieldReference(appView.graphLens().lookupField(field));
+    }
 
-        MethodResolutionResult resolutionResult =
-            isInterface.isUnknown()
-                ? appView.appInfo().unsafeResolveMethodDueToDexFormat(method)
-                : appView.appInfo().resolveMethod(method, isInterface.isTrue());
-        if (!resolutionResult.isSingleResolution()
-            || !resolutionResult.asSingleResolution().getResolvedMethod().isPublic()) {
-          setResult(true);
-          return true;
+    private boolean checkRewrittenFieldReference(DexField field) {
+      assert field.getHolderType().isClassType();
+      DexType fieldHolder = field.getHolderType();
+      if (fieldHolder.isSamePackage(getContext().getHolderType())) {
+        if (checkRewrittenTypeReference(fieldHolder)) {
+          return checkFoundPackagePrivateAccess();
+        }
+        DexClassAndField resolvedField = appView.appInfo().resolveField(field).getResolutionPair();
+        if (resolvedField == null) {
+          return setFoundPackagePrivateAccess();
+        }
+        if (resolvedField.getHolder() != getContext().getHolder()
+            && !resolvedField.getAccessFlags().isPublic()) {
+          return setFoundPackagePrivateAccess();
+        }
+        if (checkRewrittenFieldType(resolvedField)) {
+          return checkFoundPackagePrivateAccess();
         }
       }
-      return false;
+      return continueSearchForPackagePrivateAccess();
+    }
+
+    protected boolean checkRewrittenFieldType(DexClassAndField field) {
+      return continueSearchForPackagePrivateAccess();
+    }
+
+    private boolean checkRewrittenMethodReference(
+        DexMethod rewrittenMethod, OptionalBool isInterface) {
+      DexType baseType = rewrittenMethod.getHolderType().toBaseType(appView.dexItemFactory());
+      if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
+        if (checkTypeReference(rewrittenMethod.getHolderType())) {
+          return checkFoundPackagePrivateAccess();
+        }
+        MethodResolutionResult resolutionResult =
+            isInterface.isUnknown()
+                ? appView.appInfo().unsafeResolveMethodDueToDexFormat(rewrittenMethod)
+                : appView.appInfo().resolveMethod(rewrittenMethod, isInterface.isTrue());
+        if (!resolutionResult.isSingleResolution()) {
+          return setFoundPackagePrivateAccess();
+        }
+        DexClassAndMethod resolvedMethod =
+            resolutionResult.asSingleResolution().getResolutionPair();
+        if (resolvedMethod.getHolder() != getContext().getHolder()
+            && !resolvedMethod.getAccessFlags().isPublic()) {
+          return setFoundPackagePrivateAccess();
+        }
+      }
+      return continueSearchForPackagePrivateAccess();
     }
 
     private boolean checkTypeReference(DexType type) {
-      DexType baseType = appView.graphLens().lookupType(type.toBaseType(appView.dexItemFactory()));
+      return internalCheckTypeReference(type, appView.graphLens());
+    }
+
+    private boolean checkRewrittenTypeReference(DexType type) {
+      return internalCheckTypeReference(type, GraphLens.getIdentityLens());
+    }
+
+    private boolean internalCheckTypeReference(DexType type, GraphLens graphLens) {
+      DexType baseType = graphLens.lookupType(type.toBaseType(appView.dexItemFactory()));
       if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
         DexClass clazz = appView.definitionFor(baseType);
-        if (clazz == null || !clazz.accessFlags.isPublic()) {
-          setResult(true);
-          return true;
+        if (clazz == null || !clazz.isPublic()) {
+          return setFoundPackagePrivateAccess();
         }
       }
-      return false;
+      return continueSearchForPackagePrivateAccess();
     }
 
     @Override
     public void registerInitClass(DexType clazz) {
-      checkTypeReference(clazz);
+      if (appView.initClassLens().isFinal()) {
+        // The InitClass lens is always rewritten up until the most recent graph lens, so first map
+        // the class type to the most recent graph lens.
+        DexType rewrittenType = appView.graphLens().lookupType(clazz);
+        DexField initClassField = appView.initClassLens().getInitClassField(rewrittenType);
+        checkRewrittenFieldReference(initClassField);
+      } else {
+        checkTypeReference(clazz);
+      }
     }
 
     @Override
     public void registerInvokeVirtual(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), VIRTUAL);
-      checkMethodReference(lookup.getReference(), OptionalBool.FALSE);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeVirtual(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.FALSE);
     }
 
     @Override
     public void registerInvokeDirect(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), DIRECT);
-      checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeDirect(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
     }
 
     @Override
     public void registerInvokeStatic(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), Type.STATIC);
-      checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeStatic(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
     }
 
     @Override
     public void registerInvokeInterface(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), Type.INTERFACE);
-      checkMethodReference(lookup.getReference(), OptionalBool.TRUE);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeInterface(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.TRUE);
     }
 
     @Override
     public void registerInvokeSuper(DexMethod method) {
-      MethodLookupResult lookup =
-          appView.graphLens().lookupMethod(method, getContext().getReference(), Type.SUPER);
-      checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
+      MethodLookupResult lookup = appView.graphLens().lookupInvokeSuper(method, getContext());
+      checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
     }
 
     @Override
     public void registerInstanceFieldWrite(DexField field) {
-      checkFieldReference(appView.graphLens().lookupField(field));
+      checkFieldReference(field);
     }
 
     @Override
     public void registerInstanceFieldRead(DexField field) {
-      checkFieldReference(appView.graphLens().lookupField(field));
+      checkFieldReference(field);
     }
 
     @Override
@@ -2227,12 +2266,12 @@
 
     @Override
     public void registerStaticFieldRead(DexField field) {
-      checkFieldReference(appView.graphLens().lookupField(field));
+      checkFieldReference(field);
     }
 
     @Override
     public void registerStaticFieldWrite(DexField field) {
-      checkFieldReference(appView.graphLens().lookupField(field));
+      checkFieldReference(field);
     }
 
     @Override
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 907ca27..483f18b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.kotlin.TestKotlinClass.AccessorKind;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -219,7 +220,9 @@
     runTest(PROPERTIES_PACKAGE_NAME, mainClass)
         .inspect(
             inspector -> {
-              if (allowAccessModification) {
+              if (allowAccessModification
+                  || (testParameters.isDexRuntime()
+                      && testParameters.getApiLevel().isGreaterThan(AndroidApiLevel.B))) {
                 checkClassIsRemoved(inspector, testedClass.getOuterClassName());
                 return;
               }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index 59ba99b..527e568 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -509,7 +510,9 @@
             inspector -> {
               checkClassIsRemoved(inspector, testedClass.getClassName());
 
-              if (allowAccessModification) {
+              if (allowAccessModification
+                  || (testParameters.isDexRuntime()
+                      && testParameters.getApiLevel().isGreaterThan(AndroidApiLevel.B))) {
                 checkClassIsRemoved(inspector, testedClass.getOuterClassName());
                 return;
               }