Changeset 202954 in webkit
- Timestamp:
- Jul 7, 2016, 8:13:11 PM (10 years ago)
- Location:
- trunk
- Files:
-
- 7 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/js/script-tests/string-repeat.js (modified) (3 diffs)
-
LayoutTests/js/string-repeat-expected.txt (modified) (3 diffs)
-
Source/JavaScriptCore/ChangeLog (modified) (1 diff)
-
Source/JavaScriptCore/builtins/StringPrototype.js (modified) (2 diffs)
-
Source/JavaScriptCore/runtime/StringPrototype.cpp (modified) (2 diffs)
-
Source/JavaScriptCore/tests/stress/string-repeat-edge-cases.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r202953 r202954 1 2016-07-07 Joseph Pecoraro <pecoraro@apple.com> 2 3 Unexpected "Out of memory" error for "x".repeat(-1) 4 https://bugs.webkit.org/show_bug.cgi?id=159529 5 6 Reviewed by Benjamin Poulain. 7 8 Extended test coverage for: 9 10 - function properties 11 - fast path with invalid counts 12 - observable side effects for fast path which were wrong before 13 14 * js/script-tests/string-repeat.js: 15 * js/string-repeat-expected.txt: 16 1 17 2016-07-07 Ryosuke Niwa <rniwa@webkit.org> 2 18 -
trunk/LayoutTests/js/script-tests/string-repeat.js
r198838 r202954 1 description("This test checks the ES6 string functions repeat()."); 1 description("This test checks String.prototype.repeat."); 2 3 shouldBe('String.prototype.repeat.length', '1'); 4 shouldBeEqualToString('String.prototype.repeat.name', 'repeat'); 5 shouldBe('Object.getOwnPropertyDescriptor(String.prototype, "repeat").configurable', 'true'); 6 shouldBe('Object.getOwnPropertyDescriptor(String.prototype, "repeat").enumerable', 'false'); 7 shouldBe('Object.getOwnPropertyDescriptor(String.prototype, "repeat").writable', 'true'); 8 shouldBe('Object.getOwnPropertyDescriptor(String.prototype, "repeat").get', 'undefined'); 9 shouldBe('Object.getOwnPropertyDescriptor(String.prototype, "repeat").set', 'undefined'); 10 shouldBe('Object.getOwnPropertyDescriptor(String.prototype, "repeat").value', 'String.prototype.repeat'); 2 11 3 12 shouldBe("'foo bar'.repeat(+0)", "''"); … … 32 41 33 42 // Check range errors. 34 shouldThrow("'foo bar'.repeat(-1)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be infinity'"); 35 shouldThrow("'foo bar'.repeat(Infinity)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be infinity'"); 36 shouldThrow("'foo bar'.repeat(-Infinity)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be infinity'"); 43 shouldThrow("'x'.repeat(-1)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity'"); 44 shouldThrow("'x'.repeat(Infinity)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity'"); 45 shouldThrow("'x'.repeat(-Infinity)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity'"); 46 shouldThrow("'foo bar'.repeat(-1)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity'"); 47 shouldThrow("'foo bar'.repeat(Infinity)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity'"); 48 shouldThrow("'foo bar'.repeat(-Infinity)", "'RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity'"); 37 49 38 50 // Check out of memory errors. … … 44 56 shouldThrow("'foo bar'.repeat(0xFFFFFFFF + 1)", "'Error: Out of memory'"); 45 57 46 // Check side effects in repeat. 47 var sideEffect = ""; 48 var stringRepeated = new String("foo bar"); 49 stringRepeated.toString = function() { 50 sideEffect += "A"; 51 return this; 58 var sideEffect, stringRepeated, count; 59 function checkSideEffects(str) { 60 // Check side effects in repeat. 61 sideEffect = ""; 62 stringRepeated = new String(str); 63 stringRepeated.toString = function() { 64 sideEffect += "A"; 65 return this; 66 } 67 count = new Number(2); 68 count.valueOf = function() { 69 sideEffect += "B"; 70 return this; 71 } 72 // Calling stringRepeated.repeat implicitly calls stringRepeated.toString(), 73 // and count.valueOf(), in that respective order. 74 shouldBe("stringRepeated.repeat(count)", "'" + str + str + "'"); 75 shouldBe("sideEffect == 'AB'", "true"); 76 77 // If stringRepeated.toString() throws an exception count.valueOf() is not called. 78 stringRepeated.toString = function() { 79 throw "error"; 80 } 81 sideEffect = ""; 82 shouldThrow("stringRepeated.repeat(count)", "'error'"); 83 shouldBe("sideEffect == ''", "true"); 84 85 // If count throws an exception stringRepeated.toString() was called. 86 stringRepeated.toString = function() { 87 sideEffect += "A"; 88 return this; 89 } 90 count.valueOf = function() { 91 throw "error"; 92 } 93 sideEffect = ""; 94 shouldThrow("stringRepeated.repeat(count)", "'error'"); 95 shouldBe("sideEffect == 'A'", "true"); 52 96 } 53 var count = new Number(2);54 count.valueOf = function() {55 sideEffect += "B";56 return this;57 }58 // Calling stringRepeated.repeat implicitly calls stringRepeated.toString(),59 // and count.valueOf(), in that respective order.60 shouldBe("stringRepeated.repeat(count)", "'foo barfoo bar'");61 shouldBe("sideEffect == 'AB'", "true");62 97 63 // If stringRepeated throws an exception count.valueOf() is not called. 64 stringRepeated.toString = function() { 65 throw "error"; 66 } 67 sideEffect = ""; 68 shouldThrow("stringRepeated.repeat(count)", "'error'"); 69 shouldBe("sideEffect == ''", "true"); 98 // Fast path for single character string. 99 checkSideEffects("x"); 70 100 71 // If count throws an exception stringRepeated.toString() was called. 72 stringRepeated.toString = function() { 73 sideEffect += "A"; 74 return this; 75 } 76 count.valueOf = function() { 77 throw "error"; 78 } 79 sideEffect = ""; 80 shouldThrow("stringRepeated.repeat(count)", "'error'"); 81 shouldBe("sideEffect == 'A'", "true"); 101 // Slow path for any other string. 102 checkSideEffects("foo bar"); -
trunk/LayoutTests/js/string-repeat-expected.txt
r198838 r202954 1 This test checks the ES6 string functions repeat().1 This test checks String.prototype.repeat. 2 2 3 3 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". 4 4 5 5 6 PASS String.prototype.repeat.length is 1 7 PASS String.prototype.repeat.name is "repeat" 8 PASS Object.getOwnPropertyDescriptor(String.prototype, "repeat").configurable is true 9 PASS Object.getOwnPropertyDescriptor(String.prototype, "repeat").enumerable is false 10 PASS Object.getOwnPropertyDescriptor(String.prototype, "repeat").writable is true 11 PASS Object.getOwnPropertyDescriptor(String.prototype, "repeat").get is undefined 12 PASS Object.getOwnPropertyDescriptor(String.prototype, "repeat").set is undefined 13 PASS Object.getOwnPropertyDescriptor(String.prototype, "repeat").value is String.prototype.repeat 6 14 PASS 'foo bar'.repeat(+0) is '' 7 15 PASS 'foo bar'.repeat(-0) is '' … … 31 39 PASS ''.repeat(0xFFFFFFFF) is '' 32 40 PASS ''.repeat(0xFFFFFFFF + 1) is '' 33 PASS 'foo bar'.repeat(-1) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be infinity. 34 PASS 'foo bar'.repeat(Infinity) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be infinity. 35 PASS 'foo bar'.repeat(-Infinity) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be infinity. 41 PASS 'x'.repeat(-1) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity. 42 PASS 'x'.repeat(Infinity) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity. 43 PASS 'x'.repeat(-Infinity) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity. 44 PASS 'foo bar'.repeat(-1) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity. 45 PASS 'foo bar'.repeat(Infinity) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity. 46 PASS 'foo bar'.repeat(-Infinity) threw exception RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity. 36 47 PASS 'f'.repeat(0xFFFFFFFF) threw exception Error: Out of memory. 37 48 PASS 'f'.repeat(0xFFFFFFFF + 1) threw exception Error: Out of memory. … … 40 51 PASS 'foo bar'.repeat(0xFFFFFFFF) threw exception Error: Out of memory. 41 52 PASS 'foo bar'.repeat(0xFFFFFFFF + 1) threw exception Error: Out of memory. 53 PASS stringRepeated.repeat(count) is 'xx' 54 PASS sideEffect == 'AB' is true 55 PASS stringRepeated.repeat(count) threw exception error. 56 PASS sideEffect == '' is true 57 PASS stringRepeated.repeat(count) threw exception error. 58 PASS sideEffect == 'A' is true 42 59 PASS stringRepeated.repeat(count) is 'foo barfoo bar' 43 60 PASS sideEffect == 'AB' is true -
trunk/Source/JavaScriptCore/ChangeLog
r202943 r202954 1 2016-07-07 Joseph Pecoraro <pecoraro@apple.com> 2 3 Unexpected "Out of memory" error for "x".repeat(-1) 4 https://bugs.webkit.org/show_bug.cgi?id=159529 5 6 Reviewed by Benjamin Poulain. 7 8 * builtins/StringPrototype.js: 9 (globalPrivate.repeatSlowPath): 10 (repeat): 11 Move the @toInteger and range checking to the always path, 12 since the spec does say it should always happen. Also remove 13 the duplication of the fast path here. 14 15 * runtime/StringPrototype.cpp: 16 (JSC::repeatCharacter): 17 Remove unused function. 18 19 (JSC::stringProtoFuncRepeatCharacter): 20 ASSERT if given a negative number. This is a private function 21 only used internally. 22 23 * tests/stress/string-repeat-edge-cases.js: 24 (shouldThrow): 25 Update expected error message. 26 1 27 2016-07-07 Benjamin Poulain <benjamin@webkit.org> 2 28 -
trunk/Source/JavaScriptCore/builtins/StringPrototype.js
r202280 r202954 52 52 "use strict"; 53 53 54 var repeatCount = @toInteger(count);55 if (repeatCount < 0 || repeatCount === @Infinity)56 throw new @RangeError("String.prototype.repeat argument must be greater than or equal to 0 and not be infinity");57 58 54 // Return an empty string. 59 if ( repeatCount === 0 || string.length === 0)55 if (count === 0 || string.length === 0) 60 56 return ""; 61 57 62 58 // Return the original string. 63 if ( repeatCount === 1)59 if (count === 1) 64 60 return string; 65 61 66 if (string.length * repeatCount > @MAX_STRING_LENGTH)62 if (string.length * count > @MAX_STRING_LENGTH) 67 63 throw new @Error("Out of memory"); 68 64 69 if (string.length === 1) { 70 // Here, |repeatCount| is always Int32. 71 return @repeatCharacter(string, repeatCount); 72 } 73 74 // Bit operation onto |repeatCount| is safe because |repeatCount| should be within Int32 range, 65 // Bit operation onto |count| is safe because |count| should be within Int32 range, 75 66 // Repeat log N times to generate the repeated string rope. 76 67 var result = ""; 77 68 var operand = string; 78 69 while (true) { 79 if ( repeatCount & 1)70 if (count & 1) 80 71 result += operand; 81 repeatCount >>= 1;82 if (! repeatCount)72 count >>= 1; 73 if (!count) 83 74 return result; 84 75 operand += operand; … … 122 113 123 114 var string = @toString(this); 115 count = @toInteger(count); 116 117 if (count < 0 || count === @Infinity) 118 throw new @RangeError("String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity"); 119 124 120 if (string.length === 1) { 125 121 var result = @repeatCharacter(string, count); -
trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp
r202916 r202954 750 750 751 751 template <typename CharacterType> 752 static inline JS Value repeatCharacter(ExecState*exec, CharacterType character, unsigned repeatCount)752 static inline JSString* repeatCharacter(ExecState& exec, CharacterType character, unsigned repeatCount) 753 753 { 754 754 CharacterType* buffer = nullptr; 755 755 auto impl = StringImpl::tryCreateUninitialized(repeatCount, buffer); 756 if (!impl) 757 return throwOutOfMemoryError(exec); 758 759 std::fill_n(buffer, repeatCount, character); 760 761 return jsString(exec, WTFMove(impl)); 762 } 763 764 template <typename CharacterType> 765 static inline JSString* repeatCharacter(ExecState& exec, CharacterType character, unsigned repeatCount) 766 { 767 CharacterType* buffer = nullptr; 768 auto impl = StringImpl::tryCreateUninitialized(repeatCount, buffer); 769 if (!impl) 770 return throwOutOfMemoryError(&exec), nullptr; 756 if (!impl) { 757 throwOutOfMemoryError(&exec); 758 return nullptr; 759 } 771 760 772 761 std::fill_n(buffer, repeatCount, character); … … 789 778 790 779 int32_t repeatCount = exec->uncheckedArgument(1).asInt32(); 780 ASSERT(repeatCount >= 0); 781 791 782 UChar character = string->view(exec)[0]; 792 783 if (!(character & ~0xff)) -
trunk/Source/JavaScriptCore/tests/stress/string-repeat-edge-cases.js
r198838 r202954 50 50 shouldThrow(() => { 51 51 String.prototype.repeat.call("", Infinity); 52 }, `RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be infinity`);52 }, `RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity`); 53 53 54 54 shouldThrow(() => { 55 55 String.prototype.repeat.call("", -2000); 56 }, `RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be infinity`);56 }, `RangeError: String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity`); 57 57 }
Note:
See TracChangeset
for help on using the changeset viewer.