v8/test/mjsunit/harmony/string-replaceAll.js
Joshua Litt 69314b4272 [replaceAll] Fix replaceAll overflow with StringCompareSequence.
Fixes a potential overflow when using the runtime's StringCompareSequence
by checking the string length first.

Bug: chromium:1032906
Change-Id: I7cb94473ae8331dd2ecf1fa98034829bebf8a9ac
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1973936
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65558}
2019-12-26 18:47:05 +00:00

110 lines
3.1 KiB
JavaScript

// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-string-replaceall --allow-natives-syntax
assertEquals('a-b-c-d', 'a+b+c+d'.replaceAll('+', '-'));
assertEquals('aaaa', 'abcd'.replaceAll(/./g, 'a'));
assertEquals('', ''.replaceAll('a', 'b'));
assertEquals('b', ''.replaceAll('', 'b'));
assertEquals('_x_x_x_', 'xxx'.replaceAll('', '_'));
assertEquals('yx', 'xxx'.replaceAll('xx', 'y'));
assertEquals('xxxx', 'xx'.replaceAll('xx', '$&$&'));
assertEquals('ii', '.+*$.+*$'.replaceAll('.+*$', 'i'));
{
// Non regexp search value with replace method.
const nonRegExpSearchValue = {
[Symbol.replace]: (string, replacer) => {
assertEquals(string, 'barbar');
assertEquals(replacer, 'moo');
return 'foo'
},
toString: () => {
// Verify toString is not called.
unreachable();
}
};
assertEquals('foo', 'barbar'.replaceAll(nonRegExpSearchValue, 'moo'));
}
{
// A custom regexp with non coercible flags.
class RegExpNonCoercibleFlags extends RegExp {
constructor() {
super();
}
static get [Symbol.species]() {
return RegExp;
}
get flags() { return null; }
};
assertThrows(
() => { assertEquals(
'foo',
'barbar'.replaceAll(new RegExpNonCoercibleFlags, 'moo')); },
TypeError);
}
{
// Non regexp search value with replace property
const nonRegExpSearchValue = {
[Symbol.replace]: "doh",
toString: () => {
// Verify toString is not called.
unreachable();
}
};
assertThrows(
() => { 'barbar'.replaceAll(nonRegExpSearchValue, 'moo'); },
TypeError);
}
{
// Non callable, non string replace value.
const nonCallableNonStringReplace = {
toString: () => {
return 'boo';
},
};
assertEquals('booboo', 'moomoo'.replaceAll('moo', nonCallableNonStringReplace));
}
{
const positions = [];
assertEquals('bcb', 'aca'.replaceAll('a',
(searchString, position, string) => {
assertEquals('a', searchString);
assertEquals('aca', string);
positions.push(position);
return 'b';
}));
assertEquals(positions, [0,2]);
}
(function NonGlobalRegex() {
assertThrows(
() => { 'ab'.replaceAll(/./, '.'); },
TypeError);
assertThrows(
() => { 'ab'.replaceAll(/./y, '.'); },
TypeError);
})();
// Tests for stickiness gotcha.
assertEquals('o ppercase!', 'No Uppercase!'.replaceAll(/[A-Z]/g, ''));
assertEquals('o Uppercase?', 'No Uppercase?'.replaceAll(/[A-Z]/gy, ''));
assertEquals(' UPPERCASE!', 'NO UPPERCASE!'.replaceAll(/[A-Z]/gy, ''));
// Tests for slow path.
assertEquals('a', 'a'.replaceAll(%ConstructConsString('abcdefghijklmn',
'def'), 'b'));
assertEquals('b', 'abcdefghijklmndef'.replaceAll(
%ConstructConsString('abcdefghijklmn', 'def'), 'b'));
assertEquals('aaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaa'.replaceAll(
%ConstructConsString('abcdefghijklmn', 'def'), 'b'));