summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/RegExpGlobalReplaceOpt.h.js
blob: 8a1137099144a514d73d27c8365f4dd629c9defe (plain)
1
2
3
4
5
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

// Function template for the following functions:
//   * RegExpGlobalReplaceOpt
//   * RegExpGlobalReplaceOptFunc
//   * RegExpGlobalReplaceOptSubst
//   * RegExpGlobalReplaceOptElemBase
// Define the following macro and include this file to declare function:
//   * FUNC_NAME     -- function name (required)
//       e.g.
//         #define FUNC_NAME RegExpGlobalReplaceOpt
// Define the following macro (without value) to switch the code:
//   * SUBSTITUTION     -- replaceValue is a string with "$"
//   * FUNCTIONAL       -- replaceValue is a function
//   * ELEMBASE         -- replaceValue is a function that returns an element
//                         of an object
//   * none of above    -- replaceValue is a string without "$"

// ES 2017 draft 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
// steps 8.b-16.
// Optimized path for @@replace with the following conditions:
//   * global flag is true
function FUNC_NAME(rx, S, lengthS, replaceValue, flags
#ifdef SUBSTITUTION
                   , firstDollarIndex
#endif
#ifdef ELEMBASE
                   , elemBase
#endif
                  )
{
    // Step 8.a.
    var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG);

    // Step 8.b.
    var lastIndex = 0;
    rx.lastIndex = 0;

#if defined(FUNCTIONAL) || defined(ELEMBASE)
    // Save the original source and flags, so we can check if the replacer
    // function recompiled the regexp.
    var originalSource = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT);
    var originalFlags = flags;
#endif

    // Step 12 (reordered).
    var accumulatedResult = "";

    // Step 13 (reordered).
    var nextSourcePosition = 0;

    // Step 11.
    while (true) {
        // Step 11.a.
        var result = RegExpMatcher(rx, S, lastIndex);

        // Step 11.b.
        if (result === null)
            break;

        // Steps 14.a-b (skipped).
        assert(result.length >= 1, "RegExpMatcher doesn't return an empty array");

        // Step 14.c.
        var matched = result[0];

        // Step 14.d.
        var matchLength = matched.length | 0;

        // Steps 14.e-f.
        var position = result.index | 0;
        lastIndex = position + matchLength;

        // Steps g-l.
        var replacement;
#if defined(FUNCTIONAL)
        replacement = RegExpGetFunctionalReplacement(result, S, position, replaceValue);
#elif defined(SUBSTITUTION)
        // Step l.i
        var namedCaptures = result.groups;
        if (namedCaptures !== undefined) {
            namedCaptures = ToObject(namedCaptures);
        }
        // Step l.ii
        replacement = RegExpGetSubstitution(result, S, position, replaceValue,
                                            firstDollarIndex, namedCaptures);
#elif defined(ELEMBASE)
        if (IsObject(elemBase)) {
            var prop = GetStringDataProperty(elemBase, matched);
            if (prop !== undefined) {
                assert(typeof prop === "string",
                       "GetStringDataProperty should return either string or undefined");
                replacement = prop;
            } else {
                elemBase = undefined;
            }
        }

        if (!IsObject(elemBase))
            replacement = RegExpGetFunctionalReplacement(result, S, position, replaceValue);
#else
        replacement = replaceValue;
#endif

        // Step 14.m.ii.
        accumulatedResult += Substring(S, nextSourcePosition,
                                       position - nextSourcePosition) + replacement;

        // Step 14.m.iii.
        nextSourcePosition = lastIndex;

        // Step 11.c.iii.2.
        if (matchLength === 0) {
            lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1;
            if (lastIndex > lengthS)
                break;
            lastIndex |= 0;
        }

#if defined(FUNCTIONAL) || defined(ELEMBASE)
        // Ensure the current source and flags match the original regexp, the
        // replaceValue function may have called RegExp#compile.
        if (UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT) !== originalSource ||
            UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT) !== originalFlags)
        {
            rx = regexp_construct_raw_flags(originalSource, originalFlags);
        }
#endif
    }

    // Step 15.
    if (nextSourcePosition >= lengthS)
        return accumulatedResult;

    // Step 16.
    return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition);
}