2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
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.
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.
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.
22 #include "Identifier.h"
24 #include "CallFrame.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>
36 using WTF::ThreadSpecific
;
40 IdentifierTable::~IdentifierTable()
42 HashSet
<StringImpl
*>::iterator end
= m_table
.end();
43 for (HashSet
<StringImpl
*>::iterator iter
= m_table
.begin(); iter
!= end
; ++iter
)
44 (*iter
)->setIsIdentifier(false);
46 std::pair
<HashSet
<StringImpl
*>::iterator
, bool> IdentifierTable::add(StringImpl
* value
)
48 std::pair
<HashSet
<StringImpl
*>::iterator
, bool> result
= m_table
.add(value
);
49 (*result
.first
)->setIsIdentifier(true);
52 template<typename U
, typename V
>
53 std::pair
<HashSet
<StringImpl
*>::iterator
, bool> IdentifierTable::add(U value
)
55 std::pair
<HashSet
<StringImpl
*>::iterator
, bool> result
= m_table
.add
<U
, V
>(value
);
56 (*result
.first
)->setIsIdentifier(true);
60 IdentifierTable
* createIdentifierTable()
62 return new IdentifierTable
;
65 void deleteIdentifierTable(IdentifierTable
* table
)
70 bool Identifier::equal(const StringImpl
* r
, const char* s
)
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
])
77 return s
[length
] == 0;
80 struct IdentifierCStringTranslator
{
81 static unsigned hash(const char* c
)
83 return StringHasher::computeHash
<char>(c
);
86 static bool equal(StringImpl
* r
, const char* s
)
88 return Identifier::equal(r
, s
);
91 static void translate(StringImpl
*& location
, const char* c
, unsigned hash
)
93 size_t length
= strlen(c
);
95 StringImpl
* r
= StringImpl::createUninitialized(length
, d
).leakRef();
96 for (size_t i
= 0; i
!= length
; i
++)
97 d
[i
] = static_cast<unsigned char>(c
[i
]); // use unsigned char to zero-extend instead of sign-extend
103 PassRefPtr
<StringImpl
> Identifier::add(JSGlobalData
* globalData
, const char* c
)
108 return StringImpl::empty();
110 return add(globalData
, globalData
->smallStrings
.singleCharacterStringRep(static_cast<unsigned char>(c
[0])));
112 IdentifierTable
& identifierTable
= *globalData
->identifierTable
;
113 LiteralIdentifierTable
& literalIdentifierTable
= identifierTable
.literalTable();
115 const LiteralIdentifierTable::iterator
& iter
= literalIdentifierTable
.find(c
);
116 if (iter
!= literalIdentifierTable
.end())
119 pair
<HashSet
<StringImpl
*>::iterator
, bool> addResult
= identifierTable
.add
<const char*, IdentifierCStringTranslator
>(c
);
121 // If the string is newly-translated, then we need to adopt it.
122 // The boolean in the pair tells us if that is so.
123 RefPtr
<StringImpl
> addedString
= addResult
.second
? adoptRef(*addResult
.first
) : *addResult
.first
;
125 literalIdentifierTable
.add(c
, addedString
.get());
127 return addedString
.release();
130 PassRefPtr
<StringImpl
> Identifier::add(ExecState
* exec
, const char* c
)
132 return add(&exec
->globalData(), c
);
140 struct IdentifierUCharBufferTranslator
{
141 static unsigned hash(const UCharBuffer
& buf
)
143 return StringHasher::computeHash
<UChar
>(buf
.s
, buf
.length
);
146 static bool equal(StringImpl
* str
, const UCharBuffer
& buf
)
148 return Identifier::equal(str
, buf
.s
, buf
.length
);
151 static void translate(StringImpl
*& location
, const UCharBuffer
& buf
, unsigned hash
)
154 StringImpl
* r
= StringImpl::createUninitialized(buf
.length
, d
).leakRef();
155 for (unsigned i
= 0; i
!= buf
.length
; i
++)
162 uint32_t Identifier::toUInt32(const UString
& string
, bool& ok
)
166 unsigned length
= string
.length();
167 const UChar
* characters
= string
.characters();
169 // An empty string is not a number.
173 // Get the first character, turning it into a digit.
174 uint32_t value
= characters
[0] - '0';
178 // Check for leading zeros. If the first characher is 0, then the
179 // length of the string must be one - e.g. "042" is not equal to "42".
180 if (!value
&& length
> 1)
184 // Multiply value by 10, checking for overflow out of 32 bits.
185 if (value
> 0xFFFFFFFFU
/ 10)
189 // Get the next character, turning it into a digit.
190 uint32_t newValue
= *(++characters
) - '0';
194 // Add in the old value, checking for overflow out of 32 bits.
196 if (newValue
< value
)
205 PassRefPtr
<StringImpl
> Identifier::add(JSGlobalData
* globalData
, const UChar
* s
, int length
)
209 if (c
<= maxSingleCharacterString
)
210 return add(globalData
, globalData
->smallStrings
.singleCharacterStringRep(c
));
213 return StringImpl::empty();
214 UCharBuffer buf
= {s
, length
};
215 pair
<HashSet
<StringImpl
*>::iterator
, bool> addResult
= globalData
->identifierTable
->add
<UCharBuffer
, IdentifierUCharBufferTranslator
>(buf
);
217 // If the string is newly-translated, then we need to adopt it.
218 // The boolean in the pair tells us if that is so.
219 return addResult
.second
? adoptRef(*addResult
.first
) : *addResult
.first
;
222 PassRefPtr
<StringImpl
> Identifier::add(ExecState
* exec
, const UChar
* s
, int length
)
224 return add(&exec
->globalData(), s
, length
);
227 PassRefPtr
<StringImpl
> Identifier::addSlowCase(JSGlobalData
* globalData
, StringImpl
* r
)
229 ASSERT(!r
->isIdentifier());
230 // The empty & null strings are static singletons, and static strings are handled
231 // in ::add() in the header, so we should never get here with a zero length string.
234 if (r
->length() == 1) {
235 UChar c
= r
->characters()[0];
236 if (c
<= maxSingleCharacterString
)
237 r
= globalData
->smallStrings
.singleCharacterStringRep(c
);
238 if (r
->isIdentifier())
242 return *globalData
->identifierTable
->add(r
).first
;
245 PassRefPtr
<StringImpl
> Identifier::addSlowCase(ExecState
* exec
, StringImpl
* r
)
247 return addSlowCase(&exec
->globalData(), r
);
250 Identifier
Identifier::from(ExecState
* exec
, unsigned value
)
252 return Identifier(exec
, exec
->globalData().numericStrings
.add(value
));
255 Identifier
Identifier::from(ExecState
* exec
, int value
)
257 return Identifier(exec
, exec
->globalData().numericStrings
.add(value
));
260 Identifier
Identifier::from(ExecState
* exec
, double value
)
262 return Identifier(exec
, exec
->globalData().numericStrings
.add(value
));
265 Identifier
Identifier::from(JSGlobalData
* globalData
, unsigned value
)
267 return Identifier(globalData
, globalData
->numericStrings
.add(value
));
270 Identifier
Identifier::from(JSGlobalData
* globalData
, int value
)
272 return Identifier(globalData
, globalData
->numericStrings
.add(value
));
275 Identifier
Identifier::from(JSGlobalData
* globalData
, double value
)
277 return Identifier(globalData
, globalData
->numericStrings
.add(value
));
282 void Identifier::checkCurrentIdentifierTable(JSGlobalData
* globalData
)
284 // Check the identifier table accessible through the threadspecific matches the
285 // globalData's identifier table.
286 ASSERT_UNUSED(globalData
, globalData
->identifierTable
== wtfThreadData().currentIdentifierTable());
289 void Identifier::checkCurrentIdentifierTable(ExecState
* exec
)
291 checkCurrentIdentifierTable(&exec
->globalData());
296 // These only exists so that our exports are the same for debug and release builds.
297 // This would be an ASSERT_NOT_REACHED(), but we're in NDEBUG only code here!
298 void Identifier::checkCurrentIdentifierTable(JSGlobalData
*) { CRASH(); }
299 void Identifier::checkCurrentIdentifierTable(ExecState
*) { CRASH(); }