• 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