bazel syntax: avoid overloading of ParserInput.create

Now: ParserInput.from{String,CharArray,Latin1,UTF8}.

Also, avoid a handful of redundant array copies/conversions.

PiperOrigin-RevId: 319105574
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkActionFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkActionFactory.java
index f0b4cf4..b5576b0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkActionFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkActionFactory.java
@@ -688,10 +688,10 @@
     ImmutableList.Builder<Substitution> substitutionsBuilder = ImmutableList.builder();
     for (Map.Entry<String, String> substitution :
         Dict.cast(substitutionsUnchecked, String.class, String.class, "substitutions").entrySet()) {
-      // ParserInput.create(Path) uses Latin1 when reading BUILD files, which might
+      // Blaze calls ParserInput.fromLatin1 when reading BUILD files, which might
       // contain UTF-8 encoded symbols as part of template substitution.
       // As a quick fix, the substitution values are corrected before being passed on.
-      // In the long term, fixing ParserInput.create(Path) would be a better approach.
+      // In the long term, avoiding ParserInput.fromLatin would be a better approach.
       substitutionsBuilder.add(
           Substitution.of(substitution.getKey(), convertLatin1ToUtf8(substitution.getValue())));
     }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 70ca4e7..5a0fac6 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -524,9 +524,7 @@
     Globber globber =
         createLegacyGlobber(
             buildFile.asPath().getParentDirectory(), packageId, ImmutableSet.of(), locator);
-    ParserInput input =
-        ParserInput.create(
-            FileSystemUtils.convertFromLatin1(buildFileBytes), buildFile.asPath().toString());
+    ParserInput input = ParserInput.fromLatin1(buildFileBytes, buildFile.asPath().toString());
     // Options for processing BUILD files. (No prelude, so recordScope(true) is safe.)
     FileOptions options =
         FileOptions.builder()
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/ResolvedFileFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/ResolvedFileFunction.java
index 84caf2d..a1ac6eb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/ResolvedFileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/ResolvedFileFunction.java
@@ -71,7 +71,7 @@
 
         // parse
         StarlarkFile file =
-            StarlarkFile.parse(ParserInput.create(bytes, key.getPath().asPath().toString()));
+            StarlarkFile.parse(ParserInput.fromLatin1(bytes, key.getPath().asPath().toString()));
         if (!file.ok()) {
           Event.replayEventsOn(env.getListener(), file.errors());
           throw resolvedValueError("Failed to parse resolved file " + key.getPath());
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
index da1dd39..e769ac6 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java
@@ -142,7 +142,7 @@
               ? FileSystemUtils.readContent(path)
               : FileSystemUtils.readWithKnownFileSize(path, fileValue.getSize());
       digest = getDigestFromFileValueOrFromKnownFileContents(fileValue, bytes, digestHashFunction);
-      ParserInput input = ParserInput.create(bytes, path.toString());
+      ParserInput input = ParserInput.fromLatin1(bytes, path.toString());
       file = StarlarkFile.parse(input, options);
     } catch (IOException e) {
       throw new ErrorReadingStarlarkExtensionException(e, Transience.TRANSIENT);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
index fdf14ac..83d9f1a 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
@@ -1248,7 +1248,7 @@
           // If control flow reaches here, we're in territory that is deliberately unsound.
           // See the javadoc for ActionOnIOExceptionReadingBuildFile.
         }
-        input = ParserInput.create(buildFileBytes, inputFile.toString());
+        input = ParserInput.fromLatin1(buildFileBytes, inputFile.toString());
 
         // Options for processing BUILD files.
         FileOptions options =
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceASTFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceASTFunction.java
index e759b4b..4658eae 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceASTFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceASTFunction.java
@@ -98,7 +98,7 @@
     try {
       StarlarkFile file =
           StarlarkFile.parse(
-              ParserInput.create(
+              ParserInput.fromString(
                   ruleClassProvider.getDefaultWorkspacePrefix(), "/DEFAULT.WORKSPACE"),
               options);
       if (!file.ok()) {
@@ -108,7 +108,7 @@
       if (newWorkspaceFileContents != null) {
         file =
             StarlarkFile.parseWithPrelude(
-                ParserInput.create(
+                ParserInput.fromString(
                     newWorkspaceFileContents, resolvedFile.get().asPath().toString()),
                 file.getStatements(),
                 // The WORKSPACE.resolved file breaks through the usual privacy mechanism.
@@ -118,7 +118,9 @@
             FileSystemUtils.readWithKnownFileSize(repoWorkspace, repoWorkspace.getFileSize());
         file =
             StarlarkFile.parseWithPrelude(
-                ParserInput.create(bytes, repoWorkspace.toString()), file.getStatements(), options);
+                ParserInput.fromLatin1(bytes, repoWorkspace.toString()),
+                file.getStatements(),
+                options);
         if (!file.ok()) {
           Event.replayEventsOn(env.getListener(), file.errors());
           throw resolvedValueError("Failed to parse WORKSPACE file");
@@ -150,7 +152,7 @@
 
       file =
           StarlarkFile.parseWithPrelude(
-              ParserInput.create(suffix, "/DEFAULT.WORKSPACE.SUFFIX"),
+              ParserInput.fromString(suffix, "/DEFAULT.WORKSPACE.SUFFIX"),
               file.getStatements(),
               // The DEFAULT.WORKSPACE.SUFFIX file breaks through the usual privacy mechanism.
               options.toBuilder().allowLoadPrivateSymbols(true).build());
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkdebug/server/ThreadHandler.java b/src/main/java/com/google/devtools/build/lib/starlarkdebug/server/ThreadHandler.java
index d707aa0..8384537 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkdebug/server/ThreadHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkdebug/server/ThreadHandler.java
@@ -302,7 +302,7 @@
 
       // TODO(adonovan): opt: don't parse and resolve the expression every time we hit a breakpoint
       // (!).
-      ParserInput input = ParserInput.create(content, "<debug eval>");
+      ParserInput input = ParserInput.fromString(content, "<debug eval>");
       // TODO(adonovan): the module or call frame should be a parameter.
       Module module = Module.ofInnermostEnclosingStarlarkFunction(thread);
       return EvalUtils.exec(input, FileOptions.DEFAULT, module, thread);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ParserInput.java b/src/main/java/com/google/devtools/build/lib/syntax/ParserInput.java
index 388d5da..79bb694 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ParserInput.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ParserInput.java
@@ -15,11 +15,16 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
+import java.nio.charset.StandardCharsets;
 
 /**
  * The apparent name and contents of a source file, for consumption by the parser. The file name
  * appears in the location information in the syntax tree, and in error messages, but the Starlark
  * interpreter will not attempt to open the file.
+ *
+ * <p>The parser consumes a stream of chars (UTF-16 codes), and the syntax positions reported by
+ * {@link Node#getStartOffset} and {@link Location.column} are effectively indices into a char
+ * array.
  */
 public final class ParserInput {
 
@@ -43,30 +48,50 @@
 
   /** Returns an unnamed input source that reads from a list of strings, joined by newlines. */
   public static ParserInput fromLines(String... lines) {
-    return create(Joiner.on("\n").join(lines), "");
+    return fromString(Joiner.on("\n").join(lines), "");
   }
 
-  /** Returns an import source that reads from a Latin-1 encoded byte array. */
-  public static ParserInput create(byte[] bytes, String file) {
-    char[] content = convertFromLatin1(bytes);
-    return new ParserInput(content, file);
+  /**
+   * Returns an input source that reads from a UTF-8-encoded byte array. The caller is free to
+   * subsequently mutate the array.
+   */
+  public static ParserInput fromUTF8(byte[] bytes, String file) {
+    // TODO(adonovan): opt: avoid one of the two copies.
+    return fromString(new String(bytes, StandardCharsets.UTF_8), file);
+  }
+
+  /**
+   * Returns an input source that reads from a Latin1-encoded byte array. The caller is free to
+   * subsequently mutate the array.
+   *
+   * @deprecated this function exists to support legacy uses of Latin1 in Bazel. Do not use Latin1
+   *     in new applications.
+   */
+  @Deprecated
+  public static ParserInput fromLatin1(byte[] bytes, String file) {
+    char[] chars = new char[bytes.length];
+    for (int i = 0; i < bytes.length; i++) {
+      chars[i] = (char) (0xff & bytes[i]);
+    }
+    return new ParserInput(chars, file);
   }
 
   /** Returns an input source that reads from the given string. */
+  public static ParserInput fromString(String content, String file) {
+    return fromCharArray(content.toCharArray(), file);
+  }
+
+  /** Deprecated alias for {@link #fromString}. */
+  @Deprecated
   public static ParserInput create(String content, String file) {
-    return create(content.toCharArray(), file);
+    return fromString(content, file);
   }
 
-  /** Returns an input source that reads from the given char array. */
-  public static ParserInput create(char[] content, String file) {
+  /**
+   * Returns an input source that reads from the given char array. The caller must not subsequently
+   * modify the array.
+   */
+  public static ParserInput fromCharArray(char[] content, String file) {
     return new ParserInput(content, file);
   }
-
-  private static char[] convertFromLatin1(byte[] content) {
-    char[] latin1 = new char[content.length];
-    for (int i = 0; i < latin1.length; i++) { // yeah, latin1 is this easy! :-)
-      latin1[i] = (char) (0xff & content[i]);
-    }
-    return latin1;
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/skydoc/FilesystemFileAccessor.java b/src/main/java/com/google/devtools/build/skydoc/FilesystemFileAccessor.java
index 68138cf..4015cfd 100644
--- a/src/main/java/com/google/devtools/build/skydoc/FilesystemFileAccessor.java
+++ b/src/main/java/com/google/devtools/build/skydoc/FilesystemFileAccessor.java
@@ -24,7 +24,7 @@
 
   @Override
   public ParserInput inputSource(String filename) throws IOException {
-    return ParserInput.create(Files.readAllBytes(Paths.get(filename)), filename);
+    return ParserInput.fromLatin1(Files.readAllBytes(Paths.get(filename)), filename);
   }
 
   @Override
diff --git a/src/main/java/net/starlark/java/cmd/Starlark.java b/src/main/java/net/starlark/java/cmd/Starlark.java
index 6077f6d..0bd0498 100644
--- a/src/main/java/net/starlark/java/cmd/Starlark.java
+++ b/src/main/java/net/starlark/java/cmd/Starlark.java
@@ -92,7 +92,7 @@
     // lines only until the parse is complete.
 
     while ((line = prompt()) != null) {
-      ParserInput input = ParserInput.create(line, "<stdin>");
+      ParserInput input = ParserInput.fromString(line, "<stdin>");
       try {
         Object result = EvalUtils.exec(input, options, module, thread);
         if (result != com.google.devtools.build.lib.syntax.Starlark.NONE) {
@@ -125,7 +125,7 @@
   /** Execute a Starlark file. */
   private int execute(String filename, String content) {
     try {
-      EvalUtils.exec(ParserInput.create(content, filename), options, module, thread);
+      EvalUtils.exec(ParserInput.fromString(content, filename), options, module, thread);
       return 0;
     } catch (SyntaxError.Exception ex) {
       for (SyntaxError error : ex.errors()) {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTestHelper.java b/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTestHelper.java
index b291ffd..2950a82 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTestHelper.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/WorkspaceFactoryTestHelper.java
@@ -84,7 +84,7 @@
       byte[] bytes =
           FileSystemUtils.readWithKnownFileSize(workspaceFilePath, workspaceFilePath.getFileSize());
       factory.parseForTesting(
-          ParserInput.create(bytes, workspaceFilePath.toString()), eventHandler);
+          ParserInput.fromLatin1(bytes, workspaceFilePath.toString()), eventHandler);
     } catch (BuildFileContainsErrorsException e) {
       exception = e;
     } catch (IOException | InterruptedException e) {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
index 4fc948d..831caaa 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
@@ -146,7 +146,7 @@
   // TODO(adonovan): inline this into all callers. It has nothing to do with PackageFactory.
   public StarlarkFile parse(Path buildFile) throws IOException {
     byte[] bytes = FileSystemUtils.readWithKnownFileSize(buildFile, buildFile.getFileSize());
-    ParserInput input = ParserInput.create(bytes, buildFile.toString());
+    ParserInput input = ParserInput.fromLatin1(bytes, buildFile.toString());
     StarlarkFile file = StarlarkFile.parse(input);
     Event.replayEventsOn(eventHandler, file.errors());
     return file;
diff --git a/src/test/java/com/google/devtools/build/lib/profiler/memory/AllocationTrackerTest.java b/src/test/java/com/google/devtools/build/lib/profiler/memory/AllocationTrackerTest.java
index 930cfb8..52e06d7 100644
--- a/src/test/java/com/google/devtools/build/lib/profiler/memory/AllocationTrackerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/profiler/memory/AllocationTrackerTest.java
@@ -191,7 +191,7 @@
 
   private void exec(String... lines)
       throws SyntaxError.Exception, EvalException, InterruptedException {
-    ParserInput input = ParserInput.create(Joiner.on("\n").join(lines), "a.star");
+    ParserInput input = ParserInput.fromString(Joiner.on("\n").join(lines), "a.star");
     Module module =
         Module.withPredeclared(
             StarlarkSemantics.DEFAULT,
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/StarlarkRuleImplementationFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/StarlarkRuleImplementationFunctionsTest.java
index 9c5947a..ec3ff44 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/StarlarkRuleImplementationFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/StarlarkRuleImplementationFunctionsTest.java
@@ -836,10 +836,9 @@
    * usually write those files using UTF-8 encoding. Currently, the string-valued 'substitutions'
    * parameter of the template_action function contains a hack that assumes its input is a UTF-8
    * encoded string which has been ingested as Latin 1. The hack converts the string to its
-   * "correct" UTF-8 value. Once {@link
-   * com.google.devtools.build.lib.syntax.ParserInput#create(byte[],
-   * com.google.devtools.build.lib.vfs.PathFragment)} parses files using UTF-8 and the hack for the
-   * substituations parameter is removed, this test will fail.
+   * "correct" UTF-8 value. Once Blaze starts calling {@link
+   * com.google.devtools.build.lib.syntax.ParserInput#fromUTF8} instead of {@code fromLatin1} and
+   * the hack for the substituations parameter is removed, this test will fail.
    */
   @Test
   public void testCreateTemplateActionWithWrongEncoding() throws Exception {
diff --git a/src/test/java/com/google/devtools/build/lib/starlarkdebug/server/StarlarkDebugServerTest.java b/src/test/java/com/google/devtools/build/lib/starlarkdebug/server/StarlarkDebugServerTest.java
index a481c3a..8e385df 100644
--- a/src/test/java/com/google/devtools/build/lib/starlarkdebug/server/StarlarkDebugServerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/starlarkdebug/server/StarlarkDebugServerTest.java
@@ -762,7 +762,7 @@
   }
 
   private static ParserInput createInput(String filename, String... lines) {
-    return ParserInput.create(Joiner.on("\n").join(lines), filename);
+    return ParserInput.fromString(Joiner.on("\n").join(lines), filename);
   }
 
   /**
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/LexerTest.java b/src/test/java/com/google/devtools/build/lib/syntax/LexerTest.java
index cb15fd4..255f528 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/LexerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/LexerTest.java
@@ -38,7 +38,7 @@
    * error handler beforehand.
    */
   private Lexer createLexer(String input) {
-    ParserInput inputSource = ParserInput.create(input, "/some/path.txt");
+    ParserInput inputSource = ParserInput.fromString(input, "/some/path.txt");
     errors.clear();
     lastError = null;
     return new Lexer(inputSource, FileOptions.DEFAULT, errors);
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ParserInputTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ParserInputTest.java
index 1ab4c61..5b87ba5 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/ParserInputTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/ParserInputTest.java
@@ -36,7 +36,7 @@
     String content = joinLines("Line 1", "Line 2", "Line 3", "");
     Path file = scratch.file("/tmp/my/file.txt", content.getBytes(StandardCharsets.UTF_8));
     byte[] bytes = FileSystemUtils.readWithKnownFileSize(file, file.getFileSize());
-    ParserInput input = ParserInput.create(bytes, file.toString());
+    ParserInput input = ParserInput.fromLatin1(bytes, file.toString());
     assertThat(new String(input.getContent())).isEqualTo(content);
     assertThat(input.getFile()).isEqualTo("/tmp/my/file.txt");
   }
@@ -45,7 +45,7 @@
   public void testCreateFromString() {
     String content = "Content provided as a string.";
     String pathName = "/the/name/of/the/content.txt";
-    ParserInput input = ParserInput.create(content, pathName);
+    ParserInput input = ParserInput.fromString(content, pathName);
     assertThat(new String(input.getContent())).isEqualTo(content);
     assertThat(input.getFile()).isEqualTo(pathName);
   }
@@ -55,19 +55,19 @@
     String content = "Content provided as a string.";
     String pathName = "/the/name/of/the/content.txt";
     char[] contentChars = content.toCharArray();
-    ParserInput input = ParserInput.create(contentChars, pathName);
+    ParserInput input = ParserInput.fromCharArray(contentChars, pathName);
     assertThat(new String(input.getContent())).isEqualTo(content);
     assertThat(input.getFile()).isEqualTo(pathName);
   }
 
   @Test
   public void testWillNotTryToReadInputFileIfContentProvidedAsString() {
-    ParserInput.create("Content provided as string.", "/will/not/try/to/read");
+    ParserInput.fromString("Content provided as string.", "/will/not/try/to/read");
   }
 
   @Test
   public void testWillNotTryToReadInputFileIfContentProvidedAsChars() {
     char[] content = "Content provided as char array.".toCharArray();
-    ParserInput.create(content, "/will/not/try/to/read");
+    ParserInput.fromCharArray(content, "/will/not/try/to/read");
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
index d64aae2..dd16ea3 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
@@ -1310,6 +1310,6 @@
     for (int i = 0; i < 1000; i++) {
       s.append("{");
     }
-    return ParserInput.create(s.toString(), "foo.star");
+    return ParserInput.fromString(s.toString(), "foo.star");
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkFileTest.java b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkFileTest.java
index ab318af..576c913 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkFileTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkFileTest.java
@@ -31,7 +31,7 @@
    */
   private static StarlarkFile parseFile(String... lines) {
     String src = Joiner.on("\n").join(lines);
-    ParserInput input = ParserInput.create(src, "foo.star");
+    ParserInput input = ParserInput.fromString(src, "foo.star");
     return StarlarkFile.parse(input);
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadDebuggingTest.java b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadDebuggingTest.java
index 3553cb9..fa336c3 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadDebuggingTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadDebuggingTest.java
@@ -85,7 +85,7 @@
 
     // Execute a small file that calls f.
     ParserInput input =
-        ParserInput.create(
+        ParserInput.fromString(
             "def g(a, y, z):\n" // shadows global a
                 + "  f()\n"
                 + "g(4, 5, 6)",
diff --git a/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java b/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
index c054324..b5724c1 100644
--- a/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
+++ b/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
@@ -62,7 +62,7 @@
               public ParserInput inputSource(String pathString) throws IOException {
                 Path path = fileSystem.getPath("/" + pathString);
                 byte[] bytes = FileSystemUtils.asByteSource(path).read();
-                return ParserInput.create(bytes, path.toString());
+                return ParserInput.fromLatin1(bytes, path.toString());
               }
 
               @Override