JSArray* createRegExpMatchesArray(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result) { ASSERT(result); VM& vm = exec->vm(); JSArray* array = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), regExp->numSubpatterns() + 1); RELEASE_ASSERT(array); SamplingRegion samplingRegion("Reifying substring properties"); array->initializeIndex(vm, 0, jsSubstring(exec, input, result.start, result.end - result.start), ArrayWithContiguous); if (unsigned numSubpatterns = regExp->numSubpatterns()) { Vector<int, 32> subpatternResults; int position = regExp->match(vm, input->value(exec), result.start, subpatternResults); ASSERT_UNUSED(position, position >= 0 && static_cast<size_t>(position) == result.start); ASSERT(result.start == static_cast<size_t>(subpatternResults[0])); ASSERT(result.end == static_cast<size_t>(subpatternResults[1])); for (unsigned i = 1; i <= numSubpatterns; ++i) { int start = subpatternResults[2 * i]; if (start >= 0) array->initializeIndex(vm, i, jsSubstring(exec, input, start, subpatternResults[2 * i + 1] - start), ArrayWithContiguous); else array->initializeIndex(vm, i, jsUndefined(), ArrayWithContiguous); } } array->putDirect(vm, vm.propertyNames->index, jsNumber(result.start)); array->putDirect(vm, vm.propertyNames->input, input); return array; }
JSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); int len = s.size(); JSValue a0 = args.at(0); JSValue a1 = args.at(1); double start = a0.toNumber(exec); double end = a1.toNumber(exec); if (isnan(start)) start = 0; if (isnan(end)) end = 0; if (start < 0) start = 0; if (end < 0) end = 0; if (start > len) start = len; if (end > len) end = len; if (a1.isUndefined()) end = len; if (start > end) { double temp = end; end = start; start = temp; } return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(end) - static_cast<unsigned>(start)); }
void RegExpMatchesArray::reifyAllProperties(ExecState* exec) { ASSERT(m_state != ReifiedAll); ASSERT(m_result); reifyMatchPropertyIfNecessary(exec); if (unsigned numSubpatterns = m_regExp->numSubpatterns()) { Vector<int, 32> subpatternResults; int position = m_regExp->match(exec->vm(), m_input->value(exec), m_result.start, subpatternResults); ASSERT_UNUSED(position, position >= 0 && static_cast<size_t>(position) == m_result.start); ASSERT(m_result.start == static_cast<size_t>(subpatternResults[0])); ASSERT(m_result.end == static_cast<size_t>(subpatternResults[1])); for (unsigned i = 1; i <= numSubpatterns; ++i) { int start = subpatternResults[2 * i]; if (start >= 0) putDirectIndex(exec, i, jsSubstring(exec, m_input.get(), start, subpatternResults[2 * i + 1] - start)); else putDirectIndex(exec, i, jsUndefined()); } } PutPropertySlot slot; JSArray::put(this, exec, exec->propertyNames().index, jsNumber(m_result.start), slot); JSArray::put(this, exec, exec->propertyNames().input, m_input.get(), slot); m_state = ReifiedAll; }
JSString* RegExpMatchesArray::rightContext(ExecState* exec) { unsigned length = m_input->length(); if (m_result.end == length) return jsEmptyString(exec); return jsSubstring(exec, m_input.get(), m_result.end, length - m_result.end); }
void RegExpMatchesArray::reifyMatchProperty(ExecState* exec) { ASSERT(m_state == ReifiedNone); ASSERT(m_result); putDirectIndex(exec, 0, jsSubstring(exec, m_input.get(), m_result.start, m_result.end - m_result.start)); m_state = ReifiedMatch; }
JSString* RegExpCachedResult::leftContext(ExecState* exec, JSObject* owner) { // Make sure we're reified. lastResult(exec, owner); if (!m_reifiedLeftContext) m_reifiedLeftContext.set(exec->vm(), owner, m_result.start ? jsSubstring(exec, m_reifiedInput.get(), 0, m_result.start) : jsEmptyString(exec)); return m_reifiedLeftContext.get(); }
JSValuePtr RegExpConstructor::getBackref(ExecState* exec, unsigned i) const { if (d->lastOvector && i <= d->lastNumSubPatterns) { int start = d->lastOvector[2 * i]; if (start >= 0) return jsSubstring(exec, d->lastInput, start, d->lastOvector[2 * i + 1] - start); } return jsEmptyString(exec); }
JSString* RegExpCachedResult::rightContext(ExecState* exec, JSObject* owner) { // Make sure we're reified. lastResult(exec, owner); if (!m_reifiedRightContext) { unsigned length = m_reifiedInput->length(); m_reifiedRightContext.set(exec->vm(), owner, m_result.end != length ? jsSubstring(exec, m_reifiedInput.get(), m_result.end, length - m_result.end) : jsEmptyString(exec)); } return m_reifiedRightContext.get(); }
JSValuePtr RegExpConstructor::getLastParen(ExecState* exec) const { unsigned i = d->lastNumSubPatterns; if (i > 0) { ASSERT(d->lastOvector); int start = d->lastOvector[2 * i]; if (start >= 0) return jsSubstring(exec, d->lastInput, start, d->lastOvector[2 * i + 1] - start); } return jsEmptyString(exec); }
JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); JSValue a0 = args.at(0); UString u = s; RefPtr<RegExp> reg; RegExpObject* imp = 0; if (a0.isObject(&RegExpObject::info)) reg = asRegExpObject(a0)->regExp(); else { /* * ECMA 15.5.4.12 String.prototype.search (regexp) * If regexp is not an object whose [[Class]] property is "RegExp", it is * replaced with the result of the expression new RegExp(regexp). */ reg = RegExp::create(&exec->globalData(), a0.toString(exec)); } RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); int pos; int matchLength; regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength); if (!(reg->global())) { // case without 'g' flag is handled like RegExp.prototype.exec if (pos < 0) return jsNull(); return regExpConstructor->arrayOfMatches(exec); } // return array of matches MarkedArgumentBuffer list; int lastIndex = 0; while (pos >= 0) { list.append(jsSubstring(exec, u, pos, matchLength)); lastIndex = pos; pos += matchLength == 0 ? 1 : matchLength; regExpConstructor->performMatch(reg.get(), u, pos, pos, matchLength); } if (imp) imp->setLastIndex(lastIndex); if (list.isEmpty()) { // if there are no matches at all, it's important to return // Null instead of an empty array, because this matches // other browsers and because Null is a false value. return jsNull(); } return constructArray(exec, list); }
// This function construsts a substring out of a rope without flattening by reusing the existing fibers. // This can reduce memory usage substantially. Since traversing ropes is slow the function will revert // back to flattening if the rope turns out to be long. TiString* TiString::substringFromRope(TiExcState* exec, unsigned substringStart, unsigned substringLength) { ASSERT(isRope()); ASSERT(substringLength); TiGlobalData* globalData = &exec->globalData(); UString substringFibers[3]; unsigned fiberCount = 0; unsigned substringFiberCount = 0; unsigned substringEnd = substringStart + substringLength; unsigned fiberEnd = 0; RopeIterator end; for (RopeIterator it(m_fibers.data(), m_fiberCount); it != end; ++it) { ++fiberCount; StringImpl* fiberString = *it; unsigned fiberStart = fiberEnd; fiberEnd = fiberStart + fiberString->length(); if (fiberEnd <= substringStart) continue; unsigned copyStart = std::max(substringStart, fiberStart); unsigned copyEnd = std::min(substringEnd, fiberEnd); if (copyStart == fiberStart && copyEnd == fiberEnd) substringFibers[substringFiberCount++] = UString(fiberString); else substringFibers[substringFiberCount++] = UString(StringImpl::create(fiberString, copyStart - fiberStart, copyEnd - copyStart)); if (fiberEnd >= substringEnd) break; if (fiberCount > substringFromRopeCutoff || substringFiberCount >= 3) { // This turned out to be a really inefficient rope. Just flatten it. resolveRope(exec); return jsSubstring(&exec->globalData(), m_value, substringStart, substringLength); } } ASSERT(substringFiberCount && substringFiberCount <= 3); if (substringLength == 1) { ASSERT(substringFiberCount == 1); UChar c = substringFibers[0].characters()[0]; if (c <= maxSingleCharacterString) return globalData->smallStrings.singleCharacterString(globalData, c); } if (substringFiberCount == 1) return new (globalData) TiString(globalData, substringFibers[0]); if (substringFiberCount == 2) return new (globalData) TiString(globalData, substringFibers[0], substringFibers[1]); return new (globalData) TiString(globalData, substringFibers[0], substringFibers[1], substringFibers[2]); }
void RegExpMatchesArray::fillArrayInstance(ExecState* exec) { unsigned lastNumSubpatterns = m_regExpResult.lastNumSubPatterns; for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { int start = m_regExpResult.ovector[2 * i]; if (start >= 0) JSArray::putByIndex(this, exec, i, jsSubstring(exec, m_regExpResult.input, start, m_regExpResult.ovector[2 * i + 1] - start)); else JSArray::putByIndex(this, exec, i, jsUndefined()); } PutPropertySlot slot; JSArray::put(this, exec, exec->propertyNames().index, jsNumber(m_regExpResult.ovector[0]), slot); JSArray::put(this, exec, exec->propertyNames().input, jsString(exec, m_regExpResult.input), slot); m_didFillArrayInstance = true; }
void RegExpMatchesArray::fillArrayInstance(ExecState* exec) { RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(lazyCreationData()); ASSERT(d); unsigned lastNumSubpatterns = d->lastNumSubPatterns; for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { int start = d->lastOvector[2 * i]; if (start >= 0) JSArray::put(exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector[2 * i + 1] - start)); } PutPropertySlot slot; JSArray::put(exec, exec->propertyNames().index, jsNumber(exec, d->lastOvector[0]), slot); JSArray::put(exec, exec->propertyNames().input, jsString(exec, d->input), slot); delete d; setLazyCreationData(0); }
JSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); int len = s.size(); JSValue a0 = args.at(0); JSValue a1 = args.at(1); double start = a0.toInteger(exec); double length = a1.isUndefined() ? len : a1.toInteger(exec); if (start >= len || length <= 0) return jsEmptyString(exec); if (start < 0) { start += len; if (start < 0) start = 0; } if (start + length > len) length = len - start; return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(length)); }
JSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); int len = s.size(); JSValue a0 = args.at(0); JSValue a1 = args.at(1); // The arg processing is very much like ArrayProtoFunc::Slice double start = a0.toInteger(exec); double end = a1.isUndefined() ? len : a1.toInteger(exec); double from = start < 0 ? len + start : start; double to = end < 0 ? len + end : end; if (to > from && to > 0 && from < len) { if (from < 0) from = 0; if (to > len) to = len; return jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)); } return jsEmptyString(exec); }
JSValuePtr RegExpConstructor::getRightContext(ExecState* exec) const { if (d->lastOvector) return jsSubstring(exec, d->lastInput, d->lastOvector[1], d->lastInput.size() - d->lastOvector[1]); return jsEmptyString(exec); }
JSValue RegExpObject::matchGlobal(ExecState* exec, JSGlobalObject* globalObject, JSString* string) { RegExp* regExp = this->regExp(); ASSERT(regExp->global()); VM* vm = &globalObject->vm(); setLastIndex(exec, 0); if (exec->hadException()) return jsUndefined(); String s = string->value(exec); RegExpConstructor* regExpConstructor = globalObject->regExpConstructor(); MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, s, 0); // return array of matches MarkedArgumentBuffer list; // We defend ourselves from crazy. const size_t maximumReasonableMatchSize = 1000000000; if (regExp->unicode()) { unsigned stringLength = s.length(); while (result) { if (list.size() > maximumReasonableMatchSize) { throwOutOfMemoryError(exec); return jsUndefined(); } size_t end = result.end; size_t length = end - result.start; list.append(jsSubstring(exec, s, result.start, length)); if (!length) end = advanceStringUnicode(s, stringLength, end); result = regExpConstructor->performMatch(*vm, regExp, string, s, end); } } else { while (result) { if (list.size() > maximumReasonableMatchSize) { throwOutOfMemoryError(exec); return jsUndefined(); } size_t end = result.end; size_t length = end - result.start; list.append(jsSubstring(exec, s, result.start, length)); if (!length) ++end; result = regExpConstructor->performMatch(*vm, regExp, string, s, end); } } if (list.isEmpty()) { // if there are no matches at all, it's important to return // Null instead of an empty array, because this matches // other browsers and because Null is a false value. return jsNull(); } return constructArray(exec, static_cast<ArrayAllocationProfile*>(0), list); }
JSValuePtr RegExpConstructor::getLeftContext(ExecState* exec) const { if (d->lastOvector) return jsSubstring(exec, d->lastInput, 0, d->lastOvector[0]); return jsEmptyString(exec); }
JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { JSString* sourceVal = thisValue.toThisJSString(exec); const UString& source = sourceVal->value(); JSValue pattern = args.at(0); JSValue replacement = args.at(1); UString replacementString; CallData callData; CallType callType = replacement.getCallData(callData); if (callType == CallTypeNone) replacementString = replacement.toString(exec); if (pattern.isObject(&RegExpObject::info)) { RegExp* reg = asRegExpObject(pattern)->regExp(); bool global = reg->global(); RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); int lastIndex = 0; int startPosition = 0; Vector<UString::Range, 16> sourceRanges; Vector<UString, 16> replacements; // This is either a loop (if global is set) or a one-way (if not). if (global && callType == CallTypeJS) { // reg->numSubpatterns() + 1 for pattern args, + 2 for match start and sourceValue int argCount = reg->numSubpatterns() + 1 + 2; JSFunction* func = asFunction(replacement); CachedCall cachedCall(exec, func, argCount, exec->exceptionSlot()); if (exec->hadException()) return jsNull(); while (true) { int matchIndex; int matchLen; int* ovector; regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); if (matchIndex < 0) break; sourceRanges.append(UString::Range(lastIndex, matchIndex - lastIndex)); int completeMatchStart = ovector[0]; unsigned i = 0; for (; i < reg->numSubpatterns() + 1; ++i) { int matchStart = ovector[i * 2]; int matchLen = ovector[i * 2 + 1] - matchStart; if (matchStart < 0) cachedCall.setArgument(i, jsUndefined()); else cachedCall.setArgument(i, jsSubstring(exec, source, matchStart, matchLen)); } cachedCall.setArgument(i++, jsNumber(exec, completeMatchStart)); cachedCall.setArgument(i++, sourceVal); cachedCall.setThis(exec->globalThisValue()); replacements.append(cachedCall.call().toString(cachedCall.newCallFrame())); if (exec->hadException()) break; lastIndex = matchIndex + matchLen; startPosition = lastIndex; // special case of empty match if (matchLen == 0) { startPosition++; if (startPosition > source.size()) break; } } } else { do { int matchIndex; int matchLen; int* ovector; regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); if (matchIndex < 0) break; sourceRanges.append(UString::Range(lastIndex, matchIndex - lastIndex)); if (callType != CallTypeNone) { int completeMatchStart = ovector[0]; MarkedArgumentBuffer args; for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) { int matchStart = ovector[i * 2]; int matchLen = ovector[i * 2 + 1] - matchStart; if (matchStart < 0) args.append(jsUndefined()); else args.append(jsSubstring(exec, source, matchStart, matchLen)); } args.append(jsNumber(exec, completeMatchStart)); args.append(sourceVal); replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec)); if (exec->hadException()) break; } else replacements.append(substituteBackreferences(replacementString, source, ovector, reg)); lastIndex = matchIndex + matchLen; startPosition = lastIndex; // special case of empty match if (matchLen == 0) { startPosition++; if (startPosition > source.size()) break; } } while (global); } if (!lastIndex && replacements.isEmpty()) return sourceVal; if (lastIndex < source.size()) sourceRanges.append(UString::Range(lastIndex, source.size() - lastIndex)); return jsString(exec, source.spliceSubstringsWithSeparators(sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); } // Not a regular expression, so treat the pattern as a string. UString patternString = pattern.toString(exec); int matchPos = source.find(patternString); if (matchPos == -1) return sourceVal; int matchLen = patternString.size(); if (callType != CallTypeNone) { MarkedArgumentBuffer args; args.append(jsSubstring(exec, source, matchPos, matchLen)); args.append(jsNumber(exec, matchPos)); args.append(sourceVal); replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec); } int ovector[2] = { matchPos, matchPos + matchLen }; return jsString(exec, source.replaceRange(matchPos, matchLen, substituteBackreferences(replacementString, source, ovector, 0))); }
JSString* RegExpMatchesArray::leftContext(ExecState* exec) { if (!m_result.start) return jsEmptyString(exec); return jsSubstring(exec, m_input.get(), 0, m_result.start); }
JSValue IntlNumberFormat::formatToParts(ExecState& exec, double value) { VM& vm = exec.vm(); auto scope = DECLARE_THROW_SCOPE(vm); // FormatNumberToParts (ECMA-402) // https://tc39.github.io/ecma402/#sec-formatnumbertoparts // https://tc39.github.io/ecma402/#sec-partitionnumberpattern if (!m_initializedNumberFormat) return throwTypeError(&exec, scope, "Intl.NumberFormat.prototype.formatToParts called on value that's not an object initialized as a NumberFormat"_s); UErrorCode status = U_ZERO_ERROR; auto fieldItr = std::unique_ptr<UFieldPositionIterator, UFieldPositionIteratorDeleter>(ufieldpositer_open(&status)); if (U_FAILURE(status)) return throwTypeError(&exec, scope, "failed to open field position iterator"_s); status = U_ZERO_ERROR; Vector<UChar, 32> result(32); auto resultLength = unum_formatDoubleForFields(m_numberFormat.get(), value, result.data(), result.size(), fieldItr.get(), &status); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; result.grow(resultLength); unum_formatDoubleForFields(m_numberFormat.get(), value, result.data(), resultLength, fieldItr.get(), &status); } if (U_FAILURE(status)) return throwTypeError(&exec, scope, "failed to format a number."_s); int32_t literalFieldType = -1; auto literalField = IntlNumberFormatField(literalFieldType, resultLength); Vector<IntlNumberFormatField> fields(resultLength, literalField); int32_t beginIndex = 0; int32_t endIndex = 0; auto fieldType = ufieldpositer_next(fieldItr.get(), &beginIndex, &endIndex); while (fieldType >= 0) { auto size = endIndex - beginIndex; for (auto i = beginIndex; i < endIndex; ++i) { // Only override previous value if new value is more specific. if (fields[i].size >= size) fields[i] = IntlNumberFormatField(fieldType, size); } fieldType = ufieldpositer_next(fieldItr.get(), &beginIndex, &endIndex); } JSGlobalObject* globalObject = exec.jsCallee()->globalObject(vm); JSArray* parts = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0); if (!parts) return throwOutOfMemoryError(&exec, scope); unsigned index = 0; auto resultString = String(result.data(), resultLength); auto typePropertyName = Identifier::fromString(&vm, "type"); auto literalString = jsString(&exec, "literal"_s); int32_t currentIndex = 0; while (currentIndex < resultLength) { auto startIndex = currentIndex; auto fieldType = fields[currentIndex].type; while (currentIndex < resultLength && fields[currentIndex].type == fieldType) ++currentIndex; auto partType = fieldType == literalFieldType ? literalString : jsString(&exec, partTypeString(UNumberFormatFields(fieldType), value)); auto partValue = jsSubstring(&vm, resultString, startIndex, currentIndex - startIndex); JSObject* part = constructEmptyObject(&exec); part->putDirect(vm, typePropertyName, partType); part->putDirect(vm, vm.propertyNames->value, partValue); parts->putDirectIndex(&exec, index++, part); RETURN_IF_EXCEPTION(scope, { }); } return parts; }
JSValue RegExpConstructor::getRightContext(ExecState* exec) const { if (!d.lastOvector().isEmpty()) return jsSubstring(exec, d.lastInput, d.lastOvector()[1], d.lastInput.length() - d.lastOvector()[1]); return jsEmptyString(exec); }
JSValue RegExpConstructor::getLeftContext(ExecState* exec) const { if (!d.lastOvector().isEmpty()) return jsSubstring(exec, d.lastInput, 0, d.lastOvector()[0]); return jsEmptyString(exec); }
JSValue* stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args) { JSString* sourceVal = thisValue->toThisJSString(exec); const UString& source = sourceVal->value(); JSValue* pattern = args.at(exec, 0); JSValue* replacement = args.at(exec, 1); UString replacementString; CallData callData; CallType callType = replacement->getCallData(callData); if (callType == CallTypeNone) replacementString = replacement->toString(exec); if (pattern->isObject(&RegExpObject::info)) { RegExp* reg = static_cast<RegExpObject*>(pattern)->regExp(); bool global = reg->global(); RegExpConstructor* regExpObj = exec->lexicalGlobalObject()->regExpConstructor(); int lastIndex = 0; int startPosition = 0; Vector<UString::Range, 16> sourceRanges; Vector<UString, 16> replacements; // This is either a loop (if global is set) or a one-way (if not). do { int matchIndex; int matchLen; int* ovector; regExpObj->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); if (matchIndex < 0) break; sourceRanges.append(UString::Range(lastIndex, matchIndex - lastIndex)); if (callType != CallTypeNone) { int completeMatchStart = ovector[0]; ArgList args; for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) { int matchStart = ovector[i * 2]; int matchLen = ovector[i * 2 + 1] - matchStart; if (matchStart < 0) args.append(jsUndefined()); else args.append(jsSubstring(exec, source, matchStart, matchLen)); } args.append(jsNumber(exec, completeMatchStart)); args.append(sourceVal); replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args)->toString(exec)); } else replacements.append(substituteBackreferences(replacementString, source, ovector, reg)); lastIndex = matchIndex + matchLen; startPosition = lastIndex; // special case of empty match if (matchLen == 0) { startPosition++; if (startPosition > source.size()) break; } } while (global); if (lastIndex < source.size()) sourceRanges.append(UString::Range(lastIndex, source.size() - lastIndex)); UString result = source.spliceSubstringsWithSeparators(sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()); if (result == source) return sourceVal; return jsString(exec, result); } // First arg is a string UString patternString = pattern->toString(exec); int matchPos = source.find(patternString); int matchLen = patternString.size(); // Do the replacement if (matchPos == -1) return sourceVal; if (callType != CallTypeNone) { ArgList args; args.append(jsSubstring(exec, source, matchPos, matchLen)); args.append(jsNumber(exec, matchPos)); args.append(sourceVal); replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args)->toString(exec); } return jsString(exec, source.substr(0, matchPos) + replacementString + source.substr(matchPos + matchLen)); }
JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); JSValue a0 = args.at(0); JSValue a1 = args.at(1); JSArray* result = constructEmptyArray(exec); unsigned i = 0; int p0 = 0; unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec); if (a0.isObject(&RegExpObject::info)) { RegExp* reg = asRegExpObject(a0)->regExp(); if (s.isEmpty() && reg->match(s, 0) >= 0) { // empty string matched by regexp -> empty array return result; } int pos = 0; while (i != limit && pos < s.size()) { OwnArrayPtr<int> ovector; int mpos = reg->match(s, pos, &ovector); if (mpos < 0) break; int mlen = ovector[1] - ovector[0]; pos = mpos + (mlen == 0 ? 1 : mlen); if (mpos != p0 || mlen) { result->put(exec, i++, jsSubstring(exec, s, p0, mpos - p0)); p0 = mpos + mlen; } 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)); } } } else { UString u2 = a0.toString(exec); if (u2.isEmpty()) { if (s.isEmpty()) { // empty separator matches empty string -> empty array return result; } while (i != limit && p0 < s.size() - 1) result->put(exec, i++, jsSingleCharacterSubstring(exec, s, p0++)); } else { int pos; while (i != limit && (pos = s.find(u2, p0)) >= 0) { result->put(exec, i++, jsSubstring(exec, s, p0, pos - p0)); p0 = pos + u2.size(); } } } // add remaining string if (i != limit) result->put(exec, i++, jsSubstring(exec, s, p0, s.size() - p0)); return result; }