• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
destructNonRecursive()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 
~Rope()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.)
resolveRope(ExecState * exec) const79 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 
toPrimitive(ExecState *,PreferredPrimitiveType) const142 JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
143 {
144     return const_cast<JSString*>(this);
145 }
146 
getPrimitiveNumber(ExecState * exec,double & number,JSValue & result)147 bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
148 {
149     result = this;
150     number = value(exec).toDouble();
151     return false;
152 }
153 
toBoolean(ExecState *) const154 bool JSString::toBoolean(ExecState*) const
155 {
156     return m_stringLength;
157 }
158 
toNumber(ExecState * exec) const159 double JSString::toNumber(ExecState* exec) const
160 {
161     return value(exec).toDouble();
162 }
163 
toString(ExecState * exec) const164 UString JSString::toString(ExecState* exec) const
165 {
166     return value(exec);
167 }
168 
toThisString(ExecState * exec) const169 UString JSString::toThisString(ExecState* exec) const
170 {
171     return value(exec);
172 }
173 
toThisJSString(ExecState *)174 JSString* JSString::toThisJSString(ExecState*)
175 {
176     return this;
177 }
178 
create(ExecState * exec,JSString * string)179 inline StringObject* StringObject::create(ExecState* exec, JSString* string)
180 {
181     return new (exec) StringObject(exec->lexicalGlobalObject()->stringObjectStructure(), string);
182 }
183 
toObject(ExecState * exec) const184 JSObject* JSString::toObject(ExecState* exec) const
185 {
186     return StringObject::create(exec, const_cast<JSString*>(this));
187 }
188 
toThisObject(ExecState * exec) const189 JSObject* JSString::toThisObject(ExecState* exec) const
190 {
191     return StringObject::create(exec, const_cast<JSString*>(this));
192 }
193 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)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 
getStringPropertyDescriptor(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & descriptor)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 
getOwnPropertyDescriptor(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & descriptor)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 
getOwnPropertySlot(ExecState * exec,unsigned propertyName,PropertySlot & slot)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