Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(394)

Side by Side Diff: src/js/string.js

Issue 1590673002: Separate String.prototype.replace into RegExp.prototype[Symbol.replace] (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Remove extra newline from test Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/js/regexp.js ('k') | test/mjsunit/harmony/string-replace.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 (function(global, utils) { 5 (function(global, utils) {
6 6
7 %CheckIsBootstrapping(); 7 %CheckIsBootstrapping();
8 8
9 // ------------------------------------------------------------------- 9 // -------------------------------------------------------------------
10 // Imports 10 // Imports
11 11
12 var ArrayIndexOf; 12 var ArrayIndexOf;
13 var ArrayJoin; 13 var ArrayJoin;
14 var GlobalRegExp = global.RegExp; 14 var GlobalRegExp = global.RegExp;
15 var GlobalString = global.String; 15 var GlobalString = global.String;
16 var InternalArray = utils.InternalArray; 16 var InternalArray = utils.InternalArray;
17 var InternalPackedArray = utils.InternalPackedArray; 17 var InternalPackedArray = utils.InternalPackedArray;
18 var MakeRangeError; 18 var MakeRangeError;
19 var MakeTypeError; 19 var MakeTypeError;
20 var MathMax; 20 var MathMax;
21 var MathMin; 21 var MathMin;
22 var matchSymbol = utils.ImportNow("match_symbol"); 22 var matchSymbol = utils.ImportNow("match_symbol");
23 var RegExpExec;
24 var RegExpExecNoTests; 23 var RegExpExecNoTests;
25 var RegExpLastMatchInfo; 24 var replaceSymbol = utils.ImportNow("replace_symbol");
26 var searchSymbol = utils.ImportNow("search_symbol"); 25 var searchSymbol = utils.ImportNow("search_symbol");
27 var splitSymbol = utils.ImportNow("split_symbol"); 26 var splitSymbol = utils.ImportNow("split_symbol");
28 27
29 utils.Import(function(from) { 28 utils.Import(function(from) {
30 ArrayIndexOf = from.ArrayIndexOf; 29 ArrayIndexOf = from.ArrayIndexOf;
31 ArrayJoin = from.ArrayJoin; 30 ArrayJoin = from.ArrayJoin;
32 MakeRangeError = from.MakeRangeError; 31 MakeRangeError = from.MakeRangeError;
33 MakeTypeError = from.MakeTypeError; 32 MakeTypeError = from.MakeTypeError;
34 MathMax = from.MathMax; 33 MathMax = from.MathMax;
35 MathMin = from.MathMin; 34 MathMin = from.MathMin;
36 RegExpExec = from.RegExpExec;
37 RegExpExecNoTests = from.RegExpExecNoTests; 35 RegExpExecNoTests = from.RegExpExecNoTests;
38 RegExpLastMatchInfo = from.RegExpLastMatchInfo;
39 }); 36 });
40 37
41 //------------------------------------------------------------------- 38 //-------------------------------------------------------------------
42 39
43 // ECMA-262 section 15.5.4.2 40 // ECMA-262 section 15.5.4.2
44 function StringToString() { 41 function StringToString() {
45 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) { 42 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
46 throw MakeTypeError(kNotGeneric, 'String.prototype.toString'); 43 throw MakeTypeError(kNotGeneric, 'String.prototype.toString');
47 } 44 }
48 return %_ValueOf(this); 45 return %_ValueOf(this);
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 196
200 197
201 // This has the same size as the RegExpLastMatchInfo array, and can be used 198 // This has the same size as the RegExpLastMatchInfo array, and can be used
202 // for functions that expect that structure to be returned. It is used when 199 // for functions that expect that structure to be returned. It is used when
203 // the needle is a string rather than a regexp. In this case we can't update 200 // the needle is a string rather than a regexp. In this case we can't update
204 // lastMatchArray without erroneously affecting the properties on the global 201 // lastMatchArray without erroneously affecting the properties on the global
205 // RegExp object. 202 // RegExp object.
206 var reusableMatchInfo = [2, "", "", -1, -1]; 203 var reusableMatchInfo = [2, "", "", -1, -1];
207 204
208 205
209 // ECMA-262, section 15.5.4.11 206 // ES6, section 21.1.3.14
210 function StringReplace(search, replace) { 207 function StringReplace(search, replace) {
211 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace"); 208 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
212 209
213 var subject = TO_STRING(this);
214
215 // Decision tree for dispatch 210 // Decision tree for dispatch
216 // .. regexp search 211 // .. regexp search (in src/js/regexp.js, RegExpReplace)
217 // .... string replace 212 // .... string replace
218 // ...... non-global search 213 // ...... non-global search
219 // ........ empty string replace 214 // ........ empty string replace
220 // ........ non-empty string replace (with $-expansion) 215 // ........ non-empty string replace (with $-expansion)
221 // ...... global search 216 // ...... global search
222 // ........ no need to circumvent last match info override 217 // ........ no need to circumvent last match info override
223 // ........ need to circument last match info override 218 // ........ need to circument last match info override
224 // .... function replace 219 // .... function replace
225 // ...... global search 220 // ...... global search
226 // ...... non-global search 221 // ...... non-global search
227 // .. string search 222 // .. string search
228 // .... special case that replaces with one single character 223 // .... special case that replaces with one single character
229 // ...... function replace 224 // ...... function replace
230 // ...... string replace (with $-expansion) 225 // ...... string replace (with $-expansion)
231 226
232 if (IS_REGEXP(search)) { 227 if (!IS_NULL_OR_UNDEFINED(search)) {
233 if (!IS_CALLABLE(replace)) { 228 var replacer = search[replaceSymbol];
234 replace = TO_STRING(replace); 229 if (!IS_UNDEFINED(replacer)) {
230 return %_Call(replacer, search, this, replace);
231 }
232 }
235 233
236 if (!REGEXP_GLOBAL(search)) { 234 var subject = TO_STRING(this);
237 // Non-global regexp search, string replace.
238 var match = RegExpExec(search, subject, 0);
239 if (match == null) {
240 search.lastIndex = 0
241 return subject;
242 }
243 if (replace.length == 0) {
244 return %_SubString(subject, 0, match[CAPTURE0]) +
245 %_SubString(subject, match[CAPTURE1], subject.length)
246 }
247 return ExpandReplacement(replace, subject, RegExpLastMatchInfo,
248 %_SubString(subject, 0, match[CAPTURE0])) +
249 %_SubString(subject, match[CAPTURE1], subject.length);
250 }
251
252 // Global regexp search, string replace.
253 search.lastIndex = 0;
254 return %StringReplaceGlobalRegExpWithString(
255 subject, search, replace, RegExpLastMatchInfo);
256 }
257
258 if (REGEXP_GLOBAL(search)) {
259 // Global regexp search, function replace.
260 return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
261 }
262 // Non-global regexp search, function replace.
263 return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
264 }
265 235
266 search = TO_STRING(search); 236 search = TO_STRING(search);
267 237
268 if (search.length == 1 && 238 if (search.length == 1 &&
269 subject.length > 0xFF && 239 subject.length > 0xFF &&
270 IS_STRING(replace) && 240 IS_STRING(replace) &&
271 %StringIndexOf(replace, '$', 0) < 0) { 241 %StringIndexOf(replace, '$', 0) < 0) {
272 // Searching by traversing a cons string tree and replace with cons of 242 // Searching by traversing a cons string tree and replace with cons of
273 // slices works only when the replaced string is a single character, being 243 // slices works only when the replaced string is a single character, being
274 // replaced by a simple string and only pays off for long strings. 244 // replaced by a simple string and only pays off for long strings.
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
372 342
373 // Append substring between the previous and the next $ character. 343 // Append substring between the previous and the next $ character.
374 if (next > position) { 344 if (next > position) {
375 result += %_SubString(string, position, next); 345 result += %_SubString(string, position, next);
376 } 346 }
377 } 347 }
378 return result; 348 return result;
379 } 349 }
380 350
381 351
382 // Compute the string of a given regular expression capture.
383 function CaptureString(string, lastCaptureInfo, index) {
384 // Scale the index.
385 var scaled = index << 1;
386 // Compute start and end.
387 var start = lastCaptureInfo[CAPTURE(scaled)];
388 // If start isn't valid, return undefined.
389 if (start < 0) return;
390 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
391 return %_SubString(string, start, end);
392 }
393
394
395 // TODO(lrn): This array will survive indefinitely if replace is never
396 // called again. However, it will be empty, since the contents are cleared
397 // in the finally block.
398 var reusableReplaceArray = new InternalArray(4);
399
400 // Helper function for replacing regular expressions with the result of a
401 // function application in String.prototype.replace.
402 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
403 var resultArray = reusableReplaceArray;
404 if (resultArray) {
405 reusableReplaceArray = null;
406 } else {
407 // Inside a nested replace (replace called from the replacement function
408 // of another replace) or we have failed to set the reusable array
409 // back due to an exception in a replacement function. Create a new
410 // array to use in the future, or until the original is written back.
411 resultArray = new InternalArray(16);
412 }
413 var res = %RegExpExecMultiple(regexp,
414 subject,
415 RegExpLastMatchInfo,
416 resultArray);
417 regexp.lastIndex = 0;
418 if (IS_NULL(res)) {
419 // No matches at all.
420 reusableReplaceArray = resultArray;
421 return subject;
422 }
423 var len = res.length;
424 if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) {
425 // If the number of captures is two then there are no explicit captures in
426 // the regexp, just the implicit capture that captures the whole match. In
427 // this case we can simplify quite a bit and end up with something faster.
428 // The builder will consist of some integers that indicate slices of the
429 // input string and some replacements that were returned from the replace
430 // function.
431 var match_start = 0;
432 for (var i = 0; i < len; i++) {
433 var elem = res[i];
434 if (%_IsSmi(elem)) {
435 // Integers represent slices of the original string.
436 if (elem > 0) {
437 match_start = (elem >> 11) + (elem & 0x7ff);
438 } else {
439 match_start = res[++i] - elem;
440 }
441 } else {
442 var func_result = replace(elem, match_start, subject);
443 // Overwrite the i'th element in the results with the string we got
444 // back from the callback function.
445 res[i] = TO_STRING(func_result);
446 match_start += elem.length;
447 }
448 }
449 } else {
450 for (var i = 0; i < len; i++) {
451 var elem = res[i];
452 if (!%_IsSmi(elem)) {
453 // elem must be an Array.
454 // Use the apply argument as backing for global RegExp properties.
455 var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length);
456 // Overwrite the i'th element in the results with the string we got
457 // back from the callback function.
458 res[i] = TO_STRING(func_result);
459 }
460 }
461 }
462 var result = %StringBuilderConcat(res, len, subject);
463 resultArray.length = 0;
464 reusableReplaceArray = resultArray;
465 return result;
466 }
467
468
469 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
470 var matchInfo = RegExpExec(regexp, subject, 0);
471 if (IS_NULL(matchInfo)) {
472 regexp.lastIndex = 0;
473 return subject;
474 }
475 var index = matchInfo[CAPTURE0];
476 var result = %_SubString(subject, 0, index);
477 var endOfMatch = matchInfo[CAPTURE1];
478 // Compute the parameter list consisting of the match, captures, index,
479 // and subject for the replace function invocation.
480 // The number of captures plus one for the match.
481 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
482 var replacement;
483 if (m == 1) {
484 // No captures, only the match, which is always valid.
485 var s = %_SubString(subject, index, endOfMatch);
486 // Don't call directly to avoid exposing the built-in global object.
487 replacement = replace(s, index, subject);
488 } else {
489 var parameters = new InternalArray(m + 2);
490 for (var j = 0; j < m; j++) {
491 parameters[j] = CaptureString(subject, matchInfo, j);
492 }
493 parameters[j] = index;
494 parameters[j + 1] = subject;
495
496 replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2);
497 }
498
499 result += replacement; // The add method converts to string if necessary.
500 // Can't use matchInfo any more from here, since the function could
501 // overwrite it.
502 return result + %_SubString(subject, endOfMatch, subject.length);
503 }
504
505
506 // ES6 21.1.3.15. 352 // ES6 21.1.3.15.
507 function StringSearch(pattern) { 353 function StringSearch(pattern) {
508 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search"); 354 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
509 355
510 if (!IS_NULL_OR_UNDEFINED(pattern)) { 356 if (!IS_NULL_OR_UNDEFINED(pattern)) {
511 var searcher = pattern[searchSymbol]; 357 var searcher = pattern[searchSymbol];
512 if (!IS_UNDEFINED(searcher)) { 358 if (!IS_UNDEFINED(searcher)) {
513 return %_Call(searcher, pattern, this); 359 return %_Call(searcher, pattern, this);
514 } 360 }
515 } 361 }
(...skipping 575 matching lines...) Expand 10 before | Expand all | Expand 10 after
1091 "small", StringSmall, 937 "small", StringSmall,
1092 "strike", StringStrike, 938 "strike", StringStrike,
1093 "sub", StringSub, 939 "sub", StringSub,
1094 "sup", StringSup 940 "sup", StringSup
1095 ]); 941 ]);
1096 942
1097 // ------------------------------------------------------------------- 943 // -------------------------------------------------------------------
1098 // Exports 944 // Exports
1099 945
1100 utils.Export(function(to) { 946 utils.Export(function(to) {
947 to.ExpandReplacement = ExpandReplacement;
1101 to.StringCharAt = StringCharAtJS; 948 to.StringCharAt = StringCharAtJS;
1102 to.StringIndexOf = StringIndexOfJS; 949 to.StringIndexOf = StringIndexOfJS;
1103 to.StringLastIndexOf = StringLastIndexOfJS; 950 to.StringLastIndexOf = StringLastIndexOfJS;
1104 to.StringMatch = StringMatchJS; 951 to.StringMatch = StringMatchJS;
1105 to.StringReplace = StringReplace; 952 to.StringReplace = StringReplace;
1106 to.StringSlice = StringSlice; 953 to.StringSlice = StringSlice;
1107 to.StringSplit = StringSplitJS; 954 to.StringSplit = StringSplitJS;
1108 to.StringSubstr = StringSubstr; 955 to.StringSubstr = StringSubstr;
1109 to.StringSubstring = StringSubstring; 956 to.StringSubstring = StringSubstring;
1110 }); 957 });
1111 958
1112 }) 959 })
OLDNEW
« no previous file with comments | « src/js/regexp.js ('k') | test/mjsunit/harmony/string-replace.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698