Diff
Modified: branches/ftlopt/LayoutTests/ChangeLog (168634 => 168635)
--- branches/ftlopt/LayoutTests/ChangeLog 2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/LayoutTests/ChangeLog 2014-05-12 19:23:47 UTC (rev 168635)
@@ -1,3 +1,25 @@
+2014-05-08 Filip Pizlo <fpi...@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-08 Commit Queue <commit-qu...@webkit.org>
Unreviewed, rolling out r168498.
Added: branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat-weird.js (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat-weird.js (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat-weird.js 2014-05-12 19:23:47 UTC (rev 168635)
@@ -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;
+}
Added: branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat.js (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat.js (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat.js 2014-05-12 19:23:47 UTC (rev 168635)
@@ -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 + "\"";
+}
Added: branches/ftlopt/LayoutTests/js/regress/script-tests/substring.js (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/script-tests/substring.js (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/script-tests/substring.js 2014-05-12 19:23:47 UTC (rev 168635)
@@ -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];
+}
Added: branches/ftlopt/LayoutTests/js/regress/substring-concat-expected.txt (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/substring-concat-expected.txt (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-concat-expected.txt 2014-05-12 19:23:47 UTC (rev 168635)
@@ -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
+
Added: branches/ftlopt/LayoutTests/js/regress/substring-concat-weird-expected.txt (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/substring-concat-weird-expected.txt (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-concat-weird-expected.txt 2014-05-12 19:23:47 UTC (rev 168635)
@@ -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
+
Added: branches/ftlopt/LayoutTests/js/regress/substring-concat-weird.html (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/substring-concat-weird.html (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-concat-weird.html 2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Added: branches/ftlopt/LayoutTests/js/regress/substring-concat.html (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/substring-concat.html (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-concat.html 2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Added: branches/ftlopt/LayoutTests/js/regress/substring-expected.txt (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/substring-expected.txt (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-expected.txt 2014-05-12 19:23:47 UTC (rev 168635)
@@ -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
+
Added: branches/ftlopt/LayoutTests/js/regress/substring.html (0 => 168635)
--- branches/ftlopt/LayoutTests/js/regress/substring.html (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring.html 2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>
Modified: branches/ftlopt/Source/_javascript_Core/ChangeLog (168634 => 168635)
--- branches/ftlopt/Source/_javascript_Core/ChangeLog 2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/ChangeLog 2014-05-12 19:23:47 UTC (rev 168635)
@@ -1,3 +1,48 @@
+2014-05-08 Filip Pizlo <fpi...@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.
+
+ Relanding this with assertion fixes.
+
+ * heap/MarkedBlock.cpp:
+ (JSC::MarkedBlock::specializedSweep):
+ * runtime/JSString.cpp:
+ (JSC::JSRopeString::visitFibers):
+ (JSC::JSRopeString::resolveRopeInternal8):
+ (JSC::JSRopeString::resolveRopeInternal16):
+ (JSC::JSRopeString::clearFibers):
+ (JSC::JSRopeString::resolveRope):
+ (JSC::JSRopeString::resolveRopeSlowCase8):
+ (JSC::JSRopeString::resolveRopeSlowCase):
+ * 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::notSubstringSentinel):
+ (JSC::JSRopeString::substringSentinel):
+ (JSC::JSRopeString::isSubstring):
+ (JSC::JSRopeString::setIsSubstring):
+ (JSC::jsSubstring):
+ * runtime/RegExpMatchesArray.cpp:
+ (JSC::RegExpMatchesArray::reifyAllProperties):
+ * runtime/StringPrototype.cpp:
+ (JSC::stringProtoFuncSubstring):
+
2014-05-08 Mark Hahnenberg <mhahnenb...@apple.com>
Base case for get-by-id inline cache doesn't check for HasImpureGetOwnPropertySlot
Modified: branches/ftlopt/Source/_javascript_Core/heap/MarkedBlock.cpp (168634 => 168635)
--- branches/ftlopt/Source/_javascript_Core/heap/MarkedBlock.cpp 2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/heap/MarkedBlock.cpp 2014-05-12 19:23:47 UTC (rev 168635)
@@ -70,6 +70,8 @@
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.
Modified: branches/ftlopt/Source/_javascript_Core/runtime/JSString.cpp (168634 => 168635)
--- branches/ftlopt/Source/_javascript_Core/runtime/JSString.cpp 2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/runtime/JSString.cpp 2014-05-12 19:23:47 UTC (rev 168635)
@@ -82,24 +82,39 @@
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));
}
static const unsigned maxLengthForOnStackResolve = 2048;
void JSRopeString::resolveRopeInternal8(LChar* buffer) const
{
- for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
- if (m_fibers[i]->isRope()) {
+ 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 && m_fibers[i]; ++i) {
- const StringImpl& fiberString = *m_fibers[i]->m_value.impl();
+ 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;
@@ -109,16 +124,27 @@
void JSRopeString::resolveRopeInternal16(UChar* buffer) const
{
- for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
- if (m_fibers[i]->isRope()) {
+ 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 && m_fibers[i]; ++i) {
- const StringImpl& fiberString = *m_fibers[i]->m_value.impl();
+ 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);
@@ -156,8 +182,8 @@
void JSRopeString::clearFibers() 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; ++i)
+ u[i].number = 0;
}
AtomicStringImpl* JSRopeString::resolveRopeToExistingAtomicString(ExecState* exec) const
@@ -171,7 +197,7 @@
}
return nullptr;
}
-
+
if (is8Bit()) {
LChar buffer[maxLengthForOnStackResolve];
resolveRopeInternal8(buffer);
@@ -196,7 +222,14 @@
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)) {
@@ -206,7 +239,7 @@
outOfMemory(exec);
return;
}
- resolveRopeInternal8(buffer);
+ resolveRopeInternal8NoSubstring(buffer);
clearFibers();
ASSERT(!isRope());
return;
@@ -221,7 +254,7 @@
return;
}
- resolveRopeInternal16(buffer);
+ resolveRopeInternal16NoSubstring(buffer);
clearFibers();
ASSERT(!isRope());
}
@@ -241,24 +274,32 @@
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());
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);
@@ -269,8 +310,8 @@
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();
@@ -278,8 +319,21 @@
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;
}
Modified: branches/ftlopt/Source/_javascript_Core/runtime/JSString.h (168634 => 168635)
--- branches/ftlopt/Source/_javascript_Core/runtime/JSString.h 2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/runtime/JSString.h 2014-05-12 19:23:47 UTC (rev 168635)
@@ -273,8 +273,10 @@
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);
+ setIsSubstring(false);
+ 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)
@@ -282,19 +284,37 @@
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);
+ setIsSubstring(false);
+ 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());
+ setIsSubstring(true);
+ substringBase().set(vm, this, base);
+ substringOffset() = offset;
+ }
void finishCreation(VM& vm)
{
JSString::finishCreation(vm);
+ setIsSubstring(false);
+ 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());
@@ -320,10 +340,17 @@
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;
@@ -338,12 +365,54 @@
void resolveRopeSlowCase(UChar*) const;
void outOfMemory(ExecState*) const;
void resolveRopeInternal8(LChar*) const;
+ void resolveRopeInternal8NoSubstring(LChar*) const;
void resolveRopeInternal16(UChar*) const;
+ void resolveRopeInternal16NoSubstring(UChar*) const;
void clearFibers() 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;
+ }
+
+ uintptr_t& substringOffset() const
+ {
+ return u[2].number;
+ }
+
+ static uintptr_t notSubstringSentinel()
+ {
+ return 0;
+ }
+
+ static uintptr_t substringSentinel()
+ {
+ return 1;
+ }
+
+ bool isSubstring() const
+ {
+ return u[0].number == substringSentinel();
+ }
+
+ void setIsSubstring(bool isSubstring)
+ {
+ u[0].number = isSubstring ? substringSentinel() : notSubstringSentinel();
+ }
- mutable std::array<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
+ mutable union {
+ uintptr_t number;
+ WriteBarrierBase<JSString> string;
+ } u[s_maxInternalRopeLength];
};
@@ -454,10 +523,11 @@
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)
Modified: branches/ftlopt/Source/_javascript_Core/runtime/RegExpMatchesArray.cpp (168634 => 168635)
--- branches/ftlopt/Source/_javascript_Core/runtime/RegExpMatchesArray.cpp 2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/runtime/RegExpMatchesArray.cpp 2014-05-12 19:23:47 UTC (rev 168635)
@@ -74,6 +74,8 @@
ASSERT(m_state != ReifiedAll);
ASSERT(m_result);
+ SamplingRegion samplingRegion("Reifying substring properties");
+
reifyMatchPropertyIfNecessary(exec);
if (unsigned numSubpatterns = m_regExp->numSubpatterns()) {
Modified: branches/ftlopt/Source/_javascript_Core/runtime/StringPrototype.cpp (168634 => 168635)
--- branches/ftlopt/Source/_javascript_Core/runtime/StringPrototype.cpp 2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/runtime/StringPrototype.cpp 2014-05-12 19:23:47 UTC (rev 168635)
@@ -1218,6 +1218,7 @@
EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
{
+ SamplingRegion samplingRegion("Doing substringing");
JSValue thisValue = exec->thisValue();
if (!checkObjectCoercible(thisValue))
return throwVMTypeError(exec);