| 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 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 if (result !== null) lastMatchInfoOverride = null; | 196 if (result !== null) lastMatchInfoOverride = null; |
| 197 regexp.lastIndex = 0; | 197 regexp.lastIndex = 0; |
| 198 return result; | 198 return result; |
| 199 } | 199 } |
| 200 // Non-regexp argument. | 200 // Non-regexp argument. |
| 201 regexp = new $RegExp(regexp); | 201 regexp = new $RegExp(regexp); |
| 202 return RegExpExecNoTests(regexp, subject, 0); | 202 return RegExpExecNoTests(regexp, subject, 0); |
| 203 } | 203 } |
| 204 | 204 |
| 205 | 205 |
| 206 // SubString is an internal function that returns the sub string of 'string'. | |
| 207 // If resulting string is of length 1, we use the one character cache | |
| 208 // otherwise we call the runtime system. | |
| 209 function SubString(string, start, end) { | |
| 210 // Use the one character string cache. | |
| 211 if (start + 1 == end) return %_StringCharAt(string, start); | |
| 212 return %_SubString(string, start, end); | |
| 213 } | |
| 214 | |
| 215 | |
| 216 // This has the same size as the lastMatchInfo array, and can be used for | 206 // This has the same size as the lastMatchInfo array, and can be used for |
| 217 // functions that expect that structure to be returned. It is used when the | 207 // functions that expect that structure to be returned. It is used when the |
| 218 // needle is a string rather than a regexp. In this case we can't update | 208 // needle is a string rather than a regexp. In this case we can't update |
| 219 // lastMatchArray without erroneously affecting the properties on the global | 209 // lastMatchArray without erroneously affecting the properties on the global |
| 220 // RegExp object. | 210 // RegExp object. |
| 221 var reusableMatchInfo = [2, "", "", -1, -1]; | 211 var reusableMatchInfo = [2, "", "", -1, -1]; |
| 222 | 212 |
| 223 | 213 |
| 224 // ECMA-262, section 15.5.4.11 | 214 // ECMA-262, section 15.5.4.11 |
| 225 function StringReplace(search, replace) { | 215 function StringReplace(search, replace) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 289 %StringIndexOf(replace, '$', 0) < 0) { | 279 %StringIndexOf(replace, '$', 0) < 0) { |
| 290 // Searching by traversing a cons string tree and replace with cons of | 280 // Searching by traversing a cons string tree and replace with cons of |
| 291 // slices works only when the replaced string is a single character, being | 281 // slices works only when the replaced string is a single character, being |
| 292 // replaced by a simple string and only pays off for long strings. | 282 // replaced by a simple string and only pays off for long strings. |
| 293 return %StringReplaceOneCharWithString(subject, search, replace); | 283 return %StringReplaceOneCharWithString(subject, search, replace); |
| 294 } | 284 } |
| 295 var start = %StringIndexOf(subject, search, 0); | 285 var start = %StringIndexOf(subject, search, 0); |
| 296 if (start < 0) return subject; | 286 if (start < 0) return subject; |
| 297 var end = start + search.length; | 287 var end = start + search.length; |
| 298 | 288 |
| 299 var result = SubString(subject, 0, start); | 289 var result = %_SubString(subject, 0, start); |
| 300 | 290 |
| 301 // Compute the string to replace with. | 291 // Compute the string to replace with. |
| 302 if (IS_SPEC_FUNCTION(replace)) { | 292 if (IS_SPEC_FUNCTION(replace)) { |
| 303 var receiver = %GetDefaultReceiver(replace); | 293 var receiver = %GetDefaultReceiver(replace); |
| 304 result += %_CallFunction(receiver, search, start, subject, replace); | 294 result += %_CallFunction(receiver, search, start, subject, replace); |
| 305 } else { | 295 } else { |
| 306 reusableMatchInfo[CAPTURE0] = start; | 296 reusableMatchInfo[CAPTURE0] = start; |
| 307 reusableMatchInfo[CAPTURE1] = end; | 297 reusableMatchInfo[CAPTURE1] = end; |
| 308 replace = TO_STRING_INLINE(replace); | 298 replace = TO_STRING_INLINE(replace); |
| 309 result = ExpandReplacement(replace, subject, reusableMatchInfo, result); | 299 result = ExpandReplacement(replace, subject, reusableMatchInfo, result); |
| 310 } | 300 } |
| 311 | 301 |
| 312 return result + SubString(subject, end, subject.length); | 302 return result + %_SubString(subject, end, subject.length); |
| 313 } | 303 } |
| 314 | 304 |
| 315 | 305 |
| 316 // Expand the $-expressions in the string and return a new string with | 306 // Expand the $-expressions in the string and return a new string with |
| 317 // the result. | 307 // the result. |
| 318 function ExpandReplacement(string, subject, matchInfo, result) { | 308 function ExpandReplacement(string, subject, matchInfo, result) { |
| 319 var length = string.length; | 309 var length = string.length; |
| 320 var next = %StringIndexOf(string, '$', 0); | 310 var next = %StringIndexOf(string, '$', 0); |
| 321 if (next < 0) { | 311 if (next < 0) { |
| 322 if (length > 0) result += string; | 312 if (length > 0) result += string; |
| 323 return result; | 313 return result; |
| 324 } | 314 } |
| 325 | 315 |
| 326 if (next > 0) result += SubString(string, 0, next); | 316 if (next > 0) result += %_SubString(string, 0, next); |
| 327 | 317 |
| 328 while (true) { | 318 while (true) { |
| 329 var expansion = '$'; | 319 var expansion = '$'; |
| 330 var position = next + 1; | 320 var position = next + 1; |
| 331 if (position < length) { | 321 if (position < length) { |
| 332 var peek = %_StringCharCodeAt(string, position); | 322 var peek = %_StringCharCodeAt(string, position); |
| 333 if (peek == 36) { // $$ | 323 if (peek == 36) { // $$ |
| 334 ++position; | 324 ++position; |
| 335 result += '$'; | 325 result += '$'; |
| 336 } else if (peek == 38) { // $& - match | 326 } else if (peek == 38) { // $& - match |
| 337 ++position; | 327 ++position; |
| 338 result += SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); | 328 result += |
| 329 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); |
| 339 } else if (peek == 96) { // $` - prefix | 330 } else if (peek == 96) { // $` - prefix |
| 340 ++position; | 331 ++position; |
| 341 result += SubString(subject, 0, matchInfo[CAPTURE0]); | 332 result += %_SubString(subject, 0, matchInfo[CAPTURE0]); |
| 342 } else if (peek == 39) { // $' - suffix | 333 } else if (peek == 39) { // $' - suffix |
| 343 ++position; | 334 ++position; |
| 344 result += SubString(subject, matchInfo[CAPTURE1], subject.length); | 335 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length); |
| 345 } else { | 336 } else { |
| 346 result += '$'; | 337 result += '$'; |
| 347 } | 338 } |
| 348 } else { | 339 } else { |
| 349 result += '$'; | 340 result += '$'; |
| 350 } | 341 } |
| 351 | 342 |
| 352 // Go the the next $ in the string. | 343 // Go the the next $ in the string. |
| 353 next = %StringIndexOf(string, '$', position); | 344 next = %StringIndexOf(string, '$', position); |
| 354 | 345 |
| 355 // Return if there are no more $ characters in the string. If we | 346 // Return if there are no more $ characters in the string. If we |
| 356 // haven't reached the end, we need to append the suffix. | 347 // haven't reached the end, we need to append the suffix. |
| 357 if (next < 0) { | 348 if (next < 0) { |
| 358 if (position < length) { | 349 if (position < length) { |
| 359 result += SubString(string, position, length); | 350 result += %_SubString(string, position, length); |
| 360 } | 351 } |
| 361 return result; | 352 return result; |
| 362 } | 353 } |
| 363 | 354 |
| 364 // Append substring between the previous and the next $ character. | 355 // Append substring between the previous and the next $ character. |
| 365 if (next > position) { | 356 if (next > position) { |
| 366 result += SubString(string, position, next); | 357 result += %_SubString(string, position, next); |
| 367 } | 358 } |
| 368 } | 359 } |
| 369 return result; | 360 return result; |
| 370 } | 361 } |
| 371 | 362 |
| 372 | 363 |
| 373 // Compute the string of a given regular expression capture. | 364 // Compute the string of a given regular expression capture. |
| 374 function CaptureString(string, lastCaptureInfo, index) { | 365 function CaptureString(string, lastCaptureInfo, index) { |
| 375 // Scale the index. | 366 // Scale the index. |
| 376 var scaled = index << 1; | 367 var scaled = index << 1; |
| 377 // Compute start and end. | 368 // Compute start and end. |
| 378 var start = lastCaptureInfo[CAPTURE(scaled)]; | 369 var start = lastCaptureInfo[CAPTURE(scaled)]; |
| 379 // If start isn't valid, return undefined. | 370 // If start isn't valid, return undefined. |
| 380 if (start < 0) return; | 371 if (start < 0) return; |
| 381 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; | 372 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; |
| 382 return SubString(string, start, end); | 373 return %_SubString(string, start, end); |
| 383 } | 374 } |
| 384 | 375 |
| 385 | 376 |
| 386 // TODO(lrn): This array will survive indefinitely if replace is never | 377 // TODO(lrn): This array will survive indefinitely if replace is never |
| 387 // called again. However, it will be empty, since the contents are cleared | 378 // called again. However, it will be empty, since the contents are cleared |
| 388 // in the finally block. | 379 // in the finally block. |
| 389 var reusableReplaceArray = new InternalArray(16); | 380 var reusableReplaceArray = new InternalArray(16); |
| 390 | 381 |
| 391 // Helper function for replacing regular expressions with the result of a | 382 // Helper function for replacing regular expressions with the result of a |
| 392 // function application in String.prototype.replace. | 383 // function application in String.prototype.replace. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 468 } | 459 } |
| 469 | 460 |
| 470 | 461 |
| 471 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { | 462 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { |
| 472 var matchInfo = DoRegExpExec(regexp, subject, 0); | 463 var matchInfo = DoRegExpExec(regexp, subject, 0); |
| 473 if (IS_NULL(matchInfo)) { | 464 if (IS_NULL(matchInfo)) { |
| 474 regexp.lastIndex = 0; | 465 regexp.lastIndex = 0; |
| 475 return subject; | 466 return subject; |
| 476 } | 467 } |
| 477 var index = matchInfo[CAPTURE0]; | 468 var index = matchInfo[CAPTURE0]; |
| 478 var result = SubString(subject, 0, index); | 469 var result = %_SubString(subject, 0, index); |
| 479 var endOfMatch = matchInfo[CAPTURE1]; | 470 var endOfMatch = matchInfo[CAPTURE1]; |
| 480 // Compute the parameter list consisting of the match, captures, index, | 471 // Compute the parameter list consisting of the match, captures, index, |
| 481 // and subject for the replace function invocation. | 472 // and subject for the replace function invocation. |
| 482 // The number of captures plus one for the match. | 473 // The number of captures plus one for the match. |
| 483 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; | 474 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; |
| 484 var replacement; | 475 var replacement; |
| 485 var receiver = %GetDefaultReceiver(replace); | 476 var receiver = %GetDefaultReceiver(replace); |
| 486 if (m == 1) { | 477 if (m == 1) { |
| 487 // No captures, only the match, which is always valid. | 478 // No captures, only the match, which is always valid. |
| 488 var s = SubString(subject, index, endOfMatch); | 479 var s = %_SubString(subject, index, endOfMatch); |
| 489 // Don't call directly to avoid exposing the built-in global object. | 480 // Don't call directly to avoid exposing the built-in global object. |
| 490 replacement = %_CallFunction(receiver, s, index, subject, replace); | 481 replacement = %_CallFunction(receiver, s, index, subject, replace); |
| 491 } else { | 482 } else { |
| 492 var parameters = new InternalArray(m + 2); | 483 var parameters = new InternalArray(m + 2); |
| 493 for (var j = 0; j < m; j++) { | 484 for (var j = 0; j < m; j++) { |
| 494 parameters[j] = CaptureString(subject, matchInfo, j); | 485 parameters[j] = CaptureString(subject, matchInfo, j); |
| 495 } | 486 } |
| 496 parameters[j] = index; | 487 parameters[j] = index; |
| 497 parameters[j + 1] = subject; | 488 parameters[j + 1] = subject; |
| 498 | 489 |
| 499 replacement = %Apply(replace, receiver, parameters, 0, j + 2); | 490 replacement = %Apply(replace, receiver, parameters, 0, j + 2); |
| 500 } | 491 } |
| 501 | 492 |
| 502 result += replacement; // The add method converts to string if necessary. | 493 result += replacement; // The add method converts to string if necessary. |
| 503 // Can't use matchInfo any more from here, since the function could | 494 // Can't use matchInfo any more from here, since the function could |
| 504 // overwrite it. | 495 // overwrite it. |
| 505 return result + SubString(subject, endOfMatch, subject.length); | 496 return result + %_SubString(subject, endOfMatch, subject.length); |
| 506 } | 497 } |
| 507 | 498 |
| 508 | 499 |
| 509 // ECMA-262 section 15.5.4.12 | 500 // ECMA-262 section 15.5.4.12 |
| 510 function StringSearch(re) { | 501 function StringSearch(re) { |
| 511 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 502 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
| 512 throw MakeTypeError("called_on_null_or_undefined", | 503 throw MakeTypeError("called_on_null_or_undefined", |
| 513 ["String.prototype.search"]); | 504 ["String.prototype.search"]); |
| 514 } | 505 } |
| 515 var regexp; | 506 var regexp; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 561 } else { | 552 } else { |
| 562 if (end_i > s_len) { | 553 if (end_i > s_len) { |
| 563 end_i = s_len; | 554 end_i = s_len; |
| 564 } | 555 } |
| 565 } | 556 } |
| 566 | 557 |
| 567 if (end_i <= start_i) { | 558 if (end_i <= start_i) { |
| 568 return ''; | 559 return ''; |
| 569 } | 560 } |
| 570 | 561 |
| 571 return SubString(s, start_i, end_i); | 562 return %_SubString(s, start_i, end_i); |
| 572 } | 563 } |
| 573 | 564 |
| 574 | 565 |
| 575 // ECMA-262 section 15.5.4.14 | 566 // ECMA-262 section 15.5.4.14 |
| 576 function StringSplit(separator, limit) { | 567 function StringSplit(separator, limit) { |
| 577 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 568 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
| 578 throw MakeTypeError("called_on_null_or_undefined", | 569 throw MakeTypeError("called_on_null_or_undefined", |
| 579 ["String.prototype.split"]); | 570 ["String.prototype.split"]); |
| 580 } | 571 } |
| 581 var subject = TO_STRING_INLINE(this); | 572 var subject = TO_STRING_INLINE(this); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 622 | 613 |
| 623 var currentIndex = 0; | 614 var currentIndex = 0; |
| 624 var startIndex = 0; | 615 var startIndex = 0; |
| 625 var startMatch = 0; | 616 var startMatch = 0; |
| 626 var result = []; | 617 var result = []; |
| 627 | 618 |
| 628 outer_loop: | 619 outer_loop: |
| 629 while (true) { | 620 while (true) { |
| 630 | 621 |
| 631 if (startIndex === length) { | 622 if (startIndex === length) { |
| 632 result.push(SubString(subject, currentIndex, length)); | 623 result.push(%_SubString(subject, currentIndex, length)); |
| 633 break; | 624 break; |
| 634 } | 625 } |
| 635 | 626 |
| 636 var matchInfo = DoRegExpExec(separator, subject, startIndex); | 627 var matchInfo = DoRegExpExec(separator, subject, startIndex); |
| 637 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) { | 628 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) { |
| 638 result.push(SubString(subject, currentIndex, length)); | 629 result.push(%_SubString(subject, currentIndex, length)); |
| 639 break; | 630 break; |
| 640 } | 631 } |
| 641 var endIndex = matchInfo[CAPTURE1]; | 632 var endIndex = matchInfo[CAPTURE1]; |
| 642 | 633 |
| 643 // We ignore a zero-length match at the currentIndex. | 634 // We ignore a zero-length match at the currentIndex. |
| 644 if (startIndex === endIndex && endIndex === currentIndex) { | 635 if (startIndex === endIndex && endIndex === currentIndex) { |
| 645 startIndex++; | 636 startIndex++; |
| 646 continue; | 637 continue; |
| 647 } | 638 } |
| 648 | 639 |
| 649 if (currentIndex + 1 == startMatch) { | 640 result.push(%_SubString(subject, currentIndex, startMatch)); |
| 650 result.push(%_StringCharAt(subject, currentIndex)); | |
| 651 } else { | |
| 652 result.push(%_SubString(subject, currentIndex, startMatch)); | |
| 653 } | |
| 654 | 641 |
| 655 if (result.length === limit) break; | 642 if (result.length === limit) break; |
| 656 | 643 |
| 657 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE; | 644 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE; |
| 658 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) { | 645 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) { |
| 659 var start = matchInfo[i++]; | 646 var start = matchInfo[i++]; |
| 660 var end = matchInfo[i++]; | 647 var end = matchInfo[i++]; |
| 661 if (end != -1) { | 648 if (end != -1) { |
| 662 if (start + 1 == end) { | 649 result.push(%_SubString(subject, start, end)); |
| 663 result.push(%_StringCharAt(subject, start)); | |
| 664 } else { | |
| 665 result.push(%_SubString(subject, start, end)); | |
| 666 } | |
| 667 } else { | 650 } else { |
| 668 result.push(void 0); | 651 result.push(void 0); |
| 669 } | 652 } |
| 670 if (result.length === limit) break outer_loop; | 653 if (result.length === limit) break outer_loop; |
| 671 } | 654 } |
| 672 | 655 |
| 673 startIndex = currentIndex = endIndex; | 656 startIndex = currentIndex = endIndex; |
| 674 } | 657 } |
| 675 return result; | 658 return result; |
| 676 } | 659 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 700 } else { | 683 } else { |
| 701 if (end_i < 0) end_i = 0; | 684 if (end_i < 0) end_i = 0; |
| 702 if (start_i > end_i) { | 685 if (start_i > end_i) { |
| 703 var tmp = end_i; | 686 var tmp = end_i; |
| 704 end_i = start_i; | 687 end_i = start_i; |
| 705 start_i = tmp; | 688 start_i = tmp; |
| 706 } | 689 } |
| 707 } | 690 } |
| 708 } | 691 } |
| 709 | 692 |
| 710 return ((start_i + 1 == end_i) | 693 return %_SubString(s, start_i, end_i); |
| 711 ? %_StringCharAt(s, start_i) | |
| 712 : %_SubString(s, start_i, end_i)); | |
| 713 } | 694 } |
| 714 | 695 |
| 715 | 696 |
| 716 // This is not a part of ECMA-262. | 697 // This is not a part of ECMA-262. |
| 717 function StringSubstr(start, n) { | 698 function StringSubstr(start, n) { |
| 718 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 699 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
| 719 throw MakeTypeError("called_on_null_or_undefined", | 700 throw MakeTypeError("called_on_null_or_undefined", |
| 720 ["String.prototype.substr"]); | 701 ["String.prototype.substr"]); |
| 721 } | 702 } |
| 722 var s = TO_STRING_INLINE(this); | 703 var s = TO_STRING_INLINE(this); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 744 // use zero. | 725 // use zero. |
| 745 if (start < 0) { | 726 if (start < 0) { |
| 746 start += s.length; | 727 start += s.length; |
| 747 if (start < 0) start = 0; | 728 if (start < 0) start = 0; |
| 748 } | 729 } |
| 749 } | 730 } |
| 750 | 731 |
| 751 var end = start + len; | 732 var end = start + len; |
| 752 if (end > s.length) end = s.length; | 733 if (end > s.length) end = s.length; |
| 753 | 734 |
| 754 return ((start + 1 == end) | 735 return %_SubString(s, start, end); |
| 755 ? %_StringCharAt(s, start) | |
| 756 : %_SubString(s, start, end)); | |
| 757 } | 736 } |
| 758 | 737 |
| 759 | 738 |
| 760 // ECMA-262, 15.5.4.16 | 739 // ECMA-262, 15.5.4.16 |
| 761 function StringToLowerCase() { | 740 function StringToLowerCase() { |
| 762 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 741 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
| 763 throw MakeTypeError("called_on_null_or_undefined", | 742 throw MakeTypeError("called_on_null_or_undefined", |
| 764 ["String.prototype.toLowerCase"]); | 743 ["String.prototype.toLowerCase"]); |
| 765 } | 744 } |
| 766 return %StringToLowerCase(TO_STRING_INLINE(this)); | 745 return %StringToLowerCase(TO_STRING_INLINE(this)); |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1015 "fixed", StringFixed, | 994 "fixed", StringFixed, |
| 1016 "italics", StringItalics, | 995 "italics", StringItalics, |
| 1017 "small", StringSmall, | 996 "small", StringSmall, |
| 1018 "strike", StringStrike, | 997 "strike", StringStrike, |
| 1019 "sub", StringSub, | 998 "sub", StringSub, |
| 1020 "sup", StringSup | 999 "sup", StringSup |
| 1021 )); | 1000 )); |
| 1022 } | 1001 } |
| 1023 | 1002 |
| 1024 SetUpString(); | 1003 SetUpString(); |
| OLD | NEW |