Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (185898 => 185899)
--- trunk/Source/_javascript_Core/ChangeLog 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,3 +1,91 @@
+2015-06-22 Darin Adler <da...@apple.com>
+
+ Make Array.join work directly on substrings without reifying them
+ https://bugs.webkit.org/show_bug.cgi?id=146191
+
+ Reviewed by Andreas Kling.
+
+ Besides the Array.join change, this has other optimizations based on
+ profiling the Peacekeeper array benchmark.
+
+ I measured a 14% speed improvement in the Peacekeeper array benchmark.
+
+ Still a lot of low hanging fruit in that test because so many of functions
+ on the array prototype are not optimizing for simple cases. For example,
+ the reverse function does individual get and put calls even when the array
+ is entirely made up of integers in contiguous storage.
+
+ * runtime/ArrayPrototype.cpp:
+ (JSC::getProperty): Use tryGetIndexQuickly first before getPropertySlot.
+ (JSC::argumentClampedIndexFromStartOrEnd): Marked inline.
+ (JSC::shift): Use the getProperty helper in this file instead of using
+ getPropertySlot. Use putByIndexInline instead of calling putByIndex directly.
+ In both cases this can yield a faster code path.
+ (JSC::unshift): Ditto.
+ (JSC::arrayProtoFuncToString): Updated to use the new JSStringJoiner
+ interface. Changed local variable name to thisArray since it's not a
+ JSObject*. Changed loop index to i instead of k.
+ (JSC::arrayProtoFuncToLocaleString): Updated to use the new JSStringJoiner
+ interface. Renamed thisObj to thisObject. Added a missing exception check
+ after the toLocaleString function is called, but before toString is called
+ the result of that function.
+ (JSC::arrayProtoFuncJoin): Updated to use the new JSStringJointer interface.
+ Added a missing exception check after calling toString on the separator
+ but before calling get to get the first element in the array-like object
+ being joined. Changed loop index to i instead of k. Added missing exception
+ check after calling toString on each string from the array before calling
+ get for the next element.
+ (JSC::arrayProtoFuncConcat): Use JSArray::length instead of using the
+ getLength function.
+ (JSC::arrayProtoFuncReverse): Ditto. Also use putByIndexInline.
+ (JSC::arrayProtoFuncShift): Ditto.
+ (JSC::arrayProtoFuncSplice): Use getIndex instead of get, which includes some
+ additional optimizations.
+ (JSC::getOrHole): Deleted. Unused function.
+ (JSC::arrayProtoFuncUnShift): Use putByIndexInline.
+
+ * runtime/ExceptionHelpers.cpp:
+ (JSC::errorDescriptionForValue): Removed the duplicate copy of the the logic
+ from JSValue::toString.
+
+ * runtime/JSCJSValue.cpp:
+ (JSC::JSValue::toStringSlowCase): Improved the performance when converting a
+ small integer to a single character string.
+ (JSC::JSValue::toWTFStringSlowCase): Moved the contents of the
+ inlineJSValueNotStringtoString function here.
+ * runtime/JSCJSValue.h: Removed no longer used toWTFStringInline and fixed
+ a comment with a typo.
+
+ * runtime/JSObject.h:
+ (JSC::JSObject::putByIndexInline): Marked ALWAYS_INLINE because this was not
+ getting inlined at some call sites.
+ (JSC::JSObject::indexingData): Deleted. Unused function.
+ (JSC::JSObject::currentIndexingData): Deleted. Unused function.
+ (JSC::JSObject::getHolyIndexQuickly): Deleted. Unused function.
+ (JSC::JSObject::relevantLength): Deleted. Unused function.
+ (JSC::JSObject::currentRelevantLength): Deleted. Unused function.
+
+ * runtime/JSString.h: Added the StringViewWithUnderlyingString struct and
+ the viewWithUnderlyingString function. Removed the inlineJSValueNotStringtoString
+ and toWTFStringInline functions.
+
+ * runtime/JSStringJoiner.cpp:
+ (JSC::appendStringToData): Changed this to be a template instead of writing
+ it out, since StringView::getCharactersWithUpconvert does almsot exactly what
+ this function was trying to do.
+ (JSC::joinStrings): Rewrote this to use StringView.
+ (JSC::JSStringJoiner::joinedLength): Added. Factored out from the join function.
+ (JSC::JSStringJoiner::join): Rewrote to make it a bit simpler. Added an assertion
+ that we entirely filled capacity, since we are now reserving capacity and using
+ uncheckedAppend. Use String instead of RefPtr<StringImpl> because there was no
+ particular value to using the impl directly.
+
+ * runtime/JSStringJoiner.h: Changed the interface to the class to use StringView.
+ Also changed this class so it now has the responsibility to convert each JSValue
+ into a string. This let us share more code between toString and join, and also
+ lets us use the new viewWithUnderlyingString function, which could be confusing at
+ all the call sites, but is easier to understand here.
+
2015-06-23 Matthew Mirman <mmir...@apple.com>
Completes native binding descriptors with native getters and potentially setters.
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (185898 => 185899)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2015-06-24 02:33:18 UTC (rev 185899)
@@ -138,11 +138,12 @@
// ------------------------------ Array Functions ----------------------------
-// Helper function
-static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index)
+static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index)
{
- PropertySlot slot(obj);
- if (!obj->getPropertySlot(exec, index, slot))
+ if (JSValue result = object->tryGetIndexQuickly(index))
+ return result;
+ PropertySlot slot(object);
+ if (!object->getPropertySlot(exec, index, slot))
return JSValue();
return slot.getValue(exec, index);
}
@@ -160,7 +161,7 @@
obj->methodTable()->put(obj, exec, exec->propertyNames().length, value, slot);
}
-static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
+static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
{
JSValue value = exec->argument(argument);
if (value.isUndefined())
@@ -174,7 +175,6 @@
return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
}
-
// The shift/unshift function implement the shift/unshift behaviour required
// by the corresponding array prototype methods, and by splice. In both cases,
// the methods are operating an an array or array like object.
@@ -189,6 +189,7 @@
// currentCount) will be shifted to the left or right as appropriate; in the
// case of shift this must be removing values, in the case of unshift this
// must be introducing new values.
+
template<JSArray::ShiftCountMode shiftCountMode>
void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
{
@@ -207,12 +208,10 @@
for (unsigned k = header; k < length - currentCount; ++k) {
unsigned from = k + currentCount;
unsigned to = k + resultCount;
- PropertySlot slot(thisObj);
- if (thisObj->getPropertySlot(exec, from, slot)) {
- JSValue value = slot.getValue(exec, from);
+ if (JSValue value = getProperty(exec, thisObj, from)) {
if (exec->hadException())
return;
- thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, to, value, true);
+ thisObj->putByIndexInline(exec, to, value, true);
if (exec->hadException())
return;
} else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) {
@@ -227,6 +226,7 @@
}
}
}
+
template<JSArray::ShiftCountMode shiftCountMode>
void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length)
{
@@ -247,16 +247,14 @@
if (array->length() == length && array->unshiftCount<shiftCountMode>(exec, header, count))
return;
}
-
+
for (unsigned k = length - currentCount; k > header; --k) {
unsigned from = k + currentCount - 1;
unsigned to = k + resultCount - 1;
- PropertySlot slot(thisObj);
- if (thisObj->getPropertySlot(exec, from, slot)) {
- JSValue value = slot.getValue(exec, from);
+ if (JSValue value = getProperty(exec, thisObj, from)) {
if (exec->hadException())
return;
- thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, to, value, true);
+ thisObj->putByIndexInline(exec, to, value, true);
} else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, to)) {
throwTypeError(exec, ASCIILiteral("Unable to delete property."));
return;
@@ -291,80 +289,75 @@
return JSValue::encode(call(exec, function, callType, callData, thisObject, exec->emptyList()));
ASSERT(isJSArray(thisValue));
- JSArray* thisObj = asArray(thisValue);
+ JSArray* thisArray = asArray(thisValue);
- unsigned length = getLength(exec, thisObj);
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ unsigned length = thisArray->length();
- StringRecursionChecker checker(exec, thisObj);
+ StringRecursionChecker checker(exec, thisArray);
if (JSValue earlyReturnValue = checker.earlyReturnValue())
return JSValue::encode(earlyReturnValue);
- String separator(",", String::ConstructFromLiteral);
- JSStringJoiner stringJoiner(separator, length);
- for (unsigned k = 0; k < length; k++) {
- JSValue element;
- if (thisObj->canGetIndexQuickly(k))
- element = thisObj->getIndexQuickly(k);
- else {
- element = thisObj->get(exec, k);
+ JSStringJoiner joiner(*exec, ',', length);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ for (unsigned i = 0; i < length; ++i) {
+ JSValue element = thisArray->tryGetIndexQuickly(i);
+ if (!element) {
+ element = thisArray->get(exec, i);
if (exec->hadException())
return JSValue::encode(jsUndefined());
}
-
- if (element.isUndefinedOrNull())
- stringJoiner.append(String());
- else
- stringJoiner.append(element.toWTFString(exec));
-
+ joiner.append(*exec, element);
if (exec->hadException())
return JSValue::encode(jsUndefined());
}
- return JSValue::encode(stringJoiner.join(exec));
+
+ return JSValue::encode(joiner.join(*exec));
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
{
JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
- JSObject* thisObj = thisValue.toObject(exec);
+ JSObject* thisObject = thisValue.toObject(exec);
if (exec->hadException())
return JSValue::encode(jsUndefined());
- unsigned length = getLength(exec, thisObj);
+ unsigned length = getLength(exec, thisObject);
if (exec->hadException())
return JSValue::encode(jsUndefined());
- StringRecursionChecker checker(exec, thisObj);
+ StringRecursionChecker checker(exec, thisObject);
if (JSValue earlyReturnValue = checker.earlyReturnValue())
return JSValue::encode(earlyReturnValue);
- String separator(",", String::ConstructFromLiteral);
- JSStringJoiner stringJoiner(separator, length);
- for (unsigned k = 0; k < length; k++) {
- JSValue element = thisObj->get(exec, k);
+ JSStringJoiner stringJoiner(*exec, ',', length);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ for (unsigned i = 0; i < length; ++i) {
+ JSValue element = thisObject->getIndex(exec, i);
if (exec->hadException())
return JSValue::encode(jsUndefined());
- if (!element.isUndefinedOrNull()) {
- JSObject* o = element.toObject(exec);
- JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString);
+ if (element.isUndefinedOrNull())
+ continue;
+ JSValue conversionFunction = element.get(exec, exec->propertyNames().toLocaleString);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ CallData callData;
+ CallType callType = getCallData(conversionFunction, callData);
+ if (callType != CallTypeNone) {
+ element = call(exec, conversionFunction, callType, callData, element, exec->emptyList());
if (exec->hadException())
return JSValue::encode(jsUndefined());
- String str;
- CallData callData;
- CallType callType = getCallData(conversionFunction, callData);
- if (callType != CallTypeNone)
- str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toWTFString(exec);
- else
- str = element.toWTFString(exec);
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
- stringJoiner.append(str);
}
+ stringJoiner.append(*exec, element);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
}
- return JSValue::encode(stringJoiner.join(exec));
+ return JSValue::encode(stringJoiner.join(*exec));
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
@@ -378,61 +371,56 @@
if (JSValue earlyReturnValue = checker.earlyReturnValue())
return JSValue::encode(earlyReturnValue);
- String separator;
- if (!exec->argument(0).isUndefined())
- separator = exec->argument(0).toWTFString(exec);
- if (separator.isNull())
- separator = String(",", String::ConstructFromLiteral);
+ JSString* separator = nullptr;
+ if (!exec->argument(0).isUndefined()) {
+ separator = exec->argument(0).toString(exec);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ }
- JSStringJoiner stringJoiner(separator, length);
+ const LChar comma = ',';
+ JSStringJoiner stringJoiner(*exec, separator ? separator->view(exec) : StringView{ &comma, 1 }, length);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
- unsigned k = 0;
+ unsigned i = 0;
+
if (isJSArray(thisObj)) {
JSArray* array = asArray(thisObj);
-
- for (; k < length; k++) {
- if (!array->canGetIndexQuickly(k))
- break;
-
- JSValue element = array->getIndexQuickly(k);
- if (!element.isUndefinedOrNull())
- stringJoiner.append(element.toWTFStringInline(exec));
- else
- stringJoiner.append(String());
+ for (; i < length && array->canGetIndexQuickly(i); ++i) {
+ stringJoiner.append(*exec, array->getIndexQuickly(i));
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
}
}
- for (; k < length; k++) {
- JSValue element = thisObj->get(exec, k);
+ for (; i < length; ++i) {
+ JSValue element = thisObj->get(exec, i);
if (exec->hadException())
return JSValue::encode(jsUndefined());
- if (!element.isUndefinedOrNull())
- stringJoiner.append(element.toWTFStringInline(exec));
- else
- stringJoiner.append(String());
+ stringJoiner.append(*exec, element);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
}
- return JSValue::encode(stringJoiner.join(exec));
+ return JSValue::encode(stringJoiner.join(*exec));
}
EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
{
JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
- size_t argCount = exec->argumentCount();
+ unsigned argCount = exec->argumentCount();
JSValue curArg = thisValue.toObject(exec);
Checked<unsigned, RecordOverflow> finalArraySize = 0;
- for (size_t i = 0;;) {
- if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
- finalArraySize += getLength(exec, currentArray);
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
- } else
- finalArraySize++;
+ for (unsigned i = 0; ; ++i) {
+ if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg))
+ finalArraySize += currentArray->length();
+ else
+ ++finalArraySize;
if (i == argCount)
break;
curArg = exec->uncheckedArgument(i);
- ++i;
}
if (finalArraySize.hasOverflowed())
@@ -444,9 +432,9 @@
curArg = thisValue.toObject(exec);
unsigned n = 0;
- for (size_t i = 0;;) {
+ for (unsigned i = 0; ; ++i) {
if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
- unsigned length = getLength(exec, currentArray);
+ unsigned length = currentArray->length();
if (exec->hadException())
return JSValue::encode(jsUndefined());
for (unsigned k = 0; k < length; ++k) {
@@ -464,7 +452,6 @@
if (i == argCount)
break;
curArg = exec->uncheckedArgument(i);
- ++i;
}
arr->setLength(exec, n);
return JSValue::encode(arr);
@@ -550,7 +537,7 @@
return JSValue::encode(jsUndefined());
if (obj2) {
- thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, k, obj2, true);
+ thisObj->putByIndexInline(exec, k, obj2, true);
if (exec->hadException())
return JSValue::encode(jsUndefined());
} else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, k)) {
@@ -559,7 +546,7 @@
}
if (obj) {
- thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, lk1, obj, true);
+ thisObj->putByIndexInline(exec, lk1, obj, true);
if (exec->hadException())
return JSValue::encode(jsUndefined());
} else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, lk1)) {
@@ -582,7 +569,7 @@
putLength(exec, thisObj, jsNumber(length));
result = jsUndefined();
} else {
- result = thisObj->get(exec, 0);
+ result = thisObj->getIndex(exec, 0);
shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length);
if (exec->hadException())
return JSValue::encode(jsUndefined());
@@ -621,15 +608,6 @@
return JSValue::encode(result);
}
-inline JSValue getOrHole(JSObject* obj, ExecState* exec, unsigned propertyName)
-{
- PropertySlot slot(obj);
- if (obj->getPropertySlot(exec, propertyName, slot))
- return slot.getValue(exec, propertyName);
-
- return JSValue();
-}
-
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
{
// 15.4.4.12
@@ -686,7 +664,7 @@
return JSValue::encode(jsUndefined());
}
for (unsigned k = 0; k < additionalArgs; ++k) {
- thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, k + begin, exec->uncheckedArgument(k + 2), true);
+ thisObj->putByIndexInline(exec, k + begin, exec->uncheckedArgument(k + 2), true);
if (exec->hadException())
return JSValue::encode(jsUndefined());
}
@@ -711,7 +689,7 @@
return JSValue::encode(jsUndefined());
}
for (unsigned k = 0; k < nrArgs; ++k) {
- thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, k, exec->uncheckedArgument(k), true);
+ thisObj->putByIndexInline(exec, k, exec->uncheckedArgument(k), true);
if (exec->hadException())
return JSValue::encode(jsUndefined());
}
Modified: trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp (185898 => 185899)
--- trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp 2015-06-24 02:33:18 UTC (rev 185899)
@@ -82,32 +82,16 @@
JSString* errorDescriptionForValue(ExecState* exec, JSValue v)
{
- VM& vm = exec->vm();
- if (v.isNull())
- return vm.smallStrings.nullString();
- if (v.isUndefined())
- return vm.smallStrings.undefinedString();
- if (v.isInt32())
- return jsString(&vm, vm.numericStrings.add(v.asInt32()));
- if (v.isDouble())
- return jsString(&vm, vm.numericStrings.add(v.asDouble()));
- if (v.isTrue())
- return vm.smallStrings.trueString();
- if (v.isFalse())
- return vm.smallStrings.falseString();
if (v.isString())
- return jsNontrivialString(&vm, makeString('"', asString(v)->value(exec), '"'));
+ return jsNontrivialString(exec, makeString('"', asString(v)->value(exec), '"'));
if (v.isObject()) {
CallData callData;
JSObject* object = asObject(v);
if (object->methodTable()->getCallData(object, callData) != CallTypeNone)
- return vm.smallStrings.functionString();
+ return exec->vm().smallStrings.functionString();
return jsString(exec, JSObject::calculatedClassName(object));
}
-
- // The JSValue should never be empty, so this point in the code should never be reached.
- ASSERT_NOT_REACHED();
- return vm.smallStrings.emptyString();
+ return v.toString(exec);
}
static String defaultApproximateSourceError(const String& originalMessage, const String& sourceText)
Modified: trunk/Source/_javascript_Core/runtime/JSCJSValue.cpp (185898 => 185899)
--- trunk/Source/_javascript_Core/runtime/JSCJSValue.cpp 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSCJSValue.cpp 2015-06-24 02:33:18 UTC (rev 185899)
@@ -353,8 +353,12 @@
{
VM& vm = exec->vm();
ASSERT(!isString());
- if (isInt32())
- return jsString(&vm, vm.numericStrings.add(asInt32()));
+ if (isInt32()) {
+ auto integer = asInt32();
+ if (static_cast<unsigned>(integer) <= 9)
+ return vm.smallStrings.singleCharacterString(integer + '0');
+ return jsNontrivialString(&vm, vm.numericStrings.add(integer));
+ }
if (isDouble())
return jsString(&vm, vm.numericStrings.add(asDouble()));
if (isTrue())
@@ -380,7 +384,20 @@
String JSValue::toWTFStringSlowCase(ExecState* exec) const
{
- return inlineJSValueNotStringtoString(*this, exec);
+ VM& vm = exec->vm();
+ if (isInt32())
+ return vm.numericStrings.add(asInt32());
+ if (isDouble())
+ return vm.numericStrings.add(asDouble());
+ if (isTrue())
+ return vm.propertyNames->trueKeyword.string();
+ if (isFalse())
+ return vm.propertyNames->falseKeyword.string();
+ if (isNull())
+ return vm.propertyNames->nullKeyword.string();
+ if (isUndefined())
+ return vm.propertyNames->undefinedKeyword.string();
+ return toString(exec)->value(exec);
}
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/JSCJSValue.h (185898 => 185899)
--- trunk/Source/_javascript_Core/runtime/JSCJSValue.h 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSCJSValue.h 2015-06-24 02:33:18 UTC (rev 185899)
@@ -252,7 +252,6 @@
JSString* toString(ExecState*) const;
Identifier toPropertyKey(ExecState*) const;
WTF::String toWTFString(ExecState*) const;
- WTF::String toWTFStringInline(ExecState*) const;
JSObject* toObject(ExecState*) const;
JSObject* toObject(ExecState*, JSGlobalObject*) const;
@@ -262,8 +261,8 @@
int32_t toInt32(ExecState*) const;
uint32_t toUInt32(ExecState*) const;
- // Floating point conversions (this is a convenience method for webcore;
- // signle precision float is not a representation used in JS or JSC).
+ // Floating point conversions (this is a convenience function for WebCore;
+ // single precision float is not a representation used in JS or JSC).
float toFloat(ExecState* exec) const { return static_cast<float>(toNumber(exec)); }
// Object operations, with the toObject operation included.
Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (185898 => 185899)
--- trunk/Source/_javascript_Core/runtime/JSObject.h 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h 2015-06-24 02:33:18 UTC (rev 185899)
@@ -145,7 +145,7 @@
JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
- void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
+ ALWAYS_INLINE void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
{
if (canSetIndexQuickly(propertyName)) {
setIndexQuickly(exec->vm(), propertyName, value);
@@ -837,103 +837,6 @@
// This is relevant to undecided, int32, double, and contiguous.
unsigned countElements();
- // This strange method returns a pointer to the start of the indexed data
- // as if it contained JSValues. But it won't always contain JSValues.
- // Make sure you cast this to the appropriate type before using.
- template<IndexingType indexingType>
- ContiguousJSValues indexingData()
- {
- switch (indexingType) {
- case ALL_INT32_INDEXING_TYPES:
- case ALL_DOUBLE_INDEXING_TYPES:
- case ALL_CONTIGUOUS_INDEXING_TYPES:
- return m_butterfly->contiguous();
-
- case ALL_ARRAY_STORAGE_INDEXING_TYPES:
- return m_butterfly->arrayStorage()->vector();
-
- default:
- CRASH();
- return ContiguousJSValues();
- }
- }
-
- ContiguousJSValues currentIndexingData()
- {
- switch (indexingType()) {
- case ALL_INT32_INDEXING_TYPES:
- case ALL_CONTIGUOUS_INDEXING_TYPES:
- return m_butterfly->contiguous();
-
- case ALL_ARRAY_STORAGE_INDEXING_TYPES:
- return m_butterfly->arrayStorage()->vector();
-
- default:
- CRASH();
- return ContiguousJSValues();
- }
- }
-
- JSValue getHolyIndexQuickly(unsigned i)
- {
- ASSERT(i < m_butterfly->vectorLength());
- switch (indexingType()) {
- case ALL_INT32_INDEXING_TYPES:
- case ALL_CONTIGUOUS_INDEXING_TYPES:
- return m_butterfly->contiguous()[i].get();
- case ALL_DOUBLE_INDEXING_TYPES: {
- double value = m_butterfly->contiguousDouble()[i];
- if (value == value)
- return JSValue(JSValue::EncodeAsDouble, value);
- return JSValue();
- }
- case ALL_ARRAY_STORAGE_INDEXING_TYPES:
- return m_butterfly->arrayStorage()->m_vector[i].get();
- default:
- CRASH();
- return JSValue();
- }
- }
-
- template<IndexingType indexingType>
- unsigned relevantLength()
- {
- switch (indexingType) {
- case ALL_INT32_INDEXING_TYPES:
- case ALL_DOUBLE_INDEXING_TYPES:
- case ALL_CONTIGUOUS_INDEXING_TYPES:
- return m_butterfly->publicLength();
-
- case ALL_ARRAY_STORAGE_INDEXING_TYPES:
- return std::min(
- m_butterfly->arrayStorage()->length(),
- m_butterfly->arrayStorage()->vectorLength());
-
- default:
- CRASH();
- return 0;
- }
- }
-
- unsigned currentRelevantLength()
- {
- switch (indexingType()) {
- case ALL_INT32_INDEXING_TYPES:
- case ALL_DOUBLE_INDEXING_TYPES:
- case ALL_CONTIGUOUS_INDEXING_TYPES:
- return m_butterfly->publicLength();
-
- case ALL_ARRAY_STORAGE_INDEXING_TYPES:
- return std::min(
- m_butterfly->arrayStorage()->length(),
- m_butterfly->arrayStorage()->vectorLength());
-
- default:
- CRASH();
- return 0;
- }
- }
-
private:
friend class LLIntOffsetsExtractor;
Modified: trunk/Source/_javascript_Core/runtime/JSString.h (185898 => 185899)
--- trunk/Source/_javascript_Core/runtime/JSString.h 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSString.h 2015-06-24 02:33:18 UTC (rev 185899)
@@ -62,6 +62,11 @@
JSRopeString* jsStringBuilder(VM*);
+struct StringViewWithUnderlyingString {
+ StringView view;
+ String underlyingString;
+};
+
class JSString : public JSCell {
public:
friend class JIT;
@@ -144,6 +149,7 @@
AtomicString toAtomicString(ExecState*) const;
RefPtr<AtomicStringImpl> toExistingAtomicString(ExecState*) const;
StringView view(ExecState*) const;
+ StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const;
const String& value(ExecState*) const;
const String& tryGetValue() const;
const StringImpl* tryGetValueImpl() const;
@@ -384,6 +390,7 @@
void resolveRopeInternal16NoSubstring(UChar*) const;
void clearFibers() const;
StringView view(ExecState*) const;
+ StringViewWithUnderlyingString viewWithUnderlyingString(ExecState&) const;
WriteBarrierBase<JSString>& fiber(unsigned i) const
{
@@ -673,32 +680,6 @@
return toWTFStringSlowCase(exec);
}
-ALWAYS_INLINE String inlineJSValueNotStringtoString(const JSValue& value, ExecState* exec)
-{
- VM& vm = exec->vm();
- if (value.isInt32())
- return vm.numericStrings.add(value.asInt32());
- if (value.isDouble())
- return vm.numericStrings.add(value.asDouble());
- if (value.isTrue())
- return vm.propertyNames->trueKeyword.string();
- if (value.isFalse())
- return vm.propertyNames->falseKeyword.string();
- if (value.isNull())
- return vm.propertyNames->nullKeyword.string();
- if (value.isUndefined())
- return vm.propertyNames->undefinedKeyword.string();
- return value.toString(exec)->value(exec);
-}
-
-ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
-{
- if (isString())
- return static_cast<JSString*>(asCell())->value(exec);
-
- return inlineJSValueNotStringtoString(*this, exec);
-}
-
ALWAYS_INLINE StringView JSRopeString::view(ExecState* exec) const
{
if (isSubstring()) {
@@ -710,6 +691,18 @@
return StringView(m_value);
}
+ALWAYS_INLINE StringViewWithUnderlyingString JSRopeString::viewWithUnderlyingString(ExecState& state) const
+{
+ if (isSubstring()) {
+ auto& base = substringBase()->m_value;
+ if (is8Bit())
+ return { { base.characters8() + substringOffset(), m_length }, base };
+ return { { base.characters16() + substringOffset(), m_length }, base };
+ }
+ resolveRope(&state);
+ return { m_value, m_value };
+}
+
ALWAYS_INLINE StringView JSString::view(ExecState* exec) const
{
if (isRope())
@@ -717,6 +710,13 @@
return StringView(m_value);
}
+ALWAYS_INLINE StringViewWithUnderlyingString JSString::viewWithUnderlyingString(ExecState& state) const
+{
+ if (isRope())
+ return static_cast<const JSRopeString&>(*this).viewWithUnderlyingString(state);
+ return { m_value, m_value };
+}
+
inline bool JSString::isSubstring() const
{
return isRope() && static_cast<const JSRopeString*>(this)->isSubstring();
Modified: trunk/Source/_javascript_Core/runtime/JSStringJoiner.cpp (185898 => 185899)
--- trunk/Source/_javascript_Core/runtime/JSStringJoiner.cpp 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSStringJoiner.cpp 2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,105 +26,94 @@
#include "config.h"
#include "JSStringJoiner.h"
-#include "ExceptionHelpers.h"
-#include "JSScope.h"
-#include "JSString.h"
#include "JSCInlines.h"
-#include <wtf/text/StringImpl.h>
namespace JSC {
-// The destination is 16bits, at least one string is 16 bits.
-static inline void appendStringToData(UChar*& data, const String& string)
+template<typename CharacterType>
+static inline void appendStringToData(CharacterType*& data, StringView string)
{
- if (string.isNull())
- return;
-
- unsigned length = string.length();
- const StringImpl* stringImpl = string.impl();
-
- if (stringImpl->is8Bit()) {
- for (unsigned i = 0; i < length; ++i) {
- *data = ""
- ++data;
- }
- } else {
- for (unsigned i = 0; i < length; ++i) {
- *data = ""
- ++data;
- }
- }
+ string.getCharactersWithUpconvert(data);
+ data += string.length();
}
-// If the destination is 8bits, we know every string has to be 8bit.
-static inline void appendStringToData(LChar*& data, const String& string)
+template<typename CharacterType>
+static inline String joinStrings(const Vector<StringViewWithUnderlyingString>& strings, StringView separator, unsigned joinedLength)
{
- if (string.isNull())
- return;
- ASSERT(string.is8Bit());
+ ASSERT(joinedLength);
- unsigned length = string.length();
- const StringImpl* stringImpl = string.impl();
+ CharacterType* data;
+ String result = StringImpl::tryCreateUninitialized(joinedLength, data);
+ if (result.isNull())
+ return result;
- for (unsigned i = 0; i < length; ++i) {
- *data = ""
- ++data;
+ appendStringToData(data, strings[0].view);
+
+ unsigned size = strings.size();
+
+ switch (separator.length()) {
+ case 0:
+ for (unsigned i = 1; i < size; ++i)
+ appendStringToData(data, strings[i].view);
+ break;
+ case 1: {
+ CharacterType separatorCharacter = separator[0];
+ for (unsigned i = 1; i < size; ++i) {
+ *data++ = separatorCharacter;
+ appendStringToData(data, strings[i].view);
+ }
+ break;
}
+ default:
+ for (unsigned i = 1; i < size; ++i) {
+ appendStringToData(data, separator);
+ appendStringToData(data, strings[i].view);
+ }
+ }
+ ASSERT(data == result.characters<CharacterType>() + joinedLength);
+
+ return result;
}
-template<typename CharacterType>
-static inline PassRefPtr<StringImpl> joinStrings(const Vector<String>& strings, const String& separator, unsigned outputLength)
+inline unsigned JSStringJoiner::joinedLength(ExecState& state) const
{
- ASSERT(outputLength);
+ unsigned numberOfStrings = m_strings.size();
+ if (!numberOfStrings)
+ return 0;
- CharacterType* data;
- RefPtr<StringImpl> outputStringImpl = StringImpl::tryCreateUninitialized(outputLength, data);
- if (!outputStringImpl)
- return nullptr;
+ Checked<unsigned, RecordOverflow> separatorLength = m_separator.length();
+ Checked<unsigned, RecordOverflow> totalSeparatorsLength = separatorLength * (numberOfStrings - 1);
+ Checked<unsigned, RecordOverflow> totalLength = totalSeparatorsLength + m_accumulatedStringsLength;
- const String firstString = strings.first();
- appendStringToData(data, firstString);
-
- for (size_t i = 1; i < strings.size(); ++i) {
- appendStringToData(data, separator);
- appendStringToData(data, strings[i]);
+ unsigned result;
+ if (totalLength.safeGet(result) == CheckedState::DidOverflow) {
+ throwOutOfMemoryError(&state);
+ return 0;
}
-
- ASSERT(data == (outputStringImpl->characters<CharacterType>() + outputStringImpl->length()));
- return outputStringImpl.release();
+ return result;
}
-JSValue JSStringJoiner::join(ExecState* exec)
+JSValue JSStringJoiner::join(ExecState& state)
{
- if (!m_isValid)
- return throwOutOfMemoryError(exec);
+ ASSERT(m_strings.size() == m_strings.capacity());
- if (!m_strings.size())
- return jsEmptyString(exec);
+ unsigned length = joinedLength(state);
+ if (state.hadException())
+ return jsUndefined();
- Checked<unsigned, RecordOverflow> separatorLength = m_separator.length();
- // FIXME: add special cases of joinStrings() for (separatorLength == 0) and (separatorLength == 1).
- ASSERT(m_strings.size() > 0);
- Checked<unsigned, RecordOverflow> totalSeparactorsLength = separatorLength * (m_strings.size() - 1);
- Checked<unsigned, RecordOverflow> outputStringSize = totalSeparactorsLength + m_accumulatedStringsLength;
+ if (!length)
+ return jsEmptyString(&state);
- unsigned finalSize;
- if (outputStringSize.safeGet(finalSize) == CheckedState::DidOverflow)
- return throwOutOfMemoryError(exec);
-
- if (!outputStringSize)
- return jsEmptyString(exec);
-
- RefPtr<StringImpl> outputStringImpl;
- if (m_is8Bits)
- outputStringImpl = joinStrings<LChar>(m_strings, m_separator, finalSize);
+ String result;
+ if (m_isAll8Bit)
+ result = joinStrings<LChar>(m_strings, m_separator, length);
else
- outputStringImpl = joinStrings<UChar>(m_strings, m_separator, finalSize);
+ result = joinStrings<UChar>(m_strings, m_separator, length);
- if (!outputStringImpl)
- return throwOutOfMemoryError(exec);
+ if (result.isNull())
+ return throwOutOfMemoryError(&state);
- return JSString::create(exec->vm(), outputStringImpl.release());
+ return jsString(&state, WTF::move(result));
}
}
Modified: trunk/Source/_javascript_Core/runtime/JSStringJoiner.h (185898 => 185899)
--- trunk/Source/_javascript_Core/runtime/JSStringJoiner.h 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/_javascript_Core/runtime/JSStringJoiner.h 2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,50 +26,112 @@
#ifndef JSStringJoiner_h
#define JSStringJoiner_h
+#include "ExceptionHelpers.h"
#include "JSCJSValue.h"
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
namespace JSC {
-class ExecState;
-
-
class JSStringJoiner {
public:
- JSStringJoiner(const String& separator, size_t stringCount);
+ JSStringJoiner(ExecState&, LChar separator, unsigned stringCount);
+ JSStringJoiner(ExecState&, StringView separator, unsigned stringCount);
- void append(const String&);
- JSValue join(ExecState*);
+ void append(ExecState&, JSValue);
+ JSValue join(ExecState&);
+
private:
- String m_separator;
- Vector<String> m_strings;
+ void append(StringViewWithUnderlyingString&&);
+ void append8Bit(const String&);
+ void appendLiteral(const Identifier&);
+ void appendEmptyString();
+ unsigned joinedLength(ExecState&) const;
+ LChar m_singleCharacterSeparator;
+ StringView m_separator;
+ Vector<StringViewWithUnderlyingString> m_strings;
Checked<unsigned, RecordOverflow> m_accumulatedStringsLength;
- bool m_isValid;
- bool m_is8Bits;
+ bool m_isAll8Bit { true };
};
-inline JSStringJoiner::JSStringJoiner(const String& separator, size_t stringCount)
+inline JSStringJoiner::JSStringJoiner(ExecState& state, StringView separator, unsigned stringCount)
: m_separator(separator)
- , m_isValid(true)
- , m_is8Bits(m_separator.is8Bit())
+ , m_isAll8Bit(m_separator.is8Bit())
{
- ASSERT(!m_separator.isNull());
- m_isValid = m_strings.tryReserveCapacity(stringCount);
+ if (!m_strings.tryReserveCapacity(stringCount))
+ throwOutOfMemoryError(&state);
}
-inline void JSStringJoiner::append(const String& str)
+inline JSStringJoiner::JSStringJoiner(ExecState& state, LChar separator, unsigned stringCount)
+ : m_singleCharacterSeparator(separator)
+ , m_separator { &m_singleCharacterSeparator, 1 }
{
- if (!m_isValid)
+ if (!m_strings.tryReserveCapacity(stringCount))
+ throwOutOfMemoryError(&state);
+}
+
+ALWAYS_INLINE void JSStringJoiner::append(StringViewWithUnderlyingString&& string)
+{
+ m_accumulatedStringsLength += string.view.length();
+ m_isAll8Bit = m_isAll8Bit && string.view.is8Bit();
+ m_strings.uncheckedAppend(WTF::move(string));
+}
+
+ALWAYS_INLINE void JSStringJoiner::append8Bit(const String& string)
+{
+ ASSERT(string.is8Bit());
+ m_accumulatedStringsLength += string.length();
+ m_strings.uncheckedAppend({ string, string });
+}
+
+ALWAYS_INLINE void JSStringJoiner::appendLiteral(const Identifier& literal)
+{
+ m_accumulatedStringsLength += literal.length();
+ ASSERT(literal.string().is8Bit());
+ m_strings.uncheckedAppend({ literal.string(), { } });
+}
+
+ALWAYS_INLINE void JSStringJoiner::appendEmptyString()
+{
+ m_strings.uncheckedAppend({ { }, { } });
+}
+
+ALWAYS_INLINE void JSStringJoiner::append(ExecState& state, JSValue value)
+{
+ // The following code differs from using the result of JSValue::toString in the following ways:
+ // 1) It's inlined more than JSValue::toString is.
+ // 2) It includes conversion to WTF::String in a way that avoids allocating copies of substrings.
+ // 3) It doesn't create a JSString for numbers, true, or false.
+ // 4) It turns undefined and null into the empty string instead of "undefined" and "null".
+ // 5) It uses optimized code paths for all the cases known to be 8-bit and for the empty string.
+
+ if (value.isCell()) {
+ if (value.asCell()->isString()) {
+ append(asString(value)->viewWithUnderlyingString(state));
+ return;
+ }
+ append(value.toString(&state)->viewWithUnderlyingString(state));
return;
+ }
- m_strings.append(str);
- if (!str.isNull()) {
- m_accumulatedStringsLength += str.length();
- m_is8Bits = m_is8Bits && str.is8Bit();
+ if (value.isInt32()) {
+ append8Bit(state.vm().numericStrings.add(value.asInt32()));
+ return;
}
+ if (value.isDouble()) {
+ append8Bit(state.vm().numericStrings.add(value.asDouble()));
+ return;
+ }
+ if (value.isTrue()) {
+ append8Bit(state.vm().propertyNames->trueKeyword.string());
+ return;
+ }
+ if (value.isFalse()) {
+ append8Bit(state.vm().propertyNames->falseKeyword.string());
+ return;
+ }
+ ASSERT(value.isUndefinedOrNull());
+ appendEmptyString();
}
}
Modified: trunk/Source/WTF/ChangeLog (185898 => 185899)
--- trunk/Source/WTF/ChangeLog 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/WTF/ChangeLog 2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,3 +1,27 @@
+2015-06-22 Darin Adler <da...@apple.com>
+
+ Make Array.join work directly on substrings without reifying them
+ https://bugs.webkit.org/show_bug.cgi?id=146191
+
+ Reviewed by Andreas Kling.
+
+ * wtf/Vector.h: Added an overload of uncheckedAppend like the one we added
+ a while back, a non-template function that forwards through to the function
+ template. This lets us call uncheckedAppend on an argument list and have it
+ properly convert it to the Vector's element type.
+
+ * wtf/text/StringView.h:
+ (WTF::StringView::getCharactersWithUpconvert): Changed to not use memcpy;
+ saw some indication the hand-written loop was faster when profiling. Also
+ use m_length directly when we know we are dealing with an 8-bit string,
+ since the masking that the index function does is not needed in that case.
+ (WTF::StringView::UpconvertedCharacters::UpconvertedCharacters): Ditto.
+ (WTF::StringView::toString): Ditto.
+ (WTF::StringView::toFloat): Ditto.
+ (WTF::StringView::toInt): Ditto.
+ (WTF::StringView::toStringWithoutCopying): Ditto.
+ (WTF::StringView::find): Ditto.
+
2015-06-22 YunQiang Su <wzss...@gmail.com>
[WTF] Platform.h: use _ABI64 instead of _MIPS_SIM_ABI64 to determine MIPS N64
Modified: trunk/Source/WTF/wtf/Vector.h (185898 => 185899)
--- trunk/Source/WTF/wtf/Vector.h 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/WTF/wtf/Vector.h 2015-06-24 02:33:18 UTC (rev 185899)
@@ -724,9 +724,12 @@
void clear() { shrinkCapacity(0); }
void append(ValueType&& value) { append<ValueType>(std::forward<ValueType>(value)); }
+ template<typename U> void append(U&&);
+
+ void uncheckedAppend(ValueType&& value) { uncheckedAppend<ValueType>(std::forward<ValueType>(value)); }
+ template<typename U> void uncheckedAppend(U&&);
+
template<typename U> void append(const U*, size_t);
- template<typename U> void append(U&&);
- template<typename U> void uncheckedAppend(U&& val);
template<typename U, size_t otherCapacity> void appendVector(const Vector<U, otherCapacity>&);
template<typename U> bool tryAppend(const U*, size_t);
Modified: trunk/Source/WTF/wtf/text/StringView.h (185898 => 185899)
--- trunk/Source/WTF/wtf/text/StringView.h 2015-06-24 02:13:24 UTC (rev 185898)
+++ trunk/Source/WTF/wtf/text/StringView.h 2015-06-24 02:33:18 UTC (rev 185899)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -409,19 +409,23 @@
inline void StringView::getCharactersWithUpconvert(LChar* destination) const
{
ASSERT(is8Bit());
- memcpy(destination, characters8(), length());
+ auto characters8 = this->characters8();
+ for (unsigned i = 0; i < m_length; ++i)
+ destination[i] = characters8[i];
}
inline void StringView::getCharactersWithUpconvert(UChar* destination) const
{
if (is8Bit()) {
- const LChar* characters8 = this->characters8();
- unsigned length = this->length();
- for (unsigned i = 0; i < length; ++i)
+ auto characters8 = this->characters8();
+ for (unsigned i = 0; i < m_length; ++i)
destination[i] = characters8[i];
return;
}
- memcpy(destination, characters16(), length() * sizeof(UChar));
+ auto characters16 = this->characters16();
+ unsigned length = this->length();
+ for (unsigned i = 0; i < length; ++i)
+ destination[i] = characters16[i];
}
inline StringView::UpconvertedCharacters::UpconvertedCharacters(const StringView& string)
@@ -431,7 +435,7 @@
return;
}
const LChar* characters8 = string.characters8();
- unsigned length = string.length();
+ unsigned length = string.m_length;
m_upconvertedCharacters.reserveInitialCapacity(length);
for (unsigned i = 0; i < length; ++i)
m_upconvertedCharacters.uncheckedAppend(characters8[i]);
@@ -441,35 +445,35 @@
inline String StringView::toString() const
{
if (is8Bit())
- return String(characters8(), length());
+ return String(characters8(), m_length);
return String(characters16(), length());
}
inline float StringView::toFloat(bool& isValid) const
{
if (is8Bit())
- return charactersToFloat(characters8(), length(), &isValid);
+ return charactersToFloat(characters8(), m_length, &isValid);
return charactersToFloat(characters16(), length(), &isValid);
}
inline int StringView::toInt(bool& isValid) const
{
if (is8Bit())
- return charactersToInt(characters8(), length(), &isValid);
+ return charactersToInt(characters8(), m_length, &isValid);
return charactersToInt(characters16(), length(), &isValid);
}
inline String StringView::toStringWithoutCopying() const
{
if (is8Bit())
- return StringImpl::createWithoutCopying(characters8(), length());
+ return StringImpl::createWithoutCopying(characters8(), m_length);
return StringImpl::createWithoutCopying(characters16(), length());
}
inline size_t StringView::find(UChar character, unsigned start) const
{
if (is8Bit())
- return WTF::find(characters8(), length(), character, start);
+ return WTF::find(characters8(), m_length, character, start);
return WTF::find(characters16(), length(), character, start);
}