Home | History | Annotate | Download | only in runtime
      1 /*
      2  *  Copyright (C) 1999-2002 Harri Porten (porten (at) kde.org)
      3  *  Copyright (C) 2001 Peter Kelly (pmk (at) 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 void JSString::Rope::destructNonRecursive()
     35 {
     36     Vector<Rope*, 32> workQueue;
     37     Rope* rope = this;
     38 
     39     while (true) {
     40         unsigned length = rope->ropeLength();
     41         for (unsigned i = 0; i < length; ++i) {
     42             Fiber& fiber = rope->fibers(i);
     43             if (fiber.isString())
     44                 fiber.string()->deref();
     45             else {
     46                 Rope* nextRope = fiber.rope();
     47                 if (nextRope->hasOneRef())
     48                     workQueue.append(nextRope);
     49                 else
     50                     nextRope->deref();
     51             }
     52         }
     53         if (rope != this)
     54             fastFree(rope);
     55 
     56         if (workQueue.isEmpty())
     57             return;
     58 
     59         rope = workQueue.last();
     60         workQueue.removeLast();
     61     }
     62 }
     63 
     64 JSString::Rope::~Rope()
     65 {
     66     destructNonRecursive();
     67 }
     68 
     69 // Overview: this methods converts a JSString from holding a string in rope form
     70 // down to a simple UString representation.  It does so by building up the string
     71 // backwards, since we want to avoid recursion, we expect that the tree structure
     72 // representing the rope is likely imbalanced with more nodes down the left side
     73 // (since appending to the string is likely more common) - and as such resolving
     74 // in this fashion should minimize work queue size.  (If we built the queue forwards
     75 // we would likely have to place all of the constituent UString::Reps into the
     76 // Vector before performing any concatenation, but by working backwards we likely
     77 // only fill the queue with the number of substrings at any given level in a
     78 // rope-of-ropes.)
     79 void JSString::resolveRope(ExecState* exec) const
     80 {
     81     ASSERT(isRope());
     82 
     83     // Allocate the buffer to hold the final string, position initially points to the end.
     84     UChar* buffer;
     85     if (PassRefPtr<UStringImpl> newImpl = UStringImpl::tryCreateUninitialized(m_stringLength, buffer))
     86         m_value = newImpl;
     87     else {
     88         for (unsigned i = 0; i < m_ropeLength; ++i) {
     89             m_fibers[i].deref();
     90             m_fibers[i] = static_cast<void*>(0);
     91         }
     92         m_ropeLength = 0;
     93         ASSERT(!isRope());
     94         ASSERT(m_value == UString());
     95         throwOutOfMemoryError(exec);
     96         return;
     97     }
     98     UChar* position = buffer + m_stringLength;
     99 
    100     // Start with the current Rope.
    101     Vector<Rope::Fiber, 32> workQueue;
    102     Rope::Fiber currentFiber;
    103     for (unsigned i = 0; i < (m_ropeLength - 1); ++i)
    104         workQueue.append(m_fibers[i]);
    105     currentFiber = m_fibers[m_ropeLength - 1];
    106     while (true) {
    107         if (currentFiber.isRope()) {
    108             Rope* rope = currentFiber.rope();
    109             // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber'
    110             // (we will be working backwards over the rope).
    111             unsigned ropeLengthMinusOne = rope->ropeLength() - 1;
    112             for (unsigned i = 0; i < ropeLengthMinusOne; ++i)
    113                 workQueue.append(rope->fibers(i));
    114             currentFiber = rope->fibers(ropeLengthMinusOne);
    115         } else {
    116             UString::Rep* string = currentFiber.string();
    117             unsigned length = string->size();
    118             position -= length;
    119             UStringImpl::copyChars(position, string->data(), length);
    120 
    121             // Was this the last item in the work queue?
    122             if (workQueue.isEmpty()) {
    123                 // Create a string from the UChar buffer, clear the rope RefPtr.
    124                 ASSERT(buffer == position);
    125                 for (unsigned i = 0; i < m_ropeLength; ++i) {
    126                     m_fibers[i].deref();
    127                     m_fibers[i] = static_cast<void*>(0);
    128                 }
    129                 m_ropeLength = 0;
    130 
    131                 ASSERT(!isRope());
    132                 return;
    133             }
    134 
    135             // No! - set the next item up to process.
    136             currentFiber = workQueue.last();
    137             workQueue.removeLast();
    138         }
    139     }
    140 }
    141 
    142 JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
    143 {
    144     return const_cast<JSString*>(this);
    145 }
    146 
    147 bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
    148 {
    149     result = this;
    150     number = value(exec).toDouble();
    151     return false;
    152 }
    153 
    154 bool JSString::toBoolean(ExecState*) const
    155 {
    156     return m_stringLength;
    157 }
    158 
    159 double JSString::toNumber(ExecState* exec) const
    160 {
    161     return value(exec).toDouble();
    162 }
    163 
    164 UString JSString::toString(ExecState* exec) const
    165 {
    166     return value(exec);
    167 }
    168 
    169 UString JSString::toThisString(ExecState* exec) const
    170 {
    171     return value(exec);
    172 }
    173 
    174 JSString* JSString::toThisJSString(ExecState*)
    175 {
    176     return this;
    177 }
    178 
    179 inline StringObject* StringObject::create(ExecState* exec, JSString* string)
    180 {
    181     return new (exec) StringObject(exec->lexicalGlobalObject()->stringObjectStructure(), string);
    182 }
    183 
    184 JSObject* JSString::toObject(ExecState* exec) const
    185 {
    186     return StringObject::create(exec, const_cast<JSString*>(this));
    187 }
    188 
    189 JSObject* JSString::toThisObject(ExecState* exec) const
    190 {
    191     return StringObject::create(exec, const_cast<JSString*>(this));
    192 }
    193 
    194 bool JSString::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
    195 {
    196     // The semantics here are really getPropertySlot, not getOwnPropertySlot.
    197     // This function should only be called by JSValue::get.
    198     if (getStringPropertySlot(exec, propertyName, slot))
    199         return true;
    200     if (propertyName == exec->propertyNames().underscoreProto) {
    201         slot.setValue(exec->lexicalGlobalObject()->stringPrototype());
    202         return true;
    203     }
    204     slot.setBase(this);
    205     JSObject* object;
    206     for (JSValue prototype = exec->lexicalGlobalObject()->stringPrototype(); !prototype.isNull(); prototype = object->prototype()) {
    207         object = asObject(prototype);
    208         if (object->getOwnPropertySlot(exec, propertyName, slot))
    209             return true;
    210     }
    211     slot.setUndefined();
    212     return true;
    213 }
    214 
    215 bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
    216 {
    217     if (propertyName == exec->propertyNames().length) {
    218         descriptor.setDescriptor(jsNumber(exec, m_stringLength), DontEnum | DontDelete | ReadOnly);
    219         return true;
    220     }
    221 
    222     bool isStrictUInt32;
    223     unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
    224     if (isStrictUInt32 && i < m_stringLength) {
    225         descriptor.setDescriptor(jsSingleCharacterSubstring(exec, value(exec), i), DontDelete | ReadOnly);
    226         return true;
    227     }
    228 
    229     return false;
    230 }
    231 
    232 bool JSString::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
    233 {
    234     if (getStringPropertyDescriptor(exec, propertyName, descriptor))
    235         return true;
    236     if (propertyName != exec->propertyNames().underscoreProto)
    237         return false;
    238     descriptor.setDescriptor(exec->lexicalGlobalObject()->stringPrototype(), DontEnum);
    239     return true;
    240 }
    241 
    242 bool JSString::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
    243 {
    244     // The semantics here are really getPropertySlot, not getOwnPropertySlot.
    245     // This function should only be called by JSValue::get.
    246     if (getStringPropertySlot(exec, propertyName, slot))
    247         return true;
    248     return JSString::getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
    249 }
    250 
    251 } // namespace JSC
    252