Implement -renamesourcefileattribute with the source file provider API.

Bug: 201269335
Change-Id: I1cdc8b562dd9505af1e32f80fa2b9839a86cf9e4
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8617b7e..0516577 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -778,7 +778,7 @@
 
       // Overwrite SourceFile if specified. This step should be done after IR conversion.
       timing.begin("Rename SourceFile");
-      new SourceFileRewriter(appView, appView.appInfo().app()).run();
+      new SourceFileRewriter(appView).run();
       timing.end();
 
       // If a method filter is present don't produce output since the application is likely partial.
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 5441a9e..dab9181 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -321,7 +321,7 @@
     }
   }
 
-  private SourceFileEnvironment createSourceFileEnvironment(ProguardMapId proguardMapId) {
+  public static SourceFileEnvironment createSourceFileEnvironment(ProguardMapId proguardMapId) {
     if (proguardMapId == null) {
       return new SourceFileEnvironment() {
         @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index f211a3b..22eaec8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -643,7 +643,9 @@
   public final DexProto deserializeLambdaMethodProto =
       createProto(objectType, serializedLambdaType);
 
-  public final DexString defaultSourceFileAttribute = createString("SourceFile");
+  public final String defaultSourceFileAttributeString = "SourceFile";
+  public final DexString defaultSourceFileAttribute =
+      createString(defaultSourceFileAttributeString);
 
   // Dex system annotations.
   // See https://source.android.com/devices/tech/dalvik/dex-format.html#system-annotation
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 c3677ba..7b8da09 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -7,6 +7,7 @@
 
 import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.SourceFileEnvironment;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Marker;
@@ -40,6 +41,7 @@
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.ProguardMapSupplier;
+import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.utils.AsmUtils;
@@ -131,16 +133,24 @@
   }
 
   private void writeApplication(ClassFileConsumer consumer) {
-    if (proguardMapSupplier != null && options.proguardMapConsumer != null) {
-      marker.setPgMapId(proguardMapSupplier.writeProguardMap().getId());
+    ProguardMapId proguardMapId =
+        (proguardMapSupplier != null && options.proguardMapConsumer != null)
+            ? proguardMapSupplier.writeProguardMap()
+            : null;
+    if (proguardMapId != null) {
+      marker.setPgMapId(proguardMapId.getId());
     }
     Optional<String> markerString =
         includeMarker(marker) ? Optional.of(marker.toString()) : Optional.empty();
+    SourceFileEnvironment sourceFileEnvironment = null;
+    if (options.sourceFileProvider != null) {
+      sourceFileEnvironment = ApplicationWriter.createSourceFileEnvironment(proguardMapId);
+    }
     LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView);
     for (DexProgramClass clazz : application.classes()) {
       assert SyntheticNaming.verifyNotInternalSynthetic(clazz.getType());
       try {
-        writeClass(clazz, consumer, rewriter, markerString);
+        writeClass(clazz, consumer, rewriter, markerString, sourceFileEnvironment);
       } catch (ClassTooLargeException e) {
         throw appView
             .options()
@@ -172,14 +182,21 @@
       DexProgramClass clazz,
       ClassFileConsumer consumer,
       LensCodeRewriterUtils rewriter,
-      Optional<String> markerString) {
+      Optional<String> markerString,
+      SourceFileEnvironment sourceFileEnvironment) {
     ClassWriter writer = new ClassWriter(0);
     if (markerString.isPresent()) {
       int markerStringPoolIndex = writer.newConst(markerString.get());
       assert markerStringPoolIndex == MARKER_STRING_CONSTANT_POOL_INDEX;
     }
+    String sourceFile;
+    if (options.sourceFileProvider == null) {
+      sourceFile = clazz.sourceFile != null ? clazz.sourceFile.toString() : null;
+    } else {
+      sourceFile = options.sourceFileProvider.get(sourceFileEnvironment);
+    }
     String sourceDebug = getSourceDebugExtension(clazz.annotations());
-    writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, sourceDebug);
+    writer.visitSource(sourceFile, sourceDebug);
     CfVersion version = getClassFileVersion(clazz);
     if (version.isGreaterThanOrEqualTo(CfVersion.V1_8)) {
       // JDK8 and after ignore ACC_SUPER so unset it.
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index aed0499..696bce1 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -3,71 +3,73 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.SourceFileEnvironment;
+import com.android.tools.r8.SourceFileProvider;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexString;
 
-/**
- * Visit program {@link DexClass}es and replace their sourceFile with the given string.
- *
- * If -keepattribute SourceFile is not set, we rather remove that attribute.
- */
+/** Computes the source file provider based on the proguard configuration if none is set. */
 public class SourceFileRewriter {
 
   private final AppView<?> appView;
-  private final DexApplication application;
 
-  public SourceFileRewriter(AppView<?> appView, DexApplication application) {
+  public SourceFileRewriter(AppView<?> appView) {
     this.appView = appView;
-    this.application = application;
   }
 
   public void run() {
-    if (!appView.options().getProguardConfiguration().getKeepAttributes().sourceFile) {
-      doRenaming(getDefaultSourceFileAttribute());
-    } else if (appView.options().forceProguardCompatibility) {
-      runCompat();
-    } else {
-      runNonCompat();
+    if (appView.options().sourceFileProvider != null) {
+      return;
     }
+    appView.options().sourceFileProvider = computeSourceFileProvider();
   }
 
-  private void runCompat() {
+  public SourceFileProvider computeSourceFileProvider() {
+    if (!appView.options().getProguardConfiguration().getKeepAttributes().sourceFile) {
+      return rewriteToDefaultSourceFile();
+    }
+    if (appView.options().forceProguardCompatibility) {
+      return computeCompatProvider();
+    }
+    return computeNonCompatProvider();
+  }
+
+  private SourceFileProvider computeCompatProvider() {
     // Compatibility mode will only apply -renamesourcefileattribute when minifying names.
     if (appView.options().isMinifying()) {
       String renaming = getRenameSourceFileAttribute();
       if (renaming != null) {
-        doRenaming(renaming);
+        return rewriteTo(renaming);
       }
     }
+    return null;
   }
 
-  private void runNonCompat() {
+  private SourceFileProvider computeNonCompatProvider() {
     String renaming = getRenameSourceFileAttribute();
     if (renaming != null) {
-      doRenaming(renaming);
-    } else if (appView.options().isMinifying()) {
-      // TODO(b/202367773): This should also apply if optimizing.
-      doRenaming(getDefaultSourceFileAttribute());
+      return rewriteTo(renaming);
     }
+    if (appView.options().isMinifying()) {
+      // TODO(b/202367773): This should also apply if optimizing.
+      return rewriteToDefaultSourceFile();
+    }
+    return null;
   }
 
   private String getRenameSourceFileAttribute() {
     return appView.options().getProguardConfiguration().getRenameSourceFileAttribute();
   }
 
-  private DexString getDefaultSourceFileAttribute() {
-    return appView.dexItemFactory().defaultSourceFileAttribute;
+  private SourceFileProvider rewriteToDefaultSourceFile() {
+    return rewriteTo(appView.dexItemFactory().defaultSourceFileAttributeString);
   }
 
-  private void doRenaming(String renaming) {
-    doRenaming(appView.dexItemFactory().createString(renaming));
-  }
-
-  private void doRenaming(DexString renaming) {
-    for (DexClass clazz : application.classes()) {
-      clazz.sourceFile = renaming;
-    }
+  private SourceFileProvider rewriteTo(String renaming) {
+    return new SourceFileProvider() {
+      @Override
+      public String get(SourceFileEnvironment environment) {
+        return renaming;
+      }
+    };
   }
 }