Resolve misc. issues in repackaging

Bug: 165783399
Change-Id: I03f46a22129dead0a492e15a0ef8524cc82e894e
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 1605f20..500da1e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -415,13 +415,11 @@
   public DexType replacePackage(String newPackageDescriptor, DexItemFactory dexItemFactory) {
     assert isClassType();
     String descriptorString = toDescriptorString();
-    int lastPackageSeparator = descriptorString.lastIndexOf('/');
-    String newDescriptorString = "L" + newPackageDescriptor + "/";
-    if (lastPackageSeparator >= 0) {
-      newDescriptorString += descriptorString.substring(lastPackageSeparator + 1);
-    } else {
-      newDescriptorString += descriptorString.substring(1);
+    String newDescriptorString = "L";
+    if (!newPackageDescriptor.isEmpty()) {
+      newDescriptorString += newPackageDescriptor + "/";
     }
+    newDescriptorString += DescriptorUtils.getSimpleClassNameFromDescriptor(descriptorString) + ";";
     return dexItemFactory.createType(newDescriptorString);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java b/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
index 74f9880..cd3baa0 100644
--- a/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
+import java.util.function.Consumer;
 import org.objectweb.asm.ClassWriter;
 
 /** Representation of an entry in the Java InnerClasses attribute table. */
@@ -38,6 +39,15 @@
     this.innerName = innerName;
   }
 
+  public void forEachType(Consumer<DexType> consumer) {
+    if (inner != null) {
+      consumer.accept(inner);
+    }
+    if (outer != null) {
+      consumer.accept(outer);
+    }
+  }
+
   public boolean isNamed() {
     return innerName != null;
   }
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 7f7ae1e..5868aa8 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -139,7 +139,7 @@
     // Always repackage outer classes first, if any.
     InnerClassAttribute innerClassAttribute = classToRepackage.getInnerClassAttributeForThisClass();
     DexProgramClass outerClass = null;
-    if (innerClassAttribute != null) {
+    if (innerClassAttribute != null && innerClassAttribute.getOuter() != null) {
       outerClass = asProgramClassOrNull(appView.definitionFor(innerClassAttribute.getOuter()));
       if (outerClass != null) {
         if (pkg.contains(outerClass)) {
@@ -171,7 +171,10 @@
     if (proguardConfiguration.getPackageObfuscationMode().isRepackageClasses()) {
       return newPackageDescriptor;
     }
-    newPackageDescriptor += "/" + pkg.getLastPackageName();
+    if (!newPackageDescriptor.isEmpty()) {
+      newPackageDescriptor += "/";
+    }
+    newPackageDescriptor += pkg.getLastPackageName();
     String finalPackageDescriptor = newPackageDescriptor;
     for (int i = 1; seenPackageDescriptors.contains(finalPackageDescriptor); i++) {
       finalPackageDescriptor = newPackageDescriptor + INNER_CLASS_SEPARATOR + i;
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index 5152bef..b50015b 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -110,19 +110,12 @@
     }
 
     // Trace the references to the inner and outer classes.
-    clazz
-        .getInnerClasses()
-        .forEach(
-            innerClassAttribute -> {
-              registry.registerNullableTypeReference(innerClassAttribute.getInner());
-              registry.registerNullableTypeReference(innerClassAttribute.getOuter());
-            });
+    clazz.getInnerClasses().forEach(registry::registerInnerClassAttribute);
 
     // Trace the references from the enclosing method attribute.
     EnclosingMethodAttribute attr = clazz.getEnclosingMethodAttribute();
     if (attr != null) {
-      registry.registerNullableTypeReference(attr.getEnclosingClass());
-      registry.registerNullableMethodReference(attr.getEnclosingMethod());
+      registry.registerEnclosingMethodAttribute(attr);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
index 3591ff7..f519f01 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
@@ -154,9 +154,9 @@
     List<InnerClassAttribute> newInnerClassAttributes = new ArrayList<>();
     for (InnerClassAttribute innerClassAttribute : innerClassAttributes) {
       DexType innerClassType = innerClassAttribute.getInner();
-      DexType newInnerClassType = fixupType(innerClassType);
+      DexType newInnerClassType = fixupTypeOrNull(innerClassType);
       DexType outerClassType = innerClassAttribute.getOuter();
-      DexType newOuterClassType = fixupType(outerClassType);
+      DexType newOuterClassType = fixupTypeOrNull(outerClassType);
       newInnerClassAttributes.add(
           new InnerClassAttribute(
               innerClassAttribute.getAccess(),
@@ -247,6 +247,10 @@
     return changed ? newSynthesizedFrom : synthesizedFrom;
   }
 
+  private DexType fixupTypeOrNull(DexType type) {
+    return type != null ? fixupType(type) : null;
+  }
+
   private DexType fixupType(DexType type) {
     if (type.isArrayType()) {
       DexType base = type.toBaseType(dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index 72810b0..b27182d 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.repackaging;
 
+import static com.google.common.base.Predicates.alwaysTrue;
+
 import com.android.tools.r8.graph.AccessFlags;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
@@ -12,12 +14,18 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.MemberResolutionResult;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMember;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.graph.SuccessfulMemberResolutionResult;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 public class RepackagingUseRegistry extends UseRegistry {
 
@@ -65,14 +73,12 @@
     registerMemberAccess(appInfo.resolveField(field));
   }
 
-  public void registerMethodReference(DexMethod method) {
-    registerMemberAccess(appInfo.unsafeResolveMethodDueToDexFormat(method));
-  }
-
-  public void registerNullableMethodReference(DexMethod method) {
-    if (method != null) {
-      registerMethodReference(method);
-    }
+  public ProgramMethod registerMethodReference(DexMethod method) {
+    ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
+    registerMemberAccess(resolutionResult);
+    return resolutionResult.isSingleResolution()
+        ? resolutionResult.asSingleResolution().getResolvedProgramMethod()
+        : null;
   }
 
   public void registerMemberAccess(MemberResolutionResult<?, ?> resolutionResult) {
@@ -102,8 +108,12 @@
   }
 
   private void registerTypeAccess(DexType type) {
+    registerTypeAccess(type, this::registerTypeAccess);
+  }
+
+  private void registerTypeAccess(DexType type, Consumer<DexClass> consumer) {
     if (type.isArrayType()) {
-      registerTypeAccess(type.toBaseType(appInfo.dexItemFactory()));
+      registerTypeAccess(type.toBaseType(appInfo.dexItemFactory()), consumer);
       return;
     }
     if (type.isPrimitiveType() || type.isVoidType()) {
@@ -112,18 +122,22 @@
     assert type.isClassType();
     DexClass clazz = appInfo.definitionFor(type);
     if (clazz != null) {
-      registerTypeAccess(clazz);
+      consumer.accept(clazz);
     }
   }
 
   private void registerTypeAccess(DexClass clazz) {
+    registerTypeAccess(clazz, this::isOnlyAccessibleFromSamePackage);
+  }
+
+  private void registerTypeAccess(DexClass clazz, Predicate<DexProgramClass> predicate) {
     // We only want to connect the current method node to the class node if the access requires the
     // two nodes to be in the same package. Therefore, we ignore accesses to non-program classes
     // and program classes outside the current package.
     DexProgramClass programClass = clazz.asProgramClass();
     if (programClass != null) {
       RepackagingConstraintGraph.Node classNode = constraintGraph.getNode(programClass);
-      if (classNode != null && isOnlyAccessibleFromSamePackage(programClass)) {
+      if (classNode != null && predicate.test(programClass)) {
         node.addNeighbor(classNode);
       }
     }
@@ -189,14 +203,33 @@
     registerTypeAccess(type);
   }
 
-  public void registerNullableTypeReference(DexType type) {
-    if (type != null) {
-      registerTypeReference(type);
-    }
-  }
-
   @Override
   public void registerInstanceOf(DexType type) {
     registerTypeAccess(type);
   }
+
+  public void registerEnclosingMethodAttribute(EnclosingMethodAttribute enclosingMethodAttribute) {
+    // For references in enclosing method attributes we add an edge from the context to the
+    // referenced item even if the item would be accessible from another package, to make sure that
+    // we don't split such classes into different packages.
+    if (enclosingMethodAttribute.getEnclosingClass() != null) {
+      registerTypeAccess(
+          enclosingMethodAttribute.getEnclosingClass(),
+          clazz -> registerTypeAccess(clazz, alwaysTrue()));
+    }
+    if (enclosingMethodAttribute.getEnclosingMethod() != null) {
+      ProgramMethod method = registerMethodReference(enclosingMethodAttribute.getEnclosingMethod());
+      if (method != null) {
+        registerTypeAccess(method.getHolder(), alwaysTrue());
+      }
+    }
+  }
+
+  public void registerInnerClassAttribute(InnerClassAttribute innerClassAttribute) {
+    // For references in inner class attributes we add an edge from the context to the referenced
+    // class even if the referenced class would be accessible from another package, to make sure
+    // that we don't split such classes into different packages.
+    innerClassAttribute.forEachType(
+        type -> registerTypeAccess(type, clazz -> registerTypeAccess(clazz, alwaysTrue())));
+  }
 }