+ JSString* thisObject = static_cast<JSString*>(cell);
+ thisObject->JSString::~JSString();
+}
+
+void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+ JSString* thisObject = jsCast<JSString*>(cell);
+ Base::visitChildren(thisObject, visitor);
+
+ MARK_LOG_MESSAGE1("[%u]: ", thisObject->length());
+
+#if ENABLE(OBJECT_MARK_LOGGING)
+ if (!thisObject->isRope()) {
+ WTF::StringImpl* ourImpl = thisObject->m_value.impl();
+ if (ourImpl->is8Bit())
+ MARK_LOG_MESSAGE1("[8 %p]", ourImpl->characters8());
+ else
+ MARK_LOG_MESSAGE1("[16 %p]", ourImpl->characters16());
+ } else
+ MARK_LOG_MESSAGE0("[rope]: ");
+#endif
+
+ if (thisObject->isRope())
+ static_cast<JSRopeString*>(thisObject)->visitFibers(visitor);
+}
+
+void JSRopeString::visitFibers(SlotVisitor& visitor)
+{
+ for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
+ visitor.append(&m_fibers[i]);
+}
+
+void JSRopeString::resolveRope(ExecState* exec) const
+{
+ ASSERT(isRope());
+
+ if (is8Bit()) {
+ LChar* buffer;
+ if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) {
+ Heap::heap(this)->reportExtraMemoryCost(newImpl->cost());
+ m_value = newImpl.release();
+ } else {
+ outOfMemory(exec);
+ return;
+ }
+
+ for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
+ if (m_fibers[i]->isRope())
+ return resolveRopeSlowCase8(buffer);
+ }
+
+ LChar* position = buffer;
+ for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
+ StringImpl* string = m_fibers[i]->m_value.impl();
+ unsigned length = string->length();
+ StringImpl::copyChars(position, string->characters8(), length);
+ position += length;
+ m_fibers[i].clear();
+ }
+ ASSERT((buffer + m_length) == position);
+ ASSERT(!isRope());
+
+ return;
+ }
+
+ UChar* buffer;
+ if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) {
+ Heap::heap(this)->reportExtraMemoryCost(newImpl->cost());
+ m_value = newImpl.release();
+ } else {
+ outOfMemory(exec);
+ return;
+ }
+
+ for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
+ if (m_fibers[i]->isRope())
+ return resolveRopeSlowCase(buffer);
+ }
+
+ UChar* position = buffer;
+ for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
+ StringImpl* string = m_fibers[i]->m_value.impl();
+ unsigned length = string->length();
+ if (string->is8Bit())
+ StringImpl::copyChars(position, string->characters8(), length);
+ else
+ StringImpl::copyChars(position, string->characters16(), length);
+ position += length;
+ m_fibers[i].clear();
+ }
+ ASSERT((buffer + m_length) == position);
+ ASSERT(!isRope());
+}
+
+// Overview: These functions convert a JSString from holding a string in rope form
+// down to a simple String 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 StringImpls 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 JSRopeString::resolveRopeSlowCase8(LChar* buffer) const
+{
+ LChar* position = buffer + m_length; // We will be working backwards over the rope.
+ Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method.
+
+ for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
+ workQueue.append(m_fibers[i].get());
+ // Clearing here works only because there are no GC points in this method.
+ m_fibers[i].clear();
+ }
+
+ while (!workQueue.isEmpty()) {
+ JSString* currentFiber = workQueue.last();
+ workQueue.removeLast();
+
+ if (currentFiber->isRope()) {
+ JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
+ for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i)
+ workQueue.append(currentFiberAsRope->m_fibers[i].get());
+ continue;
+ }
+
+ StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
+ unsigned length = string->length();
+ position -= length;
+ StringImpl::copyChars(position, string->characters8(), length);
+ }
+
+ ASSERT(buffer == position);
+ ASSERT(!isRope());