Bug 851064: Allow one-level deep ropes when flattening for substr, r=evilpies

This commit is contained in:
Hannes Verschore 2013-06-03 11:27:07 +02:00
parent dbebffe87c
commit 6a37df8ca1
6 changed files with 139 additions and 23 deletions

View File

@ -367,14 +367,10 @@ ArrayConcatDense(JSContext *cx, HandleObject obj1, HandleObject obj2, HandleObje
bool
CharCodeAt(JSContext *cx, HandleString str, int32_t index, uint32_t *code)
{
JS_ASSERT(index >= 0 &&
static_cast<uint32_t>(index) < str->length());
const jschar *chars = str->getChars(cx);
if (!chars)
jschar c;
if (!str->getChar(cx, index, &c))
return false;
*code = chars[index];
*code = c;
return true;
}

View File

@ -0,0 +1,13 @@
var base = "azertyuiopqsdfghjklmwxcvbn";
function createRopedString() {
var test = "";
for (var i=0; i<2; i++) {
test += base;
}
return test;
}
assertEq(createRopedString().substr(0,10), base.substr(0,10));
assertEq(createRopedString().substr(0,26), base.substr(0,26));
assertEq(createRopedString().substr(26,10), base.substr(0,10));
assertEq(createRopedString().substr(24,10), base.substr(24,2) + base.substr(0,8));

View File

@ -570,6 +570,59 @@ ValueToIntegerRange(JSContext *cx, const Value &v, int32_t *out)
return true;
}
static JSString *
DoSubstr(JSContext *cx, JSString *str, size_t begin, size_t len)
{
/*
* Optimization for one level deep ropes.
* This is common for the following pattern:
*
* while() {
* text = text.substr(0, x) + "bla" + text.substr(x)
* test.charCodeAt(x + 1)
* }
*/
if (str->isRope()) {
JSRope *rope = &str->asRope();
/* Substring is totally in leftChild of rope. */
if (begin + len <= rope->leftChild()->length()) {
str = rope->leftChild();
return js_NewDependentString(cx, str, begin, len);
}
/* Substring is totally in rightChild of rope. */
if (begin >= rope->leftChild()->length()) {
str = rope->rightChild();
begin -= rope->leftChild()->length();
return js_NewDependentString(cx, str, begin, len);
}
/*
* Requested substring is partly in the left and partly in right child.
* Create a rope of substrings for both childs.
*/
JS_ASSERT (begin < rope->leftChild()->length() &&
begin + len > rope->leftChild()->length());
size_t lhsLength = rope->leftChild()->length() - begin;
size_t rhsLength = begin + len - rope->leftChild()->length();
RootedString lhs(cx, js_NewDependentString(cx, rope->leftChild(),
begin, lhsLength));
if (!lhs)
return NULL;
RootedString rhs(cx, js_NewDependentString(cx, rope->rightChild(), 0, rhsLength));
if (!rhs)
return NULL;
return JSRope::new_<CanGC>(cx, lhs, rhs, len);
}
return js_NewDependentString(cx, str, begin, len);
}
static JSBool
str_substring(JSContext *cx, unsigned argc, Value *vp)
{
@ -620,7 +673,7 @@ str_substring(JSContext *cx, unsigned argc, Value *vp)
}
}
str = js_NewDependentString(cx, str, size_t(begin), size_t(end - begin));
str = DoSubstr(cx, str, size_t(begin), size_t(end - begin));
if (!str)
return false;
}
@ -859,12 +912,10 @@ js_str_charCodeAt(JSContext *cx, unsigned argc, Value *vp)
i = size_t(d);
}
const jschar *chars;
chars = str->getChars(cx);
if (!chars)
jschar c;
if (!str->getChar(cx, i, &c))
return false;
args.rval().setInt32(chars[i]);
args.rval().setInt32(c);
return true;
out_of_range:
@ -3109,7 +3160,7 @@ str_substr(JSContext *cx, unsigned argc, Value *vp)
len = length - begin;
}
str = js_NewDependentString(cx, str, size_t(begin), size_t(len));
str = DoSubstr(cx, str, size_t(begin), size_t(len));
if (!str)
return false;
}

View File

@ -404,10 +404,10 @@ inline JSLinearString *
js::StaticStrings::getUnitStringForElement(JSContext *cx, JSString *str, size_t index)
{
JS_ASSERT(index < str->length());
const jschar *chars = str->getChars(cx);
if (!chars)
jschar c;
if (!str->getChar(cx, index, &c))
return NULL;
jschar c = chars[index];
if (c < UNIT_STATIC_LIMIT)
return getUnit(c);
return js_NewDependentString(cx, str, index, 1);

View File

@ -204,15 +204,36 @@ JSRope::flattenInternal(JSContext *maybecx)
jschar *pos;
JSRuntime *rt = runtime();
if (this->leftChild()->isExtensible()) {
JSExtensibleString &left = this->leftChild()->asExtensible();
/* Find the left most string, containing the first string. */
JSRope *leftMostRope = this;
while (leftMostRope->leftChild()->isRope())
leftMostRope = &leftMostRope->leftChild()->asRope();
if (leftMostRope->leftChild()->isExtensible()) {
JSExtensibleString &left = leftMostRope->leftChild()->asExtensible();
size_t capacity = left.capacity();
if (capacity >= wholeLength) {
if (b == WithIncrementalBarrier) {
JSString::writeBarrierPre(d.u1.left);
JSString::writeBarrierPre(d.s.u2.right);
/*
* Simulate a left-most traversal from the root to leftMost->leftChild()
* via first_visit_node
*/
while (str != leftMostRope) {
JS_ASSERT(str->isRope());
if (b == WithIncrementalBarrier) {
JSString::writeBarrierPre(str->d.u1.left);
JSString::writeBarrierPre(str->d.s.u2.right);
}
JSString *child = str->d.u1.left;
str->d.u1.chars = left.chars();
child->d.s.u3.parent = str;
child->d.lengthAndFlags = 0x200;
str = child;
}
str->d.u1.chars = left.chars();
if (b == WithIncrementalBarrier) {
JSString::writeBarrierPre(str->d.u1.left);
JSString::writeBarrierPre(str->d.s.u2.right);
}
wholeCapacity = capacity;
wholeChars = const_cast<jschar *>(left.chars());
size_t bits = left.d.lengthAndFlags;

View File

@ -269,6 +269,7 @@ class JSString : public js::gc::Cell
inline const jschar *getChars(JSContext *cx);
inline const jschar *getCharsZ(JSContext *cx);
inline bool getChar(JSContext *cx, size_t index, jschar *code);
/* Fallible conversions to more-derived string types. */
@ -891,6 +892,40 @@ JSString::getChars(JSContext *cx)
return NULL;
}
JS_ALWAYS_INLINE bool
JSString::getChar(JSContext *cx, size_t index, jschar *code)
{
JS_ASSERT(index < length());
/*
* Optimization for one level deep ropes.
* This is common for the following pattern:
*
* while() {
* text = text.substr(0, x) + "bla" + text.substr(x)
* test.charCodeAt(x + 1)
* }
*/
const jschar *chars;
if (isRope()) {
JSRope *rope = &asRope();
if (uint32_t(index) < rope->leftChild()->length()) {
chars = rope->leftChild()->getChars(cx);
} else {
chars = rope->rightChild()->getChars(cx);
index -= rope->leftChild()->length();
}
} else {
chars = getChars(cx);
}
if (!chars)
return false;
*code = chars[index];
return true;
}
JS_ALWAYS_INLINE const jschar *
JSString::getCharsZ(JSContext *cx)
{