+ if (isSubstring()) {
+ visitor.append(&substringBase());
+ return;
+ }
+ for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+ visitor.append(&fiber(i));
+}
+
+static const unsigned maxLengthForOnStackResolve = 2048;
+
+void JSRopeString::resolveRopeInternal8(LChar* buffer) const
+{
+ if (isSubstring()) {
+ StringImpl::copyChars(
+ buffer, substringBase()->m_value.characters8() + substringOffset(), m_length);
+ return;
+ }
+
+ resolveRopeInternal8NoSubstring(buffer);
+}
+
+void JSRopeString::resolveRopeInternal8NoSubstring(LChar* buffer) const
+{
+ for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+ if (fiber(i)->isRope()) {
+ resolveRopeSlowCase8(buffer);
+ return;
+ }
+ }
+
+ LChar* position = buffer;
+ for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+ const StringImpl& fiberString = *fiber(i)->m_value.impl();
+ unsigned length = fiberString.length();
+ StringImpl::copyChars(position, fiberString.characters8(), length);
+ position += length;
+ }
+ ASSERT((buffer + m_length) == position);
+}
+
+void JSRopeString::resolveRopeInternal16(UChar* buffer) const
+{
+ if (isSubstring()) {
+ StringImpl::copyChars(
+ buffer, substringBase()->m_value.characters16() + substringOffset(), m_length);
+ return;
+ }
+
+ resolveRopeInternal16NoSubstring(buffer);
+}
+
+void JSRopeString::resolveRopeInternal16NoSubstring(UChar* buffer) const
+{
+ for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+ if (fiber(i)->isRope()) {
+ resolveRopeSlowCase(buffer);
+ return;
+ }
+ }
+
+ UChar* position = buffer;
+ for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+ const StringImpl& fiberString = *fiber(i)->m_value.impl();
+ unsigned length = fiberString.length();
+ if (fiberString.is8Bit())
+ StringImpl::copyChars(position, fiberString.characters8(), length);
+ else
+ StringImpl::copyChars(position, fiberString.characters16(), length);
+ position += length;
+ }
+ ASSERT((buffer + m_length) == position);
+}
+
+void JSRopeString::resolveRopeToAtomicString(ExecState* exec) const
+{
+ if (m_length > maxLengthForOnStackResolve) {
+ resolveRope(exec);
+ m_value = AtomicString(m_value);
+ setIs8Bit(m_value.impl()->is8Bit());
+ return;
+ }
+
+ if (is8Bit()) {
+ LChar buffer[maxLengthForOnStackResolve];
+ resolveRopeInternal8(buffer);
+ m_value = AtomicString(buffer, m_length);
+ setIs8Bit(m_value.impl()->is8Bit());
+ } else {
+ UChar buffer[maxLengthForOnStackResolve];
+ resolveRopeInternal16(buffer);
+ m_value = AtomicString(buffer, m_length);
+ setIs8Bit(m_value.impl()->is8Bit());
+ }
+
+ clearFibers();
+
+ // If we resolved a string that didn't previously exist, notify the heap that we've grown.
+ if (m_value.impl()->hasOneRef())
+ Heap::heap(this)->reportExtraMemoryAllocated(m_value.impl()->cost());
+}
+
+void JSRopeString::clearFibers() const
+{
+ for (size_t i = 0; i < s_maxInternalRopeLength; ++i)
+ u[i].number = 0;
+}
+
+RefPtr<AtomicStringImpl> JSRopeString::resolveRopeToExistingAtomicString(ExecState* exec) const
+{
+ if (m_length > maxLengthForOnStackResolve) {
+ resolveRope(exec);
+ if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(m_value.impl())) {
+ m_value = *existingAtomicString;
+ setIs8Bit(m_value.impl()->is8Bit());
+ clearFibers();
+ return existingAtomicString;
+ }
+ return nullptr;
+ }
+
+ if (is8Bit()) {
+ LChar buffer[maxLengthForOnStackResolve];
+ resolveRopeInternal8(buffer);
+ if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(buffer, m_length)) {
+ m_value = *existingAtomicString;
+ setIs8Bit(m_value.impl()->is8Bit());
+ clearFibers();
+ return existingAtomicString;
+ }
+ } else {
+ UChar buffer[maxLengthForOnStackResolve];
+ resolveRopeInternal16(buffer);
+ if (RefPtr<AtomicStringImpl> existingAtomicString = AtomicStringImpl::lookUp(buffer, m_length)) {
+ m_value = *existingAtomicString;
+ setIs8Bit(m_value.impl()->is8Bit());
+ clearFibers();
+ return existingAtomicString;
+ }
+ }
+
+ return nullptr;