| 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 | 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(index: i); | 
| 43 |             if (fiber.isString()) | 
| 44 |                 fiber.string()->deref(); | 
| 45 |             else { | 
| 46 |                 Rope* nextRope = fiber.rope(); | 
| 47 |                 if (nextRope->hasOneRef()) | 
| 48 |                     workQueue.append(val: 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(length: m_stringLength, output&: 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(val: 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(val: rope->fibers(index: i)); | 
| 114 |             currentFiber = rope->fibers(index: ropeLengthMinusOne); | 
| 115 |         } else { | 
| 116 |             UString::Rep* string = currentFiber.string(); | 
| 117 |             unsigned length = string->size(); | 
| 118 |             position -= length; | 
| 119 |             UStringImpl::copyChars(destination: position, source: string->data(), numCharacters: 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, string: const_cast<JSString*>(this)); | 
| 187 | } | 
| 188 |  | 
| 189 | JSObject* JSString::toThisObject(ExecState* exec) const | 
| 190 | { | 
| 191 |     return StringObject::create(exec, string: 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(value: 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(value: jsNumber(exec, i: m_stringLength), attributes: DontEnum | DontDelete | ReadOnly); | 
| 219 |         return true; | 
| 220 |     } | 
| 221 |      | 
| 222 |     bool isStrictUInt32; | 
| 223 |     unsigned i = propertyName.toStrictUInt32(ok: &isStrictUInt32); | 
| 224 |     if (isStrictUInt32 && i < m_stringLength) { | 
| 225 |         descriptor.setDescriptor(value: jsSingleCharacterSubstring(exec, s: value(exec), offset: i), attributes: 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(value: exec->lexicalGlobalObject()->stringPrototype(), attributes: 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, propertyName: Identifier::from(exec, y: propertyName), slot); | 
| 249 | } | 
| 250 |  | 
| 251 | } // namespace JSC | 
| 252 |  |