JavaScriptCore/runtime/JSString.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
       
     3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
       
     4  *  Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved.
       
     5  *
       
     6  *  This library is free software; you can redistribute it and/or
       
     7  *  modify it under the terms of the GNU Library General Public
       
     8  *  License as published by the Free Software Foundation; either
       
     9  *  version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  *  This library is distributed in the hope that it will be useful,
       
    12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  *  Library General Public License for more details.
       
    15  *
       
    16  *  You should have received a copy of the GNU Library General Public License
       
    17  *  along with this library; see the file COPYING.LIB.  If not, write to
       
    18  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    19  *  Boston, MA 02110-1301, USA.
       
    20  *
       
    21  */
       
    22 
       
    23 #include "config.h"
       
    24 #include "JSString.h"
       
    25 
       
    26 #include "JSGlobalObject.h"
       
    27 #include "JSObject.h"
       
    28 #include "Operations.h"
       
    29 #include "StringObject.h"
       
    30 #include "StringPrototype.h"
       
    31 
       
    32 namespace JSC {
       
    33 
       
    34 // Overview: this methods converts a JSString from holding a string in rope form
       
    35 // down to a simple UString representation.  It does so by building up the string
       
    36 // backwards, since we want to avoid recursion, we expect that the tree structure
       
    37 // representing the rope is likely imbalanced with more nodes down the left side
       
    38 // (since appending to the string is likely more common) - and as such resolving
       
    39 // in this fashion should minimize work queue size.  (If we built the queue forwards
       
    40 // we would likely have to place all of the constituent UStringImpls into the
       
    41 // Vector before performing any concatenation, but by working backwards we likely
       
    42 // only fill the queue with the number of substrings at any given level in a
       
    43 // rope-of-ropes.)
       
    44 void JSString::resolveRope(ExecState* exec) const
       
    45 {
       
    46     ASSERT(isRope());
       
    47 
       
    48     // Allocate the buffer to hold the final string, position initially points to the end.
       
    49     UChar* buffer;
       
    50     if (PassRefPtr<UStringImpl> newImpl = UStringImpl::tryCreateUninitialized(m_length, buffer))
       
    51         m_value = newImpl;
       
    52     else {
       
    53         for (unsigned i = 0; i < m_fiberCount; ++i) {
       
    54             RopeImpl::deref(m_other.m_fibers[i]);
       
    55             m_other.m_fibers[i] = 0;
       
    56         }
       
    57         m_fiberCount = 0;
       
    58         ASSERT(!isRope());
       
    59         ASSERT(m_value == UString());
       
    60         if (exec)
       
    61             throwOutOfMemoryError(exec);
       
    62         return;
       
    63     }
       
    64     UChar* position = buffer + m_length;
       
    65 
       
    66     // Start with the current RopeImpl.
       
    67     Vector<RopeImpl::Fiber, 32> workQueue;
       
    68     RopeImpl::Fiber currentFiber;
       
    69     for (unsigned i = 0; i < (m_fiberCount - 1); ++i)
       
    70         workQueue.append(m_other.m_fibers[i]);
       
    71     currentFiber = m_other.m_fibers[m_fiberCount - 1];
       
    72     while (true) {
       
    73         if (RopeImpl::isRope(currentFiber)) {
       
    74             RopeImpl* rope = static_cast<RopeImpl*>(currentFiber);
       
    75             // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber'
       
    76             // (we will be working backwards over the rope).
       
    77             unsigned fiberCountMinusOne = rope->fiberCount() - 1;
       
    78             for (unsigned i = 0; i < fiberCountMinusOne; ++i)
       
    79                 workQueue.append(rope->fibers()[i]);
       
    80             currentFiber = rope->fibers()[fiberCountMinusOne];
       
    81         } else {
       
    82             UStringImpl* string = static_cast<UStringImpl*>(currentFiber);
       
    83             unsigned length = string->length();
       
    84             position -= length;
       
    85             UStringImpl::copyChars(position, string->characters(), length);
       
    86 
       
    87             // Was this the last item in the work queue?
       
    88             if (workQueue.isEmpty()) {
       
    89                 // Create a string from the UChar buffer, clear the rope RefPtr.
       
    90                 ASSERT(buffer == position);
       
    91                 for (unsigned i = 0; i < m_fiberCount; ++i) {
       
    92                     RopeImpl::deref(m_other.m_fibers[i]);
       
    93                     m_other.m_fibers[i] = 0;
       
    94                 }
       
    95                 m_fiberCount = 0;
       
    96 
       
    97                 ASSERT(!isRope());
       
    98                 return;
       
    99             }
       
   100 
       
   101             // No! - set the next item up to process.
       
   102             currentFiber = workQueue.last();
       
   103             workQueue.removeLast();
       
   104         }
       
   105     }
       
   106 }
       
   107 
       
   108 JSValue JSString::replaceCharacter(ExecState* exec, UChar character, const UString& replacement)
       
   109 {
       
   110     if (!isRope()) {
       
   111         unsigned matchPosition = m_value.find(character);
       
   112         if (matchPosition == UString::NotFound)
       
   113             return JSValue(this);
       
   114         return jsString(exec, m_value.substr(0, matchPosition), replacement, m_value.substr(matchPosition + 1));
       
   115     }
       
   116 
       
   117     RopeIterator end;
       
   118     
       
   119     // Count total fibers and find matching string.
       
   120     size_t fiberCount = 0;
       
   121     UStringImpl* matchString = 0;
       
   122     int matchPosition = -1;
       
   123     for (RopeIterator it(m_other.m_fibers.data(), m_fiberCount); it != end; ++it) {
       
   124         ++fiberCount;
       
   125         if (matchString)
       
   126             continue;
       
   127 
       
   128         UStringImpl* string = *it;
       
   129         matchPosition = string->find(character);
       
   130         if (matchPosition == -1)
       
   131             continue;
       
   132         matchString = string;
       
   133     }
       
   134 
       
   135     if (!matchString)
       
   136         return this;
       
   137 
       
   138     RopeBuilder builder(replacement.size() ? fiberCount + 2 : fiberCount + 1);
       
   139     if (UNLIKELY(builder.isOutOfMemory()))
       
   140         return throwOutOfMemoryError(exec);
       
   141 
       
   142     for (RopeIterator it(m_other.m_fibers.data(), m_fiberCount); it != end; ++it) {
       
   143         UStringImpl* string = *it;
       
   144         if (string != matchString) {
       
   145             builder.append(UString(string));
       
   146             continue;
       
   147         }
       
   148 
       
   149         builder.append(UString(string).substr(0, matchPosition));
       
   150         if (replacement.size())
       
   151             builder.append(replacement);
       
   152         builder.append(UString(string).substr(matchPosition + 1));
       
   153         matchString = 0;
       
   154     }
       
   155 
       
   156     JSGlobalData* globalData = &exec->globalData();
       
   157     return JSValue(new (globalData) JSString(globalData, builder.release()));
       
   158 }
       
   159 
       
   160 JSString* JSString::getIndexSlowCase(ExecState* exec, unsigned i)
       
   161 {
       
   162     ASSERT(isRope());
       
   163     resolveRope(exec);
       
   164     // Return a safe no-value result, this should never be used, since the excetion will be thrown.
       
   165     if (exec->exception())
       
   166         return jsString(exec, "");
       
   167     ASSERT(!isRope());
       
   168     ASSERT(i < m_value.size());
       
   169     return jsSingleCharacterSubstring(exec, m_value, i);
       
   170 }
       
   171 
       
   172 JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
       
   173 {
       
   174     return const_cast<JSString*>(this);
       
   175 }
       
   176 
       
   177 bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
       
   178 {
       
   179     result = this;
       
   180     number = value(exec).toDouble();
       
   181     return false;
       
   182 }
       
   183 
       
   184 bool JSString::toBoolean(ExecState*) const
       
   185 {
       
   186     return m_length;
       
   187 }
       
   188 
       
   189 double JSString::toNumber(ExecState* exec) const
       
   190 {
       
   191     return value(exec).toDouble();
       
   192 }
       
   193 
       
   194 UString JSString::toString(ExecState* exec) const
       
   195 {
       
   196     return value(exec);
       
   197 }
       
   198 
       
   199 inline StringObject* StringObject::create(ExecState* exec, JSString* string)
       
   200 {
       
   201     return new (exec) StringObject(exec->lexicalGlobalObject()->stringObjectStructure(), string);
       
   202 }
       
   203 
       
   204 JSObject* JSString::toObject(ExecState* exec) const
       
   205 {
       
   206     return StringObject::create(exec, const_cast<JSString*>(this));
       
   207 }
       
   208 
       
   209 JSObject* JSString::toThisObject(ExecState* exec) const
       
   210 {
       
   211     return StringObject::create(exec, const_cast<JSString*>(this));
       
   212 }
       
   213 
       
   214 bool JSString::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
       
   215 {
       
   216     // The semantics here are really getPropertySlot, not getOwnPropertySlot.
       
   217     // This function should only be called by JSValue::get.
       
   218     if (getStringPropertySlot(exec, propertyName, slot))
       
   219         return true;
       
   220     if (propertyName == exec->propertyNames().underscoreProto) {
       
   221         slot.setValue(exec->lexicalGlobalObject()->stringPrototype());
       
   222         return true;
       
   223     }
       
   224     slot.setBase(this);
       
   225     JSObject* object;
       
   226     for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) {
       
   227         object = asObject(prototype);
       
   228         if (object->getOwnPropertySlot(exec, propertyName, slot))
       
   229             return true;
       
   230     }
       
   231     slot.setUndefined();
       
   232     return true;
       
   233 }
       
   234 
       
   235 bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
       
   236 {
       
   237     if (propertyName == exec->propertyNames().length) {
       
   238         descriptor.setDescriptor(jsNumber(exec, m_length), DontEnum | DontDelete | ReadOnly);
       
   239         return true;
       
   240     }
       
   241     
       
   242     bool isStrictUInt32;
       
   243     unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
       
   244     if (isStrictUInt32 && i < m_length) {
       
   245         descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly);
       
   246         return true;
       
   247     }
       
   248     
       
   249     return false;
       
   250 }
       
   251 
       
   252 bool JSString::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
       
   253 {
       
   254     if (getStringPropertyDescriptor(exec, propertyName, descriptor))
       
   255         return true;
       
   256     if (propertyName != exec->propertyNames().underscoreProto)
       
   257         return false;
       
   258     descriptor.setDescriptor(exec->lexicalGlobalObject()->stringPrototype(), DontEnum);
       
   259     return true;
       
   260 }
       
   261 
       
   262 bool JSString::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
       
   263 {
       
   264     // The semantics here are really getPropertySlot, not getOwnPropertySlot.
       
   265     // This function should only be called by JSValue::get.
       
   266     if (getStringPropertySlot(exec, propertyName, slot))
       
   267         return true;
       
   268     return JSString::getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
       
   269 }
       
   270 
       
   271 } // namespace JSC