Make interned strings non-movable.

At present objects referenced from dex files must have stable reference
values.  With this change, only non-moving strings are interned.  If a
user interns a movable string a non-moving copy is made and the copy is
added to the intern table.

As part of this change, the internal string hash code access routine will
update the hash code slot of a string object.  In addition, StringObject
has been made a subclass of Object eliminating various down-casts that
would otherwise be explicitly required.

Change-Id: I6b015b972aac44948470c0034ad17e5eef456aeb
diff --git a/vm/Intern.cpp b/vm/Intern.cpp
index 8215107..9faca45 100644
--- a/vm/Intern.cpp
+++ b/vm/Intern.cpp
@@ -51,22 +51,36 @@
     gDvm.literalStrings = NULL;
 }
 
+static StringObject* lookupString(HashTable* table, u4 key, StringObject* value)
+{
+    void* entry = dvmHashTableLookup(table, key, (void*)value,
+                                     dvmHashcmpStrings, false);
+    return (StringObject*)entry;
+}
+
+static StringObject* insertString(HashTable* table, u4 key, StringObject* value)
+{
+    if (dvmIsNonMovingObject(value) == false) {
+        value = (StringObject*)dvmCloneObject(value, ALLOC_NON_MOVING);
+    }
+    void* entry = dvmHashTableLookup(table, key, (void*)value,
+                                     dvmHashcmpStrings, true);
+    assert(entry == value);
+    return (StringObject*)entry;
+}
+
 static StringObject* lookupInternedString(StringObject* strObj, bool isLiteral)
 {
     StringObject* found;
-    u4 hash;
 
     assert(strObj != NULL);
-    hash = dvmComputeStringHash(strObj);
+    u4 key = dvmComputeStringHash(strObj);
     dvmLockMutex(&gDvm.internLock);
     if (isLiteral) {
         /*
          * Check the literal table for a match.
          */
-        StringObject* literal = (StringObject*)dvmHashTableLookup(gDvm.literalStrings,
-                                                   hash, strObj,
-                                                   dvmHashcmpStrings,
-                                                   false);
+        StringObject* literal = lookupString(gDvm.literalStrings, key, strObj);
         if (literal != NULL) {
             /*
              * A match was found in the literal table, the easy case.
@@ -77,50 +91,33 @@
              * There is no match in the literal table, check the
              * interned string table.
              */
-            StringObject* interned = (StringObject*)dvmHashTableLookup(gDvm.internedStrings,
-                                                        hash, strObj,
-                                                        dvmHashcmpStrings,
-                                                        false);
+            StringObject* interned = lookupString(gDvm.internedStrings, key, strObj);
             if (interned != NULL) {
                 /*
                  * A match was found in the interned table.  Move the
                  * matching string to the literal table.
                  */
-                dvmHashTableRemove(gDvm.internedStrings, hash, interned);
-                found = (StringObject*)dvmHashTableLookup(gDvm.literalStrings,
-                                           hash, interned,
-                                           dvmHashcmpStrings,
-                                           true);
-                assert(found == interned);
+                dvmHashTableRemove(gDvm.internedStrings, key, interned);
+                found = insertString(gDvm.literalStrings, key, interned);
             } else {
                 /*
                  * No match in the literal table or the interned
                  * table.  Insert into the literal table.
                  */
-                found = (StringObject*)dvmHashTableLookup(gDvm.literalStrings,
-                                           hash, strObj,
-                                           dvmHashcmpStrings,
-                                           true);
-                assert(found == strObj);
+                found = insertString(gDvm.literalStrings, key, strObj);
             }
         }
     } else {
         /*
          * Check the literal table for a match.
          */
-        found = (StringObject*)dvmHashTableLookup(gDvm.literalStrings,
-                                   hash, strObj,
-                                   dvmHashcmpStrings,
-                                   false);
+        found = lookupString(gDvm.literalStrings, key, strObj);
         if (found == NULL) {
             /*
              * No match was found in the literal table.  Insert into
              * the intern table.
              */
-            found = (StringObject*)dvmHashTableLookup(gDvm.internedStrings,
-                                       hash, strObj,
-                                       dvmHashcmpStrings,
-                                       true);
+            found = insertString(gDvm.internedStrings, key, strObj);
         }
     }
     assert(found != NULL);
@@ -152,19 +149,15 @@
  * Returns true if the object is a weak interned string.  Any string
  * interned by the user is weak.
  */
-bool dvmIsWeakInternedString(const StringObject* strObj)
+bool dvmIsWeakInternedString(StringObject* strObj)
 {
-    StringObject* found;
-    u4 hash;
-
     assert(strObj != NULL);
     if (gDvm.internedStrings == NULL) {
         return false;
     }
     dvmLockMutex(&gDvm.internLock);
-    hash = dvmComputeStringHash(strObj);
-    found = (StringObject*)dvmHashTableLookup(gDvm.internedStrings, hash,
-                               (StringObject*)strObj, dvmHashcmpStrings, false);
+    u4 key = dvmComputeStringHash(strObj);
+    StringObject* found = lookupString(gDvm.internedStrings, key, strObj);
     dvmUnlockMutex(&gDvm.internLock);
     return found == strObj;
 }
diff --git a/vm/Intern.h b/vm/Intern.h
index 6630b69..207a038 100644
--- a/vm/Intern.h
+++ b/vm/Intern.h
@@ -23,7 +23,7 @@
 void dvmStringInternShutdown(void);
 StringObject* dvmLookupInternedString(StringObject* strObj);
 StringObject* dvmLookupImmortalInternedString(StringObject* strObj);
-bool dvmIsWeakInternedString(const StringObject* strObj);
+bool dvmIsWeakInternedString(StringObject* strObj);
 void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *));
 
 #endif /*_DALVIK_INTERN*/
diff --git a/vm/UtfString.cpp b/vm/UtfString.cpp
index 17ce40a..0b7e4a0 100644
--- a/vm/UtfString.cpp
+++ b/vm/UtfString.cpp
@@ -204,12 +204,18 @@
     return hash;
 }
 
-u4 dvmComputeStringHash(const StringObject* strObj) {
-    const ArrayObject* chars = (ArrayObject*) dvmGetFieldObject((Object*) strObj,
+u4 dvmComputeStringHash(StringObject* strObj) {
+    int hashCode = dvmGetFieldInt(strObj, STRING_FIELDOFF_HASHCODE);
+    if (hashCode != 0) {
+      return hashCode;
+    }
+    int len = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT);
+    int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars = (ArrayObject*) dvmGetFieldObject(strObj,
                                 STRING_FIELDOFF_VALUE);
-    int len = dvmGetFieldInt((Object*) strObj, STRING_FIELDOFF_COUNT);
-    int offset = dvmGetFieldInt((Object*) strObj, STRING_FIELDOFF_OFFSET);
-    return computeUtf16Hash((u2*)(void*)chars->contents + offset, len);
+    hashCode = computeUtf16Hash((u2*)(void*)chars->contents + offset, len);
+    dvmSetFieldInt(strObj, STRING_FIELDOFF_HASHCODE, hashCode);
+    return hashCode;
 }
 
 /*
diff --git a/vm/UtfString.h b/vm/UtfString.h
index b577d3a..488ba9b 100644
--- a/vm/UtfString.h
+++ b/vm/UtfString.h
@@ -51,9 +51,10 @@
 u4 dvmComputeUtf8Hash(const char* str);
 
 /*
- * Hash function for string objects.
+ * Hash function for string objects. Ensures the hash code field is
+ * populated and returns its value.
  */
-u4 dvmComputeStringHash(const StringObject* strObj);
+u4 dvmComputeStringHash(StringObject* strObj);
 
 /*
  * Create a java.lang.String[] from an array of C strings.
diff --git a/vm/alloc/Alloc.cpp b/vm/alloc/Alloc.cpp
index a77c770..0fe22ea 100644
--- a/vm/alloc/Alloc.cpp
+++ b/vm/alloc/Alloc.cpp
@@ -366,3 +366,8 @@
 {
     return dvmHeapSourceContainsAddress(address);
 }
+
+bool dvmIsNonMovingObject(const Object* object)
+{
+    return true;
+}
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
index c215fc2..41a3466 100644
--- a/vm/alloc/Alloc.h
+++ b/vm/alloc/Alloc.h
@@ -144,4 +144,6 @@
  */
 bool dvmIsHeapAddress(void *address);
 
+bool dvmIsNonMovingObject(const Object* object);
+
 #endif /*_DALVIK_ALLOC_ALLOC*/
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
index c647a90..0b4d740 100644
--- a/vm/oo/Object.h
+++ b/vm/oo/Object.h
@@ -242,9 +242,7 @@
  * Currently this is just equal to DataObject, and we pull the fields out
  * like we do for any other object.
  */
-struct StringObject {
-    Object          obj;                /* MUST be first item */
-
+struct StringObject : Object {
     /* variable #of u4 slots; u8 uses 2 slots */
     u4              instanceData[1];
 };
diff --git a/vm/reflect/Annotation.cpp b/vm/reflect/Annotation.cpp
index 6b33af3..f015910 100644
--- a/vm/reflect/Annotation.cpp
+++ b/vm/reflect/Annotation.cpp
@@ -1490,7 +1490,7 @@
     }
 
     *pName = (StringObject*) avalue.value.l;
-    assert(*pName == NULL || (*pName)->obj.clazz == gDvm.classJavaLangString);
+    assert(*pName == NULL || (*pName)->clazz == gDvm.classJavaLangString);
 
     ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "accessFlags");
     if (ptr == NULL) {