Changeset 252836 in webkit


Ignore:
Timestamp:
Nov 23, 2019 3:23:31 PM (4 years ago)
Author:
Ross Kirsling
Message:

[JSC] GetSubstitution is performed incorrectly via RegExp.prototype[@@replace]
https://bugs.webkit.org/show_bug.cgi?id=204490

Reviewed by Mark Lam.

JSTests:

  • stress/replace-indexed-backreferences.js: Added.
  • test262/expectations.yaml: Mark four test cases as passing.

Source/JavaScriptCore:

String.prototype.replace and RegExp.prototype[Symbol.replace] are meant to perform the same substitution
of $-backreferences (called GetSubstitution in the spec: https://tc39.es/ecma262/#sec-getsubstitution).

The implementation of this in StringPrototype.cpp is correct but the one in RegExpPrototype.js is not.
In particular, the latter *removes* backreferences with out-of-range indices, instead of leaving them as-is.

One thing that is *not* broken in either implementation and thus maintained here is the fact $10 is interpreted
as $1 followed by a 0 when we have 1 <= n < 10 captures (and analogously for other invalid $nn backreferences).
This behavior is consistent across all engines but currently described incorrectly in the spec; this patch thus
aligns with the spec PR currently open to correct this (https://github.com/tc39/ecma262/pull/1732).

  • builtins/RegExpPrototype.js:

(getSubstitution): Ensure that invalid backreferences remain untouched in the output string.
(replace): Fix off-by-one error when populating captures list. We shouldn't be reserving a slot for the full match.

Location:
trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r252800 r252836  
     12019-11-23  Ross Kirsling  <ross.kirsling@sony.com>
     2
     3        [JSC] GetSubstitution is performed incorrectly via RegExp.prototype[@@replace]
     4        https://bugs.webkit.org/show_bug.cgi?id=204490
     5
     6        Reviewed by Mark Lam.
     7
     8        * stress/replace-indexed-backreferences.js: Added.
     9        * test262/expectations.yaml: Mark four test cases as passing.
     10
    1112019-11-22  Tadeu Zagallo  <tzagallo@apple.com>
    212
  • trunk/JSTests/test262/expectations.yaml

    r252765 r252836  
    15071507  default: 'Test262Error: Expected SameValue(«», «foo[toString value]bar») to be true'
    15081508  strict mode: 'Test262Error: Expected SameValue(«», «foo[toString value]bar») to be true'
    1509 test/built-ins/RegExp/prototype/Symbol.replace/subst-capture-idx-1.js:
    1510   default: 'Test262Error: Expected SameValue(«a[cd]e», «a[cd$4$0]e») to be true'
    1511   strict mode: 'Test262Error: Expected SameValue(«a[cd]e», «a[cd$4$0]e») to be true'
    1512 test/built-ins/RegExp/prototype/Symbol.replace/subst-capture-idx-2.js:
    1513   default: 'Test262Error: Expected SameValue(«a[cd]e», «a[cd$04$00]e») to be true'
    1514   strict mode: 'Test262Error: Expected SameValue(«a[cd]e», «a[cd$04$00]e») to be true'
    15151509test/built-ins/RegExp/prototype/Symbol.search/u-lastindex-advance.js:
    15161510  default: 'Test262Error: Expected SameValue(«1», «-1») to be true'
  • trunk/Source/JavaScriptCore/ChangeLog

    r252825 r252836  
     12019-11-23  Ross Kirsling  <ross.kirsling@sony.com>
     2
     3        [JSC] GetSubstitution is performed incorrectly via RegExp.prototype[@@replace]
     4        https://bugs.webkit.org/show_bug.cgi?id=204490
     5
     6        Reviewed by Mark Lam.
     7
     8        String.prototype.replace and RegExp.prototype[Symbol.replace] are meant to perform the same substitution
     9        of $-backreferences (called GetSubstitution in the spec: https://tc39.es/ecma262/#sec-getsubstitution).
     10
     11        The implementation of this in StringPrototype.cpp is correct but the one in RegExpPrototype.js is not.
     12        In particular, the latter *removes* backreferences with out-of-range indices, instead of leaving them as-is.
     13
     14        One thing that is *not* broken in either implementation and thus maintained here is the fact $10 is interpreted
     15        as $1 followed by a 0 when we have 1 <= n < 10 captures (and analogously for other invalid $nn backreferences).
     16        This behavior is consistent across all engines but currently described incorrectly in the spec; this patch thus
     17        aligns with the spec PR currently open to correct this (https://github.com/tc39/ecma262/pull/1732).
     18
     19        * builtins/RegExpPrototype.js:
     20        (getSubstitution): Ensure that invalid backreferences remain untouched in the output string.
     21        (replace): Fix off-by-one error when populating captures list. We shouldn't be reserving a slot for the full match.
     22
    1232019-11-22  Saam Barati  <sbarati@apple.com>
    224
  • trunk/Source/JavaScriptCore/builtins/RegExpPrototype.js

    r246692 r252836  
    223223                    let chCode = ch.charCodeAt(0);
    224224                    if (chCode >= 0x30 && chCode <= 0x39) {
     225                        let originalStart = start - 1;
    225226                        start++;
     227
    226228                        let n = chCode - 0x30;
    227                         if (n > m)
     229                        if (n > m) {
     230                            result = result + replacement.substring(originalStart, start);
    228231                            break;
     232                        }
     233
    229234                        if (start < replacementLength) {
    230235                            let nextChCode = replacement.charCodeAt(start);
     
    238243                        }
    239244
    240                         if (n == 0)
     245                        if (n == 0) {
     246                            result = result + replacement.substring(originalStart, start);
    241247                            break;
    242 
    243                         if (captures[n] != @undefined)
    244                             result = result + captures[n];
     248                        }
     249
     250                        let capture = captures[n - 1];
     251                        if (capture !== @undefined)
     252                            result = result + capture;
    245253                    } else
    246254                        result = result + "$";
     
    314322            if (capN !== @undefined)
    315323                capN = @toString(capN);
    316             captures[n] = capN;
     324            captures.@push(capN);
    317325        }
    318326
     
    320328
    321329        if (functionalReplace) {
    322             let replacerArgs = [ matched ].concat(captures.slice(1));
     330            let replacerArgs = [ matched ].concat(captures);
    323331            replacerArgs.@push(position);
    324332            replacerArgs.@push(str);
Note: See TracChangeset for help on using the changeset viewer.