1 /* 2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 * 19 */ 20 21 #include "config.h" 22 #include "Identifier.h" 23 24 #include "CallFrame.h" 25 #include "JSObject.h" 26 #include "NumericStrings.h" 27 #include "ScopeChain.h" 28 #include <new> // for placement new 29 #include <string.h> // for strlen 30 #include <wtf/Assertions.h> 31 #include <wtf/FastMalloc.h> 32 #include <wtf/HashSet.h> 33 #include <wtf/WTFThreadData.h> 34 #include <wtf/text/StringHash.h> 35 36 using WTF::ThreadSpecific; 37 38 namespace JSC { 39 ~IdentifierTable()40 IdentifierTable::~IdentifierTable() 41 { 42 HashSet<StringImpl*>::iterator end = m_table.end(); 43 for (HashSet<StringImpl*>::iterator iter = m_table.begin(); iter != end; ++iter) 44 (*iter)->setIsIdentifier(false); 45 } add(StringImpl * value)46 std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(StringImpl* value) 47 { 48 std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add(value); 49 (*result.first)->setIsIdentifier(true); 50 return result; 51 } 52 template<typename U, typename V> add(U value)53 std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(U value) 54 { 55 std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add<U, V>(value); 56 (*result.first)->setIsIdentifier(true); 57 return result; 58 } 59 createIdentifierTable()60 IdentifierTable* createIdentifierTable() 61 { 62 return new IdentifierTable; 63 } 64 deleteIdentifierTable(IdentifierTable * table)65 void deleteIdentifierTable(IdentifierTable* table) 66 { 67 delete table; 68 } 69 equal(const StringImpl * r,const char * s)70 bool Identifier::equal(const StringImpl* r, const char* s) 71 { 72 int length = r->length(); 73 const UChar* d = r->characters(); 74 for (int i = 0; i != length; ++i) 75 if (d[i] != (unsigned char)s[i]) 76 return false; 77 return s[length] == 0; 78 } 79 equal(const StringImpl * r,const UChar * s,unsigned length)80 bool Identifier::equal(const StringImpl* r, const UChar* s, unsigned length) 81 { 82 if (r->length() != length) 83 return false; 84 const UChar* d = r->characters(); 85 for (unsigned i = 0; i != length; ++i) 86 if (d[i] != s[i]) 87 return false; 88 return true; 89 } 90 91 struct IdentifierCStringTranslator { hashJSC::IdentifierCStringTranslator92 static unsigned hash(const char* c) 93 { 94 return StringHasher::computeHash<char>(c); 95 } 96 equalJSC::IdentifierCStringTranslator97 static bool equal(StringImpl* r, const char* s) 98 { 99 return Identifier::equal(r, s); 100 } 101 translateJSC::IdentifierCStringTranslator102 static void translate(StringImpl*& location, const char* c, unsigned hash) 103 { 104 size_t length = strlen(c); 105 UChar* d; 106 StringImpl* r = StringImpl::createUninitialized(length, d).leakRef(); 107 for (size_t i = 0; i != length; i++) 108 d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend 109 r->setHash(hash); 110 location = r; 111 } 112 }; 113 add(JSGlobalData * globalData,const char * c)114 PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const char* c) 115 { 116 if (!c) 117 return 0; 118 if (!c[0]) 119 return StringImpl::empty(); 120 if (!c[1]) 121 return add(globalData, globalData->smallStrings.singleCharacterStringRep(static_cast<unsigned char>(c[0]))); 122 123 IdentifierTable& identifierTable = *globalData->identifierTable; 124 LiteralIdentifierTable& literalIdentifierTable = identifierTable.literalTable(); 125 126 const LiteralIdentifierTable::iterator& iter = literalIdentifierTable.find(c); 127 if (iter != literalIdentifierTable.end()) 128 return iter->second; 129 130 pair<HashSet<StringImpl*>::iterator, bool> addResult = identifierTable.add<const char*, IdentifierCStringTranslator>(c); 131 132 // If the string is newly-translated, then we need to adopt it. 133 // The boolean in the pair tells us if that is so. 134 RefPtr<StringImpl> addedString = addResult.second ? adoptRef(*addResult.first) : *addResult.first; 135 136 literalIdentifierTable.add(c, addedString.get()); 137 138 return addedString.release(); 139 } 140 add(ExecState * exec,const char * c)141 PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const char* c) 142 { 143 return add(&exec->globalData(), c); 144 } 145 146 struct UCharBuffer { 147 const UChar* s; 148 unsigned int length; 149 }; 150 151 struct IdentifierUCharBufferTranslator { hashJSC::IdentifierUCharBufferTranslator152 static unsigned hash(const UCharBuffer& buf) 153 { 154 return StringHasher::computeHash<UChar>(buf.s, buf.length); 155 } 156 equalJSC::IdentifierUCharBufferTranslator157 static bool equal(StringImpl* str, const UCharBuffer& buf) 158 { 159 return Identifier::equal(str, buf.s, buf.length); 160 } 161 translateJSC::IdentifierUCharBufferTranslator162 static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash) 163 { 164 UChar* d; 165 StringImpl* r = StringImpl::createUninitialized(buf.length, d).leakRef(); 166 for (unsigned i = 0; i != buf.length; i++) 167 d[i] = buf.s[i]; 168 r->setHash(hash); 169 location = r; 170 } 171 }; 172 toUInt32(const UString & string,bool & ok)173 uint32_t Identifier::toUInt32(const UString& string, bool& ok) 174 { 175 ok = false; 176 177 unsigned length = string.length(); 178 const UChar* characters = string.characters(); 179 180 // An empty string is not a number. 181 if (!length) 182 return 0; 183 184 // Get the first character, turning it into a digit. 185 uint32_t value = characters[0] - '0'; 186 if (value > 9) 187 return 0; 188 189 // Check for leading zeros. If the first characher is 0, then the 190 // length of the string must be one - e.g. "042" is not equal to "42". 191 if (!value && length > 1) 192 return 0; 193 194 while (--length) { 195 // Multiply value by 10, checking for overflow out of 32 bits. 196 if (value > 0xFFFFFFFFU / 10) 197 return 0; 198 value *= 10; 199 200 // Get the next character, turning it into a digit. 201 uint32_t newValue = *(++characters) - '0'; 202 if (newValue > 9) 203 return 0; 204 205 // Add in the old value, checking for overflow out of 32 bits. 206 newValue += value; 207 if (newValue < value) 208 return 0; 209 value = newValue; 210 } 211 212 ok = true; 213 return value; 214 } 215 add(JSGlobalData * globalData,const UChar * s,int length)216 PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const UChar* s, int length) 217 { 218 if (length == 1) { 219 UChar c = s[0]; 220 if (c <= maxSingleCharacterString) 221 return add(globalData, globalData->smallStrings.singleCharacterStringRep(c)); 222 } 223 if (!length) 224 return StringImpl::empty(); 225 UCharBuffer buf = {s, length}; 226 pair<HashSet<StringImpl*>::iterator, bool> addResult = globalData->identifierTable->add<UCharBuffer, IdentifierUCharBufferTranslator>(buf); 227 228 // If the string is newly-translated, then we need to adopt it. 229 // The boolean in the pair tells us if that is so. 230 return addResult.second ? adoptRef(*addResult.first) : *addResult.first; 231 } 232 add(ExecState * exec,const UChar * s,int length)233 PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const UChar* s, int length) 234 { 235 return add(&exec->globalData(), s, length); 236 } 237 addSlowCase(JSGlobalData * globalData,StringImpl * r)238 PassRefPtr<StringImpl> Identifier::addSlowCase(JSGlobalData* globalData, StringImpl* r) 239 { 240 ASSERT(!r->isIdentifier()); 241 // The empty & null strings are static singletons, and static strings are handled 242 // in ::add() in the header, so we should never get here with a zero length string. 243 ASSERT(r->length()); 244 245 if (r->length() == 1) { 246 UChar c = r->characters()[0]; 247 if (c <= maxSingleCharacterString) 248 r = globalData->smallStrings.singleCharacterStringRep(c); 249 if (r->isIdentifier()) 250 return r; 251 } 252 253 return *globalData->identifierTable->add(r).first; 254 } 255 addSlowCase(ExecState * exec,StringImpl * r)256 PassRefPtr<StringImpl> Identifier::addSlowCase(ExecState* exec, StringImpl* r) 257 { 258 return addSlowCase(&exec->globalData(), r); 259 } 260 from(ExecState * exec,unsigned value)261 Identifier Identifier::from(ExecState* exec, unsigned value) 262 { 263 return Identifier(exec, exec->globalData().numericStrings.add(value)); 264 } 265 from(ExecState * exec,int value)266 Identifier Identifier::from(ExecState* exec, int value) 267 { 268 return Identifier(exec, exec->globalData().numericStrings.add(value)); 269 } 270 from(ExecState * exec,double value)271 Identifier Identifier::from(ExecState* exec, double value) 272 { 273 return Identifier(exec, exec->globalData().numericStrings.add(value)); 274 } 275 from(JSGlobalData * globalData,unsigned value)276 Identifier Identifier::from(JSGlobalData* globalData, unsigned value) 277 { 278 return Identifier(globalData, globalData->numericStrings.add(value)); 279 } 280 from(JSGlobalData * globalData,int value)281 Identifier Identifier::from(JSGlobalData* globalData, int value) 282 { 283 return Identifier(globalData, globalData->numericStrings.add(value)); 284 } 285 from(JSGlobalData * globalData,double value)286 Identifier Identifier::from(JSGlobalData* globalData, double value) 287 { 288 return Identifier(globalData, globalData->numericStrings.add(value)); 289 } 290 291 #ifndef NDEBUG 292 checkCurrentIdentifierTable(JSGlobalData * globalData)293 void Identifier::checkCurrentIdentifierTable(JSGlobalData* globalData) 294 { 295 // Check the identifier table accessible through the threadspecific matches the 296 // globalData's identifier table. 297 ASSERT_UNUSED(globalData, globalData->identifierTable == wtfThreadData().currentIdentifierTable()); 298 } 299 checkCurrentIdentifierTable(ExecState * exec)300 void Identifier::checkCurrentIdentifierTable(ExecState* exec) 301 { 302 checkCurrentIdentifierTable(&exec->globalData()); 303 } 304 305 #else 306 307 // These only exists so that our exports are the same for debug and release builds. 308 // This would be an ASSERT_NOT_REACHED(), but we're in NDEBUG only code here! checkCurrentIdentifierTable(JSGlobalData *)309 void Identifier::checkCurrentIdentifierTable(JSGlobalData*) { CRASH(); } checkCurrentIdentifierTable(ExecState *)310 void Identifier::checkCurrentIdentifierTable(ExecState*) { CRASH(); } 311 312 #endif 313 314 } // namespace JSC 315