RELNOTES: Add global hash() function for strings (only)

--
MOS_MIGRATED_REVID=127979748
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
index a7d168d..c108325 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
@@ -1998,6 +1998,24 @@
     }
   };
 
+  @SkylarkSignature(name = "hash", returnType = Integer.class,
+      doc = "Return a hash value for a string. Hash values of equal strings are always equal to "
+          + "one another, but may change over different invocations of the Skylark interpreter. "
+          + "Hashing of values besides strings is not currently supported.",
+          // Java guarantees that Strings are hashed using a specific algorithm and are therefore
+          // consistent across all invocations. Rather than re-export this promise to the user,
+          // we'll just provide the same basic guarantee as Java and Python do for hashing any
+          // kind of value.
+      parameters = {
+        @Param(name = "value", type = String.class,
+            doc = "String value to hash")
+      })
+  private static final BuiltinFunction hash = new BuiltinFunction("hash") {
+    public Integer invoke(String value) throws EvalException {
+      return value.hashCode();
+    }
+  };
+
   @SkylarkSignature(name = "range", returnType = MutableList.class,
       doc = "Creates a list where items go from <code>start</code> to <code>stop</code>, using a "
           + "<code>step</code> increment. If a single argument is provided, items will "
@@ -2017,8 +2035,8 @@
       useLocation = true,
       useEnvironment = true)
   private static final BuiltinFunction range = new BuiltinFunction("range") {
-      public MutableList<?> invoke(Integer startOrStop, Object stopOrNone, Integer step,
-          Location loc, Environment env)
+    public MutableList<?> invoke(Integer startOrStop, Object stopOrNone, Integer step,
+        Location loc, Environment env)
         throws EvalException, ConversionException {
       int start;
       int stop;
@@ -2327,7 +2345,7 @@
   static final List<BaseFunction> skylarkGlobalFunctions =
       ImmutableList.<BaseFunction>builder()
           .addAll(buildGlobalFunctions)
-          .add(dir, fail, getattr, hasattr, print, struct, type)
+          .add(dir, fail, getattr, hasattr, hash, print, struct, type)
           .build();
 
   /**
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
index a9f82d5..b93ce6e 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
@@ -1484,6 +1484,20 @@
   }
 
   @Test
+  public void testHash() throws Exception {
+    // This test is too strong, in that it tests whether the hash value of the string follows
+    // Java's specific algorithm for Strings. If our hash implementation is changed then this
+    // test will have to be modified.
+    new SkylarkTest()
+        .testStatement("hash('skylark')", "skylark".hashCode())
+        .testStatement("hash('google')", "google".hashCode())
+        .testIfErrorContains(
+            "Method hash(value: string) is not applicable for arguments (NoneType): "
+                + "'value' is NoneType, but should be string",
+            "hash(None)");
+  }
+
+  @Test
   public void testRange() throws Exception {
     new BothModesTest()
         .testStatement("str(range(5))", "[0, 1, 2, 3, 4]")