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 36using WTF::ThreadSpecific; 37 38namespace JSC { 39 40IdentifierTable::~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} 46std::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} 52template<typename U, typename V> 53std::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 60IdentifierTable* createIdentifierTable() 61{ 62 return new IdentifierTable; 63} 64 65void deleteIdentifierTable(IdentifierTable* table) 66{ 67 delete table; 68} 69 70bool 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 80bool 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 91struct IdentifierCStringTranslator { 92 static unsigned hash(const char* c) 93 { 94 return StringHasher::computeHash<char>(c); 95 } 96 97 static bool equal(StringImpl* r, const char* s) 98 { 99 return Identifier::equal(r, s); 100 } 101 102 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 114PassRefPtr<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 141PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const char* c) 142{ 143 return add(&exec->globalData(), c); 144} 145 146struct UCharBuffer { 147 const UChar* s; 148 unsigned int length; 149}; 150 151struct IdentifierUCharBufferTranslator { 152 static unsigned hash(const UCharBuffer& buf) 153 { 154 return StringHasher::computeHash<UChar>(buf.s, buf.length); 155 } 156 157 static bool equal(StringImpl* str, const UCharBuffer& buf) 158 { 159 return Identifier::equal(str, buf.s, buf.length); 160 } 161 162 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 173uint32_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 216PassRefPtr<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 233PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const UChar* s, int length) 234{ 235 return add(&exec->globalData(), s, length); 236} 237 238PassRefPtr<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 256PassRefPtr<StringImpl> Identifier::addSlowCase(ExecState* exec, StringImpl* r) 257{ 258 return addSlowCase(&exec->globalData(), r); 259} 260 261Identifier Identifier::from(ExecState* exec, unsigned value) 262{ 263 return Identifier(exec, exec->globalData().numericStrings.add(value)); 264} 265 266Identifier Identifier::from(ExecState* exec, int value) 267{ 268 return Identifier(exec, exec->globalData().numericStrings.add(value)); 269} 270 271Identifier Identifier::from(ExecState* exec, double value) 272{ 273 return Identifier(exec, exec->globalData().numericStrings.add(value)); 274} 275 276Identifier Identifier::from(JSGlobalData* globalData, unsigned value) 277{ 278 return Identifier(globalData, globalData->numericStrings.add(value)); 279} 280 281Identifier Identifier::from(JSGlobalData* globalData, int value) 282{ 283 return Identifier(globalData, globalData->numericStrings.add(value)); 284} 285 286Identifier Identifier::from(JSGlobalData* globalData, double value) 287{ 288 return Identifier(globalData, globalData->numericStrings.add(value)); 289} 290 291#ifndef NDEBUG 292 293void 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 300void 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! 309void Identifier::checkCurrentIdentifierTable(JSGlobalData*) { CRASH(); } 310void Identifier::checkCurrentIdentifierTable(ExecState*) { CRASH(); } 311 312#endif 313 314} // namespace JSC 315