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