src/3rdparty/webkit/JavaScriptCore/runtime/JSString.cpp
changeset 30 5dc02b23752f
parent 0 1918ee327afb
--- a/src/3rdparty/webkit/JavaScriptCore/runtime/JSString.cpp	Wed Jun 23 19:07:03 2010 +0300
+++ b/src/3rdparty/webkit/JavaScriptCore/runtime/JSString.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -25,41 +25,127 @@
 
 #include "JSGlobalObject.h"
 #include "JSObject.h"
+#include "Operations.h"
 #include "StringObject.h"
 #include "StringPrototype.h"
 
 namespace JSC {
 
+// Overview: this methods converts a JSString from holding a string in rope form
+// down to a simple UString representation.  It does so by building up the string
+// backwards, since we want to avoid recursion, we expect that the tree structure
+// representing the rope is likely imbalanced with more nodes down the left side
+// (since appending to the string is likely more common) - and as such resolving
+// in this fashion should minimize work queue size.  (If we built the queue forwards
+// we would likely have to place all of the constituent UStringImpls into the
+// Vector before performing any concatenation, but by working backwards we likely
+// only fill the queue with the number of substrings at any given level in a
+// rope-of-ropes.)
+void JSString::resolveRope(ExecState* exec) const
+{
+    ASSERT(isRope());
+
+    // Allocate the buffer to hold the final string, position initially points to the end.
+    UChar* buffer;
+    if (PassRefPtr<UStringImpl> newImpl = UStringImpl::tryCreateUninitialized(m_length, buffer))
+        m_value = newImpl;
+    else {
+        for (unsigned i = 0; i < m_fiberCount; ++i) {
+            m_other.m_fibers[i]->deref();
+            m_other.m_fibers[i] = 0;
+        }
+        m_fiberCount = 0;
+        ASSERT(!isRope());
+        ASSERT(m_value == UString());
+        throwOutOfMemoryError(exec);
+        return;
+    }
+    UChar* position = buffer + m_length;
+
+    // Start with the current Rope.
+    Vector<Rope::Fiber, 32> workQueue;
+    Rope::Fiber currentFiber;
+    for (unsigned i = 0; i < (m_fiberCount - 1); ++i)
+        workQueue.append(m_other.m_fibers[i]);
+    currentFiber = m_other.m_fibers[m_fiberCount - 1];
+    while (true) {
+        if (currentFiber->isRope()) {
+            Rope* rope = static_cast<URopeImpl*>(currentFiber);
+            // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber'
+            // (we will be working backwards over the rope).
+            unsigned fiberCountMinusOne = rope->fiberCount() - 1;
+            for (unsigned i = 0; i < fiberCountMinusOne; ++i)
+                workQueue.append(rope->fibers(i));
+            currentFiber = rope->fibers(fiberCountMinusOne);
+        } else {
+            UStringImpl* string = static_cast<UStringImpl*>(currentFiber);
+            unsigned length = string->length();
+            position -= length;
+            UStringImpl::copyChars(position, string->characters(), length);
+
+            // Was this the last item in the work queue?
+            if (workQueue.isEmpty()) {
+                // Create a string from the UChar buffer, clear the rope RefPtr.
+                ASSERT(buffer == position);
+                for (unsigned i = 0; i < m_fiberCount; ++i) {
+                    m_other.m_fibers[i]->deref();
+                    m_other.m_fibers[i] = 0;
+                }
+                m_fiberCount = 0;
+
+                ASSERT(!isRope());
+                return;
+            }
+
+            // No! - set the next item up to process.
+            currentFiber = workQueue.last();
+            workQueue.removeLast();
+        }
+    }
+}
+
+JSString* JSString::getIndexSlowCase(ExecState* exec, unsigned i)
+{
+    ASSERT(isRope());
+    resolveRope(exec);
+    // Return a safe no-value result, this should never be used, since the excetion will be thrown.
+    if (exec->exception())
+        return jsString(exec, "");
+    ASSERT(!isRope());
+    ASSERT(i < m_value.size());
+    return jsSingleCharacterSubstring(exec, m_value, i);
+}
+
 JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
 {
     return const_cast<JSString*>(this);
 }
 
-bool JSString::getPrimitiveNumber(ExecState*, double& number, JSValue& value)
+bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
 {
-    value = this;
-    number = m_value.toDouble();
+    result = this;
+    number = value(exec).toDouble();
     return false;
 }
 
 bool JSString::toBoolean(ExecState*) const
 {
-    return !m_value.isEmpty();
+    return m_length;
 }
 
-double JSString::toNumber(ExecState*) const
+double JSString::toNumber(ExecState* exec) const
 {
-    return m_value.toDouble();
+    return value(exec).toDouble();
 }
 
-UString JSString::toString(ExecState*) const
+UString JSString::toString(ExecState* exec) const
 {
-    return m_value;
+    return value(exec);
 }
 
-UString JSString::toThisString(ExecState*) const
+UString JSString::toThisString(ExecState* exec) const
 {
-    return m_value;
+    return value(exec);
 }
 
 JSString* JSString::toThisJSString(ExecState*)
@@ -106,14 +192,14 @@
 bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
 {
     if (propertyName == exec->propertyNames().length) {
-        descriptor.setDescriptor(jsNumber(exec, m_value.size()), DontEnum | DontDelete | ReadOnly);
+        descriptor.setDescriptor(jsNumber(exec, m_length), DontEnum | DontDelete | ReadOnly);
         return true;
     }
     
     bool isStrictUInt32;
     unsigned i = propertyName.toStrictUInt32(&isStrictUInt32);
-    if (isStrictUInt32 && i < static_cast<unsigned>(m_value.size())) {
-        descriptor.setDescriptor(jsSingleCharacterSubstring(exec, m_value, i), DontDelete | ReadOnly);
+    if (isStrictUInt32 && i < m_length) {
+        descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly);
         return true;
     }