OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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(); |
OLD | NEW |