string-replaceAll.js 3.05 KB
Newer Older
1 2 3 4
// 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.
//
5
// Flags: --harmony-string-replaceall --allow-natives-syntax
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

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, ''));
101 102 103 104 105 106

// Tests for slow path.
assertEquals('a', 'a'.replaceAll(%ConstructConsString('abcdefghijklmn',
                                                      'def'), 'b'));
assertEquals('b', 'abcdefghijklmndef'.replaceAll(
    %ConstructConsString('abcdefghijklmn', 'def'), 'b'));
107 108 109

assertEquals('aaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaa'.replaceAll(
    %ConstructConsString('abcdefghijklmn', 'def'), 'b'));