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

Side by Side Diff: src/string.js

Issue 10134057: Use simple concat of substrings instead of ReplaceStringBuilder for non-global replacements. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 8 years, 8 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | 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 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 %StringIndexOf(replace, '$', 0) < 0) { 270 %StringIndexOf(replace, '$', 0) < 0) {
271 // Searching by traversing a cons string tree and replace with cons of 271 // Searching by traversing a cons string tree and replace with cons of
272 // slices works only when the replaced string is a single character, being 272 // slices works only when the replaced string is a single character, being
273 // replaced by a simple string and only pays off for long strings. 273 // replaced by a simple string and only pays off for long strings.
274 return %StringReplaceOneCharWithString(subject, search, replace); 274 return %StringReplaceOneCharWithString(subject, search, replace);
275 } 275 }
276 var start = %StringIndexOf(subject, search, 0); 276 var start = %StringIndexOf(subject, search, 0);
277 if (start < 0) return subject; 277 if (start < 0) return subject;
278 var end = start + search.length; 278 var end = start + search.length;
279 279
280 var builder = new ReplaceResultBuilder(subject); 280 var result = SubString(subject, 0, start);
281 // prefix
282 builder.addSpecialSlice(0, start);
283 281
284 // Compute the string to replace with. 282 // Compute the string to replace with.
285 if (IS_SPEC_FUNCTION(replace)) { 283 if (IS_SPEC_FUNCTION(replace)) {
286 var receiver = %GetDefaultReceiver(replace); 284 var receiver = %GetDefaultReceiver(replace);
287 builder.add(%_CallFunction(receiver, 285 result += %_CallFunction(receiver, search, start, subject, replace);
288 search,
289 start,
290 subject,
291 replace));
292 } else { 286 } else {
293 reusableMatchInfo[CAPTURE0] = start; 287 reusableMatchInfo[CAPTURE0] = start;
294 reusableMatchInfo[CAPTURE1] = end; 288 reusableMatchInfo[CAPTURE1] = end;
295 replace = TO_STRING_INLINE(replace); 289 replace = TO_STRING_INLINE(replace);
296 ExpandReplacement(replace, subject, reusableMatchInfo, builder); 290 result = ExpandReplacement(replace, subject, reusableMatchInfo, result);
297 } 291 }
298 292
299 // suffix 293 return result + SubString(subject, end, subject.length);
300 builder.addSpecialSlice(end, subject.length);
301
302 return builder.generate();
303 } 294 }
304 295
305 296
306 // Expand the $-expressions in the string and return a new string with 297 // Expand the $-expressions in the string and return a new string with
307 // the result. 298 // the result.
308 function ExpandReplacement(string, subject, matchInfo, builder) { 299 function ExpandReplacement(string, subject, matchInfo, result) {
309 var length = string.length; 300 var length = string.length;
310 var builder_elements = builder.elements;
311 var next = %StringIndexOf(string, '$', 0); 301 var next = %StringIndexOf(string, '$', 0);
312 if (next < 0) { 302 if (next < 0) {
313 if (length > 0) builder_elements.push(string); 303 if (length > 0) result += string;
314 return; 304 return result;
315 } 305 }
316 306
317 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102. 307 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
318 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; // Includes the match. 308 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; // Includes the match.
319 309
320 if (next > 0) builder_elements.push(SubString(string, 0, next)); 310 if (next > 0) result += SubString(string, 0, next);
321 311
322 while (true) { 312 while (true) {
323 var expansion = '$'; 313 var expansion = '$';
324 var position = next + 1; 314 var position = next + 1;
325 if (position < length) { 315 if (position < length) {
326 var peek = %_StringCharCodeAt(string, position); 316 var peek = %_StringCharCodeAt(string, position);
327 if (peek == 36) { // $$ 317 if (peek == 36) { // $$
328 ++position; 318 ++position;
329 builder_elements.push('$'); 319 result += '$';
330 } else if (peek == 38) { // $& - match 320 } else if (peek == 38) { // $& - match
331 ++position; 321 ++position;
332 builder.addSpecialSlice(matchInfo[CAPTURE0], 322 result += SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
333 matchInfo[CAPTURE1]);
334 } else if (peek == 96) { // $` - prefix 323 } else if (peek == 96) { // $` - prefix
335 ++position; 324 ++position;
336 builder.addSpecialSlice(0, matchInfo[CAPTURE0]); 325 result += SubString(subject, 0, matchInfo[CAPTURE0]);
337 } else if (peek == 39) { // $' - suffix 326 } else if (peek == 39) { // $' - suffix
338 ++position; 327 ++position;
339 builder.addSpecialSlice(matchInfo[CAPTURE1], subject.length); 328 result += SubString(subject, matchInfo[CAPTURE1], subject.length);
340 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9 329 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9
341 ++position; 330 ++position;
342 var n = peek - 48; 331 var n = peek - 48;
343 if (position < length) { 332 if (position < length) {
344 peek = %_StringCharCodeAt(string, position); 333 peek = %_StringCharCodeAt(string, position);
345 // $nn, 01 <= nn <= 99 334 // $nn, 01 <= nn <= 99
346 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) { 335 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
347 var nn = n * 10 + (peek - 48); 336 var nn = n * 10 + (peek - 48);
348 if (nn < m) { 337 if (nn < m) {
349 // If the two digit capture reference is within range of 338 // If the two digit capture reference is within range of
350 // the captures, we use it instead of the single digit 339 // the captures, we use it instead of the single digit
351 // one. Otherwise, we fall back to using the single 340 // one. Otherwise, we fall back to using the single
352 // digit reference. This matches the behavior of 341 // digit reference. This matches the behavior of
353 // SpiderMonkey. 342 // SpiderMonkey.
354 ++position; 343 ++position;
355 n = nn; 344 n = nn;
356 } 345 }
357 } 346 }
358 } 347 }
359 if (0 < n && n < m) { 348 if (0 < n && n < m) {
360 addCaptureString(builder, matchInfo, n); 349 result = addCaptureString(result, subject, matchInfo, n);
361 } else { 350 } else {
362 // Because of the captures range check in the parsing of two 351 // Because of the captures range check in the parsing of two
363 // digit capture references, we can only enter here when a 352 // digit capture references, we can only enter here when a
364 // single digit capture reference is outside the range of 353 // single digit capture reference is outside the range of
365 // captures. 354 // captures.
366 builder_elements.push('$'); 355 result += '$';
367 --position; 356 --position;
368 } 357 }
369 } else { 358 } else {
370 builder_elements.push('$'); 359 result += '$';
371 } 360 }
372 } else { 361 } else {
373 builder_elements.push('$'); 362 result += '$';
374 } 363 }
375 364
376 // Go the the next $ in the string. 365 // Go the the next $ in the string.
377 next = %StringIndexOf(string, '$', position); 366 next = %StringIndexOf(string, '$', position);
378 367
379 // Return if there are no more $ characters in the string. If we 368 // Return if there are no more $ characters in the string. If we
380 // haven't reached the end, we need to append the suffix. 369 // haven't reached the end, we need to append the suffix.
381 if (next < 0) { 370 if (next < 0) {
382 if (position < length) { 371 if (position < length) {
383 builder_elements.push(SubString(string, position, length)); 372 result += SubString(string, position, length);
384 } 373 }
385 return; 374 return result;
386 } 375 }
387 376
388 // Append substring between the previous and the next $ character. 377 // Append substring between the previous and the next $ character.
389 if (next > position) { 378 if (next > position) {
390 builder_elements.push(SubString(string, position, next)); 379 result += SubString(string, position, next);
391 } 380 }
392 } 381 }
382 return result;
393 } 383 }
394 384
395 385
396 // Compute the string of a given regular expression capture. 386 // Compute the string of a given regular expression capture.
397 function CaptureString(string, lastCaptureInfo, index) { 387 function CaptureString(string, lastCaptureInfo, index) {
398 // Scale the index. 388 // Scale the index.
399 var scaled = index << 1; 389 var scaled = index << 1;
400 // Compute start and end. 390 // Compute start and end.
401 var start = lastCaptureInfo[CAPTURE(scaled)]; 391 var start = lastCaptureInfo[CAPTURE(scaled)];
402 // If start isn't valid, return undefined. 392 // If start isn't valid, return undefined.
403 if (start < 0) return; 393 if (start < 0) return;
404 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; 394 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
405 return SubString(string, start, end); 395 return SubString(string, start, end);
406 } 396 }
407 397
408 398
409 // Add the string of a given regular expression capture to the 399 // Add the string of a given regular expression capture to the
410 // ReplaceResultBuilder 400 // ReplaceResultBuilder
Erik Corry 2012/04/26 08:38:54 Comment out of date
Yang 2012/04/26 11:17:08 Done.
411 function addCaptureString(builder, matchInfo, index) { 401 function addCaptureString(result, subject, matchInfo, index) {
412 // Scale the index. 402 // Scale the index.
413 var scaled = index << 1; 403 var scaled = index << 1;
414 // Compute start and end. 404 // Compute start and end.
415 var start = matchInfo[CAPTURE(scaled)]; 405 var start = matchInfo[CAPTURE(scaled)];
416 if (start < 0) return; 406 if (start < 0) return;
Erik Corry 2012/04/26 08:38:54 This return seems wrong. Should we not return a s
Yang 2012/04/26 11:17:08 Turns out this is dead code. addCaptureString is o
417 var end = matchInfo[CAPTURE(scaled + 1)]; 407 var end = matchInfo[CAPTURE(scaled + 1)];
418 builder.addSpecialSlice(start, end); 408 return result + SubString(subject, start, end);
419 } 409 }
420 410
421 // TODO(lrn): This array will survive indefinitely if replace is never 411 // TODO(lrn): This array will survive indefinitely if replace is never
422 // called again. However, it will be empty, since the contents are cleared 412 // called again. However, it will be empty, since the contents are cleared
423 // in the finally block. 413 // in the finally block.
424 var reusableReplaceArray = new InternalArray(16); 414 var reusableReplaceArray = new InternalArray(16);
425 415
426 // Helper function for replacing regular expressions with the result of a 416 // Helper function for replacing regular expressions with the result of a
427 // function application in String.prototype.replace. 417 // function application in String.prototype.replace.
428 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { 418 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 var result = resultBuilder.generate(); 489 var result = resultBuilder.generate();
500 resultArray.length = 0; 490 resultArray.length = 0;
501 reusableReplaceArray = resultArray; 491 reusableReplaceArray = resultArray;
502 return result; 492 return result;
503 } 493 }
504 494
505 495
506 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { 496 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
507 var matchInfo = DoRegExpExec(regexp, subject, 0); 497 var matchInfo = DoRegExpExec(regexp, subject, 0);
508 if (IS_NULL(matchInfo)) return subject; 498 if (IS_NULL(matchInfo)) return subject;
509 var result = new ReplaceResultBuilder(subject);
510 var index = matchInfo[CAPTURE0]; 499 var index = matchInfo[CAPTURE0];
511 result.addSpecialSlice(0, index); 500 var result = SubString(subject, 0, index);
512 var endOfMatch = matchInfo[CAPTURE1]; 501 var endOfMatch = matchInfo[CAPTURE1];
513 // Compute the parameter list consisting of the match, captures, index, 502 // Compute the parameter list consisting of the match, captures, index,
514 // and subject for the replace function invocation. 503 // and subject for the replace function invocation.
515 // The number of captures plus one for the match. 504 // The number of captures plus one for the match.
516 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; 505 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
517 var replacement; 506 var replacement;
518 var receiver = %GetDefaultReceiver(replace); 507 var receiver = %GetDefaultReceiver(replace);
519 if (m == 1) { 508 if (m == 1) {
520 // No captures, only the match, which is always valid. 509 // No captures, only the match, which is always valid.
521 var s = SubString(subject, index, endOfMatch); 510 var s = SubString(subject, index, endOfMatch);
522 // Don't call directly to avoid exposing the built-in global object. 511 // Don't call directly to avoid exposing the built-in global object.
523 replacement = %_CallFunction(receiver, s, index, subject, replace); 512 replacement = %_CallFunction(receiver, s, index, subject, replace);
524 } else { 513 } else {
525 var parameters = new InternalArray(m + 2); 514 var parameters = new InternalArray(m + 2);
526 for (var j = 0; j < m; j++) { 515 for (var j = 0; j < m; j++) {
527 parameters[j] = CaptureString(subject, matchInfo, j); 516 parameters[j] = CaptureString(subject, matchInfo, j);
528 } 517 }
529 parameters[j] = index; 518 parameters[j] = index;
530 parameters[j + 1] = subject; 519 parameters[j + 1] = subject;
531 520
532 replacement = %Apply(replace, receiver, parameters, 0, j + 2); 521 replacement = %Apply(replace, receiver, parameters, 0, j + 2);
533 } 522 }
534 523
535 result.add(replacement); // The add method converts to string if necessary. 524 result += replacement; // The add method converts to string if necessary.
536 // Can't use matchInfo any more from here, since the function could 525 // Can't use matchInfo any more from here, since the function could
537 // overwrite it. 526 // overwrite it.
538 result.addSpecialSlice(endOfMatch, subject.length); 527 return result + SubString(subject, endOfMatch, subject.length);
539 return result.generate();
540 } 528 }
541 529
542 530
543 // ECMA-262 section 15.5.4.12 531 // ECMA-262 section 15.5.4.12
544 function StringSearch(re) { 532 function StringSearch(re) {
545 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { 533 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
546 throw MakeTypeError("called_on_null_or_undefined", 534 throw MakeTypeError("called_on_null_or_undefined",
547 ["String.prototype.search"]); 535 ["String.prototype.search"]);
548 } 536 }
549 var regexp; 537 var regexp;
(...skipping 489 matching lines...) Expand 10 before | Expand all | Expand 10 after
1039 "fixed", StringFixed, 1027 "fixed", StringFixed,
1040 "italics", StringItalics, 1028 "italics", StringItalics,
1041 "small", StringSmall, 1029 "small", StringSmall,
1042 "strike", StringStrike, 1030 "strike", StringStrike,
1043 "sub", StringSub, 1031 "sub", StringSub,
1044 "sup", StringSup 1032 "sup", StringSup
1045 )); 1033 ));
1046 } 1034 }
1047 1035
1048 SetUpString(); 1036 SetUpString();
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698