Move scope helpers to static methods.

Change-Id: I646a21c88ba042bf074d4a009608505deb31f797
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index c03cb20..d5a5e01 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -269,21 +269,19 @@
 
   private void parseMappingInformation(
       BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
-    MappingInformation info =
-        MappingInformation.fromJsonObject(
-            version, parseJsonInComment(), diagnosticsHandler, lineNo, implicitSingletonScope);
-    if (info == null) {
-      return;
-    }
-    MetaInfMappingInformation generatorInfo = info.asMetaInfMappingInformation();
-    if (generatorInfo != null) {
-      version = generatorInfo.getMapVersion();
-    }
-    if (info.isScopedMappingInformation()) {
-      info.asScopedMappingInformation().forEach(onMappingInfo);
-    } else {
-      onMappingInfo.accept(ScopeReference.globalScope(), info);
-    }
+    MappingInformation.fromJsonObject(
+        version,
+        parseJsonInComment(),
+        diagnosticsHandler,
+        lineNo,
+        implicitSingletonScope,
+        (reference, info) -> {
+          MetaInfMappingInformation generatorInfo = info.asMetaInfMappingInformation();
+          if (generatorInfo != null) {
+            version = generatorInfo.getMapVersion();
+          }
+          onMappingInfo.accept(reference, info);
+        });
   }
 
   private void parseMemberMappings(Builder mapBuilder, ClassNaming.Builder classNamingBuilder)
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
index a49c4de..be80029 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
@@ -6,34 +6,23 @@
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.naming.MapVersion;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.naming.mappinginformation.ScopedMappingInformation.ScopeReference;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
+import java.util.function.BiConsumer;
 
-public class CompilerSynthesizedMappingInformation extends ScopedMappingInformation {
+public class CompilerSynthesizedMappingInformation extends MappingInformation {
 
   public static final String ID = "com.android.tools.r8.synthesized";
 
-  public static class Builder extends ScopedMappingInformation.Builder<Builder> {
-
-    @Override
-    public String getId() {
-      return ID;
-    }
-
-    @Override
-    public Builder self() {
-      return this;
-    }
+  public static class Builder {
 
     public CompilerSynthesizedMappingInformation build() {
-      return new CompilerSynthesizedMappingInformation(buildScope());
+      return new CompilerSynthesizedMappingInformation();
     }
   }
 
-  private CompilerSynthesizedMappingInformation(ImmutableList<ScopeReference> scope) {
-    super(scope);
-  }
+  private CompilerSynthesizedMappingInformation() {}
 
   public static Builder builder() {
     return new Builder();
@@ -60,22 +49,27 @@
   }
 
   @Override
-  protected JsonObject serializeToJsonObject(JsonObject object) {
+  public String serialize() {
+    JsonObject object = new JsonObject();
     object.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
-    return object;
+    return object.toString();
   }
 
-  public static CompilerSynthesizedMappingInformation deserialize(
+  public static void deserialize(
       MapVersion version,
       JsonObject object,
       DiagnosticsHandler diagnosticsHandler,
       int lineNumber,
-      ScopeReference implicitSingletonScope) {
+      ScopeReference implicitSingletonScope,
+      BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
     if (version.isLessThan(MapVersion.MapVersionExperimental)) {
-      return null;
+      return;
     }
-    return builder()
-        .deserializeFromJsonObject(object, implicitSingletonScope, diagnosticsHandler, lineNumber)
-        .build();
+    CompilerSynthesizedMappingInformation info = builder().build();
+    for (ScopeReference reference :
+        ScopedMappingInformation.deserializeScope(
+            object, implicitSingletonScope, diagnosticsHandler, lineNumber, version)) {
+      onMappingInfo.accept(reference, info);
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
index 39d0072..1133160 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
@@ -6,20 +6,21 @@
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.naming.MapVersion;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.naming.mappinginformation.ScopedMappingInformation.ClassScopeReference;
+import com.android.tools.r8.naming.mappinginformation.ScopedMappingInformation.ScopeReference;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
+import java.util.function.BiConsumer;
 
-public class FileNameInformation extends ScopedMappingInformation {
+public class FileNameInformation extends MappingInformation {
 
   private final String fileName;
 
   public static final String ID = "sourceFile";
   static final String FILE_NAME_KEY = "fileName";
 
-  private FileNameInformation(String fileName, ImmutableList<ScopeReference> scopeReferences) {
-    super(scopeReferences);
+  private FileNameInformation(String fileName) {
     this.fileName = fileName;
   }
 
@@ -47,43 +48,36 @@
     return !information.isFileNameInformation();
   }
 
-  public static FileNameInformation build(ScopeReference classScope, String fileName) {
-    return new FileNameInformation(fileName, ImmutableList.of(classScope));
+  public static FileNameInformation build(String fileName) {
+    return new FileNameInformation(fileName);
   }
 
-  // Hard override of serialize as there is no current support for scope in source-file info.
-  // This should be removed for experimental support of scope in the external format.
   @Override
   public String serialize() {
-    return serializeToJsonObject(new JsonObject()).toString();
-  }
-
-  @Override
-  protected JsonObject serializeToJsonObject(JsonObject object) {
+    JsonObject object = new JsonObject();
     object.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
     object.add(FILE_NAME_KEY, new JsonPrimitive(fileName));
-    return object;
+    return object.toString();
   }
 
-  public static FileNameInformation deserialize(
+  public static void deserialize(
       MapVersion version,
       JsonObject object,
       DiagnosticsHandler diagnosticsHandler,
       int lineNumber,
-      ScopeReference implicitSingletonScope) {
+      ScopeReference implicitSingletonScope,
+      BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
     assert implicitSingletonScope instanceof ClassScopeReference;
     try {
       JsonElement fileName =
           getJsonElementFromObject(object, diagnosticsHandler, lineNumber, FILE_NAME_KEY, ID);
-      if (fileName == null) {
-        return null;
+      if (fileName != null) {
+        onMappingInfo.accept(
+            implicitSingletonScope, new FileNameInformation(fileName.getAsString()));
       }
-      return new FileNameInformation(
-          fileName.getAsString(), ImmutableList.of(implicitSingletonScope));
     } catch (UnsupportedOperationException | IllegalStateException ignored) {
       diagnosticsHandler.info(
           MappingInformationDiagnostics.invalidValueForObjectWithId(lineNumber, FILE_NAME_KEY, ID));
-      return null;
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index beb0b29..0495ad2 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -9,35 +9,16 @@
 import com.android.tools.r8.naming.mappinginformation.ScopedMappingInformation.ScopeReference;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
+import java.util.function.BiConsumer;
 
 public abstract class MappingInformation {
 
-  static final int NO_LINE_NUMBER = -1;
-
   public static final String MAPPING_ID_KEY = "id";
 
-  private final int lineNumber;
-
-  MappingInformation(int lineNumber) {
-    this.lineNumber = lineNumber;
-  }
-
   public abstract String getId();
 
-  public int getLineNumber() {
-    return lineNumber;
-  }
-
   public abstract String serialize();
 
-  public boolean isScopedMappingInformation() {
-    return false;
-  }
-
-  public ScopedMappingInformation asScopedMappingInformation() {
-    return null;
-  }
-
   public boolean isMetaInfMappingInformation() {
     return false;
   }
@@ -64,52 +45,62 @@
 
   public abstract boolean allowOther(MappingInformation information);
 
-  public static MappingInformation fromJsonObject(
+  public static void fromJsonObject(
       MapVersion version,
       JsonObject object,
       DiagnosticsHandler diagnosticsHandler,
       int lineNumber,
-      ScopeReference implicitSingletonScope) {
+      ScopeReference implicitSingletonScope,
+      BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
     if (object == null) {
       diagnosticsHandler.info(MappingInformationDiagnostics.notValidJson(lineNumber));
-      return null;
+      return;
     }
     JsonElement id = object.get(MAPPING_ID_KEY);
     if (id == null) {
       diagnosticsHandler.info(
           MappingInformationDiagnostics.noKeyInJson(lineNumber, MAPPING_ID_KEY));
-      return null;
+      return;
     }
     String idString = id.getAsString();
     if (idString == null) {
       diagnosticsHandler.info(
           MappingInformationDiagnostics.notValidString(lineNumber, MAPPING_ID_KEY));
-      return null;
+      return;
     }
-    return deserialize(
-        idString, version, object, diagnosticsHandler, lineNumber, implicitSingletonScope);
+    deserialize(
+        idString,
+        version,
+        object,
+        diagnosticsHandler,
+        lineNumber,
+        implicitSingletonScope,
+        onMappingInfo);
   }
 
-  private static MappingInformation deserialize(
+  private static void deserialize(
       String id,
       MapVersion version,
       JsonObject object,
       DiagnosticsHandler diagnosticsHandler,
       int lineNumber,
-      ScopeReference implicitSingletonScope) {
+      ScopeReference implicitSingletonScope,
+      BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
     switch (id) {
       case MetaInfMappingInformation.ID:
-        return MetaInfMappingInformation.deserialize(
-            version, object, diagnosticsHandler, lineNumber);
+        MetaInfMappingInformation.deserialize(
+            version, object, diagnosticsHandler, lineNumber, onMappingInfo);
+        return;
       case FileNameInformation.ID:
-        return FileNameInformation.deserialize(
-            version, object, diagnosticsHandler, lineNumber, implicitSingletonScope);
+        FileNameInformation.deserialize(
+            version, object, diagnosticsHandler, lineNumber, implicitSingletonScope, onMappingInfo);
+        return;
       case CompilerSynthesizedMappingInformation.ID:
-        return CompilerSynthesizedMappingInformation.deserialize(
-            version, object, diagnosticsHandler, lineNumber, implicitSingletonScope);
+        CompilerSynthesizedMappingInformation.deserialize(
+            version, object, diagnosticsHandler, lineNumber, implicitSingletonScope, onMappingInfo);
+        return;
       default:
         diagnosticsHandler.info(MappingInformationDiagnostics.noHandlerFor(lineNumber, id));
-        return null;
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java
index abe2990..75a4753 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java
@@ -8,8 +8,10 @@
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.mappinginformation.ScopedMappingInformation.ScopeReference;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
+import java.util.function.BiConsumer;
 
 public class MetaInfMappingInformation extends MappingInformation {
 
@@ -19,7 +21,7 @@
   private final MapVersion mapVersion;
 
   public MetaInfMappingInformation(MapVersion mapVersion) {
-    super(NO_LINE_NUMBER);
+    super();
     this.mapVersion = mapVersion;
   }
 
@@ -55,22 +57,23 @@
     return result.toString();
   }
 
-  public static MetaInfMappingInformation deserialize(
+  public static void deserialize(
       MapVersion version,
       JsonObject object,
       DiagnosticsHandler diagnosticsHandler,
-      int lineNumber) {
+      int lineNumber,
+      BiConsumer<ScopeReference, MappingInformation> onMappingInfo) {
     // Parsing the generator information must support parsing at all map versions as it itself is
     // what establishes the version.
     String mapVersion = object.get(MAP_VERSION_KEY).getAsString();
     if (mapVersion == null) {
       noKeyForObjectWithId(lineNumber, MAP_VERSION_KEY, MAPPING_ID_KEY, ID);
-      return null;
+      return;
     }
     MapVersion mapVersion1 = MapVersion.fromName(mapVersion);
     if (mapVersion1 == null) {
-      return null;
+      return;
     }
-    return new MetaInfMappingInformation(mapVersion1);
+    onMappingInfo.accept(ScopeReference.globalScope(), new MetaInfMappingInformation(mapVersion1));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopedMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopedMappingInformation.java
index e3f43d5..d6eff65 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopedMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/ScopedMappingInformation.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -13,9 +14,10 @@
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
-import java.util.function.BiConsumer;
+import java.util.Collections;
+import java.util.List;
 
-public abstract class ScopedMappingInformation extends MappingInformation {
+public abstract class ScopedMappingInformation {
 
   // Abstraction for the items referenced in a scope.
   // We should consider passing in a scope reference factory.
@@ -118,77 +120,48 @@
     }
   }
 
-  public abstract static class Builder<B extends Builder<B>> {
-    public abstract String getId();
-
-    public abstract B self();
-
-    private final ImmutableList.Builder<ScopeReference> scope = ImmutableList.builder();
-
-    public B deserializeFromJsonObject(
-        JsonObject object,
-        ScopeReference implicitSingletonScope,
-        DiagnosticsHandler diagnosticsHandler,
-        int lineNumber) {
-      JsonArray scopeArray = object.getAsJsonArray(SCOPE_KEY);
-      if (scopeArray != null) {
-        for (JsonElement element : scopeArray) {
-          addScopeReference(ScopeReference.fromReferenceString(element.getAsString()));
-        }
-      } else if (implicitSingletonScope != null) {
-        addScopeReference(implicitSingletonScope);
-      } else {
-        diagnosticsHandler.info(
-            MappingInformationDiagnostics.noKeyForObjectWithId(
-                lineNumber, SCOPE_KEY, MAPPING_ID_KEY, getId()));
-      }
-      return self();
-    }
-
-    public B addScopeReference(ScopeReference reference) {
-      scope.add(reference);
-      return self();
-    }
-
-    public ImmutableList<ScopeReference> buildScope() {
-      return scope.build();
-    }
-  }
-
+  private static final MapVersion SCOPE_SUPPORTED = MapVersion.MapVersionExperimental;
   public static final String SCOPE_KEY = "scope";
 
-  private final ImmutableList<ScopeReference> scopeReferences;
-
-  public ScopedMappingInformation(ImmutableList<ScopeReference> scopeReferences) {
-    super(NO_LINE_NUMBER);
-    this.scopeReferences = scopeReferences;
-    assert !scopeReferences.isEmpty() : "Expected a scope. Global scope not yet in use.";
-  }
-
-  protected abstract JsonObject serializeToJsonObject(JsonObject object);
-
-  @Override
-  public final boolean isScopedMappingInformation() {
-    return true;
-  }
-
-  @Override
-  public final ScopedMappingInformation asScopedMappingInformation() {
-    return this;
-  }
-
-  public void forEach(BiConsumer<ScopeReference, MappingInformation> fn) {
-    for (ScopeReference reference : scopeReferences) {
-      fn.accept(reference, this);
+  public static List<ScopeReference> deserializeScope(
+      JsonObject object,
+      ScopeReference implicitSingletonScope,
+      DiagnosticsHandler diagnosticsHandler,
+      int lineNumber,
+      MapVersion version) {
+    // Prior to support, the scope is always the implicit scope.
+    if (version.isLessThan(SCOPE_SUPPORTED)) {
+      return Collections.singletonList(implicitSingletonScope);
     }
+    // If the scope key is absent, the implicit scope is assumed.
+    JsonArray scopeArray = object.getAsJsonArray(SCOPE_KEY);
+    if (scopeArray == null) {
+      return Collections.singletonList(implicitSingletonScope);
+    }
+    ImmutableList.Builder<ScopeReference> builder = ImmutableList.builder();
+    for (JsonElement element : scopeArray) {
+      builder.add(ScopeReference.fromReferenceString(element.getAsString()));
+    }
+    return builder.build();
   }
 
-  @Override
-  public String serialize() {
-    JsonObject object = serializeToJsonObject(new JsonObject());
+  public static void serializeScope(
+      JsonObject object,
+      ScopeReference currentImplicitScope,
+      List<ScopeReference> scopeReferences,
+      MapVersion version) {
+    assert !scopeReferences.isEmpty();
+    // If emitting a non-experimental version the scope is always implicit.
+    if (version.isLessThan(SCOPE_SUPPORTED)) {
+      assert currentImplicitScope.equals(scopeReferences.get(0));
+      return;
+    }
+    // If the scope matches the implicit scope don't add it explicitly.
+    if (scopeReferences.size() == 1 && scopeReferences.get(0).equals(currentImplicitScope)) {
+      return;
+    }
     JsonArray scopeArray = new JsonArray();
     scopeReferences.forEach(ref -> scopeArray.add(ref.toReferenceString()));
     object.add(SCOPE_KEY, scopeArray);
-    return object.toString();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 890107b..43fdc86 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -302,7 +302,7 @@
         if (!RetraceUtils.hasPredictableSourceFileName(clazz.toSourceString(), sourceFile)) {
           classNameMapperBuilder.addMappingInformation(
               classScope,
-              FileNameInformation.build(classScope, sourceFile),
+              FileNameInformation.build(sourceFile),
               conflictingInfo -> {
                 throw new Unreachable();
               });
@@ -312,7 +312,7 @@
       if (isSyntheticClass && appView.options().testing.enableExperimentalMapFileVersion) {
         classNameMapperBuilder.addMappingInformation(
             classScope,
-            CompilerSynthesizedMappingInformation.builder().addScopeReference(classScope).build(),
+            CompilerSynthesizedMappingInformation.builder().build(),
             conflictingInfo -> {
               throw new Unreachable();
             });