Home | History | Annotate | Download | only in runtime
      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:
     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 
     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>
     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 
     61     void remove(UString::Rep* r) { m_table.remove(r); }
     62 
     63     LiteralIdentifierTable& literalTable() { return m_literalTable; }
     64 
     65 private:
     66     HashSet<UString::Rep*> m_table;
     67     LiteralIdentifierTable m_literalTable;
     68 };
     69 
     70 IdentifierTable* createIdentifierTable()
     71 {
     72     return new IdentifierTable;
     73 }
     74 
     75 void deleteIdentifierTable(IdentifierTable* table)
     76 {
     77     delete table;
     78 }
     79 
     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 
     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 {
    102     static unsigned hash(const char* c)
    103     {
    104         return UString::Rep::computeHash(c);
    105     }
    106 
    107     static bool equal(UString::Rep* r, const char* s)
    108     {
    109         return Identifier::equal(r, s);
    110     }
    111 
    112     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 
    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 
    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 {
    164     static unsigned hash(const UCharBuffer& buf)
    165     {
    166         return UString::Rep::computeHash(buf.s, buf.length);
    167     }
    168 
    169     static bool equal(UString::Rep* str, const UCharBuffer& buf)
    170     {
    171         return Identifier::equal(str, buf.s, buf.length);
    172     }
    173 
    174     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 
    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 
    204 PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const UChar* s, int length)
    205 {
    206     return add(&exec->globalData(), s, length);
    207 }
    208 
    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 
    230 PassRefPtr<UString::Rep> Identifier::addSlowCase(ExecState* exec, UString::Rep* r)
    231 {
    232     return addSlowCase(&exec->globalData(), r);
    233 }
    234 
    235 void Identifier::remove(UString::Rep* r)
    236 {
    237     currentIdentifierTable()->remove(r);
    238 }
    239 
    240 #ifndef NDEBUG
    241 
    242 void Identifier::checkSameIdentifierTable(ExecState* exec, UString::Rep*)
    243 {
    244     ASSERT_UNUSED(exec, exec->globalData().identifierTable == currentIdentifierTable());
    245 }
    246 
    247 void Identifier::checkSameIdentifierTable(JSGlobalData* globalData, UString::Rep*)
    248 {
    249     ASSERT_UNUSED(globalData, globalData->identifierTable == currentIdentifierTable());
    250 }
    251 
    252 #else
    253 
    254 void Identifier::checkSameIdentifierTable(ExecState*, UString::Rep*)
    255 {
    256 }
    257 
    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;
    269 static void createIdentifierTableSpecificCallback()
    270 {
    271     ASSERT(!g_identifierTableSpecific);
    272     g_identifierTableSpecific = new ThreadSpecific<ThreadIdentifierTableData>();
    273 }
    274 void createIdentifierTableSpecific()
    275 {
    276     pthread_once(&createIdentifierTableSpecificOnce, createIdentifierTableSpecificCallback);
    277     ASSERT(g_identifierTableSpecific);
    278 }
    279 
    280 #else
    281 
    282 void createIdentifierTableSpecific()
    283 {
    284     ASSERT(!g_identifierTableSpecific);
    285     g_identifierTableSpecific = new ThreadSpecific<ThreadIdentifierTableData>();
    286 }
    287 
    288 #endif
    289 
    290 } // namespace JSC
    291