Changeset 252683 in webkit
- Timestamp:
- Nov 19, 2019, 9:12:14 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r252680 r252683 1 2019-11-19 Ross Kirsling <ross.kirsling@sony.com> 2 3 Implement String.prototype.replaceAll 4 https://bugs.webkit.org/show_bug.cgi?id=202471 5 6 Reviewed by Yusuke Suzuki. 7 8 * stress/string-replaceall.js: Added. 9 1 10 2019-11-19 Robin Morisset <rmorisset@apple.com> 2 11 -
trunk/LayoutTests/ChangeLog
r252681 r252683 1 2019-11-19 Ross Kirsling <ross.kirsling@sony.com> 2 3 Implement String.prototype.replaceAll 4 https://bugs.webkit.org/show_bug.cgi?id=202471 5 6 Reviewed by Yusuke Suzuki. 7 8 * js/Object-getOwnPropertyNames-expected.txt: 9 * js/script-tests/Object-getOwnPropertyNames.js: 10 Grrr, why is this a layout test... 11 1 12 2019-11-19 Youenn Fablet <youenn@apple.com> 2 13 -
trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt
r246567 r252683 50 50 PASS getSortedOwnPropertyNames(Array.prototype) is ['concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toString', 'unshift', 'values'] 51 51 PASS getSortedOwnPropertyNames(String) is ['fromCharCode', 'fromCodePoint', 'length', 'name', 'prototype', 'raw'] 52 PASS getSortedOwnPropertyNames(String.prototype) is ['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', ' search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']52 PASS getSortedOwnPropertyNames(String.prototype) is ['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'replaceAll', 'search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf'] 53 53 PASS getSortedOwnPropertyNames(Boolean) is ['length', 'name', 'prototype'] 54 54 PASS getSortedOwnPropertyNames(Boolean.prototype) is ['constructor', 'toString', 'valueOf'] -
trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
r246567 r252683 59 59 "Array.prototype": "['concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toString', 'unshift', 'values']", 60 60 "String": "['fromCharCode', 'fromCodePoint', 'length', 'name', 'prototype', 'raw']", 61 "String.prototype": "['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', ' search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']",61 "String.prototype": "['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'replaceAll', 'search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']", 62 62 "Boolean": "['length', 'name', 'prototype']", 63 63 "Boolean.prototype": "['constructor', 'toString', 'valueOf']", -
trunk/Source/JavaScriptCore/ChangeLog
r252680 r252683 1 2019-11-19 Ross Kirsling <ross.kirsling@sony.com> 2 3 Implement String.prototype.replaceAll 4 https://bugs.webkit.org/show_bug.cgi?id=202471 5 6 Reviewed by Yusuke Suzuki. 7 8 Implement the stage 3 proposal here: 9 https://github.com/tc39/proposal-string-replaceall 10 11 String.prototype.replaceAll is the same as String.prototype.replace, except: 12 1. When the first argument is a string, all instances of the search string are replaced. 13 2. When the first argument is a non-global regular expression, a TypeError is thrown. 14 15 * builtins/BuiltinNames.h: 16 * builtins/StringPrototype.js: 17 (replaceAll): Added. 18 * runtime/StringPrototype.cpp: 19 (JSC::StringPrototype::finishCreation): 20 (JSC::jsSpliceSubstringsWithSeparators): Add early out for single-replacement case. 21 (JSC::replaceUsingStringSearch): Add global replacement logic, following replaceUsingRegExpSearch. 22 (JSC::replace): 23 (JSC::stringProtoFuncReplaceUsingStringSearch): 24 (JSC::stringProtoFuncReplaceAllUsingStringSearch): Added. 25 1 26 2019-11-19 Robin Morisset <rmorisset@apple.com> 2 27 -
trunk/Source/JavaScriptCore/builtins/BuiltinNames.h
r252032 r252683 133 133 macro(replaceUsingRegExp) \ 134 134 macro(replaceUsingStringSearch) \ 135 macro(replaceAllUsingStringSearch) \ 135 136 macro(makeTypeError) \ 136 137 macro(mapBucket) \ -
trunk/Source/JavaScriptCore/builtins/StringPrototype.js
r251483 r252683 260 260 } 261 261 262 function replaceAll(search, replace) 263 { 264 "use strict"; 265 266 if (@isUndefinedOrNull(this)) 267 @throwTypeError("String.prototype.replaceAll requires |this| not to be null nor undefined"); 268 269 if (search != null) { 270 if (@isRegExp(search) && !@stringIncludesInternal.@call(@toString(search.flags), "g")) 271 @throwTypeError("String.prototype.replaceAll argument must not be a non-global regular expression"); 272 273 var replacer = search.@replaceSymbol; 274 if (replacer !== @undefined) { 275 if (!@hasObservableSideEffectsForStringReplace(search, replacer)) 276 return @toString(this).@replaceUsingRegExp(search, replace); 277 return replacer.@call(search, this, replace); 278 } 279 } 280 281 var thisString = @toString(this); 282 var searchString = @toString(search); 283 return thisString.@replaceAllUsingStringSearch(searchString, replace); 284 } 285 262 286 function search(regexp) 263 287 { -
trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp
r252374 r252683 71 71 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(JSGlobalObject*, CallFrame*); 72 72 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(JSGlobalObject*, CallFrame*); 73 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceAllUsingStringSearch(JSGlobalObject*, CallFrame*); 73 74 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(JSGlobalObject*, CallFrame*); 74 75 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(JSGlobalObject*, CallFrame*); … … 98 99 /* Source for StringConstructor.lut.h 99 100 @begin stringPrototypeTable 100 concat JSBuiltin DontEnum|Function 1 101 match JSBuiltin DontEnum|Function 1 102 matchAll JSBuiltin DontEnum|Function 1 103 padStart JSBuiltin DontEnum|Function 1 104 padEnd JSBuiltin DontEnum|Function 1 105 repeat JSBuiltin DontEnum|Function 1 106 replace JSBuiltin DontEnum|Function 2 107 search JSBuiltin DontEnum|Function 1 108 split JSBuiltin DontEnum|Function 1 109 anchor JSBuiltin DontEnum|Function 1 110 big JSBuiltin DontEnum|Function 0 111 bold JSBuiltin DontEnum|Function 0 112 blink JSBuiltin DontEnum|Function 0 113 fixed JSBuiltin DontEnum|Function 0 114 fontcolor JSBuiltin DontEnum|Function 1 115 fontsize JSBuiltin DontEnum|Function 1 116 italics JSBuiltin DontEnum|Function 0 117 link JSBuiltin DontEnum|Function 1 118 small JSBuiltin DontEnum|Function 0 119 strike JSBuiltin DontEnum|Function 0 120 sub JSBuiltin DontEnum|Function 0 121 sup JSBuiltin DontEnum|Function 0 101 concat JSBuiltin DontEnum|Function 1 102 match JSBuiltin DontEnum|Function 1 103 matchAll JSBuiltin DontEnum|Function 1 104 padStart JSBuiltin DontEnum|Function 1 105 padEnd JSBuiltin DontEnum|Function 1 106 repeat JSBuiltin DontEnum|Function 1 107 replace JSBuiltin DontEnum|Function 2 108 replaceAll JSBuiltin DontEnum|Function 2 109 search JSBuiltin DontEnum|Function 1 110 split JSBuiltin DontEnum|Function 1 111 anchor JSBuiltin DontEnum|Function 1 112 big JSBuiltin DontEnum|Function 0 113 bold JSBuiltin DontEnum|Function 0 114 blink JSBuiltin DontEnum|Function 0 115 fixed JSBuiltin DontEnum|Function 0 116 fontcolor JSBuiltin DontEnum|Function 1 117 fontsize JSBuiltin DontEnum|Function 1 118 italics JSBuiltin DontEnum|Function 0 119 link JSBuiltin DontEnum|Function 1 120 small JSBuiltin DontEnum|Function 0 121 strike JSBuiltin DontEnum|Function 0 122 sub JSBuiltin DontEnum|Function 0 123 sup JSBuiltin DontEnum|Function 0 122 124 @end 123 125 */ … … 143 145 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingRegExpPrivateName(), stringProtoFuncReplaceUsingRegExp, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, StringPrototypeReplaceRegExpIntrinsic); 144 146 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingStringSearchPrivateName(), stringProtoFuncReplaceUsingStringSearch, static_cast<unsigned>(PropertyAttribute::DontEnum), 2); 147 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceAllUsingStringSearchPrivateName(), stringProtoFuncReplaceAllUsingStringSearch, static_cast<unsigned>(PropertyAttribute::DontEnum), 2); 145 148 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, StringPrototypeSliceIntrinsic); 146 149 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, static_cast<unsigned>(PropertyAttribute::DontEnum), 2); … … 395 398 } 396 399 400 if (rangeCount == 2 && separatorCount == 1) { 401 String leftPart(StringImpl::createSubstringSharingImpl(*source.impl(), substringRanges[0].position, substringRanges[0].length)); 402 String rightPart(StringImpl::createSubstringSharingImpl(*source.impl(), substringRanges[1].position, substringRanges[1].length)); 403 RELEASE_AND_RETURN(scope, jsString(globalObject, leftPart, separators[0], rightPart)); 404 } 405 397 406 Checked<int, RecordOverflow> totalLength = 0; 398 407 bool allSeparators8Bit = true; … … 768 777 } 769 778 770 static ALWAYS_INLINE JSString* replaceUsingStringSearch(VM& vm, JSGlobalObject* globalObject, JSString* jsString, JSValue searchValue, JSValue replaceValue)779 static ALWAYS_INLINE JSString* replaceUsingStringSearch(VM& vm, JSGlobalObject* globalObject, CallFrame* callFrame, JSString* jsString, JSValue searchValue, JSValue replaceValue, bool isGlobal) 771 780 { 772 781 auto scope = DECLARE_THROW_SCOPE(vm); … … 778 787 779 788 size_t matchStart = string.find(searchString); 780 781 789 if (matchStart == notFound) 782 790 return jsString; … … 784 792 CallData callData; 785 793 CallType callType = getCallData(vm, replaceValue, callData); 786 if (callType != CallType::None) { 787 MarkedArgumentBuffer args; 788 auto* substring = jsSubstring(vm, string, matchStart, searchString.impl()->length()); 794 Optional<CachedCall> cachedCall; 795 String replaceString; 796 if (callType == CallType::None) { 797 replaceString = replaceValue.toWTFString(globalObject); 789 798 RETURN_IF_EXCEPTION(scope, nullptr); 790 args.append(substring); 791 args.append(jsNumber(matchStart)); 792 args.append(jsString); 793 ASSERT(!args.hasOverflowed()); 794 replaceValue = call(globalObject, replaceValue, callType, callData, jsUndefined(), args); 795 RETURN_IF_EXCEPTION(scope, nullptr); 796 } 797 798 String replaceString = replaceValue.toWTFString(globalObject); 799 RETURN_IF_EXCEPTION(scope, nullptr); 800 801 StringImpl* stringImpl = string.impl(); 802 String leftPart(StringImpl::createSubstringSharingImpl(*stringImpl, 0, matchStart)); 803 804 size_t matchEnd = matchStart + searchString.impl()->length(); 805 int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)}; 806 String middlePart; 807 if (callType != CallType::None) 808 middlePart = replaceString; 809 else { 810 StringBuilder replacement(StringBuilder::OverflowHandler::RecordOverflow); 811 substituteBackreferences(replacement, replaceString, string, ovector, 0); 812 if (UNLIKELY(replacement.hasOverflowed())) 799 } else { 800 cachedCall.emplace(globalObject, callFrame, jsCast<JSFunction*>(replaceValue), 3); 801 cachedCall->setThis(jsUndefined()); 802 } 803 804 size_t endOfLastMatch = 0; 805 size_t searchStringLength = searchString.length(); 806 Vector<StringRange, 16> sourceRanges; 807 Vector<String, 16> replacements; 808 do { 809 if (cachedCall) { 810 auto* substring = jsSubstring(vm, string, matchStart, searchStringLength); 811 RETURN_IF_EXCEPTION(scope, nullptr); 812 cachedCall->clearArguments(); 813 cachedCall->appendArgument(substring); 814 cachedCall->appendArgument(jsNumber(matchStart)); 815 cachedCall->appendArgument(jsString); 816 if (UNLIKELY(cachedCall->hasOverflowedArguments())) 817 OUT_OF_MEMORY(globalObject, scope); 818 JSValue replacement = cachedCall->call(); 819 RETURN_IF_EXCEPTION(scope, nullptr); 820 replaceString = replacement.toWTFString(globalObject); 821 RETURN_IF_EXCEPTION(scope, nullptr); 822 } 823 824 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(endOfLastMatch, matchStart - endOfLastMatch))) 813 825 OUT_OF_MEMORY(globalObject, scope); 814 middlePart = replacement.toString(); 815 } 816 817 size_t leftLength = stringImpl->length() - matchEnd; 818 String rightPart(StringImpl::createSubstringSharingImpl(*stringImpl, matchEnd, leftLength)); 819 RELEASE_AND_RETURN(scope, JSC::jsString(globalObject, leftPart, middlePart, rightPart)); 826 827 size_t matchEnd = matchStart + searchStringLength; 828 int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)}; 829 if (cachedCall) { 830 replacements.append(replaceString); 831 RETURN_IF_EXCEPTION(scope, nullptr); 832 } else { 833 StringBuilder replacement(StringBuilder::OverflowHandler::RecordOverflow); 834 substituteBackreferences(replacement, replaceString, string, ovector, nullptr); 835 if (UNLIKELY(replacement.hasOverflowed())) 836 OUT_OF_MEMORY(globalObject, scope); 837 replacements.append(replacement.toString()); 838 } 839 840 endOfLastMatch = matchEnd; 841 if (!isGlobal) 842 break; 843 matchStart = string.find(searchString, UNLIKELY(!searchStringLength) ? endOfLastMatch + 1 : endOfLastMatch); 844 } while (matchStart != notFound); 845 846 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(endOfLastMatch, string.length() - endOfLastMatch))) 847 OUT_OF_MEMORY(globalObject, scope); 848 RELEASE_AND_RETURN(scope, jsSpliceSubstringsWithSeparators(globalObject, jsString, string, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); 820 849 } 821 850 … … 873 902 if (searchValue.inherits<RegExpObject>(vm)) 874 903 return replaceUsingRegExpSearch(vm, globalObject, callFrame, string, searchValue, replaceValue); 875 return replaceUsingStringSearch(vm, globalObject, string, searchValue, replaceValue); 904 constexpr bool isGlobal = false; 905 return replaceUsingStringSearch(vm, globalObject, callFrame, string, searchValue, replaceValue, isGlobal); 876 906 } 877 907 … … 913 943 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 914 944 915 RELEASE_AND_RETURN(scope, JSValue::encode(replaceUsingStringSearch(vm, globalObject, string, callFrame->argument(0), callFrame->argument(1)))); 945 constexpr bool isGlobal = false; 946 RELEASE_AND_RETURN(scope, JSValue::encode(replaceUsingStringSearch(vm, globalObject, callFrame, string, callFrame->argument(0), callFrame->argument(1), isGlobal))); 947 } 948 949 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceAllUsingStringSearch(JSGlobalObject* globalObject, CallFrame* callFrame) 950 { 951 VM& vm = globalObject->vm(); 952 auto scope = DECLARE_THROW_SCOPE(vm); 953 954 JSString* string = callFrame->thisValue().toString(globalObject); 955 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 956 957 constexpr bool isGlobal = true; 958 RELEASE_AND_RETURN(scope, JSValue::encode(replaceUsingStringSearch(vm, globalObject, callFrame, string, callFrame->argument(0), callFrame->argument(1), isGlobal))); 916 959 } 917 960 -
trunk/Source/WTF/ChangeLog
r252642 r252683 1 2019-11-19 Ross Kirsling <ross.kirsling@sony.com> 2 3 Implement String.prototype.replaceAll 4 https://bugs.webkit.org/show_bug.cgi?id=202471 5 6 Reviewed by Yusuke Suzuki. 7 8 * wtf/text/StringCommon.h: 9 (WTF::findCommon): 10 Fix logic: "start > length" early out should come before "empty search string" early out. 11 1 12 2019-11-19 Yusuke Suzuki <ysuzuki@apple.com> 2 13 -
trunk/Source/WTF/wtf/text/StringCommon.h
r248831 r252683 571 571 } 572 572 573 if (start > haystack.length()) 574 return notFound; 575 573 576 if (!needleLength) 574 577 return std::min(start, haystack.length()); 575 578 576 if (start > haystack.length())577 return notFound;578 579 unsigned searchLength = haystack.length() - start; 579 580 if (needleLength > searchLength)
Note:
See TracChangeset
for help on using the changeset viewer.