• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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