https://bugs.webkit.org/show_bug.cgi?id=162887
Reviewed by Filip Pizlo and Yusuke Suzuki.
JSTests:
* microbenchmarks/to-lower-case.js: Added.
(assert):
(foo):
(bar):
* stress/to-lower-case.js: Added.
(assert):
(foo):
Source/JavaScriptCore:
This patch makes ToLowerCase an intrinsic in the DFG/FTL. On the fast
path, the intrinsic will loop over an 8-bit string ensuring it's already
lower case, and simply return the string. In the slow path, it'll call
into C code to make a new string.
This is a 7-8% speedup on ES6SampleBench/Basic.
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileToLowerCase):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileToLowerCase):
* jit/JITOperations.h:
* runtime/Intrinsic.h:
* runtime/StringPrototype.cpp:
(JSC::StringPrototype::finishCreation):
Source/WTF:
This patch exposes a new StringImpl function called convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit
which extracts slow path for the 8-bit part of convertToLowercaseWithoutLocale
into a helper function. I decided to extract this into its own function because
it may be the case that JSCs JITs will want to continue the operation
after it has already ensured that part of an 8-bit string is lower case.
* wtf/text/StringImpl.cpp:
(WTF::StringImpl::convertToLowercaseWithoutLocale):
(WTF::StringImpl::convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit):
* wtf/text/StringImpl.h:
* wtf/text/WTFString.cpp:
(WTF::String::convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit):
* wtf/text/WTFString.h:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@206804
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2016-10-04 Saam Barati <sbarati@apple.com>
+
+ String.prototype.toLowerCase should be a DFG/FTL intrinsic
+ https://bugs.webkit.org/show_bug.cgi?id=162887
+
+ Reviewed by Filip Pizlo and Yusuke Suzuki.
+
+ * microbenchmarks/to-lower-case.js: Added.
+ (assert):
+ (foo):
+ (bar):
+ * stress/to-lower-case.js: Added.
+ (assert):
+ (foo):
+
2016-10-04 JF Bastien <jfbastien@apple.com>
WebAssembly: handle a few corner cases
--- /dev/null
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion");
+}
+noInline(assert);
+
+let tests = [
+ ["foo", "foo"],
+ ["foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", "foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"],
+];
+
+function foo(a) {
+ return a.toLowerCase();
+}
+noInline(foo);
+
+function bar(a) {
+ a.toLowerCase();
+ assert(true); // side effects
+ a.toLowerCase();
+}
+noInline(bar);
+
+let start = Date.now();
+for (let i = 0; i < 500000; i++) {
+ for (let i = 0; i < tests.length; i++) {
+ let test = tests[i][0];
+ let result = tests[i][1];
+ assert(foo(test) === result);
+ bar(test);
+ }
+}
+
+const verbose = false;
+if (verbose)
+ print(Date.now() - start);
--- /dev/null
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion");
+}
+
+let tests = [
+ ["FOO", "foo"],
+ ["fff\u00C2", "fff\u00E2"],
+ ["foo", "foo"],
+ ["foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", "foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"],
+ ["BaR", "bar"],
+ ["FOO\u00A9", "foo\u00A9"],
+ ["#$#$", "#$#$"],
+ ["&&&\u00A9", "&&&\u00A9"],
+ ["&&&\u00C2", "&&&\u00E2"],
+ ["ABC\u0100", "abc\u0101"],
+];
+
+function foo(a) {
+ return a.toLowerCase();
+}
+noInline(foo);
+
+for (let i = 0; i < 10000; i++) {
+ for (let i = 0; i < tests.length; i++) {
+ let test = tests[i][0];
+ let result = tests[i][1];
+ assert(foo(test) === result);
+ }
+}
+2016-10-04 Saam Barati <sbarati@apple.com>
+
+ String.prototype.toLowerCase should be a DFG/FTL intrinsic
+ https://bugs.webkit.org/show_bug.cgi?id=162887
+
+ Reviewed by Filip Pizlo and Yusuke Suzuki.
+
+ This patch makes ToLowerCase an intrinsic in the DFG/FTL. On the fast
+ path, the intrinsic will loop over an 8-bit string ensuring it's already
+ lower case, and simply return the string. In the slow path, it'll call
+ into C code to make a new string.
+
+ This is a 7-8% speedup on ES6SampleBench/Basic.
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileToLowerCase):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLAbstractHeapRepository.h:
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileToLowerCase):
+ * jit/JITOperations.h:
+ * runtime/Intrinsic.h:
+ * runtime/StringPrototype.cpp:
+ (JSC::StringPrototype::finishCreation):
+
2016-10-04 Brian Burg <bburg@apple.com>
Web Inspector: don't synchronously send a listing message if we might need to query _WKAutomationDelegate
break;
}
+ case ToLowerCase: {
+ forNode(node).setType(m_graph, SpecString);
+ break;
+ }
+
case LoadFromJSMapBucket:
forNode(node).makeHeapTop();
break;
return true;
}
+ case ToLowerCaseIntrinsic: {
+ if (argumentCountIncludingThis != 1)
+ return false;
+
+ if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+ return false;
+
+ insertChecks();
+ Node* thisString = get(virtualRegisterForArgument(0, registerOffset));
+ Node* result = addToGraph(ToLowerCase, thisString);
+ set(VirtualRegister(resultOperand), result);
+ return true;
+ }
+
default:
return false;
}
read(MiscFields);
def(HeapLocation(MapHasLoc, MiscFields, node->child1()), LazyNode(node));
return;
+
+ case ToLowerCase:
+ def(PureValue(node));
+ return;
case LastNodeType:
RELEASE_ASSERT_NOT_REACHED();
case StringReplace:
case StringReplaceRegExp:
case CreateRest:
+ case ToLowerCase:
return true;
case MultiPutByOffset:
break;
}
+ case ToLowerCase: {
+ // We currently only support StringUse since that will ensure that
+ // ToLowerCase is a pure operation. If we decide to update this with
+ // more types in the future, we need to ensure that the clobberize rules
+ // are correct.
+ fixEdge<StringUse>(node->child1());
+ break;
+ }
+
case DefineAccessorProperty: {
fixEdge<CellUse>(m_graph.varArgChild(node, 0));
Edge& propertyEdge = m_graph.varArgChild(node, 1);
macro(GetMapBucket, NodeResultJS) \
macro(LoadFromJSMapBucket, NodeResultJS) \
macro(IsNonEmptyMapBucket, NodeResultBoolean) \
+ \
+ macro(ToLowerCase, NodeResultJS) \
// This enum generates a monotonically increasing id for all Node types,
// and is used by the subsequent enum to fill out the id (as accessed via the NodeIdMask).
return string->value(exec).impl();
}
+JSString* JIT_OPERATION operationToLowerCase(ExecState* exec, JSString* string, uint32_t failingIndex)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ const String& inputString = string->value(exec);
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ String lowercasedString = inputString.is8Bit() ? inputString.convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(failingIndex) : inputString.convertToLowercaseWithoutLocale();
+ if (lowercasedString.impl() == inputString.impl())
+ return string;
+ scope.release();
+ return jsString(exec, lowercasedString);
+}
+
JSString* JIT_OPERATION operationSingleCharacterString(ExecState* exec, int32_t character)
{
VM& vm = exec->vm();
StringImpl* JIT_OPERATION operationResolveRope(ExecState*, JSString*);
JSString* JIT_OPERATION operationSingleCharacterString(ExecState*, int32_t);
+JSString* JIT_OPERATION operationToLowerCase(ExecState*, JSString*, uint32_t);
+
int32_t JIT_OPERATION operationMapHash(ExecState*, EncodedJSValue input);
JSCell* JIT_OPERATION operationJSMapFindBucket(ExecState*, JSCell*, EncodedJSValue, int32_t);
JSCell* JIT_OPERATION operationJSSetFindBucket(ExecState*, JSCell*, EncodedJSValue, int32_t);
setPrediction(SpecInt32Only);
break;
}
+
+ case ToLowerCase:
+ setPrediction(SpecString);
+ break;
+
case ArithPow:
case ArithSqrt:
case ArithFRound:
case PutDynamicVar:
case ResolveScope:
case MapHash:
+ case ToLowerCase:
case GetMapBucket:
case LoadFromJSMapBucket:
case IsNonEmptyMapBucket:
jump(notTaken);
}
+void SpeculativeJIT::compileToLowerCase(Node* node)
+{
+ ASSERT(node->op() == ToLowerCase);
+ SpeculateCellOperand string(this, node->child1());
+ GPRTemporary temp(this);
+ GPRTemporary index(this);
+ GPRTemporary charReg(this);
+ GPRTemporary length(this);
+
+ GPRReg stringGPR = string.gpr();
+ GPRReg tempGPR = temp.gpr();
+ GPRReg indexGPR = index.gpr();
+ GPRReg charGPR = charReg.gpr();
+ GPRReg lengthGPR = length.gpr();
+
+ speculateString(node->child1(), stringGPR);
+
+ CCallHelpers::JumpList slowPath;
+
+ m_jit.move(TrustedImmPtr(0), indexGPR);
+
+ m_jit.loadPtr(MacroAssembler::Address(stringGPR, JSString::offsetOfValue()), tempGPR);
+ slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, tempGPR));
+
+ slowPath.append(m_jit.branchTest32(
+ MacroAssembler::Zero, MacroAssembler::Address(tempGPR, StringImpl::flagsOffset()),
+ MacroAssembler::TrustedImm32(StringImpl::flagIs8Bit())));
+ m_jit.load32(MacroAssembler::Address(tempGPR, StringImpl::lengthMemoryOffset()), lengthGPR);
+ m_jit.loadPtr(MacroAssembler::Address(tempGPR, StringImpl::dataOffset()), tempGPR);
+
+ auto loopStart = m_jit.label();
+ auto loopDone = m_jit.branch32(CCallHelpers::AboveOrEqual, indexGPR, lengthGPR);
+ m_jit.load8(MacroAssembler::BaseIndex(tempGPR, indexGPR, MacroAssembler::TimesOne), charGPR);
+ slowPath.append(m_jit.branchTest32(CCallHelpers::NonZero, charGPR, TrustedImm32(~0x7F)));
+ m_jit.sub32(TrustedImm32('A'), charGPR);
+ slowPath.append(m_jit.branch32(CCallHelpers::BelowOrEqual, charGPR, TrustedImm32('Z' - 'A')));
+
+ m_jit.add32(TrustedImm32(1), indexGPR);
+ m_jit.jump().linkTo(loopStart, &m_jit);
+
+ slowPath.link(&m_jit);
+ silentSpillAllRegisters(lengthGPR);
+ callOperation(operationToLowerCase, lengthGPR, stringGPR, indexGPR);
+ silentFillAllRegisters(lengthGPR);
+ m_jit.exceptionCheck();
+ auto done = m_jit.jump();
+
+ loopDone.link(&m_jit);
+ m_jit.move(stringGPR, lengthGPR);
+
+ done.link(&m_jit);
+ cellResult(lengthGPR, node);
+}
+
void SpeculativeJIT::compilePeepHoleInt32Branch(Node* node, Node* branchNode, JITCompiler::RelationalCondition condition)
{
BasicBlock* taken = branchNode->branchData()->taken.block;
m_jit.setupArgumentsWithExecState(cell);
return appendCallSetResult(operation, result);
}
+ JITCompiler::Call callOperation(Jss_JITOperation_EJssUi operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallSetResult(operation, result);
+ }
JITCompiler::Call callOperation(P_JITOperation_EO operation, GPRReg result, GPRReg object)
{
m_jit.setupArgumentsWithExecState(object);
void compileCompareEqPtr(Node*);
void compileDefineDataProperty(Node*);
void compileDefineAccessorProperty(Node*);
+ void compileToLowerCase(Node*);
void moveTrueTo(GPRReg);
void moveFalseTo(GPRReg);
} }
break;
}
+
+ case ToLowerCase: {
+ compileToLowerCase(node);
+ break;
+ }
case GetByValWithThis: {
JSValueOperand base(this, node->child1());
break;
}
+ case ToLowerCase: {
+ compileToLowerCase(node);
+ break;
+ }
+
case IsObject: {
JSValueOperand value(this, node->child1());
GPRTemporary result(this, Reuse, value);
macro(ScopedArgumentsTable_length, ScopedArgumentsTable::offsetOfLength()) \
macro(StringImpl_data, StringImpl::dataOffset()) \
macro(StringImpl_hashAndFlags, StringImpl::flagsOffset()) \
+ macro(StringImpl_length, StringImpl::lengthMemoryOffset()) \
macro(Structure_classInfo, Structure::classInfoOffset()) \
macro(Structure_globalObject, Structure::globalObjectOffset()) \
macro(Structure_prototype, Structure::prototypeOffset()) \
case CompareStrictEq:
case DefineDataProperty:
case DefineAccessorProperty:
+ case ToLowerCase:
// These are OK.
break;
case Unreachable:
compileUnreachable();
break;
+ case ToLowerCase:
+ compileToLowerCase();
+ break;
case PhantomLocal:
case LoopHint:
nonSpeculativeCompare(intFunctor, fallbackFunction);
}
+ void compileToLowerCase()
+ {
+ LBasicBlock notRope = m_out.newBlock();
+ LBasicBlock is8Bit = m_out.newBlock();
+ LBasicBlock loopTop = m_out.newBlock();
+ LBasicBlock loopBody = m_out.newBlock();
+ LBasicBlock slowPath = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+
+ LValue string = lowString(m_node->child1());
+ ValueFromBlock startIndex = m_out.anchor(m_out.constInt32(0));
+ ValueFromBlock startIndexForCall = m_out.anchor(m_out.constInt32(0));
+ LValue impl = m_out.loadPtr(string, m_heaps.JSString_value);
+ m_out.branch(m_out.isZero64(impl),
+ unsure(slowPath), unsure(notRope));
+
+ LBasicBlock lastNext = m_out.appendTo(notRope, is8Bit);
+
+ m_out.branch(
+ m_out.testIsZero32(
+ m_out.load32(impl, m_heaps.StringImpl_hashAndFlags),
+ m_out.constInt32(StringImpl::flagIs8Bit())),
+ unsure(slowPath), unsure(is8Bit));
+
+ m_out.appendTo(is8Bit, loopTop);
+ LValue length = m_out.load32(impl, m_heaps.StringImpl_length);
+ LValue buffer = m_out.loadPtr(impl, m_heaps.StringImpl_data);
+ ValueFromBlock fastResult = m_out.anchor(string);
+ m_out.jump(loopTop);
+
+ m_out.appendTo(loopTop, loopBody);
+ LValue index = m_out.phi(Int32, startIndex);
+ ValueFromBlock indexFromBlock = m_out.anchor(index);
+ m_out.branch(m_out.below(index, length),
+ unsure(loopBody), unsure(continuation));
+
+ m_out.appendTo(loopBody, slowPath);
+
+ LValue byte = m_out.load8ZeroExt32(m_out.baseIndex(m_heaps.characters8, buffer, m_out.zeroExtPtr(index)));
+ LValue isInvalidAsciiRange = m_out.bitAnd(byte, m_out.constInt32(~0x7F));
+ LValue isUpperCase = m_out.belowOrEqual(m_out.sub(byte, m_out.constInt32('A')), m_out.constInt32('Z' - 'A'));
+ LValue isBadCharacter = m_out.bitOr(isInvalidAsciiRange, isUpperCase);
+ m_out.addIncomingToPhi(index, m_out.anchor(m_out.add(index, m_out.int32One)));
+ m_out.branch(isBadCharacter, unsure(slowPath), unsure(loopTop));
+
+ m_out.appendTo(slowPath, continuation);
+ LValue slowPathIndex = m_out.phi(Int32, startIndexForCall, indexFromBlock);
+ ValueFromBlock slowResult = m_out.anchor(vmCall(pointerType(), m_out.operation(operationToLowerCase), m_callFrame, string, slowPathIndex));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ setJSValue(m_out.phi(pointerType(), fastResult, slowResult));
+ }
+
void compileResolveScope()
{
UniquedStringImpl* uid = m_graph.identifiers()[m_node->identifierNumber()];
typedef StringImpl* (JIT_OPERATION *T_JITOperation_EJss)(ExecState*, JSString*);
typedef JSString* (JIT_OPERATION *Jss_JITOperation_EZ)(ExecState*, int32_t);
typedef JSString* (JIT_OPERATION *Jss_JITOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
+typedef JSString* (JIT_OPERATION *Jss_JITOperation_EJssUi)(ExecState*, JSString*, uint32_t);
// This method is used to lookup an exception hander, keyed by faultLocation, which is
// the return location from one of the calls out to one of the helper operations above.
JSMapHasIntrinsic,
JSSetHasIntrinsic,
HasOwnPropertyIntrinsic,
+ ToLowerCaseIntrinsic,
// Getter intrinsics.
TypedArrayLengthIntrinsic,
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, DontEnum, 2);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, DontEnum, 2);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, DontEnum, 2);
- JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
+ JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0, ToLowerCaseIntrinsic);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
#if ENABLE(INTL)
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringPrototypeLocaleCompareCodeGenerator, DontEnum);
+2016-10-04 Saam Barati <sbarati@apple.com>
+
+ String.prototype.toLowerCase should be a DFG/FTL intrinsic
+ https://bugs.webkit.org/show_bug.cgi?id=162887
+
+ Reviewed by Filip Pizlo and Yusuke Suzuki.
+
+ This patch exposes a new StringImpl function called convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit
+ which extracts slow path for the 8-bit part of convertToLowercaseWithoutLocale
+ into a helper function. I decided to extract this into its own function because
+ it may be the case that JSCs JITs will want to continue the operation
+ after it has already ensured that part of an 8-bit string is lower case.
+
+ * wtf/text/StringImpl.cpp:
+ (WTF::StringImpl::convertToLowercaseWithoutLocale):
+ (WTF::StringImpl::convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit):
+ * wtf/text/StringImpl.h:
+ * wtf/text/WTFString.cpp:
+ (WTF::String::convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit):
+ * wtf/text/WTFString.h:
+
2016-10-04 Chris Dumez <cdumez@apple.com>
Implement KeyboardEvent.code from the UI Event spec
Ref<StringImpl> StringImpl::convertToLowercaseWithoutLocale()
{
// Note: At one time this was a hot function in the Dromaeo benchmark, specifically the
- // no-op code path up through the first 'return' statement.
+ // no-op code path that may return ourself if we find no upper case letters and no invalid
+ // ASCII letters.
// First scan the string for uppercase and non-ASCII characters:
if (is8Bit()) {
- unsigned failingIndex;
for (unsigned i = 0; i < m_length; ++i) {
LChar character = m_data8[i];
- if (UNLIKELY((character & ~0x7F) || isASCIIUpper(character))) {
- failingIndex = i;
- goto SlowPath;
- }
- }
- return *this;
-
-SlowPath:
- LChar* data8;
- auto newImpl = createUninitializedInternalNonEmpty(m_length, data8);
-
- for (unsigned i = 0; i < failingIndex; ++i)
- data8[i] = m_data8[i];
-
- for (unsigned i = failingIndex; i < m_length; ++i) {
- LChar character = m_data8[i];
- if (!(character & ~0x7F))
- data8[i] = toASCIILower(character);
- else {
- ASSERT(u_tolower(character) <= 0xFF);
- data8[i] = static_cast<LChar>(u_tolower(character));
- }
+ if (UNLIKELY((character & ~0x7F) || isASCIIUpper(character)))
+ return convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(i);
}
- return newImpl;
+ return *this;
}
+
bool noUpper = true;
unsigned ored = 0;
return newImpl;
}
+Ref<StringImpl> StringImpl::convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(unsigned failingIndex)
+{
+ ASSERT(is8Bit());
+ LChar* data8;
+ auto newImpl = createUninitializedInternalNonEmpty(m_length, data8);
+
+ for (unsigned i = 0; i < failingIndex; ++i) {
+ ASSERT(!(m_data8[i] & ~0x7F) && !isASCIIUpper(m_data8[i]));
+ data8[i] = m_data8[i];
+ }
+
+ for (unsigned i = failingIndex; i < m_length; ++i) {
+ LChar character = m_data8[i];
+ if (!(character & ~0x7F))
+ data8[i] = toASCIILower(character);
+ else {
+ ASSERT(u_tolower(character) <= 0xFF);
+ data8[i] = static_cast<LChar>(u_tolower(character));
+ }
+ }
+
+ return newImpl;
+}
+
Ref<StringImpl> StringImpl::convertToUppercaseWithoutLocale()
{
// This function could be optimized for no-op cases the way
WTF_EXPORT_STRING_API Ref<StringImpl> convertToASCIILowercase();
WTF_EXPORT_STRING_API Ref<StringImpl> convertToASCIIUppercase();
WTF_EXPORT_STRING_API Ref<StringImpl> convertToLowercaseWithoutLocale();
+ WTF_EXPORT_STRING_API Ref<StringImpl> convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(unsigned);
WTF_EXPORT_STRING_API Ref<StringImpl> convertToUppercaseWithoutLocale();
WTF_EXPORT_STRING_API Ref<StringImpl> convertToLowercaseWithLocale(const AtomicString& localeIdentifier);
WTF_EXPORT_STRING_API Ref<StringImpl> convertToUppercaseWithLocale(const AtomicString& localeIdentifier);
return m_impl->convertToLowercaseWithoutLocale();
}
+String String::convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(unsigned failingIndex) const
+{
+ if (!m_impl)
+ return String();
+ return m_impl->convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(failingIndex);
+}
+
String String::convertToUppercaseWithoutLocale() const
{
if (!m_impl)
WTF_EXPORT_STRING_API String convertToASCIILowercase() const;
WTF_EXPORT_STRING_API String convertToASCIIUppercase() const;
WTF_EXPORT_STRING_API String convertToLowercaseWithoutLocale() const;
+ WTF_EXPORT_STRING_API String convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(unsigned) const;
WTF_EXPORT_STRING_API String convertToUppercaseWithoutLocale() const;
WTF_EXPORT_STRING_API String convertToLowercaseWithLocale(const AtomicString& localeIdentifier) const;
WTF_EXPORT_STRING_API String convertToUppercaseWithLocale(const AtomicString& localeIdentifier) const;