jsSubstring() should be lazy
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 5 May 2014 04:54:19 +0000 (04:54 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 5 May 2014 04:54:19 +0000 (04:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=132556

Reviewed by Andreas Kling.

Source/JavaScriptCore:
jsSubstring() is now lazy by using a special rope that is a substring instead of a
concatenation. To make this patch super simple, we require that a substring's base is
never a rope. Hence, when resolving a rope, we either go down a non-recursive substring
path, or we go down a concatenation path which may see exactly one level of substrings in
its fibers.

This is up to a 50% speed-up on microbenchmarks and a 10% speed-up on Octane/regexp.

* heap/MarkedBlock.cpp:
(JSC::MarkedBlock::specializedSweep):
* runtime/JSString.cpp:
(JSC::JSRopeString::visitFibers):
(JSC::JSRopeString::resolveRope):
(JSC::JSRopeString::resolveRopeSlowCase8):
(JSC::JSRopeString::resolveRopeSlowCase):
(JSC::JSRopeString::outOfMemory):
* runtime/JSString.h:
(JSC::JSRopeString::finishCreation):
(JSC::JSRopeString::append):
(JSC::JSRopeString::create):
(JSC::JSRopeString::offsetOfFibers):
(JSC::JSRopeString::fiber):
(JSC::JSRopeString::substringBase):
(JSC::JSRopeString::substringOffset):
(JSC::JSRopeString::substringSentinel):
(JSC::JSRopeString::isSubstring):
(JSC::jsSubstring):
* runtime/RegExpMatchesArray.cpp:
(JSC::RegExpMatchesArray::reifyAllProperties):
* runtime/StringPrototype.cpp:
(JSC::stringProtoFuncSubstring):

LayoutTests:
These tests get 35-50% faster.

* js/regress/script-tests/substring-concat-weird.js: Added.
(foo):
* js/regress/script-tests/substring-concat.js: Added.
(foo):
* js/regress/script-tests/substring.js: Added.
(foo):
* js/regress/substring-concat-expected.txt: Added.
* js/regress/substring-concat-weird-expected.txt: Added.
* js/regress/substring-concat-weird.html: Added.
* js/regress/substring-concat.html: Added.
* js/regress/substring-expected.txt: Added.
* js/regress/substring.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@168254 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/script-tests/substring-concat-weird.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/substring-concat.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/substring.js [new file with mode: 0644]
LayoutTests/js/regress/substring-concat-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/substring-concat-weird-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/substring-concat-weird.html [new file with mode: 0644]
LayoutTests/js/regress/substring-concat.html [new file with mode: 0644]
LayoutTests/js/regress/substring-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/substring.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/heap/MarkedBlock.cpp
Source/JavaScriptCore/runtime/JSString.cpp
Source/JavaScriptCore/runtime/JSString.h
Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp
Source/JavaScriptCore/runtime/StringPrototype.cpp

index d96d4b7..b83835f 100644 (file)
@@ -1,3 +1,25 @@
+2014-05-04  Filip Pizlo  <fpizlo@apple.com>
+
+        jsSubstring() should be lazy
+        https://bugs.webkit.org/show_bug.cgi?id=132556
+
+        Reviewed by Andreas Kling.
+        
+        These tests get 35-50% faster.
+
+        * js/regress/script-tests/substring-concat-weird.js: Added.
+        (foo):
+        * js/regress/script-tests/substring-concat.js: Added.
+        (foo):
+        * js/regress/script-tests/substring.js: Added.
+        (foo):
+        * js/regress/substring-concat-expected.txt: Added.
+        * js/regress/substring-concat-weird-expected.txt: Added.
+        * js/regress/substring-concat-weird.html: Added.
+        * js/regress/substring-concat.html: Added.
+        * js/regress/substring-expected.txt: Added.
+        * js/regress/substring.html: Added.
+
 2014-05-04  Andreas Kling  <akling@apple.com>
 
         Mark compositing/visibility/visibility-image-layers-dynamic.html as failing after r168244.
diff --git a/LayoutTests/js/regress/script-tests/substring-concat-weird.js b/LayoutTests/js/regress/script-tests/substring-concat-weird.js
new file mode 100644 (file)
index 0000000..bed985a
--- /dev/null
@@ -0,0 +1,18 @@
+function foo() {
+    return a + b;
+}
+
+var array = Array(10007);
+
+var string = foo.toString();
+
+for (var i = 0; i < 500000; ++i) {
+    array[i % array.length] = "foo " + string.substring(i % string.length, (i / string.length) % string.length) + " bar";
+    array[i % array.length] = "this " + array[i % array.length] + " that";
+}
+
+for (var i = 0; i < array.length; ++i) {
+    var thing = array[i].substring(9, array[i].length - 9);
+    if (string.indexOf(thing) < 0)
+        throw "Error: bad substring: " + thing;
+}
diff --git a/LayoutTests/js/regress/script-tests/substring-concat.js b/LayoutTests/js/regress/script-tests/substring-concat.js
new file mode 100644 (file)
index 0000000..d18e63f
--- /dev/null
@@ -0,0 +1,17 @@
+function foo() {
+    return a + b;
+}
+
+var array = Array(10007);
+
+var string = foo.toString();
+
+for (var i = 0; i < 700000; ++i) {
+    array[i % array.length] = "foo " + string.substring(i % string.length, (i / string.length) % string.length) + " bar";
+}
+
+for (var i = 0; i < array.length; ++i) {
+    var thing = array[i].substring(4, array[i].length - 4);
+    if (string.indexOf(thing) < 0)
+        throw "Error: bad substring: \"" + thing + "\"";
+}
diff --git a/LayoutTests/js/regress/script-tests/substring.js b/LayoutTests/js/regress/script-tests/substring.js
new file mode 100644 (file)
index 0000000..8e21b7e
--- /dev/null
@@ -0,0 +1,16 @@
+function foo() {
+    return a + b;
+}
+
+var array = Array(10007);
+
+var string = foo.toString();
+
+for (var i = 0; i < 1000000; ++i) {
+    array[i % array.length] = string.substring(i % string.length, (i / string.length) % string.length);
+}
+
+for (var i = 0; i < array.length; ++i) {
+    if (string.indexOf(array[i]) < 0)
+        throw "Error: bad substring: " + array[i];
+}
diff --git a/LayoutTests/js/regress/substring-concat-expected.txt b/LayoutTests/js/regress/substring-concat-expected.txt
new file mode 100644 (file)
index 0000000..cd5838d
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/substring-concat
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/substring-concat-weird-expected.txt b/LayoutTests/js/regress/substring-concat-weird-expected.txt
new file mode 100644 (file)
index 0000000..35486d7
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/substring-concat-weird
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/substring-concat-weird.html b/LayoutTests/js/regress/substring-concat-weird.html
new file mode 100644 (file)
index 0000000..139ff3e
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/substring-concat-weird.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/substring-concat.html b/LayoutTests/js/regress/substring-concat.html
new file mode 100644 (file)
index 0000000..3c0638d
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/substring-concat.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/substring-expected.txt b/LayoutTests/js/regress/substring-expected.txt
new file mode 100644 (file)
index 0000000..a873268
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/substring
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/substring.html b/LayoutTests/js/regress/substring.html
new file mode 100644 (file)
index 0000000..c6812ac
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/substring.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 04a6c35..15a4d6c 100644 (file)
@@ -1,3 +1,42 @@
+2014-05-04  Filip Pizlo  <fpizlo@apple.com>
+
+        jsSubstring() should be lazy
+        https://bugs.webkit.org/show_bug.cgi?id=132556
+
+        Reviewed by Andreas Kling.
+        
+        jsSubstring() is now lazy by using a special rope that is a substring instead of a
+        concatenation. To make this patch super simple, we require that a substring's base is
+        never a rope. Hence, when resolving a rope, we either go down a non-recursive substring
+        path, or we go down a concatenation path which may see exactly one level of substrings in
+        its fibers.
+        
+        This is up to a 50% speed-up on microbenchmarks and a 10% speed-up on Octane/regexp.
+
+        * heap/MarkedBlock.cpp:
+        (JSC::MarkedBlock::specializedSweep):
+        * runtime/JSString.cpp:
+        (JSC::JSRopeString::visitFibers):
+        (JSC::JSRopeString::resolveRope):
+        (JSC::JSRopeString::resolveRopeSlowCase8):
+        (JSC::JSRopeString::resolveRopeSlowCase):
+        (JSC::JSRopeString::outOfMemory):
+        * runtime/JSString.h:
+        (JSC::JSRopeString::finishCreation):
+        (JSC::JSRopeString::append):
+        (JSC::JSRopeString::create):
+        (JSC::JSRopeString::offsetOfFibers):
+        (JSC::JSRopeString::fiber):
+        (JSC::JSRopeString::substringBase):
+        (JSC::JSRopeString::substringOffset):
+        (JSC::JSRopeString::substringSentinel):
+        (JSC::JSRopeString::isSubstring):
+        (JSC::jsSubstring):
+        * runtime/RegExpMatchesArray.cpp:
+        (JSC::RegExpMatchesArray::reifyAllProperties):
+        * runtime/StringPrototype.cpp:
+        (JSC::stringProtoFuncSubstring):
+
 2014-05-02  Michael Saboff  <msaboff@apple.com>
 
         "arm64 function not 4-byte aligned" warnings when building JSC
index 104f926..8933fff 100644 (file)
@@ -70,6 +70,8 @@ MarkedBlock::FreeList MarkedBlock::specializedSweep()
     ASSERT(blockState != Allocated && blockState != FreeListed);
     ASSERT(!(dtorType == MarkedBlock::None && sweepMode == SweepOnly));
 
+    SamplingRegion samplingRegion((dtorType != MarkedBlock::None && blockState != New) ? "Calling destructors" : "sweeping");
+    
     // This produces a free list that is ordered in reverse through the block.
     // This is fine, since the allocation code makes no assumptions about the
     // order of the free list.
index 00f05f5..5a5e80c 100644 (file)
@@ -82,14 +82,25 @@ void JSString::visitChildren(JSCell* cell, SlotVisitor& visitor)
 
 void JSRopeString::visitFibers(SlotVisitor& visitor)
 {
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        visitor.append(&m_fibers[i]);
+    if (isSubstring()) {
+        visitor.append(&substringBase());
+        return;
+    }
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+        visitor.append(&fiber(i));
 }
 
 void JSRopeString::resolveRope(ExecState* exec) const
 {
     ASSERT(isRope());
-
+    
+    if (isSubstring()) {
+        ASSERT(!substringBase()->isRope());
+        m_value = substringBase()->m_value.substring(substringOffset(), m_length);
+        substringBase().clear();
+        return;
+    }
+    
     if (is8Bit()) {
         LChar* buffer;
         if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) {
@@ -99,19 +110,19 @@ void JSRopeString::resolveRope(ExecState* exec) const
             outOfMemory(exec);
             return;
         }
-
-        for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
-            if (m_fibers[i]->isRope())
+        
+        for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+            if (fiber(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();
+        for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+            StringImpl* string = fiber(i)->m_value.impl();
             unsigned length = string->length();
             StringImpl::copyChars(position, string->characters8(), length);
             position += length;
-            m_fibers[i].clear();
+            fiber(i).clear();
         }
         ASSERT((buffer + m_length) == position);
         ASSERT(!isRope());
@@ -128,21 +139,21 @@ void JSRopeString::resolveRope(ExecState* exec) const
         return;
     }
 
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
-        if (m_fibers[i]->isRope())
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        if (fiber(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();
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        StringImpl* string = fiber(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();
+        fiber(i).clear();
     }
     ASSERT((buffer + m_length) == position);
     ASSERT(!isRope());
@@ -163,27 +174,35 @@ 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());
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        workQueue.append(fiber(i).get());
         // Clearing here works only because there are no GC points in this method.
-        m_fibers[i].clear();
+        fiber(i).clear();
     }
 
     while (!workQueue.isEmpty()) {
         JSString* currentFiber = workQueue.last();
         workQueue.removeLast();
-
+        
+        const LChar* characters;
+        
         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();
+            if (!currentFiberAsRope->isSubstring()) {
+                for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i)
+                    workQueue.append(currentFiberAsRope->fiber(i).get());
+                continue;
+            }
+            ASSERT(!currentFiberAsRope->substringBase()->isRope());
+            characters =
+                currentFiberAsRope->substringBase()->m_value.characters8() +
+                currentFiberAsRope->substringOffset();
+        } else
+            characters = currentFiber->m_value.characters8();
+
+        unsigned length = currentFiber->length();
         position -= length;
-        StringImpl::copyChars(position, string->characters8(), length);
+        StringImpl::copyChars(position, characters, length);
     }
 
     ASSERT(buffer == position);
@@ -195,8 +214,8 @@ void JSRopeString::resolveRopeSlowCase(UChar* buffer) const
     UChar* position = buffer + m_length; // We will be working backwards over the rope.
     Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK.
 
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        workQueue.append(m_fibers[i].get());
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+        workQueue.append(fiber(i).get());
 
     while (!workQueue.isEmpty()) {
         JSString* currentFiber = workQueue.last();
@@ -204,8 +223,21 @@ void JSRopeString::resolveRopeSlowCase(UChar* buffer) const
 
         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());
+            if (currentFiberAsRope->isSubstring()) {
+                ASSERT(!currentFiberAsRope->substringBase()->isRope());
+                StringImpl* string = static_cast<StringImpl*>(
+                    currentFiberAsRope->substringBase()->m_value.impl());
+                unsigned offset = currentFiberAsRope->substringOffset();
+                unsigned length = currentFiberAsRope->length();
+                position -= length;
+                if (string->is8Bit())
+                    StringImpl::copyChars(position, string->characters8() + offset, length);
+                else
+                    StringImpl::copyChars(position, string->characters16() + offset, length);
+                continue;
+            }
+            for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i)
+                workQueue.append(currentFiberAsRope->fiber(i).get());
             continue;
         }
 
@@ -224,8 +256,8 @@ void JSRopeString::resolveRopeSlowCase(UChar* buffer) const
 
 void JSRopeString::outOfMemory(ExecState* exec) const
 {
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        m_fibers[i].clear();
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+        u[i].string.clear();
     ASSERT(isRope());
     ASSERT(m_value.isNull());
     if (exec)
index 4226f74..561391b 100644 (file)
@@ -270,8 +270,9 @@ namespace JSC {
             Base::finishCreation(vm);
             m_length = s1->length() + s2->length();
             setIs8Bit(s1->is8Bit() && s2->is8Bit());
-            m_fibers[0].set(vm, this, s1);
-            m_fibers[1].set(vm, this, s2);
+            fiber(0).set(vm, this, s1);
+            fiber(1).set(vm, this, s2);
+            fiber(2).clear();
         }
             
         void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
@@ -279,19 +280,35 @@ namespace JSC {
             Base::finishCreation(vm);
             m_length = s1->length() + s2->length() + s3->length();
             setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
-            m_fibers[0].set(vm, this, s1);
-            m_fibers[1].set(vm, this, s2);
-            m_fibers[2].set(vm, this, s3);
+            fiber(0).set(vm, this, s1);
+            fiber(1).set(vm, this, s2);
+            fiber(2).set(vm, this, s3);
+        }
+        
+        void finishCreation(VM& vm, JSString* base, unsigned offset, unsigned length)
+        {
+            Base::finishCreation(vm);
+            ASSERT(!base->isRope());
+            ASSERT(!sumOverflows<int32_t>(offset, length));
+            ASSERT(offset + length <= base->length());
+            m_length = length;
+            setIs8Bit(base->is8Bit());
+            u[0].string.setWithoutWriteBarrier(substringSentinel());
+            substringBase().set(vm, this, base);
+            substringOffset() = offset;
         }
 
         void finishCreation(VM& vm)
         {
             JSString::finishCreation(vm);
+            fiber(0).clear();
+            fiber(1).clear();
+            fiber(2).clear();
         }
 
         void append(VM& vm, size_t index, JSString* jsString)
         {
-            m_fibers[index].set(vm, this, jsString);
+            fiber(index).set(vm, this, jsString);
             m_length += jsString->m_length;
             RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0);
             setIs8Bit(is8Bit() && jsString->is8Bit());
@@ -317,10 +334,17 @@ namespace JSC {
             newString->finishCreation(vm, s1, s2, s3);
             return newString;
         }
+        
+        static JSString* create(VM& vm, JSString* base, unsigned offset, unsigned length)
+        {
+            JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
+            newString->finishCreation(vm, base, offset, length);
+            return newString;
+        }
 
         void visitFibers(SlotVisitor&);
             
-        static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, m_fibers); }
+        static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, u); }
 
         static const unsigned s_maxInternalRopeLength = 3;
             
@@ -334,8 +358,38 @@ namespace JSC {
         void outOfMemory(ExecState*) const;
             
         JS_EXPORT_PRIVATE JSString* getIndexSlowCase(ExecState*, unsigned);
+        
+        WriteBarrierBase<JSString>& fiber(unsigned i) const
+        {
+            ASSERT(!isSubstring());
+            ASSERT(i < s_maxInternalRopeLength);
+            return u[i].string;
+        }
+        
+        WriteBarrierBase<JSString>& substringBase() const
+        {
+            return u[1].string;
+        }
+        
+        unsigned& substringOffset() const
+        {
+            return u[2].number;
+        }
+        
+        static JSString* substringSentinel()
+        {
+            return bitwise_cast<JSString*>(static_cast<uintptr_t>(1));
+        }
+        
+        bool isSubstring() const
+        {
+            return u[0].string.get() == substringSentinel();
+        }
 
-        mutable std::array<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
+        mutable union {
+            unsigned number;
+            WriteBarrierBase<JSString> string;
+        } u[s_maxInternalRopeLength];
     };
 
 
@@ -421,10 +475,11 @@ namespace JSC {
         ASSERT(offset <= static_cast<unsigned>(s->length()));
         ASSERT(length <= static_cast<unsigned>(s->length()));
         ASSERT(offset + length <= static_cast<unsigned>(s->length()));
-        VM* vm = &exec->vm();
+        VM& vm = exec->vm();
         if (!length)
-            return vm->smallStrings.emptyString();
-        return jsSubstring(vm, s->value(exec), offset, length);
+            return vm.smallStrings.emptyString();
+        s->value(exec); // For effect. We need to ensure that any string that is used as a substring base is not a rope.
+        return JSRopeString::create(vm, s, offset, length);
     }
 
     inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length)
index f0cc10b..de34e5c 100644 (file)
@@ -74,6 +74,8 @@ void RegExpMatchesArray::reifyAllProperties(ExecState* exec)
     ASSERT(m_state != ReifiedAll);
     ASSERT(m_result);
  
+    SamplingRegion samplingRegion("Reifying substring properties");
+    
     reifyMatchPropertyIfNecessary(exec);
 
     if (unsigned numSubpatterns = m_regExp->numSubpatterns()) {
index c5d7d2b..fd19447 100644 (file)
@@ -1218,6 +1218,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
 {
+    SamplingRegion samplingRegion("Doing substringing");
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec);