| Differences between
and this patch
- a/Source/JavaScriptCore/ChangeLog +43 lines
Lines 1-3 a/Source/JavaScriptCore/ChangeLog_sec1
1
2015-11-02  Keith Miller  <keith_miller@apple.com>
2
3
        [ES6] Add support for Symbol.replace.
4
        https://bugs.webkit.org/show_bug.cgi?id=150812
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        This patch adds support for Symbol.replace. This patch changes
9
        String.prototype.replace so it now calls the Symbol.replace property
10
        on the first argument. The underlying behavior for both a string
11
        first argument and a non-string first argument remains unchanged.
12
        In a future patch we need to update the code to reflectthe ES6 spec.
13
        Finally, most of the replace helper code has been moved to
14
        RegExpPrototype.cpp; partly, because the code was no longer relevent
15
        but also because some of the code is shared but inherently tied to
16
        regular expressions.
17
18
        * runtime/CommonIdentifiers.h:
19
        * runtime/JSGlobalObject.cpp:
20
        (JSC::JSGlobalObject::init):
21
        * runtime/RegExpPrototype.cpp:
22
        (JSC::RegExpPrototype::finishCreation):
23
        (JSC::StringRange::StringRange):
24
        (JSC::substituteBackreferencesSlow):
25
        (JSC::jsSpliceSubstrings):
26
        (JSC::jsSpliceSubstringsWithSeparators):
27
        (JSC::removeUsingRegExpSearch):
28
        (JSC::regExpProtoFuncReplaceSymbol):
29
        * runtime/RegExpPrototype.h:
30
        (JSC::RegExpPrototype::create):
31
        (JSC::substituteBackreferences):
32
        * runtime/StringPrototype.cpp:
33
        (JSC::replaceUsingStringSearch):
34
        (JSC::stringProtoFuncReplace):
35
        (JSC::substituteBackreferencesSlow): Deleted.
36
        (JSC::substituteBackreferences): Deleted.
37
        (JSC::StringRange::StringRange): Deleted.
38
        (JSC::jsSpliceSubstrings): Deleted.
39
        (JSC::jsSpliceSubstringsWithSeparators): Deleted.
40
        (JSC::removeUsingRegExpSearch): Deleted.
41
        (JSC::replaceUsingRegExpSearch): Deleted.
42
        * tests/es6.yaml:
43
1
2015-10-16  Keith Miller  <keith_miller@apple.com>
44
2015-10-16  Keith Miller  <keith_miller@apple.com>
2
45
3
        Unreviewed. Build fix for 191215.
46
        Unreviewed. Build fix for 191215.
- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h -2 / +3 lines
Lines 245-251 a/Source/JavaScriptCore/runtime/CommonIdentifiers.h_sec1
245
    macro(hasInstance) \
245
    macro(hasInstance) \
246
    macro(isConcatSpreadable) \
246
    macro(isConcatSpreadable) \
247
    macro(match) \
247
    macro(match) \
248
    macro(replace) \
249
    macro(search) \
248
    macro(search) \
250
    macro(species) \
249
    macro(species) \
251
    macro(split) \
250
    macro(split) \
Lines 254-260 a/Source/JavaScriptCore/runtime/CommonIdentifiers.h_sec2
254
253
255
#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \
254
#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \
256
    macro(iterator) \
255
    macro(iterator) \
257
    macro(unscopables)
256
    macro(unscopables) \
257
    macro(replace)
258
258
259
259
#define JSC_COMMON_BYTECODE_INTRINSICS_EACH_NAME(macro) \
260
#define JSC_COMMON_BYTECODE_INTRINSICS_EACH_NAME(macro) \
260
    macro(putByValDirect) \
261
    macro(putByValDirect) \
- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp -1 / +1 lines
Lines 354-360 void JSGlobalObject::init(VM& vm) a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp_sec1
354
354
355
    RegExp* emptyRegex = RegExp::create(vm, "", NoFlags);
355
    RegExp* emptyRegex = RegExp::create(vm, "", NoFlags);
356
    
356
    
357
    m_regExpPrototype.set(vm, this, RegExpPrototype::create(vm, RegExpPrototype::createStructure(vm, this, m_objectPrototype.get()), emptyRegex));
357
    m_regExpPrototype.set(vm, this, RegExpPrototype::create(vm, this, RegExpPrototype::createStructure(vm, this, m_objectPrototype.get()), emptyRegex));
358
    m_regExpStructure.set(vm, this, RegExpObject::createStructure(vm, this, m_regExpPrototype.get()));
358
    m_regExpStructure.set(vm, this, RegExpObject::createStructure(vm, this, m_regExpPrototype.get()));
359
    m_regExpMatchesArrayStructure.set(vm, this, createRegExpMatchesArrayStructure(vm, *this));
359
    m_regExpMatchesArrayStructure.set(vm, this, createRegExpMatchesArrayStructure(vm, *this));
360
360
- a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp +463 lines
Lines 22-27 a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp_sec1
22
#include "RegExpPrototype.h"
22
#include "RegExpPrototype.h"
23
23
24
#include "ArrayPrototype.h"
24
#include "ArrayPrototype.h"
25
#include "CachedCall.h"
25
#include "Error.h"
26
#include "Error.h"
26
#include "JSArray.h"
27
#include "JSArray.h"
27
#include "JSCJSValue.h"
28
#include "JSCJSValue.h"
Lines 32-37 a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp_sec2
32
#include "Lexer.h"
33
#include "Lexer.h"
33
#include "ObjectPrototype.h"
34
#include "ObjectPrototype.h"
34
#include "JSCInlines.h"
35
#include "JSCInlines.h"
36
#include "RegExpConstructor.h"
35
#include "RegExpObject.h"
37
#include "RegExpObject.h"
36
#include "RegExp.h"
38
#include "RegExp.h"
37
#include "RegExpCache.h"
39
#include "RegExpCache.h"
Lines 43-48 static EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState*); a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp_sec3
43
static EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState*);
45
static EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState*);
44
static EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState*);
46
static EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState*);
45
static EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState*);
47
static EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState*);
48
static EncodedJSValue JSC_HOST_CALL regExpProtoFuncReplaceSymbol(ExecState*);
46
static EncodedJSValue JSC_HOST_CALL regExpProtoGetterGlobal(ExecState*);
49
static EncodedJSValue JSC_HOST_CALL regExpProtoGetterGlobal(ExecState*);
47
static EncodedJSValue JSC_HOST_CALL regExpProtoGetterIgnoreCase(ExecState*);
50
static EncodedJSValue JSC_HOST_CALL regExpProtoGetterIgnoreCase(ExecState*);
48
static EncodedJSValue JSC_HOST_CALL regExpProtoGetterMultiline(ExecState*);
51
static EncodedJSValue JSC_HOST_CALL regExpProtoGetterMultiline(ExecState*);
Lines 81-88 bool RegExpPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, Prop a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp_sec4
81
    return getStaticFunctionSlot<Base>(exec, regExpPrototypeTable, jsCast<RegExpPrototype*>(object), propertyName, slot);
84
    return getStaticFunctionSlot<Base>(exec, regExpPrototypeTable, jsCast<RegExpPrototype*>(object), propertyName, slot);
82
}
85
}
83
86
87
void RegExpPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
88
{
89
    Base::finishCreation(vm);
90
    
91
    JSC_NATIVE_FUNCTION(vm.propertyNames->replaceSymbol, regExpProtoFuncReplaceSymbol, DontEnum | DontDelete | ReadOnly, 2);
92
}
93
84
// ------------------------------ Functions ---------------------------
94
// ------------------------------ Functions ---------------------------
85
95
96
97
struct StringRange {
98
    StringRange(int pos, int len)
99
        : position(pos)
100
        , length(len)
101
    {
102
    }
103
104
    StringRange()
105
    {
106
    }
107
108
    int position;
109
    int length;
110
};
111
112
String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i)
113
{
114
    StringBuilder substitutedReplacement;
115
    int offset = 0;
116
    do {
117
        if (i + 1 == replacement.length())
118
            break;
119
120
        UChar ref = replacement[i + 1];
121
        if (ref == '$') {
122
            // "$$" -> "$"
123
            ++i;
124
            substitutedReplacement.append(replacement.substring(offset, i - offset));
125
            offset = i + 1;
126
            continue;
127
        }
128
129
        int backrefStart;
130
        int backrefLength;
131
        int advance = 0;
132
        if (ref == '&') {
133
            backrefStart = ovector[0];
134
            backrefLength = ovector[1] - backrefStart;
135
        } else if (ref == '`') {
136
            backrefStart = 0;
137
            backrefLength = ovector[0];
138
        } else if (ref == '\'') {
139
            backrefStart = ovector[1];
140
            backrefLength = source.length() - backrefStart;
141
        } else if (reg && ref >= '0' && ref <= '9') {
142
            // 1- and 2-digit back references are allowed
143
            unsigned backrefIndex = ref - '0';
144
            if (backrefIndex > reg->numSubpatterns())
145
                continue;
146
            if (replacement.length() > i + 2) {
147
                ref = replacement[i + 2];
148
                if (ref >= '0' && ref <= '9') {
149
                    backrefIndex = 10 * backrefIndex + ref - '0';
150
                    if (backrefIndex > reg->numSubpatterns())
151
                        backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference
152
                    else
153
                        advance = 1;
154
                }
155
            }
156
            if (!backrefIndex)
157
                continue;
158
            backrefStart = ovector[2 * backrefIndex];
159
            backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
160
        } else
161
            continue;
162
163
        if (i - offset)
164
            substitutedReplacement.append(replacement.substring(offset, i - offset));
165
        i += 1 + advance;
166
        offset = i + 1;
167
        if (backrefStart >= 0)
168
            substitutedReplacement.append(source.substring(backrefStart, backrefLength));
169
    } while ((i = replacement.find('$', i + 1)) != notFound);
170
171
    if (replacement.length() - offset)
172
        substitutedReplacement.append(replacement.substring(offset));
173
174
    return substitutedReplacement.toString();
175
}
176
177
static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount)
178
{
179
    if (rangeCount == 1) {
180
        int sourceSize = source.length();
181
        int position = substringRanges[0].position;
182
        int length = substringRanges[0].length;
183
        if (position <= 0 && length >= sourceSize)
184
            return sourceVal;
185
        // We could call String::substringSharingImpl(), but this would result in redundant checks.
186
        return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
187
    }
188
189
    int totalLength = 0;
190
    for (int i = 0; i < rangeCount; i++)
191
        totalLength += substringRanges[i].length;
192
193
    if (!totalLength)
194
        return jsEmptyString(exec);
195
196
    if (source.is8Bit()) {
197
        LChar* buffer;
198
        const LChar* sourceData = source.characters8();
199
        RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
200
        if (!impl)
201
            return throwOutOfMemoryError(exec);
202
203
        int bufferPos = 0;
204
        for (int i = 0; i < rangeCount; i++) {
205
            if (int srcLen = substringRanges[i].length) {
206
                StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
207
                bufferPos += srcLen;
208
            }
209
        }
210
211
        return jsString(exec, impl.release());
212
    }
213
214
    UChar* buffer;
215
    const UChar* sourceData = source.characters16();
216
217
    RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
218
    if (!impl)
219
        return throwOutOfMemoryError(exec);
220
221
    int bufferPos = 0;
222
    for (int i = 0; i < rangeCount; i++) {
223
        if (int srcLen = substringRanges[i].length) {
224
            StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
225
            bufferPos += srcLen;
226
        }
227
    }
228
229
    return jsString(exec, impl.release());
230
}
231
232
static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount)
233
{
234
    if (rangeCount == 1 && !separatorCount) {
235
        int sourceSize = source.length();
236
        int position = substringRanges[0].position;
237
        int length = substringRanges[0].length;
238
        if (position <= 0 && length >= sourceSize)
239
            return sourceVal;
240
        // We could call String::substringSharingImpl(), but this would result in redundant checks.
241
        return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
242
    }
243
244
    Checked<int, RecordOverflow> totalLength = 0;
245
    bool allSeparators8Bit = true;
246
    for (int i = 0; i < rangeCount; i++)
247
        totalLength += substringRanges[i].length;
248
    for (int i = 0; i < separatorCount; i++) {
249
        totalLength += separators[i].length();
250
        if (separators[i].length() && !separators[i].is8Bit())
251
            allSeparators8Bit = false;
252
    }
253
    if (totalLength.hasOverflowed())
254
        return throwOutOfMemoryError(exec);
255
256
    if (!totalLength)
257
        return jsEmptyString(exec);
258
259
    if (source.is8Bit() && allSeparators8Bit) {
260
        LChar* buffer;
261
        const LChar* sourceData = source.characters8();
262
263
        RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
264
        if (!impl)
265
            return throwOutOfMemoryError(exec);
266
267
        int maxCount = std::max(rangeCount, separatorCount);
268
        int bufferPos = 0;
269
        for (int i = 0; i < maxCount; i++) {
270
            if (i < rangeCount) {
271
                if (int srcLen = substringRanges[i].length) {
272
                    StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
273
                    bufferPos += srcLen;
274
                }
275
            }
276
            if (i < separatorCount) {
277
                if (int sepLen = separators[i].length()) {
278
                    StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
279
                    bufferPos += sepLen;
280
                }
281
            }
282
        }
283
284
        return jsString(exec, impl.release());
285
    }
286
287
    UChar* buffer;
288
    RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
289
    if (!impl)
290
        return throwOutOfMemoryError(exec);
291
292
    int maxCount = std::max(rangeCount, separatorCount);
293
    int bufferPos = 0;
294
    for (int i = 0; i < maxCount; i++) {
295
        if (i < rangeCount) {
296
            if (int srcLen = substringRanges[i].length) {
297
                if (source.is8Bit())
298
                    StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen);
299
                else
300
                    StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen);
301
                bufferPos += srcLen;
302
            }
303
        }
304
        if (i < separatorCount) {
305
            if (int sepLen = separators[i].length()) {
306
                if (separators[i].is8Bit())
307
                    StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
308
                else
309
                    StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen);
310
                bufferPos += sepLen;
311
            }
312
        }
313
    }
314
    
315
    return jsString(exec, impl.release());
316
}
317
318
static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp)
319
{
320
    size_t lastIndex = 0;
321
    unsigned startPosition = 0;
322
323
    Vector<StringRange, 16> sourceRanges;
324
    VM* vm = &exec->vm();
325
    RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
326
    unsigned sourceLen = source.length();
327
328
    while (true) {
329
        MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition);
330
        if (!result)
331
            break;
332
333
        if (lastIndex < result.start)
334
            sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
335
336
        lastIndex = result.end;
337
        startPosition = lastIndex;
338
339
        // special case of empty match
340
        if (result.empty()) {
341
            startPosition++;
342
            if (startPosition > sourceLen)
343
                break;
344
        }
345
    }
346
347
    if (!lastIndex)
348
        return JSValue::encode(string);
349
350
    if (static_cast<unsigned>(lastIndex) < sourceLen)
351
        sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
352
    
353
    return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
354
}
355
356
EncodedJSValue JSC_HOST_CALL regExpProtoFuncReplaceSymbol(ExecState* exec)
357
{
358
    // FIXME: This function should probably be reimplemented in JS when updating to ES6 as it will
359
    // need to perform a lot of [[Get]]s.
360
361
    JSValue searchValue = exec->thisValue();
362
    if (!jsDynamicCast<RegExpObject*>(searchValue))
363
        return throwVMTypeError(exec, "RegExp.prototype[Symbol.replace] expected |this| to be a RegExp object.");
364
    JSString* string = exec->argument(0).toString(exec);
365
    JSValue replaceValue = exec->argument(1);
366
367
    String replacementString;
368
    CallData callData;
369
    CallType callType = getCallData(replaceValue, callData);
370
    if (callType == CallTypeNone)
371
        replacementString = replaceValue.toString(exec)->value(exec);
372
373
    const String& source = string->value(exec);
374
    unsigned sourceLen = source.length();
375
    if (exec->hadException())
376
        return JSValue::encode(JSValue());
377
    RegExpObject* regExpObject = asRegExpObject(searchValue);
378
    RegExp* regExp = regExpObject->regExp();
379
    bool global = regExp->global();
380
381
    if (global) {
382
        // ES5.1 15.5.4.10 step 8.a.
383
        regExpObject->setLastIndex(exec, 0);
384
        if (exec->hadException())
385
            return JSValue::encode(JSValue());
386
387
        if (callType == CallTypeNone && !replacementString.length())
388
            return removeUsingRegExpSearch(exec, string, source, regExp);
389
    }
390
391
    RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
392
393
    size_t lastIndex = 0;
394
    unsigned startPosition = 0;
395
396
    Vector<StringRange, 16> sourceRanges;
397
    Vector<String, 16> replacements;
398
399
    // This is either a loop (if global is set) or a one-way (if not).
400
    if (global && callType == CallTypeJS) {
401
        // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string
402
        int argCount = regExp->numSubpatterns() + 1 + 2;
403
        JSFunction* func = jsCast<JSFunction*>(replaceValue);
404
        CachedCall cachedCall(exec, func, argCount);
405
        if (exec->hadException())
406
            return JSValue::encode(jsNull());
407
        VM* vm = &exec->vm();
408
        if (source.is8Bit()) {
409
            while (true) {
410
                int* ovector;
411
                MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
412
                if (!result)
413
                    break;
414
415
                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
416
417
                unsigned i = 0;
418
                for (; i < regExp->numSubpatterns() + 1; ++i) {
419
                    int matchStart = ovector[i * 2];
420
                    int matchLen = ovector[i * 2 + 1] - matchStart;
421
422
                    if (matchStart < 0)
423
                        cachedCall.setArgument(i, jsUndefined());
424
                    else
425
                        cachedCall.setArgument(i, jsSubstring8(vm, source, matchStart, matchLen));
426
                }
427
428
                cachedCall.setArgument(i++, jsNumber(result.start));
429
                cachedCall.setArgument(i++, string);
430
431
                cachedCall.setThis(jsUndefined());
432
                JSValue jsResult = cachedCall.call();
433
                replacements.append(jsResult.toString(exec)->value(exec));
434
                if (exec->hadException())
435
                    break;
436
437
                lastIndex = result.end;
438
                startPosition = lastIndex;
439
440
                // special case of empty match
441
                if (result.empty()) {
442
                    startPosition++;
443
                    if (startPosition > sourceLen)
444
                        break;
445
                }
446
            }
447
        } else {
448
            while (true) {
449
                int* ovector;
450
                MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
451
                if (!result)
452
                    break;
453
454
                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
455
456
                unsigned i = 0;
457
                for (; i < regExp->numSubpatterns() + 1; ++i) {
458
                    int matchStart = ovector[i * 2];
459
                    int matchLen = ovector[i * 2 + 1] - matchStart;
460
461
                    if (matchStart < 0)
462
                        cachedCall.setArgument(i, jsUndefined());
463
                    else
464
                        cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen));
465
                }
466
467
                cachedCall.setArgument(i++, jsNumber(result.start));
468
                cachedCall.setArgument(i++, string);
469
470
                cachedCall.setThis(jsUndefined());
471
                JSValue jsResult = cachedCall.call();
472
                replacements.append(jsResult.toString(exec)->value(exec));
473
                if (exec->hadException())
474
                    break;
475
476
                lastIndex = result.end;
477
                startPosition = lastIndex;
478
479
                // special case of empty match
480
                if (result.empty()) {
481
                    startPosition++;
482
                    if (startPosition > sourceLen)
483
                        break;
484
                }
485
            }
486
        }
487
    } else {
488
        VM* vm = &exec->vm();
489
        do {
490
            int* ovector;
491
            MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
492
            if (!result)
493
                break;
494
495
            if (callType != CallTypeNone) {
496
                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
497
498
                MarkedArgumentBuffer args;
499
500
                for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
501
                    int matchStart = ovector[i * 2];
502
                    int matchLen = ovector[i * 2 + 1] - matchStart;
503
504
                    if (matchStart < 0)
505
                        args.append(jsUndefined());
506
                    else
507
                        args.append(jsSubstring(exec, source, matchStart, matchLen));
508
                }
509
510
                args.append(jsNumber(result.start));
511
                args.append(string);
512
513
                replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec));
514
                if (exec->hadException())
515
                    break;
516
            } else {
517
                int replLen = replacementString.length();
518
                if (lastIndex < result.start || replLen) {
519
                    sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
520
521
                    if (replLen)
522
                        replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
523
                    else
524
                        replacements.append(String());
525
                }
526
            }
527
528
            lastIndex = result.end;
529
            startPosition = lastIndex;
530
531
            // special case of empty match
532
            if (result.empty()) {
533
                startPosition++;
534
                if (startPosition > sourceLen)
535
                    break;
536
            }
537
        } while (global);
538
    }
539
540
    if (!lastIndex && replacements.isEmpty())
541
        return JSValue::encode(string);
542
543
    if (static_cast<unsigned>(lastIndex) < sourceLen)
544
        sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
545
546
    return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
547
}
548
86
EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec)
549
EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec)
87
{
550
{
88
    JSValue thisValue = exec->thisValue();
551
    JSValue thisValue = exec->thisValue();
- a/Source/JavaScriptCore/runtime/RegExpPrototype.h -2 / +14 lines
Lines 31-40 public: a/Source/JavaScriptCore/runtime/RegExpPrototype.h_sec1
31
    typedef RegExpObject Base;
31
    typedef RegExpObject Base;
32
    static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot;
32
    static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot;
33
33
34
    static RegExpPrototype* create(VM& vm, Structure* structure, RegExp* regExp)
34
    static RegExpPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure, RegExp* regExp)
35
    {
35
    {
36
        RegExpPrototype* prototype = new (NotNull, allocateCell<RegExpPrototype>(vm.heap)) RegExpPrototype(vm, structure, regExp);
36
        RegExpPrototype* prototype = new (NotNull, allocateCell<RegExpPrototype>(vm.heap)) RegExpPrototype(vm, structure, regExp);
37
        prototype->finishCreation(vm);
37
        prototype->finishCreation(vm, globalObject);
38
        return prototype;
38
        return prototype;
39
    }
39
    }
40
40
Lines 50-57 protected: a/Source/JavaScriptCore/runtime/RegExpPrototype.h_sec2
50
50
51
private:
51
private:
52
    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
52
    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
53
    void finishCreation(VM&, JSGlobalObject*);
53
};
54
};
54
55
56
String NEVER_INLINE substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp*, size_t);
57
58
inline String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
59
{
60
    size_t i = replacement.find('$');
61
    if (UNLIKELY(i != notFound))
62
        return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
63
64
    return replacement;
65
}
66
55
} // namespace JSC
67
} // namespace JSC
56
68
57
#endif // RegExpPrototype_h
69
#endif // RegExpPrototype_h
- a/Source/JavaScriptCore/runtime/StringPrototype.cpp -459 / +26 lines
Lines 41-46 a/Source/JavaScriptCore/runtime/StringPrototype.cpp_sec1
41
#include "RegExpConstructor.h"
41
#include "RegExpConstructor.h"
42
#include "RegExpMatchesArray.h"
42
#include "RegExpMatchesArray.h"
43
#include "RegExpObject.h"
43
#include "RegExpObject.h"
44
#include "RegExpPrototype.h"
44
#include <algorithm>
45
#include <algorithm>
45
#include <wtf/ASCIICType.h>
46
#include <wtf/ASCIICType.h>
46
#include <wtf/MathExtras.h>
47
#include <wtf/MathExtras.h>
Lines 185-644 static inline JSString* jsSubstring(ExecState* exec, JSValue originalValue, cons a/Source/JavaScriptCore/runtime/StringPrototype.cpp_sec2
185
    return jsSubstring(exec, string, offset, length);
186
    return jsSubstring(exec, string, offset, length);
186
}
187
}
187
188
188
static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i)
189
static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
189
{
190
    StringBuilder substitutedReplacement;
191
    int offset = 0;
192
    do {
193
        if (i + 1 == replacement.length())
194
            break;
195
196
        UChar ref = replacement[i + 1];
197
        if (ref == '$') {
198
            // "$$" -> "$"
199
            ++i;
200
            substitutedReplacement.append(replacement.substring(offset, i - offset));
201
            offset = i + 1;
202
            continue;
203
        }
204
205
        int backrefStart;
206
        int backrefLength;
207
        int advance = 0;
208
        if (ref == '&') {
209
            backrefStart = ovector[0];
210
            backrefLength = ovector[1] - backrefStart;
211
        } else if (ref == '`') {
212
            backrefStart = 0;
213
            backrefLength = ovector[0];
214
        } else if (ref == '\'') {
215
            backrefStart = ovector[1];
216
            backrefLength = source.length() - backrefStart;
217
        } else if (reg && ref >= '0' && ref <= '9') {
218
            // 1- and 2-digit back references are allowed
219
            unsigned backrefIndex = ref - '0';
220
            if (backrefIndex > reg->numSubpatterns())
221
                continue;
222
            if (replacement.length() > i + 2) {
223
                ref = replacement[i + 2];
224
                if (ref >= '0' && ref <= '9') {
225
                    backrefIndex = 10 * backrefIndex + ref - '0';
226
                    if (backrefIndex > reg->numSubpatterns())
227
                        backrefIndex = backrefIndex / 10;   // Fall back to the 1-digit reference
228
                    else
229
                        advance = 1;
230
                }
231
            }
232
            if (!backrefIndex)
233
                continue;
234
            backrefStart = ovector[2 * backrefIndex];
235
            backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
236
        } else
237
            continue;
238
239
        if (i - offset)
240
            substitutedReplacement.append(replacement.substring(offset, i - offset));
241
        i += 1 + advance;
242
        offset = i + 1;
243
        if (backrefStart >= 0)
244
            substitutedReplacement.append(source.substring(backrefStart, backrefLength));
245
    } while ((i = replacement.find('$', i + 1)) != notFound);
246
247
    if (replacement.length() - offset)
248
        substitutedReplacement.append(replacement.substring(offset));
249
250
    return substitutedReplacement.toString();
251
}
252
253
static inline String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
254
{
255
    size_t i = replacement.find('$');
256
    if (UNLIKELY(i != notFound))
257
        return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
258
259
    return replacement;
260
}
261
262
struct StringRange {
263
    StringRange(int pos, int len)
264
        : position(pos)
265
        , length(len)
266
    {
267
    }
268
269
    StringRange()
270
    {
271
    }
272
273
    int position;
274
    int length;
275
};
276
277
static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount)
278
{
279
    if (rangeCount == 1) {
280
        int sourceSize = source.length();
281
        int position = substringRanges[0].position;
282
        int length = substringRanges[0].length;
283
        if (position <= 0 && length >= sourceSize)
284
            return sourceVal;
285
        // We could call String::substringSharingImpl(), but this would result in redundant checks.
286
        return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
287
    }
288
289
    int totalLength = 0;
290
    for (int i = 0; i < rangeCount; i++)
291
        totalLength += substringRanges[i].length;
292
293
    if (!totalLength)
294
        return jsEmptyString(exec);
295
296
    if (source.is8Bit()) {
297
        LChar* buffer;
298
        const LChar* sourceData = source.characters8();
299
        RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
300
        if (!impl)
301
            return throwOutOfMemoryError(exec);
302
303
        int bufferPos = 0;
304
        for (int i = 0; i < rangeCount; i++) {
305
            if (int srcLen = substringRanges[i].length) {
306
                StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
307
                bufferPos += srcLen;
308
            }
309
        }
310
311
        return jsString(exec, impl.release());
312
    }
313
314
    UChar* buffer;
315
    const UChar* sourceData = source.characters16();
316
317
    RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
318
    if (!impl)
319
        return throwOutOfMemoryError(exec);
320
321
    int bufferPos = 0;
322
    for (int i = 0; i < rangeCount; i++) {
323
        if (int srcLen = substringRanges[i].length) {
324
            StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
325
            bufferPos += srcLen;
326
        }
327
    }
328
329
    return jsString(exec, impl.release());
330
}
331
332
static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount)
333
{
334
    if (rangeCount == 1 && separatorCount == 0) {
335
        int sourceSize = source.length();
336
        int position = substringRanges[0].position;
337
        int length = substringRanges[0].length;
338
        if (position <= 0 && length >= sourceSize)
339
            return sourceVal;
340
        // We could call String::substringSharingImpl(), but this would result in redundant checks.
341
        return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
342
    }
343
344
    Checked<int, RecordOverflow> totalLength = 0;
345
    bool allSeparators8Bit = true;
346
    for (int i = 0; i < rangeCount; i++)
347
        totalLength += substringRanges[i].length;
348
    for (int i = 0; i < separatorCount; i++) {
349
        totalLength += separators[i].length();
350
        if (separators[i].length() && !separators[i].is8Bit())
351
            allSeparators8Bit = false;
352
    }
353
    if (totalLength.hasOverflowed())
354
        return throwOutOfMemoryError(exec);
355
356
    if (!totalLength)
357
        return jsEmptyString(exec);
358
359
    if (source.is8Bit() && allSeparators8Bit) {
360
        LChar* buffer;
361
        const LChar* sourceData = source.characters8();
362
363
        RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
364
        if (!impl)
365
            return throwOutOfMemoryError(exec);
366
367
        int maxCount = std::max(rangeCount, separatorCount);
368
        int bufferPos = 0;
369
        for (int i = 0; i < maxCount; i++) {
370
            if (i < rangeCount) {
371
                if (int srcLen = substringRanges[i].length) {
372
                    StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
373
                    bufferPos += srcLen;
374
                }
375
            }
376
            if (i < separatorCount) {
377
                if (int sepLen = separators[i].length()) {
378
                    StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
379
                    bufferPos += sepLen;
380
                }
381
            }
382
        }        
383
384
        return jsString(exec, impl.release());
385
    }
386
387
    UChar* buffer;
388
    RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
389
    if (!impl)
390
        return throwOutOfMemoryError(exec);
391
392
    int maxCount = std::max(rangeCount, separatorCount);
393
    int bufferPos = 0;
394
    for (int i = 0; i < maxCount; i++) {
395
        if (i < rangeCount) {
396
            if (int srcLen = substringRanges[i].length) {
397
                if (source.is8Bit())
398
                    StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen);
399
                else
400
                    StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen);
401
                bufferPos += srcLen;
402
            }
403
        }
404
        if (i < separatorCount) {
405
            if (int sepLen = separators[i].length()) {
406
                if (separators[i].is8Bit())
407
                    StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
408
                else
409
                    StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen);
410
                bufferPos += sepLen;
411
            }
412
        }
413
    }
414
415
    return jsString(exec, impl.release());
416
}
417
418
static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp)
419
{
420
    size_t lastIndex = 0;
421
    unsigned startPosition = 0;
422
423
    Vector<StringRange, 16> sourceRanges;
424
    VM* vm = &exec->vm();
425
    RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
426
    unsigned sourceLen = source.length();
427
428
    while (true) {
429
        MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition);
430
        if (!result)
431
            break;
432
433
        if (lastIndex < result.start)
434
            sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
435
436
        lastIndex = result.end;
437
        startPosition = lastIndex;
438
439
        // special case of empty match
440
        if (result.empty()) {
441
            startPosition++;
442
            if (startPosition > sourceLen)
443
                break;
444
        }
445
    }
446
447
    if (!lastIndex)
448
        return JSValue::encode(string);
449
450
    if (static_cast<unsigned>(lastIndex) < sourceLen)
451
        sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
452
453
    return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
454
}
455
456
static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue)
457
{
458
    JSValue replaceValue = exec->argument(1);
459
    String replacementString;
460
    CallData callData;
461
    CallType callType = getCallData(replaceValue, callData);
462
    if (callType == CallTypeNone)
463
        replacementString = replaceValue.toString(exec)->value(exec);
464
465
    const String& source = string->value(exec);
466
    unsigned sourceLen = source.length();
467
    if (exec->hadException())
468
        return JSValue::encode(JSValue());
469
    RegExpObject* regExpObject = asRegExpObject(searchValue);
470
    RegExp* regExp = regExpObject->regExp();
471
    bool global = regExp->global();
472
473
    if (global) {
474
        // ES5.1 15.5.4.10 step 8.a.
475
        regExpObject->setLastIndex(exec, 0);
476
        if (exec->hadException())
477
            return JSValue::encode(JSValue());
478
479
        if (callType == CallTypeNone && !replacementString.length())
480
            return removeUsingRegExpSearch(exec, string, source, regExp);
481
    }
482
483
    RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
484
485
    size_t lastIndex = 0;
486
    unsigned startPosition = 0;
487
488
    Vector<StringRange, 16> sourceRanges;
489
    Vector<String, 16> replacements;
490
491
    // This is either a loop (if global is set) or a one-way (if not).
492
    if (global && callType == CallTypeJS) {
493
        // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string
494
        int argCount = regExp->numSubpatterns() + 1 + 2;
495
        JSFunction* func = jsCast<JSFunction*>(replaceValue);
496
        CachedCall cachedCall(exec, func, argCount);
497
        if (exec->hadException())
498
            return JSValue::encode(jsNull());
499
        VM* vm = &exec->vm();
500
        if (source.is8Bit()) {
501
            while (true) {
502
                int* ovector;
503
                MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
504
                if (!result)
505
                    break;
506
507
                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
508
509
                unsigned i = 0;
510
                for (; i < regExp->numSubpatterns() + 1; ++i) {
511
                    int matchStart = ovector[i * 2];
512
                    int matchLen = ovector[i * 2 + 1] - matchStart;
513
514
                    if (matchStart < 0)
515
                        cachedCall.setArgument(i, jsUndefined());
516
                    else
517
                        cachedCall.setArgument(i, jsSubstring8(vm, source, matchStart, matchLen));
518
                }
519
520
                cachedCall.setArgument(i++, jsNumber(result.start));
521
                cachedCall.setArgument(i++, string);
522
523
                cachedCall.setThis(jsUndefined());
524
                JSValue jsResult = cachedCall.call();
525
                replacements.append(jsResult.toString(exec)->value(exec));
526
                if (exec->hadException())
527
                    break;
528
529
                lastIndex = result.end;
530
                startPosition = lastIndex;
531
532
                // special case of empty match
533
                if (result.empty()) {
534
                    startPosition++;
535
                    if (startPosition > sourceLen)
536
                        break;
537
                }
538
            }
539
        } else {
540
            while (true) {
541
                int* ovector;
542
                MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
543
                if (!result)
544
                    break;
545
546
                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
547
548
                unsigned i = 0;
549
                for (; i < regExp->numSubpatterns() + 1; ++i) {
550
                    int matchStart = ovector[i * 2];
551
                    int matchLen = ovector[i * 2 + 1] - matchStart;
552
553
                    if (matchStart < 0)
554
                        cachedCall.setArgument(i, jsUndefined());
555
                    else
556
                        cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen));
557
                }
558
559
                cachedCall.setArgument(i++, jsNumber(result.start));
560
                cachedCall.setArgument(i++, string);
561
562
                cachedCall.setThis(jsUndefined());
563
                JSValue jsResult = cachedCall.call();
564
                replacements.append(jsResult.toString(exec)->value(exec));
565
                if (exec->hadException())
566
                    break;
567
568
                lastIndex = result.end;
569
                startPosition = lastIndex;
570
571
                // special case of empty match
572
                if (result.empty()) {
573
                    startPosition++;
574
                    if (startPosition > sourceLen)
575
                        break;
576
                }
577
            }
578
        }
579
    } else {
580
        VM* vm = &exec->vm();
581
        do {
582
            int* ovector;
583
            MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
584
            if (!result)
585
                break;
586
587
            if (callType != CallTypeNone) {
588
                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
589
590
                MarkedArgumentBuffer args;
591
592
                for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
593
                    int matchStart = ovector[i * 2];
594
                    int matchLen = ovector[i * 2 + 1] - matchStart;
595
596
                    if (matchStart < 0)
597
                        args.append(jsUndefined());
598
                    else
599
                        args.append(jsSubstring(exec, source, matchStart, matchLen));
600
                }
601
602
                args.append(jsNumber(result.start));
603
                args.append(string);
604
605
                replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec));
606
                if (exec->hadException())
607
                    break;
608
            } else {
609
                int replLen = replacementString.length();
610
                if (lastIndex < result.start || replLen) {
611
                    sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
612
613
                    if (replLen)
614
                        replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
615
                    else
616
                        replacements.append(String());
617
                }
618
            }
619
620
            lastIndex = result.end;
621
            startPosition = lastIndex;
622
623
            // special case of empty match
624
            if (result.empty()) {
625
                startPosition++;
626
                if (startPosition > sourceLen)
627
                    break;
628
            }
629
        } while (global);
630
    }
631
632
    if (!lastIndex && replacements.isEmpty())
633
        return JSValue::encode(string);
634
635
    if (static_cast<unsigned>(lastIndex) < sourceLen)
636
        sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
637
638
    return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
639
}
640
641
static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue)
642
{
190
{
643
    const String& string = jsString->value(exec);
191
    const String& string = jsString->value(exec);
644
    String searchString = searchValue.toString(exec)->value(exec);
192
    String searchString = searchValue.toString(exec)->value(exec);
Lines 650-656 static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* a/Source/JavaScriptCore/runtime/StringPrototype.cpp_sec3
650
    if (matchStart == notFound)
198
    if (matchStart == notFound)
651
        return JSValue::encode(jsString);
199
        return JSValue::encode(jsString);
652
200
653
    JSValue replaceValue = exec->argument(1);
654
    CallData callData;
201
    CallData callData;
655
    CallType callType = getCallData(replaceValue, callData);
202
    CallType callType = getCallData(replaceValue, callData);
656
    if (callType != CallTypeNone) {
203
    if (callType != CallTypeNone) {
Lines 758-769 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec) a/Source/JavaScriptCore/runtime/StringPrototype.cpp_sec4
758
    JSValue thisValue = exec->thisValue();
305
    JSValue thisValue = exec->thisValue();
759
    if (!checkObjectCoercible(thisValue))
306
    if (!checkObjectCoercible(thisValue))
760
        return throwVMTypeError(exec);
307
        return throwVMTypeError(exec);
761
    JSString* string = thisValue.toString(exec);
762
    JSValue searchValue = exec->argument(0);
308
    JSValue searchValue = exec->argument(0);
309
    JSValue replaceValue = exec->argument(1);
310
311
    if (!searchValue.isUndefinedOrNull()) {
312
        JSValue replaceFunction = searchValue.get(exec, exec->propertyNames().replaceSymbol);
313
        if (exec->hadException())
314
            return JSValue::encode(jsUndefined());
315
316
        if (!replaceFunction.isUndefinedOrNull()) {
317
            CallData callData;
318
            CallType callType = getCallData(replaceFunction, callData);
319
            if (callType == CallTypeNone)
320
                return throwVMTypeError(exec, "Symbol.replace on the first argument of String.prototype.replace was not a function");
321
322
            MarkedArgumentBuffer callArgs;
323
            callArgs.append(thisValue);
324
            callArgs.append(replaceValue);
325
            return JSValue::encode(call(exec, replaceFunction, callType, callData, searchValue, callArgs));
326
        }
327
    }
328
329
    JSString* string = thisValue.toString(exec);
330
    if (exec->hadException())
331
        return JSValue::encode(jsUndefined());
763
332
764
    if (searchValue.inherits(RegExpObject::info()))
333
    return replaceUsingStringSearch(exec, string, searchValue, replaceValue);
765
        return replaceUsingRegExpSearch(exec, string, searchValue);
766
    return replaceUsingStringSearch(exec, string, searchValue);
767
}
334
}
768
335
769
EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
336
EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
- a/Source/JavaScriptCore/tests/es6.yaml -2 / +2 lines
Lines 1065-1071 a/Source/JavaScriptCore/tests/es6.yaml_sec1
1065
- path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.match].js
1065
- path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.match].js
1066
  cmd: runES6 :fail
1066
  cmd: runES6 :fail
1067
- path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.replace].js
1067
- path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.replace].js
1068
  cmd: runES6 :fail
1068
  cmd: runES6 :normal
1069
- path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.search].js
1069
- path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.search].js
1070
  cmd: runES6 :fail
1070
  cmd: runES6 :fail
1071
- path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.split].js
1071
- path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.split].js
Lines 1177-1183 a/Source/JavaScriptCore/tests/es6.yaml_sec2
1177
- path: es6/well-known_symbols_Symbol.match.js
1177
- path: es6/well-known_symbols_Symbol.match.js
1178
  cmd: runES6 :fail
1178
  cmd: runES6 :fail
1179
- path: es6/well-known_symbols_Symbol.replace.js
1179
- path: es6/well-known_symbols_Symbol.replace.js
1180
  cmd: runES6 :fail
1180
  cmd: runES6 :normal
1181
- path: es6/well-known_symbols_Symbol.search.js
1181
- path: es6/well-known_symbols_Symbol.search.js
1182
  cmd: runES6 :fail
1182
  cmd: runES6 :fail
1183
- path: es6/well-known_symbols_Symbol.species_Array.prototype.concat.js
1183
- path: es6/well-known_symbols_Symbol.species_Array.prototype.concat.js
- a/LayoutTests/ChangeLog +12 lines
Lines 1-3 a/LayoutTests/ChangeLog_sec1
1
2015-11-02  Keith Miller  <keith_miller@apple.com>
2
3
        [ES6] Add support for Symbol.replace.
4
        https://bugs.webkit.org/show_bug.cgi?id=150812
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        Fix test as Symbol now has a replace property.
9
10
        * js/Object-getOwnPropertyNames-expected.txt:
11
        * js/script-tests/Object-getOwnPropertyNames.js:
12
1
2015-10-16  Brent Fulgham  <bfulgham@apple.com>
13
2015-10-16  Brent Fulgham  <bfulgham@apple.com>
2
14
3
        Test fix after r191211
15
        Test fix after r191211
- a/LayoutTests/js/Object-getOwnPropertyNames-expected.txt -1 / +1 lines
Lines 61-67 PASS getSortedOwnPropertyNames(Error) is ['length', 'name', 'prototype'] a/LayoutTests/js/Object-getOwnPropertyNames-expected.txt_sec1
61
PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'name', 'toString']
61
PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'name', 'toString']
62
PASS getSortedOwnPropertyNames(Math) is ['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']
62
PASS getSortedOwnPropertyNames(Math) is ['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']
63
PASS getSortedOwnPropertyNames(JSON) is ['parse', 'stringify']
63
PASS getSortedOwnPropertyNames(JSON) is ['parse', 'stringify']
64
PASS getSortedOwnPropertyNames(Symbol) is ['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'unscopables']
64
PASS getSortedOwnPropertyNames(Symbol) is ['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'replace', 'unscopables']
65
PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'toString', 'valueOf']
65
PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'toString', 'valueOf']
66
PASS globalPropertyNames.indexOf('NaN') != -1 is true
66
PASS globalPropertyNames.indexOf('NaN') != -1 is true
67
PASS globalPropertyNames.indexOf('Infinity') != -1 is true
67
PASS globalPropertyNames.indexOf('Infinity') != -1 is true
- a/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js -1 / +1 lines
Lines 70-76 var expectedPropertyNamesSet = { a/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js_sec1
70
    "Error.prototype": "['constructor', 'message', 'name', 'toString']",
70
    "Error.prototype": "['constructor', 'message', 'name', 'toString']",
71
    "Math": "['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']",
71
    "Math": "['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']",
72
    "JSON": "['parse', 'stringify']",
72
    "JSON": "['parse', 'stringify']",
73
    "Symbol": "['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'unscopables']",
73
    "Symbol": "['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'replace', 'unscopables']",
74
    "Symbol.prototype": "['constructor', 'toString', 'valueOf']"
74
    "Symbol.prototype": "['constructor', 'toString', 'valueOf']"
75
};
75
};
76
76

Return to Bug 150812