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 <new> // for placement new
26 #include <string.h> // for strlen
27 #include <wtf/Assertions.h>
28 #include <wtf/FastMalloc.h>
29 #include <wtf/HashSet.h>
30
31 using WTF::ThreadSpecific;
32
33 namespace JSC {
34
35 typedef HashMap<const char*, RefPtr<UString::Rep>, PtrHash<const char*> > LiteralIdentifierTable;
36
37 class IdentifierTable : public FastAllocBase {
38 public:
~IdentifierTable()39 ~IdentifierTable()
40 {
41 HashSet<UString::Rep*>::iterator end = m_table.end();
42 for (HashSet<UString::Rep*>::iterator iter = m_table.begin(); iter != end; ++iter)
43 (*iter)->setIsIdentifier(false);
44 }
45
add(UString::Rep * value)46 std::pair<HashSet<UString::Rep*>::iterator, bool> add(UString::Rep* value)
47 {
48 std::pair<HashSet<UString::Rep*>::iterator, bool> result = m_table.add(value);
49 (*result.first)->setIsIdentifier(true);
50 return result;
51 }
52
53 template<typename U, typename V>
add(U value)54 std::pair<HashSet<UString::Rep*>::iterator, bool> add(U value)
55 {
56 std::pair<HashSet<UString::Rep*>::iterator, bool> result = m_table.add<U, V>(value);
57 (*result.first)->setIsIdentifier(true);
58 return result;
59 }
60
remove(UString::Rep * r)61 void remove(UString::Rep* r) { m_table.remove(r); }
62
literalTable()63 LiteralIdentifierTable& literalTable() { return m_literalTable; }
64
65 private:
66 HashSet<UString::Rep*> m_table;
67 LiteralIdentifierTable m_literalTable;
68 };
69
createIdentifierTable()70 IdentifierTable* createIdentifierTable()
71 {
72 return new IdentifierTable;
73 }
74
deleteIdentifierTable(IdentifierTable * table)75 void deleteIdentifierTable(IdentifierTable* table)
76 {
77 delete table;
78 }
79
equal(const UString::Rep * r,const char * s)80 bool Identifier::equal(const UString::Rep* r, const char* s)
81 {
82 int length = r->size();
83 const UChar* d = r->data();
84 for (int i = 0; i != length; ++i)
85 if (d[i] != (unsigned char)s[i])
86 return false;
87 return s[length] == 0;
88 }
89
equal(const UString::Rep * r,const UChar * s,int length)90 bool Identifier::equal(const UString::Rep* r, const UChar* s, int length)
91 {
92 if (r->size() != length)
93 return false;
94 const UChar* d = r->data();
95 for (int i = 0; i != length; ++i)
96 if (d[i] != s[i])
97 return false;
98 return true;
99 }
100
101 struct CStringTranslator {
hashJSC::CStringTranslator102 static unsigned hash(const char* c)
103 {
104 return UString::Rep::computeHash(c);
105 }
106
equalJSC::CStringTranslator107 static bool equal(UString::Rep* r, const char* s)
108 {
109 return Identifier::equal(r, s);
110 }
111
translateJSC::CStringTranslator112 static void translate(UString::Rep*& location, const char* c, unsigned hash)
113 {
114 size_t length = strlen(c);
115 UChar* d;
116 UString::Rep* r = UString::Rep::createUninitialized(length, d).releaseRef();
117 for (size_t i = 0; i != length; i++)
118 d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
119 r->setHash(hash);
120 location = r;
121 }
122 };
123
add(JSGlobalData * globalData,const char * c)124 PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const char* c)
125 {
126 ASSERT(c);
127
128 if (!c[0]) {
129 UString::Rep::empty().hash();
130 return &UString::Rep::empty();
131 }
132 if (!c[1])
133 return add(globalData, globalData->smallStrings.singleCharacterStringRep(static_cast<unsigned char>(c[0])));
134
135 IdentifierTable& identifierTable = *globalData->identifierTable;
136 LiteralIdentifierTable& literalIdentifierTable = identifierTable.literalTable();
137
138 const LiteralIdentifierTable::iterator& iter = literalIdentifierTable.find(c);
139 if (iter != literalIdentifierTable.end())
140 return iter->second;
141
142 pair<HashSet<UString::Rep*>::iterator, bool> addResult = identifierTable.add<const char*, CStringTranslator>(c);
143
144 // If the string is newly-translated, then we need to adopt it.
145 // The boolean in the pair tells us if that is so.
146 RefPtr<UString::Rep> addedString = addResult.second ? adoptRef(*addResult.first) : *addResult.first;
147
148 literalIdentifierTable.add(c, addedString.get());
149
150 return addedString.release();
151 }
152
add(ExecState * exec,const char * c)153 PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const char* c)
154 {
155 return add(&exec->globalData(), c);
156 }
157
158 struct UCharBuffer {
159 const UChar* s;
160 unsigned int length;
161 };
162
163 struct UCharBufferTranslator {
hashJSC::UCharBufferTranslator164 static unsigned hash(const UCharBuffer& buf)
165 {
166 return UString::Rep::computeHash(buf.s, buf.length);
167 }
168
equalJSC::UCharBufferTranslator169 static bool equal(UString::Rep* str, const UCharBuffer& buf)
170 {
171 return Identifier::equal(str, buf.s, buf.length);
172 }
173
translateJSC::UCharBufferTranslator174 static void translate(UString::Rep*& location, const UCharBuffer& buf, unsigned hash)
175 {
176 UChar* d;
177 UString::Rep* r = UString::Rep::createUninitialized(buf.length, d).releaseRef();
178 for (unsigned i = 0; i != buf.length; i++)
179 d[i] = buf.s[i];
180 r->setHash(hash);
181 location = r;
182 }
183 };
184
add(JSGlobalData * globalData,const UChar * s,int length)185 PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const UChar* s, int length)
186 {
187 if (length == 1) {
188 UChar c = s[0];
189 if (c <= 0xFF)
190 return add(globalData, globalData->smallStrings.singleCharacterStringRep(c));
191 }
192 if (!length) {
193 UString::Rep::empty().hash();
194 return &UString::Rep::empty();
195 }
196 UCharBuffer buf = {s, length};
197 pair<HashSet<UString::Rep*>::iterator, bool> addResult = globalData->identifierTable->add<UCharBuffer, UCharBufferTranslator>(buf);
198
199 // If the string is newly-translated, then we need to adopt it.
200 // The boolean in the pair tells us if that is so.
201 return addResult.second ? adoptRef(*addResult.first) : *addResult.first;
202 }
203
add(ExecState * exec,const UChar * s,int length)204 PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const UChar* s, int length)
205 {
206 return add(&exec->globalData(), s, length);
207 }
208
addSlowCase(JSGlobalData * globalData,UString::Rep * r)209 PassRefPtr<UString::Rep> Identifier::addSlowCase(JSGlobalData* globalData, UString::Rep* r)
210 {
211 ASSERT(!r->isIdentifier());
212 if (r->size() == 1) {
213 UChar c = r->data()[0];
214 if (c <= 0xFF)
215 r = globalData->smallStrings.singleCharacterStringRep(c);
216 if (r->isIdentifier()) {
217 #ifndef NDEBUG
218 checkSameIdentifierTable(globalData, r);
219 #endif
220 return r;
221 }
222 }
223 if (!r->size()) {
224 UString::Rep::empty().hash();
225 return &UString::Rep::empty();
226 }
227 return *globalData->identifierTable->add(r).first;
228 }
229
addSlowCase(ExecState * exec,UString::Rep * r)230 PassRefPtr<UString::Rep> Identifier::addSlowCase(ExecState* exec, UString::Rep* r)
231 {
232 return addSlowCase(&exec->globalData(), r);
233 }
234
remove(UString::Rep * r)235 void Identifier::remove(UString::Rep* r)
236 {
237 currentIdentifierTable()->remove(r);
238 }
239
240 #ifndef NDEBUG
241
checkSameIdentifierTable(ExecState * exec,UString::Rep *)242 void Identifier::checkSameIdentifierTable(ExecState* exec, UString::Rep*)
243 {
244 ASSERT_UNUSED(exec, exec->globalData().identifierTable == currentIdentifierTable());
245 }
246
checkSameIdentifierTable(JSGlobalData * globalData,UString::Rep *)247 void Identifier::checkSameIdentifierTable(JSGlobalData* globalData, UString::Rep*)
248 {
249 ASSERT_UNUSED(globalData, globalData->identifierTable == currentIdentifierTable());
250 }
251
252 #else
253
checkSameIdentifierTable(ExecState *,UString::Rep *)254 void Identifier::checkSameIdentifierTable(ExecState*, UString::Rep*)
255 {
256 }
257
checkSameIdentifierTable(JSGlobalData *,UString::Rep *)258 void Identifier::checkSameIdentifierTable(JSGlobalData*, UString::Rep*)
259 {
260 }
261
262 #endif
263
264 ThreadSpecific<ThreadIdentifierTableData>* g_identifierTableSpecific = 0;
265
266 #if ENABLE(JSC_MULTIPLE_THREADS)
267
268 pthread_once_t createIdentifierTableSpecificOnce = PTHREAD_ONCE_INIT;
createIdentifierTableSpecificCallback()269 static void createIdentifierTableSpecificCallback()
270 {
271 ASSERT(!g_identifierTableSpecific);
272 g_identifierTableSpecific = new ThreadSpecific<ThreadIdentifierTableData>();
273 }
createIdentifierTableSpecific()274 void createIdentifierTableSpecific()
275 {
276 pthread_once(&createIdentifierTableSpecificOnce, createIdentifierTableSpecificCallback);
277 ASSERT(g_identifierTableSpecific);
278 }
279
280 #else
281
createIdentifierTableSpecific()282 void createIdentifierTableSpecific()
283 {
284 ASSERT(!g_identifierTableSpecific);
285 g_identifierTableSpecific = new ThreadSpecific<ThreadIdentifierTableData>();
286 }
287
288 #endif
289
290 } // namespace JSC
291