|
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 "NumericStrings.h" |
|
26 #include <new> // for placement new |
|
27 #include <string.h> // for strlen |
|
28 #include <wtf/Assertions.h> |
|
29 #include <wtf/FastMalloc.h> |
|
30 #include <wtf/HashSet.h> |
|
31 #include <wtf/WTFThreadData.h> |
|
32 #include <wtf/text/StringHash.h> |
|
33 |
|
34 using WTF::ThreadSpecific; |
|
35 |
|
36 namespace JSC { |
|
37 |
|
38 IdentifierTable::~IdentifierTable() |
|
39 { |
|
40 HashSet<StringImpl*>::iterator end = m_table.end(); |
|
41 for (HashSet<StringImpl*>::iterator iter = m_table.begin(); iter != end; ++iter) |
|
42 (*iter)->setIsIdentifier(false); |
|
43 } |
|
44 std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(StringImpl* value) |
|
45 { |
|
46 std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add(value); |
|
47 (*result.first)->setIsIdentifier(true); |
|
48 return result; |
|
49 } |
|
50 template<typename U, typename V> |
|
51 std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(U value) |
|
52 { |
|
53 std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add<U, V>(value); |
|
54 (*result.first)->setIsIdentifier(true); |
|
55 return result; |
|
56 } |
|
57 |
|
58 IdentifierTable* createIdentifierTable() |
|
59 { |
|
60 return new IdentifierTable; |
|
61 } |
|
62 |
|
63 void deleteIdentifierTable(IdentifierTable* table) |
|
64 { |
|
65 delete table; |
|
66 } |
|
67 |
|
68 bool Identifier::equal(const UString::Rep* r, const char* s) |
|
69 { |
|
70 int length = r->length(); |
|
71 const UChar* d = r->characters(); |
|
72 for (int i = 0; i != length; ++i) |
|
73 if (d[i] != (unsigned char)s[i]) |
|
74 return false; |
|
75 return s[length] == 0; |
|
76 } |
|
77 |
|
78 bool Identifier::equal(const UString::Rep* r, const UChar* s, unsigned length) |
|
79 { |
|
80 if (r->length() != length) |
|
81 return false; |
|
82 const UChar* d = r->characters(); |
|
83 for (unsigned i = 0; i != length; ++i) |
|
84 if (d[i] != s[i]) |
|
85 return false; |
|
86 return true; |
|
87 } |
|
88 |
|
89 struct IdentifierCStringTranslator { |
|
90 static unsigned hash(const char* c) |
|
91 { |
|
92 return UString::Rep::computeHash(c); |
|
93 } |
|
94 |
|
95 static bool equal(UString::Rep* r, const char* s) |
|
96 { |
|
97 return Identifier::equal(r, s); |
|
98 } |
|
99 |
|
100 static void translate(UString::Rep*& location, const char* c, unsigned hash) |
|
101 { |
|
102 size_t length = strlen(c); |
|
103 UChar* d; |
|
104 UString::Rep* r = UString::Rep::createUninitialized(length, d).releaseRef(); |
|
105 for (size_t i = 0; i != length; i++) |
|
106 d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend |
|
107 r->setHash(hash); |
|
108 location = r; |
|
109 } |
|
110 }; |
|
111 |
|
112 PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const char* c) |
|
113 { |
|
114 if (!c) |
|
115 return UString::null().rep(); |
|
116 if (!c[0]) |
|
117 return UString::Rep::empty(); |
|
118 if (!c[1]) |
|
119 return add(globalData, globalData->smallStrings.singleCharacterStringRep(static_cast<unsigned char>(c[0]))); |
|
120 |
|
121 IdentifierTable& identifierTable = *globalData->identifierTable; |
|
122 LiteralIdentifierTable& literalIdentifierTable = identifierTable.literalTable(); |
|
123 |
|
124 const LiteralIdentifierTable::iterator& iter = literalIdentifierTable.find(c); |
|
125 if (iter != literalIdentifierTable.end()) |
|
126 return iter->second; |
|
127 |
|
128 pair<HashSet<UString::Rep*>::iterator, bool> addResult = identifierTable.add<const char*, IdentifierCStringTranslator>(c); |
|
129 |
|
130 // If the string is newly-translated, then we need to adopt it. |
|
131 // The boolean in the pair tells us if that is so. |
|
132 RefPtr<UString::Rep> addedString = addResult.second ? adoptRef(*addResult.first) : *addResult.first; |
|
133 |
|
134 literalIdentifierTable.add(c, addedString.get()); |
|
135 |
|
136 return addedString.release(); |
|
137 } |
|
138 |
|
139 PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const char* c) |
|
140 { |
|
141 return add(&exec->globalData(), c); |
|
142 } |
|
143 |
|
144 struct UCharBuffer { |
|
145 const UChar* s; |
|
146 unsigned int length; |
|
147 }; |
|
148 |
|
149 struct IdentifierUCharBufferTranslator { |
|
150 static unsigned hash(const UCharBuffer& buf) |
|
151 { |
|
152 return UString::Rep::computeHash(buf.s, buf.length); |
|
153 } |
|
154 |
|
155 static bool equal(UString::Rep* str, const UCharBuffer& buf) |
|
156 { |
|
157 return Identifier::equal(str, buf.s, buf.length); |
|
158 } |
|
159 |
|
160 static void translate(UString::Rep*& location, const UCharBuffer& buf, unsigned hash) |
|
161 { |
|
162 UChar* d; |
|
163 UString::Rep* r = UString::Rep::createUninitialized(buf.length, d).releaseRef(); |
|
164 for (unsigned i = 0; i != buf.length; i++) |
|
165 d[i] = buf.s[i]; |
|
166 r->setHash(hash); |
|
167 location = r; |
|
168 } |
|
169 }; |
|
170 |
|
171 PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const UChar* s, int length) |
|
172 { |
|
173 if (length == 1) { |
|
174 UChar c = s[0]; |
|
175 if (c <= 0xFF) |
|
176 return add(globalData, globalData->smallStrings.singleCharacterStringRep(c)); |
|
177 } |
|
178 if (!length) |
|
179 return UString::Rep::empty(); |
|
180 UCharBuffer buf = {s, length}; |
|
181 pair<HashSet<UString::Rep*>::iterator, bool> addResult = globalData->identifierTable->add<UCharBuffer, IdentifierUCharBufferTranslator>(buf); |
|
182 |
|
183 // If the string is newly-translated, then we need to adopt it. |
|
184 // The boolean in the pair tells us if that is so. |
|
185 return addResult.second ? adoptRef(*addResult.first) : *addResult.first; |
|
186 } |
|
187 |
|
188 PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const UChar* s, int length) |
|
189 { |
|
190 return add(&exec->globalData(), s, length); |
|
191 } |
|
192 |
|
193 PassRefPtr<UString::Rep> Identifier::addSlowCase(JSGlobalData* globalData, UString::Rep* r) |
|
194 { |
|
195 ASSERT(!r->isIdentifier()); |
|
196 // The empty & null strings are static singletons, and static strings are handled |
|
197 // in ::add() in the header, so we should never get here with a zero length string. |
|
198 ASSERT(r->length()); |
|
199 |
|
200 if (r->length() == 1) { |
|
201 UChar c = r->characters()[0]; |
|
202 if (c <= 0xFF) |
|
203 r = globalData->smallStrings.singleCharacterStringRep(c); |
|
204 if (r->isIdentifier()) |
|
205 return r; |
|
206 } |
|
207 |
|
208 return *globalData->identifierTable->add(r).first; |
|
209 } |
|
210 |
|
211 PassRefPtr<UString::Rep> Identifier::addSlowCase(ExecState* exec, UString::Rep* r) |
|
212 { |
|
213 return addSlowCase(&exec->globalData(), r); |
|
214 } |
|
215 |
|
216 Identifier Identifier::from(ExecState* exec, unsigned value) |
|
217 { |
|
218 return Identifier(exec, exec->globalData().numericStrings.add(value)); |
|
219 } |
|
220 |
|
221 Identifier Identifier::from(ExecState* exec, int value) |
|
222 { |
|
223 return Identifier(exec, exec->globalData().numericStrings.add(value)); |
|
224 } |
|
225 |
|
226 Identifier Identifier::from(ExecState* exec, double value) |
|
227 { |
|
228 return Identifier(exec, exec->globalData().numericStrings.add(value)); |
|
229 } |
|
230 |
|
231 Identifier Identifier::from(JSGlobalData* globalData, unsigned value) |
|
232 { |
|
233 return Identifier(globalData, globalData->numericStrings.add(value)); |
|
234 } |
|
235 |
|
236 Identifier Identifier::from(JSGlobalData* globalData, int value) |
|
237 { |
|
238 return Identifier(globalData, globalData->numericStrings.add(value)); |
|
239 } |
|
240 |
|
241 Identifier Identifier::from(JSGlobalData* globalData, double value) |
|
242 { |
|
243 return Identifier(globalData, globalData->numericStrings.add(value)); |
|
244 } |
|
245 |
|
246 #ifndef NDEBUG |
|
247 |
|
248 void Identifier::checkCurrentIdentifierTable(JSGlobalData* globalData) |
|
249 { |
|
250 // Check the identifier table accessible through the threadspecific matches the |
|
251 // globalData's identifier table. |
|
252 ASSERT_UNUSED(globalData, globalData->identifierTable == wtfThreadData().currentIdentifierTable()); |
|
253 } |
|
254 |
|
255 void Identifier::checkCurrentIdentifierTable(ExecState* exec) |
|
256 { |
|
257 checkCurrentIdentifierTable(&exec->globalData()); |
|
258 } |
|
259 |
|
260 #else |
|
261 |
|
262 // These only exists so that our exports are the same for debug and release builds. |
|
263 // This would be an ASSERT_NOT_REACHED(), but we're in NDEBUG only code here! |
|
264 void Identifier::checkCurrentIdentifierTable(JSGlobalData*) { CRASH(); } |
|
265 void Identifier::checkCurrentIdentifierTable(ExecState*) { CRASH(); } |
|
266 |
|
267 #endif |
|
268 |
|
269 } // namespace JSC |