Clean up UtfString.c, two ways.

The class and member init code got pulled out, and put it into
InitRefs.c (for further cleansing in a future change).

The instance creation code got refactored a bit, to keep things
simpler and tidier.

Change-Id: I22d8abff141ea889b3455a1cebcf51e333cd8c92
diff --git a/vm/Globals.h b/vm/Globals.h
index 9c27f7a..1bc0a9a 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -327,7 +327,6 @@
     int         offJavaLangClass_pd;
 
     /* field offsets - String */
-    int         javaLangStringReady;    /* 0=not init, 1=ready, -1=initing */
     int         offJavaLangString_value;
     int         offJavaLangString_count;
     int         offJavaLangString_offset;
diff --git a/vm/InitRefs.c b/vm/InitRefs.c
index b979efc..11bf04e 100644
--- a/vm/InitRefs.c
+++ b/vm/InitRefs.c
@@ -562,6 +562,52 @@
     return true;
 }
 
+static bool find9(void) {
+    gDvm.offJavaLangString_value =
+        dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C");
+    gDvm.offJavaLangString_count =
+        dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I");
+    gDvm.offJavaLangString_offset =
+        dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I");
+    gDvm.offJavaLangString_hashCode =
+        dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I");
+
+    if (gDvm.offJavaLangString_value < 0 ||
+        gDvm.offJavaLangString_count < 0 ||
+        gDvm.offJavaLangString_offset < 0 ||
+        gDvm.offJavaLangString_hashCode < 0)
+    {
+        LOGE("VM-required field missing from java/lang/String\n");
+        return false;
+    }
+
+    bool badValue = false;
+    if (gDvm.offJavaLangString_value != STRING_FIELDOFF_VALUE) {
+        LOGE("InlineNative: String.value offset = %d, expected %d\n",
+            gDvm.offJavaLangString_value, STRING_FIELDOFF_VALUE);
+        badValue = true;
+    }
+    if (gDvm.offJavaLangString_count != STRING_FIELDOFF_COUNT) {
+        LOGE("InlineNative: String.count offset = %d, expected %d\n",
+            gDvm.offJavaLangString_count, STRING_FIELDOFF_COUNT);
+        badValue = true;
+    }
+    if (gDvm.offJavaLangString_offset != STRING_FIELDOFF_OFFSET) {
+        LOGE("InlineNative: String.offset offset = %d, expected %d\n",
+            gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET);
+        badValue = true;
+    }
+    if (gDvm.offJavaLangString_hashCode != STRING_FIELDOFF_HASHCODE) {
+        LOGE("InlineNative: String.hashCode offset = %d, expected %d\n",
+            gDvm.offJavaLangString_hashCode, STRING_FIELDOFF_HASHCODE);
+        badValue = true;
+    }
+    if (badValue)
+        return false;
+
+    return true;
+}
+
 /* (documented in header) */
 bool dvmFindRequiredClassesAndMembers(void) {
     bool ok = true;
@@ -574,6 +620,7 @@
     ok &= find6();
     ok &= find7();
     ok &= find8();
+    ok &= find9();
 
     return ok;
 }
diff --git a/vm/UtfString.c b/vm/UtfString.c
index d3acf0e..58545cd 100644
--- a/vm/UtfString.c
+++ b/vm/UtfString.c
@@ -25,93 +25,52 @@
 #include <stdlib.h>
 
 /*
- * Initialize string globals.
- *
- * This isn't part of the VM init sequence because it's hard to get the
- * timing right -- we need it to happen after java/lang/String has been
- * loaded, but before anybody wants to use a string.  It's easiest to
- * just initialize it on first use.
- *
- * In some unusual circumstances (e.g. trying to throw an exception because
- * String implements java/lang/CharSequence, but CharSequence doesn't exist)
- * we can try to create an exception string internally before anything has
- * really tried to use String.  In that case we basically self-destruct.
- *
- * We're expecting to be essentially single-threaded at this point.
- * We employ atomics to ensure everything is observed correctly, and also
- * to guarantee that we do detect a problem if our assumption is wrong.
+ * Allocate a new instance of the class String, performing first-use
+ * initialization of the class if necessary. Upon success, the
+ * returned value will have all its fields except hashCode already
+ * filled in, including a reference to a newly-allocated char[] for
+ * the contents, sized as given. Additionally, a reference to the
+ * chars array is stored to the pChars pointer. Callers must
+ * subsequently call dvmReleaseTrackedAlloc() on the result pointer.
+ * This function returns NULL on failure.
  */
-static bool stringStartup()
+static StringObject* makeStringObject(u4 charsLength, ArrayObject** pChars)
 {
-    if (gDvm.javaLangStringReady < 0) {
-        LOGE("ERROR: reentrant string initialization\n");
-        assert(false);
-        return false;
+    /*
+     * The String class should have already gotten found (but not
+     * necessarily initialized) before making it here. We assert it
+     * explicitly, since historically speaking, we have had bugs with
+     * regard to when the class String gets set up. The assert helps
+     * make any regressions easier to diagnose.
+     */
+    assert(gDvm.classJavaLangString != NULL);
+
+    if (!dvmIsClassInitialized(gDvm.classJavaLangString)) {
+        /* Perform first-time use initialization of the class. */
+        if (!dvmInitClass(gDvm.classJavaLangString)) {
+            LOGE("FATAL: Could not initialize class String\n");
+            dvmAbort();
+        }
     }
 
-    if (android_atomic_acquire_cas(0, -1, &gDvm.javaLangStringReady) != 0) {
-        LOGE("ERROR: initial string-ready state not 0 (%d)\n",
-            gDvm.javaLangStringReady);
-        return false;
+    Object* result = dvmAllocObject(gDvm.classJavaLangString, ALLOC_DEFAULT);
+    if (result == NULL) {
+        return NULL;
     }
 
-    if (gDvm.classJavaLangString == NULL)
-        gDvm.classJavaLangString =
-            dvmFindSystemClassNoInit("Ljava/lang/String;");
-
-    gDvm.offJavaLangString_value =
-        dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C");
-    gDvm.offJavaLangString_count =
-        dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I");
-    gDvm.offJavaLangString_offset =
-        dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I");
-    gDvm.offJavaLangString_hashCode =
-        dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I");
-
-    if (gDvm.offJavaLangString_value < 0 ||
-        gDvm.offJavaLangString_count < 0 ||
-        gDvm.offJavaLangString_offset < 0 ||
-        gDvm.offJavaLangString_hashCode < 0)
-    {
-        LOGE("VM-required field missing from java/lang/String\n");
-        return false;
+    ArrayObject* chars = dvmAllocPrimitiveArray('C', charsLength, ALLOC_DEFAULT);
+    if (chars == NULL) {
+        dvmReleaseTrackedAlloc(result, NULL);
+        return NULL;
     }
 
-    bool badValue = false;
-    if (gDvm.offJavaLangString_value != STRING_FIELDOFF_VALUE) {
-        LOGE("InlineNative: String.value offset = %d, expected %d\n",
-            gDvm.offJavaLangString_value, STRING_FIELDOFF_VALUE);
-        badValue = true;
-    }
-    if (gDvm.offJavaLangString_count != STRING_FIELDOFF_COUNT) {
-        LOGE("InlineNative: String.count offset = %d, expected %d\n",
-            gDvm.offJavaLangString_count, STRING_FIELDOFF_COUNT);
-        badValue = true;
-    }
-    if (gDvm.offJavaLangString_offset != STRING_FIELDOFF_OFFSET) {
-        LOGE("InlineNative: String.offset offset = %d, expected %d\n",
-            gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET);
-        badValue = true;
-    }
-    if (gDvm.offJavaLangString_hashCode != STRING_FIELDOFF_HASHCODE) {
-        LOGE("InlineNative: String.hashCode offset = %d, expected %d\n",
-            gDvm.offJavaLangString_hashCode, STRING_FIELDOFF_HASHCODE);
-        badValue = true;
-    }
-    if (badValue)
-        return false;
+    dvmSetFieldInt(result, STRING_FIELDOFF_COUNT, charsLength);
+    dvmSetFieldObject(result, STRING_FIELDOFF_VALUE, (Object*) chars);
+    dvmReleaseTrackedAlloc((Object*) chars, NULL);
+    /* Leave offset and hashCode set to zero. */
 
-    android_atomic_release_store(1, &gDvm.javaLangStringReady);
-
-    return true;
-}
-
-/*
- * Discard heap-allocated storage.
- */
-void dvmStringShutdown()
-{
-    // currently unused
+    *pChars = chars;
+    return (StringObject*) result;
 }
 
 /*
@@ -278,104 +237,41 @@
 StringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str,
     u4 utf16Length)
 {
-    StringObject* newObj;
-    ArrayObject* chars;
-    u4 hashCode = 0;
-
-    //LOGV("Creating String from '%s'\n", utf8Str);
     assert(utf8Str != NULL);
 
-    if (gDvm.javaLangStringReady <= 0) {
-        if (!stringStartup())
-            return NULL;
-    }
-
-    /* init before alloc */
-    if (!dvmIsClassInitialized(gDvm.classJavaLangString) &&
-        !dvmInitClass(gDvm.classJavaLangString))
-    {
+    ArrayObject* chars;
+    StringObject* newObj = makeStringObject(utf16Length, &chars);
+    if (newObj == NULL) {
         return NULL;
     }
 
-    newObj = (StringObject*) dvmAllocObject(gDvm.classJavaLangString,
-                ALLOC_DEFAULT);
-    if (newObj == NULL)
-        return NULL;
+    dvmConvertUtf8ToUtf16((u2*) chars->contents, utf8Str);
 
-    chars = dvmAllocPrimitiveArray('C', utf16Length, ALLOC_DEFAULT);
-    if (chars == NULL) {
-        dvmReleaseTrackedAlloc((Object*) newObj, NULL);
-        return NULL;
-    }
-    dvmConvertUtf8ToUtf16((u2*)chars->contents, utf8Str);
-    hashCode = dvmComputeUtf16Hash((u2*) chars->contents, utf16Length);
+    u4 hashCode = dvmComputeUtf16Hash((u2*) chars->contents, utf16Length);
+    dvmSetFieldInt((Object*) newObj, STRING_FIELDOFF_HASHCODE, hashCode);
 
-    dvmSetFieldObject((Object*)newObj, STRING_FIELDOFF_VALUE,
-        (Object*)chars);
-    dvmReleaseTrackedAlloc((Object*) chars, NULL);
-    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_COUNT, utf16Length);
-    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
-    /* leave offset set to zero */
-
-    /* debugging stuff */
-    //dvmDumpObject((Object*)newObj);
-    //printHexDumpEx(ANDROID_LOG_DEBUG, chars->contents, utf16Length * 2,
-    //    kHexDumpMem);
-
-    /* caller may need to dvmReleaseTrackedAlloc(newObj) */
     return newObj;
 }
 
 /*
- * Create a new java/lang/String object, using the Unicode data.
+ * Create a new java/lang/String object, using the given Unicode data.
  */
 StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len)
 {
-    StringObject* newObj;
-    ArrayObject* chars;
-    u4 hashCode = 0;
-
-    /* we allow a null pointer if the length is zero */
+    /* We allow a NULL pointer if the length is zero. */
     assert(len == 0 || unichars != NULL);
 
-    if (gDvm.javaLangStringReady <= 0) {
-        if (!stringStartup())
-            return NULL;
-    }
-
-    /* init before alloc */
-    if (!dvmIsClassInitialized(gDvm.classJavaLangString) &&
-        !dvmInitClass(gDvm.classJavaLangString))
-    {
+    ArrayObject* chars;
+    StringObject* newObj = makeStringObject(len, &chars);
+    if (newObj == NULL) {
         return NULL;
     }
 
-    newObj = (StringObject*) dvmAllocObject(gDvm.classJavaLangString,
-        ALLOC_DEFAULT);
-    if (newObj == NULL)
-        return NULL;
+    if (len > 0) memcpy(chars->contents, unichars, len * sizeof(u2));
 
-    chars = dvmAllocPrimitiveArray('C', len, ALLOC_DEFAULT);
-    if (chars == NULL) {
-        dvmReleaseTrackedAlloc((Object*) newObj, NULL);
-        return NULL;
-    }
-    if (len > 0)
-        memcpy(chars->contents, unichars, len * sizeof(u2));
-    hashCode = dvmComputeUtf16Hash((u2*) chars->contents, len);
-
-    dvmSetFieldObject((Object*)newObj, STRING_FIELDOFF_VALUE,
-        (Object*)chars);
-    dvmReleaseTrackedAlloc((Object*) chars, NULL);
-    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_COUNT, len);
+    u4 hashCode = dvmComputeUtf16Hash((u2*) chars->contents, len);
     dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
-    /* leave offset set to zero */
 
-    /* debugging stuff */
-    //dvmDumpObject((Object*)newObj);
-    //printHexDumpEx(ANDROID_LOG_DEBUG, chars->contents, len*2, kHexDumpMem);
-
-    /* caller must dvmReleaseTrackedAlloc(newObj) */
     return newObj;
 }
 
@@ -391,7 +287,7 @@
     int len, byteLen, offset;
     const u2* data;
 
-    assert(gDvm.javaLangStringReady > 0);
+    assert(gDvm.classJavaLangString != NULL);
 
     if (jstr == NULL)
         return NULL;
@@ -436,7 +332,7 @@
     int len, offset;
     const u2* data;
 
-    assert(gDvm.javaLangStringReady > 0);
+    assert(gDvm.classJavaLangString != NULL);
 
     if (jstr == NULL)
         return 0;       // should we throw something?  assert?
@@ -498,7 +394,7 @@
     ArrayObject* chars2;
     int len1, len2, offset1, offset2;
 
-    assert(gDvm.javaLangStringReady > 0);
+    assert(gDvm.classJavaLangString != NULL);
 
     /* get offset and length into char array; all values are in 16-bit units */
     len1 = dvmGetFieldInt((Object*) strObj1, STRING_FIELDOFF_COUNT);