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