// ------------------------------ Functions --------------------------
+// Helper for producing a JSString for 'string', where 'string' was been produced by
+// calling ToString on 'originalValue'. In cases where 'originalValue' already was a
+// string primitive we can just use this, otherwise we need to allocate a new JSString.
+static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const UString& string)
+{
+ if (originalValue.isString()) {
+ ASSERT(asString(originalValue)->value(exec) == string);
+ return asString(originalValue);
+ }
+ return jsString(exec, string);
+}
+
static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, size_t i)
{
Vector<UChar> substitutedReplacement;
return JSValue::encode(jsEmptyString(exec));
}
+// ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit)
EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec)
{
+ // 1. Call CheckObjectCoercible passing the this value as its argument.
JSValue thisValue = exec->hostThisValue();
- if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
+ if (thisValue.isUndefinedOrNull())
return throwVMTypeError(exec);
- UString s = thisValue.toString(exec);
- JSGlobalData* globalData = &exec->globalData();
- JSValue a0 = exec->argument(0);
- JSValue a1 = exec->argument(1);
+ // 2. Let S be the result of calling ToString, giving it the this value as its argument.
+ // 6. Let s be the number of characters in S.
+ UString input = thisValue.toString(exec);
+ // 3. Let A be a new array created as if by the expression new Array()
+ // where Array is the standard built-in constructor with that name.
JSArray* result = constructEmptyArray(exec);
- unsigned i = 0;
- unsigned p0 = 0;
- unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec);
- if (a0.inherits(&RegExpObject::s_info)) {
- RegExp* reg = asRegExpObject(a0)->regExp();
- if (s.isEmpty() && reg->match(*globalData, s, 0) >= 0) {
- // empty string matched by regexp -> empty array
+
+ // 4. Let lengthA be 0.
+ unsigned resultLength = 0;
+
+ // 5. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
+ JSValue limitValue = exec->argument(1);
+ unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
+
+ // 7. Let p = 0.
+ size_t position = 0;
+
+ // 8. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
+ // otherwise let R = ToString(separator).
+ JSValue separatorValue = exec->argument(0);
+ if (separatorValue.inherits(&RegExpObject::s_info)) {
+ JSGlobalData* globalData = &exec->globalData();
+ RegExp* reg = asRegExpObject(separatorValue)->regExp();
+
+ // 9. If lim == 0, return A.
+ if (!limit)
+ return JSValue::encode(result);
+
+ // 10. If separator is undefined, then
+ if (separatorValue.isUndefined()) {
+ // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
+ // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+ result->put(exec, 0, jsStringWithReuse(exec, thisValue, input));
+ // b. Return A.
return JSValue::encode(result);
}
- unsigned pos = 0;
- while (i != limit && pos < s.length()) {
+
+ // 11. If s == 0, then
+ if (input.isEmpty()) {
+ // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
+ // b. If z is not failure, return A.
+ // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
+ // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+ // d. Return A.
+ if (reg->match(*globalData, input, 0) < 0)
+ result->put(exec, 0, jsStringWithReuse(exec, thisValue, input));
+ return JSValue::encode(result);
+ }
+
+ // 12. Let q = p.
+ size_t matchPosition = 0;
+ // 13. Repeat, while q != s
+ while (matchPosition < input.length()) {
+ // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
Vector<int, 32> ovector;
- int mpos = reg->match(*globalData, s, pos, &ovector);
+ int mpos = reg->match(*globalData, input, matchPosition, &ovector);
+ // b. If z is failure, then let q = q + 1.
if (mpos < 0)
break;
- int mlen = ovector[1] - ovector[0];
- pos = mpos + (mlen == 0 ? 1 : mlen);
- if (static_cast<unsigned>(mpos) != p0 || mlen) {
- result->put(exec, i++, jsSubstring(exec, s, p0, mpos - p0));
- p0 = mpos + mlen;
+ matchPosition = mpos;
+
+ // c. Else, z is not failure
+ // i. z must be a State. Let e be z's endIndex and let cap be z's captures array.
+ size_t matchEnd = ovector[1];
+
+ // ii. If e == p, then let q = q + 1.
+ if (matchEnd == position) {
+ ++matchPosition;
+ continue;
}
- for (unsigned si = 1; si <= reg->numSubpatterns(); ++si) {
- int spos = ovector[si * 2];
- if (spos < 0)
- result->put(exec, i++, jsUndefined());
- else
- result->put(exec, i++, jsSubstring(exec, s, spos, ovector[si * 2 + 1] - spos));
+ // iii. Else, e != p
+
+ // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
+ // through q (exclusive).
+ // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
+ // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+ result->put(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
+ // 3. Increment lengthA by 1.
+ // 4. If lengthA == lim, return A.
+ if (++resultLength == limit)
+ return JSValue::encode(result);
+
+ // 5. Let p = e.
+ // 8. Let q = p.
+ position = matchEnd;
+ matchPosition = matchEnd;
+
+ // 6. Let i = 0.
+ // 7. Repeat, while i is not equal to the number of elements in cap.
+ // a Let i = i + 1.
+ for (unsigned i = 1; i <= reg->numSubpatterns(); ++i) {
+ // b Call the [[DefineOwnProperty]] internal method of A with arguments
+ // ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]:
+ // true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+ int sub = ovector[i * 2];
+ result->put(exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, input, sub, ovector[i * 2 + 1] - sub));
+ // c Increment lengthA by 1.
+ // d If lengthA == lim, return A.
+ if (++resultLength == limit)
+ return JSValue::encode(result);
}
}
} else {
- UString u2 = a0.toString(exec);
- if (u2.isEmpty()) {
- if (s.isEmpty()) {
- // empty separator matches empty string -> empty array
+ UString separator = separatorValue.toString(exec);
+
+ // 9. If lim == 0, return A.
+ if (!limit)
+ return JSValue::encode(result);
+
+ // 10. If separator is undefined, then
+ JSValue separatorValue = exec->argument(0);
+ if (separatorValue.isUndefined()) {
+ // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
+ // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+ result->put(exec, 0, jsStringWithReuse(exec, thisValue, input));
+ // b. Return A.
+ return JSValue::encode(result);
+ }
+
+ // 11. If s == 0, then
+ if (input.isEmpty()) {
+ // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
+ // b. If z is not failure, return A.
+ // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
+ // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+ // d. Return A.
+ if (!separator.isEmpty())
+ result->put(exec, 0, jsStringWithReuse(exec, thisValue, input));
+ return JSValue::encode(result);
+ }
+
+ // Optimized case for splitting on the empty string.
+ if (separator.isEmpty()) {
+ limit = std::min(limit, input.length());
+ // Zero limt/input length handled in steps 9/11 respectively, above.
+ ASSERT(limit);
+
+ do {
+ result->put(exec, position, jsSingleCharacterSubstring(exec, input, position));
+ } while (++position < limit);
+
+ return JSValue::encode(result);
+ }
+
+ // 12. Let q = p.
+ size_t matchPosition;
+ // 13. Repeat, while q != s
+ // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
+ // b. If z is failure, then let q = q+1.
+ // c. Else, z is not failure
+ while ((matchPosition = input.find(separator, position)) != notFound) {
+ // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
+ // through q (exclusive).
+ // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
+ // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+ result->put(exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
+ // 3. Increment lengthA by 1.
+ // 4. If lengthA == lim, return A.
+ if (++resultLength == limit)
return JSValue::encode(result);
- }
- while (i != limit && p0 < s.length() - 1)
- result->put(exec, i++, jsSingleCharacterSubstring(exec, s, p0++));
- } else {
- size_t pos;
- while (i != limit && (pos = s.find(u2, p0)) != notFound) {
- result->put(exec, i++, jsSubstring(exec, s, p0, pos - p0));
- p0 = pos + u2.length();
- }
+
+ // 5. Let p = e.
+ // 8. Let q = p.
+ position = matchPosition + separator.length();
}
}
- // add remaining string
- if (i != limit)
- result->put(exec, i++, jsSubstring(exec, s, p0, s.length() - p0));
+ // 14. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
+ // through s (exclusive).
+ // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor
+ // {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+ result->put(exec, resultLength++, jsSubstring(exec, input, position, input.length() - position));
+ // 16. Return A.
return JSValue::encode(result);
}