| 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 if (next > 0) result += SubString(string, 0, next); |
| 318 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; // Includes the match. | |
| 319 | |
| 320 if (next > 0) builder_elements.push(SubString(string, 0, next)); | |
| 321 | 308 |
| 322 while (true) { | 309 while (true) { |
| 323 var expansion = '$'; | 310 var expansion = '$'; |
| 324 var position = next + 1; | 311 var position = next + 1; |
| 325 if (position < length) { | 312 if (position < length) { |
| 326 var peek = %_StringCharCodeAt(string, position); | 313 var peek = %_StringCharCodeAt(string, position); |
| 327 if (peek == 36) { // $$ | 314 if (peek == 36) { // $$ |
| 328 ++position; | 315 ++position; |
| 329 builder_elements.push('$'); | 316 result += '$'; |
| 330 } else if (peek == 38) { // $& - match | 317 } else if (peek == 38) { // $& - match |
| 331 ++position; | 318 ++position; |
| 332 builder.addSpecialSlice(matchInfo[CAPTURE0], | 319 result += SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); |
| 333 matchInfo[CAPTURE1]); | |
| 334 } else if (peek == 96) { // $` - prefix | 320 } else if (peek == 96) { // $` - prefix |
| 335 ++position; | 321 ++position; |
| 336 builder.addSpecialSlice(0, matchInfo[CAPTURE0]); | 322 result += SubString(subject, 0, matchInfo[CAPTURE0]); |
| 337 } else if (peek == 39) { // $' - suffix | 323 } else if (peek == 39) { // $' - suffix |
| 338 ++position; | 324 ++position; |
| 339 builder.addSpecialSlice(matchInfo[CAPTURE1], subject.length); | 325 result += SubString(subject, matchInfo[CAPTURE1], subject.length); |
| 340 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9 | |
| 341 ++position; | |
| 342 var n = peek - 48; | |
| 343 if (position < length) { | |
| 344 peek = %_StringCharCodeAt(string, position); | |
| 345 // $nn, 01 <= nn <= 99 | |
| 346 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) { | |
| 347 var nn = n * 10 + (peek - 48); | |
| 348 if (nn < m) { | |
| 349 // If the two digit capture reference is within range of | |
| 350 // the captures, we use it instead of the single digit | |
| 351 // one. Otherwise, we fall back to using the single | |
| 352 // digit reference. This matches the behavior of | |
| 353 // SpiderMonkey. | |
| 354 ++position; | |
| 355 n = nn; | |
| 356 } | |
| 357 } | |
| 358 } | |
| 359 if (0 < n && n < m) { | |
| 360 addCaptureString(builder, matchInfo, n); | |
| 361 } else { | |
| 362 // Because of the captures range check in the parsing of two | |
| 363 // digit capture references, we can only enter here when a | |
| 364 // single digit capture reference is outside the range of | |
| 365 // captures. | |
| 366 builder_elements.push('$'); | |
| 367 --position; | |
| 368 } | |
| 369 } else { | 326 } else { |
| 370 builder_elements.push('$'); | 327 result += '$'; |
| 371 } | 328 } |
| 372 } else { | 329 } else { |
| 373 builder_elements.push('$'); | 330 result += '$'; |
| 374 } | 331 } |
| 375 | 332 |
| 376 // Go the the next $ in the string. | 333 // Go the the next $ in the string. |
| 377 next = %StringIndexOf(string, '$', position); | 334 next = %StringIndexOf(string, '$', position); |
| 378 | 335 |
| 379 // Return if there are no more $ characters in the string. If we | 336 // Return if there are no more $ characters in the string. If we |
| 380 // haven't reached the end, we need to append the suffix. | 337 // haven't reached the end, we need to append the suffix. |
| 381 if (next < 0) { | 338 if (next < 0) { |
| 382 if (position < length) { | 339 if (position < length) { |
| 383 builder_elements.push(SubString(string, position, length)); | 340 result += SubString(string, position, length); |
| 384 } | 341 } |
| 385 return; | 342 return result; |
| 386 } | 343 } |
| 387 | 344 |
| 388 // Append substring between the previous and the next $ character. | 345 // Append substring between the previous and the next $ character. |
| 389 if (next > position) { | 346 if (next > position) { |
| 390 builder_elements.push(SubString(string, position, next)); | 347 result += SubString(string, position, next); |
| 391 } | 348 } |
| 392 } | 349 } |
| 350 return result; |
| 393 } | 351 } |
| 394 | 352 |
| 395 | 353 |
| 396 // Compute the string of a given regular expression capture. | 354 // Compute the string of a given regular expression capture. |
| 397 function CaptureString(string, lastCaptureInfo, index) { | 355 function CaptureString(string, lastCaptureInfo, index) { |
| 398 // Scale the index. | 356 // Scale the index. |
| 399 var scaled = index << 1; | 357 var scaled = index << 1; |
| 400 // Compute start and end. | 358 // Compute start and end. |
| 401 var start = lastCaptureInfo[CAPTURE(scaled)]; | 359 var start = lastCaptureInfo[CAPTURE(scaled)]; |
| 402 // If start isn't valid, return undefined. | 360 // If start isn't valid, return undefined. |
| 403 if (start < 0) return; | 361 if (start < 0) return; |
| 404 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; | 362 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; |
| 405 return SubString(string, start, end); | 363 return SubString(string, start, end); |
| 406 } | 364 } |
| 407 | 365 |
| 408 | 366 |
| 409 // Add the string of a given regular expression capture to the | |
| 410 // ReplaceResultBuilder | |
| 411 function addCaptureString(builder, matchInfo, index) { | |
| 412 // Scale the index. | |
| 413 var scaled = index << 1; | |
| 414 // Compute start and end. | |
| 415 var start = matchInfo[CAPTURE(scaled)]; | |
| 416 if (start < 0) return; | |
| 417 var end = matchInfo[CAPTURE(scaled + 1)]; | |
| 418 builder.addSpecialSlice(start, end); | |
| 419 } | |
| 420 | |
| 421 // TODO(lrn): This array will survive indefinitely if replace is never | 367 // TODO(lrn): This array will survive indefinitely if replace is never |
| 422 // called again. However, it will be empty, since the contents are cleared | 368 // called again. However, it will be empty, since the contents are cleared |
| 423 // in the finally block. | 369 // in the finally block. |
| 424 var reusableReplaceArray = new InternalArray(16); | 370 var reusableReplaceArray = new InternalArray(16); |
| 425 | 371 |
| 426 // Helper function for replacing regular expressions with the result of a | 372 // Helper function for replacing regular expressions with the result of a |
| 427 // function application in String.prototype.replace. | 373 // function application in String.prototype.replace. |
| 428 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { | 374 function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { |
| 429 var resultArray = reusableReplaceArray; | 375 var resultArray = reusableReplaceArray; |
| 430 if (resultArray) { | 376 if (resultArray) { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 499 var result = resultBuilder.generate(); | 445 var result = resultBuilder.generate(); |
| 500 resultArray.length = 0; | 446 resultArray.length = 0; |
| 501 reusableReplaceArray = resultArray; | 447 reusableReplaceArray = resultArray; |
| 502 return result; | 448 return result; |
| 503 } | 449 } |
| 504 | 450 |
| 505 | 451 |
| 506 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { | 452 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { |
| 507 var matchInfo = DoRegExpExec(regexp, subject, 0); | 453 var matchInfo = DoRegExpExec(regexp, subject, 0); |
| 508 if (IS_NULL(matchInfo)) return subject; | 454 if (IS_NULL(matchInfo)) return subject; |
| 509 var result = new ReplaceResultBuilder(subject); | |
| 510 var index = matchInfo[CAPTURE0]; | 455 var index = matchInfo[CAPTURE0]; |
| 511 result.addSpecialSlice(0, index); | 456 var result = SubString(subject, 0, index); |
| 512 var endOfMatch = matchInfo[CAPTURE1]; | 457 var endOfMatch = matchInfo[CAPTURE1]; |
| 513 // Compute the parameter list consisting of the match, captures, index, | 458 // Compute the parameter list consisting of the match, captures, index, |
| 514 // and subject for the replace function invocation. | 459 // and subject for the replace function invocation. |
| 515 // The number of captures plus one for the match. | 460 // The number of captures plus one for the match. |
| 516 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; | 461 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; |
| 517 var replacement; | 462 var replacement; |
| 518 var receiver = %GetDefaultReceiver(replace); | 463 var receiver = %GetDefaultReceiver(replace); |
| 519 if (m == 1) { | 464 if (m == 1) { |
| 520 // No captures, only the match, which is always valid. | 465 // No captures, only the match, which is always valid. |
| 521 var s = SubString(subject, index, endOfMatch); | 466 var s = SubString(subject, index, endOfMatch); |
| 522 // Don't call directly to avoid exposing the built-in global object. | 467 // Don't call directly to avoid exposing the built-in global object. |
| 523 replacement = %_CallFunction(receiver, s, index, subject, replace); | 468 replacement = %_CallFunction(receiver, s, index, subject, replace); |
| 524 } else { | 469 } else { |
| 525 var parameters = new InternalArray(m + 2); | 470 var parameters = new InternalArray(m + 2); |
| 526 for (var j = 0; j < m; j++) { | 471 for (var j = 0; j < m; j++) { |
| 527 parameters[j] = CaptureString(subject, matchInfo, j); | 472 parameters[j] = CaptureString(subject, matchInfo, j); |
| 528 } | 473 } |
| 529 parameters[j] = index; | 474 parameters[j] = index; |
| 530 parameters[j + 1] = subject; | 475 parameters[j + 1] = subject; |
| 531 | 476 |
| 532 replacement = %Apply(replace, receiver, parameters, 0, j + 2); | 477 replacement = %Apply(replace, receiver, parameters, 0, j + 2); |
| 533 } | 478 } |
| 534 | 479 |
| 535 result.add(replacement); // The add method converts to string if necessary. | 480 result += replacement; // The add method converts to string if necessary. |
| 536 // Can't use matchInfo any more from here, since the function could | 481 // Can't use matchInfo any more from here, since the function could |
| 537 // overwrite it. | 482 // overwrite it. |
| 538 result.addSpecialSlice(endOfMatch, subject.length); | 483 return result + SubString(subject, endOfMatch, subject.length); |
| 539 return result.generate(); | |
| 540 } | 484 } |
| 541 | 485 |
| 542 | 486 |
| 543 // ECMA-262 section 15.5.4.12 | 487 // ECMA-262 section 15.5.4.12 |
| 544 function StringSearch(re) { | 488 function StringSearch(re) { |
| 545 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 489 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
| 546 throw MakeTypeError("called_on_null_or_undefined", | 490 throw MakeTypeError("called_on_null_or_undefined", |
| 547 ["String.prototype.search"]); | 491 ["String.prototype.search"]); |
| 548 } | 492 } |
| 549 var regexp; | 493 var regexp; |
| (...skipping 489 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1039 "fixed", StringFixed, | 983 "fixed", StringFixed, |
| 1040 "italics", StringItalics, | 984 "italics", StringItalics, |
| 1041 "small", StringSmall, | 985 "small", StringSmall, |
| 1042 "strike", StringStrike, | 986 "strike", StringStrike, |
| 1043 "sub", StringSub, | 987 "sub", StringSub, |
| 1044 "sup", StringSup | 988 "sup", StringSup |
| 1045 )); | 989 )); |
| 1046 } | 990 } |
| 1047 | 991 |
| 1048 SetUpString(); | 992 SetUpString(); |
| OLD | NEW |