auto import from //depot/cupcake/@135843
diff --git a/vm/Intern.c b/vm/Intern.c
new file mode 100644
index 0000000..8584333
--- /dev/null
+++ b/vm/Intern.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * String interning.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+#define INTERN_STRING_IMMORTAL_BIT (1<<0)
+#define SET_IMMORTAL_BIT(strObj) \
+            ((uintptr_t)(strObj) | INTERN_STRING_IMMORTAL_BIT)
+#define STRIP_IMMORTAL_BIT(strObj) \
+            ((uintptr_t)(strObj) & ~INTERN_STRING_IMMORTAL_BIT)
+#define IS_IMMORTAL(strObj) \
+            ((uintptr_t)(strObj) & INTERN_STRING_IMMORTAL_BIT)
+
+
+/*
+ * Prep string interning.
+ */
+bool dvmStringInternStartup(void)
+{
+    gDvm.internedStrings = dvmHashTableCreate(256, NULL);
+    if (gDvm.internedStrings == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Chuck the intern list.
+ *
+ * The contents of the list are StringObjects that live on the GC heap.
+ */
+void dvmStringInternShutdown(void)
+{
+    dvmHashTableFree(gDvm.internedStrings);
+    gDvm.internedStrings = NULL;
+}
+
+
+/*
+ * Compare two string objects that may have INTERN_STRING_IMMORTAL_BIT
+ * set in their pointer values.
+ */
+static int hashcmpImmortalStrings(const void* vstrObj1, const void* vstrObj2)
+{
+    return dvmHashcmpStrings((const void*) STRIP_IMMORTAL_BIT(vstrObj1),
+                             (const void*) STRIP_IMMORTAL_BIT(vstrObj2));
+}
+
+static StringObject* lookupInternedString(StringObject* strObj, bool immortal)
+{
+    StringObject* found;
+    u4 hash;
+
+    assert(strObj != NULL);
+    hash = dvmComputeStringHash(strObj);
+
+    if (false) {
+        char* debugStr = dvmCreateCstrFromString(strObj);
+        LOGV("+++ dvmLookupInternedString searching for '%s'\n", debugStr);
+        free(debugStr);
+    }
+
+    if (immortal) {
+        strObj = (StringObject*) SET_IMMORTAL_BIT(strObj);
+    }
+
+    dvmHashTableLock(gDvm.internedStrings);
+
+    found = (StringObject*) dvmHashTableLookup(gDvm.internedStrings,
+                                hash, strObj, hashcmpImmortalStrings, true);
+    if (immortal && !IS_IMMORTAL(found)) {
+        /* Make this entry immortal.  We have to use the existing object
+         * because, as an interned string, it's not allowed to change.
+         *
+         * There's no way to get a pointer to the actual hash table entry,
+         * so the only way to modify the existing entry is to remove,
+         * modify, and re-add it.
+         */
+        dvmHashTableRemove(gDvm.internedStrings, hash, found);
+        found = (StringObject*) SET_IMMORTAL_BIT(found);
+        found = (StringObject*) dvmHashTableLookup(gDvm.internedStrings,
+                                    hash, found, hashcmpImmortalStrings, true);
+        assert(IS_IMMORTAL(found));
+    }
+
+    dvmHashTableUnlock(gDvm.internedStrings);
+
+    //if (found == strObj)
+    //    LOGVV("+++  added string\n");
+    return (StringObject*) STRIP_IMMORTAL_BIT(found);
+}
+
+/*
+ * Find an entry in the interned string list.
+ *
+ * If the string doesn't already exist, the StringObject is added to
+ * the list.  Otherwise, the existing entry is returned.
+ */
+StringObject* dvmLookupInternedString(StringObject* strObj)
+{
+    return lookupInternedString(strObj, false);
+}
+
+/*
+ * Same as dvmLookupInternedString(), but guarantees that the
+ * returned string is immortal.
+ */
+StringObject* dvmLookupImmortalInternedString(StringObject* strObj)
+{
+    return lookupInternedString(strObj, true);
+}
+
+/*
+ * Mark all immortal interned string objects so that they don't
+ * get collected by the GC.  Non-immortal strings may or may not
+ * get marked by other references.
+ */
+static int markStringObject(void* strObj, void* arg)
+{
+    UNUSED_PARAMETER(arg);
+
+    if (IS_IMMORTAL(strObj)) {
+        dvmMarkObjectNonNull((Object*) STRIP_IMMORTAL_BIT(strObj));
+    }
+    return 0;
+}
+
+void dvmGcScanInternedStrings()
+{
+    /* It's possible for a GC to happen before dvmStringInternStartup()
+     * is called.
+     */
+    if (gDvm.internedStrings != NULL) {
+        dvmHashTableLock(gDvm.internedStrings);
+        dvmHashForeach(gDvm.internedStrings, markStringObject, NULL);
+        dvmHashTableUnlock(gDvm.internedStrings);
+    }
+}
+
+/*
+ * Called by the GC after all reachable objects have been
+ * marked.  isUnmarkedObject is a function suitable for passing
+ * to dvmHashForeachRemove();  it must strip the low bits from
+ * its pointer argument to deal with the immortal bit, though.
+ */
+void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *))
+{
+    /* It's possible for a GC to happen before dvmStringInternStartup()
+     * is called.
+     */
+    if (gDvm.internedStrings != NULL) {
+        dvmHashTableLock(gDvm.internedStrings);
+        dvmHashForeachRemove(gDvm.internedStrings, isUnmarkedObject);
+        dvmHashTableUnlock(gDvm.internedStrings);
+    }
+}