1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 /* 17 * String interning. 18 */ 19 #include "Dalvik.h" 20 21 #include <stddef.h> 22 23 /* 24 * Prep string interning. 25 */ 26 bool dvmStringInternStartup(void) 27 { 28 dvmInitMutex(&gDvm.internLock); 29 gDvm.internedStrings = dvmHashTableCreate(256, NULL); 30 if (gDvm.internedStrings == NULL) 31 return false; 32 gDvm.literalStrings = dvmHashTableCreate(256, NULL); 33 if (gDvm.literalStrings == NULL) 34 return false; 35 return true; 36 } 37 38 /* 39 * Chuck the intern list. 40 * 41 * The contents of the list are StringObjects that live on the GC heap. 42 */ 43 void dvmStringInternShutdown(void) 44 { 45 if (gDvm.internedStrings != NULL || gDvm.literalStrings != NULL) { 46 dvmDestroyMutex(&gDvm.internLock); 47 } 48 dvmHashTableFree(gDvm.internedStrings); 49 gDvm.internedStrings = NULL; 50 dvmHashTableFree(gDvm.literalStrings); 51 gDvm.literalStrings = NULL; 52 } 53 54 static StringObject* lookupInternedString(StringObject* strObj, bool isLiteral) 55 { 56 StringObject* found; 57 u4 hash; 58 59 assert(strObj != NULL); 60 hash = dvmComputeStringHash(strObj); 61 dvmLockMutex(&gDvm.internLock); 62 if (isLiteral) { 63 /* 64 * Check the literal table for a match. 65 */ 66 StringObject* literal = dvmHashTableLookup(gDvm.literalStrings, 67 hash, strObj, 68 dvmHashcmpStrings, 69 false); 70 if (literal != NULL) { 71 /* 72 * A match was found in the literal table, the easy case. 73 */ 74 found = literal; 75 } else { 76 /* 77 * There is no match in the literal table, check the 78 * interned string table. 79 */ 80 StringObject* interned = dvmHashTableLookup(gDvm.internedStrings, 81 hash, strObj, 82 dvmHashcmpStrings, 83 false); 84 if (interned != NULL) { 85 /* 86 * A match was found in the interned table. Move the 87 * matching string to the literal table. 88 */ 89 dvmHashTableRemove(gDvm.internedStrings, hash, interned); 90 found = dvmHashTableLookup(gDvm.literalStrings, 91 hash, interned, 92 dvmHashcmpStrings, 93 true); 94 assert(found == interned); 95 } else { 96 /* 97 * No match in the literal table or the interned 98 * table. Insert into the literal table. 99 */ 100 found = dvmHashTableLookup(gDvm.literalStrings, 101 hash, strObj, 102 dvmHashcmpStrings, 103 true); 104 assert(found == strObj); 105 } 106 } 107 } else { 108 /* 109 * Check the literal table for a match. 110 */ 111 found = dvmHashTableLookup(gDvm.literalStrings, 112 hash, strObj, 113 dvmHashcmpStrings, 114 false); 115 if (found == NULL) { 116 /* 117 * No match was found in the literal table. Insert into 118 * the intern table. 119 */ 120 found = dvmHashTableLookup(gDvm.internedStrings, 121 hash, strObj, 122 dvmHashcmpStrings, 123 true); 124 } 125 } 126 assert(found != NULL); 127 dvmUnlockMutex(&gDvm.internLock); 128 return found; 129 } 130 131 /* 132 * Find an entry in the interned string table. 133 * 134 * If the string doesn't already exist, the StringObject is added to 135 * the table. Otherwise, the existing entry is returned. 136 */ 137 StringObject* dvmLookupInternedString(StringObject* strObj) 138 { 139 return lookupInternedString(strObj, false); 140 } 141 142 /* 143 * Same as dvmLookupInternedString(), but guarantees that the 144 * returned string is a literal. 145 */ 146 StringObject* dvmLookupImmortalInternedString(StringObject* strObj) 147 { 148 return lookupInternedString(strObj, true); 149 } 150 151 /* 152 * Returns true if the object is a weak interned string. Any string 153 * interned by the user is weak. 154 */ 155 bool dvmIsWeakInternedString(const StringObject* strObj) 156 { 157 StringObject* found; 158 u4 hash; 159 160 assert(strObj != NULL); 161 if (gDvm.internedStrings == NULL) { 162 return false; 163 } 164 dvmLockMutex(&gDvm.internLock); 165 hash = dvmComputeStringHash(strObj); 166 found = dvmHashTableLookup(gDvm.internedStrings, hash, (void*)strObj, 167 dvmHashcmpStrings, false); 168 dvmUnlockMutex(&gDvm.internLock); 169 return found == strObj; 170 } 171 172 static int markStringObject(void* strObj, void* arg) 173 { 174 UNUSED_PARAMETER(arg); 175 dvmMarkObjectNonNull(strObj); 176 return 0; 177 } 178 179 /* 180 * Blacken string references from the literal string table. The 181 * literal table is a root. 182 */ 183 void dvmGcScanInternedStrings() 184 { 185 /* It's possible for a GC to happen before dvmStringInternStartup() 186 * is called. 187 */ 188 if (gDvm.literalStrings != NULL) { 189 dvmHashForeach(gDvm.literalStrings, markStringObject, NULL); 190 } 191 } 192 193 /* 194 * Clear white references from the intern table. 195 */ 196 void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *)) 197 { 198 /* It's possible for a GC to happen before dvmStringInternStartup() 199 * is called. 200 */ 201 if (gDvm.internedStrings != NULL) { 202 dvmLockMutex(&gDvm.internLock); 203 dvmHashForeachRemove(gDvm.internedStrings, isUnmarkedObject); 204 dvmUnlockMutex(&gDvm.internLock); 205 } 206 } 207