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

Side by Side Diff: sdk/lib/_internal/compiler/js_lib/js_helper.dart

Issue 988523002: Fix int.parse bug (dart2js version) (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 9 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 | sdk/lib/_internal/compiler/js_lib/js_number.dart » ('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 (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library _js_helper; 5 library _js_helper;
6 6
7 import 'dart:_async_await_error_codes' as async_error_codes; 7 import 'dart:_async_await_error_codes' as async_error_codes;
8 8
9 import 'dart:_js_embedded_names' show 9 import 'dart:_js_embedded_names' show
10 DEFERRED_LIBRARY_URIS, 10 DEFERRED_LIBRARY_URIS,
(...skipping 574 matching lines...) Expand 10 before | Expand all | Expand 10 after
585 585
586 static int objectHashCode(object) { 586 static int objectHashCode(object) {
587 int hash = JS('int|Null', r'#.$identityHash', object); 587 int hash = JS('int|Null', r'#.$identityHash', object);
588 if (hash == null) { 588 if (hash == null) {
589 hash = JS('int', '(Math.random() * 0x3fffffff) | 0'); 589 hash = JS('int', '(Math.random() * 0x3fffffff) | 0');
590 JS('void', r'#.$identityHash = #', object, hash); 590 JS('void', r'#.$identityHash = #', object, hash);
591 } 591 }
592 return JS('int', '#', hash); 592 return JS('int', '#', hash);
593 } 593 }
594 594
595 static _throwFormatException(String string) { 595 @NoInline()
596 throw new FormatException(string); 596 static int _parseIntError(String source, int handleError(String source)) {
597 if (handleError == null) throw new FormatException(source);
598 return handleError(source);
597 } 599 }
598 600
599 static int parseInt(String source, 601 static int parseInt(String source,
600 int radix, 602 int radix,
601 int handleError(String source)) { 603 int handleError(String source)) {
602 if (handleError == null) handleError = _throwFormatException;
603
604 checkString(source); 604 checkString(source);
605 var match = JS('JSExtendableArray|Null', 605 var re = JS('', r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i');
606 r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(#)', 606 var match = JS('JSExtendableArray|Null', '#.exec(#)', re, source);
607 source);
608 int digitsIndex = 1; 607 int digitsIndex = 1;
609 int hexIndex = 2; 608 int hexIndex = 2;
610 int decimalIndex = 3; 609 int decimalIndex = 3;
611 int nonDecimalHexIndex = 4; 610 int nonDecimalHexIndex = 4;
611 if (match == null) {
612 // TODO(sra): It might be that the match failed due to unrecognized U+0085
613 // spaces. We could replace them with U+0020 spaces and try matching
614 // again.
615 return _parseIntError(source, handleError);
616 }
617 String decimalMatch = match[decimalIndex];
612 if (radix == null) { 618 if (radix == null) {
613 radix = 10; 619 if (decimalMatch != null) {
614 if (match != null) { 620 // Cannot fail because we know that the digits are all decimal.
615 if (match[hexIndex] != null) { 621 return JS('int', r'parseInt(#, 10)', source);
616 // Cannot fail because we know that the digits are all hex.
617 return JS('num', r'parseInt(#, 16)', source);
618 }
619 if (match[decimalIndex] != null) {
620 // Cannot fail because we know that the digits are all decimal.
621 return JS('num', r'parseInt(#, 10)', source);
622 }
623 return handleError(source);
624 } 622 }
625 } else { 623 if (match[hexIndex] != null) {
626 if (radix is! int) throw new ArgumentError("Radix is not an integer"); 624 // Cannot fail because we know that the digits are all hex.
627 if (radix < 2 || radix > 36) { 625 return JS('int', r'parseInt(#, 16)', source);
628 throw new RangeError("Radix $radix not in range 2..36");
629 } 626 }
630 if (match != null) { 627 return _parseIntError(source, handleError);
631 if (radix == 10 && match[decimalIndex] != null) { 628 }
632 // Cannot fail because we know that the digits are all decimal. 629
633 return JS('num', r'parseInt(#, 10)', source); 630 if (radix is! int) throw new ArgumentError("Radix is not an integer");
634 } 631 if (radix < 2 || radix > 36) {
635 if (radix < 10 || match[decimalIndex] == null) { 632 throw new RangeError.range(radix, 2, 36, "radix");
636 // We know that the characters must be ASCII as otherwise the 633 }
637 // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus 634 if (radix == 10 && decimalMatch != null) {
638 // guaranteed to be a safe operation, since it preserves digits 635 // Cannot fail because we know that the digits are all decimal.
639 // and lower-cases ASCII letters. 636 return JS('int', r'parseInt(#, 10)', source);
640 int maxCharCode; 637 }
641 if (radix <= 10) { 638 // If radix >= 10 and we have only decimal digits the string is safe.
642 // Allow all digits less than the radix. For example 0, 1, 2 for 639 // Otherwise we need to check the digits.
643 // radix 3. 640 if (radix < 10 || decimalMatch == null) {
644 // "0".codeUnitAt(0) + radix - 1; 641 // We know that the characters must be ASCII as otherwise the
645 maxCharCode = 0x30 + radix - 1; 642 // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus
646 } else { 643 // guaranteed to be a safe operation, since it preserves digits
647 // Letters are located after the digits in ASCII. Therefore we 644 // and lower-cases ASCII letters.
648 // only check for the character code. The regexp above made already 645 int maxCharCode;
649 // sure that the string does not contain anything but digits or 646 if (radix <= 10) {
650 // letters. 647 // Allow all digits less than the radix. For example 0, 1, 2 for
651 // "a".codeUnitAt(0) + (radix - 10) - 1; 648 // radix 3.
652 maxCharCode = 0x61 + radix - 10 - 1; 649 // "0".codeUnitAt(0) + radix - 1;
653 } 650 maxCharCode = (0x30 - 1) + radix;
654 String digitsPart = match[digitsIndex]; 651 } else {
655 for (int i = 0; i < digitsPart.length; i++) { 652 // Letters are located after the digits in ASCII. Therefore we
656 int characterCode = digitsPart.codeUnitAt(0) | 0x20; 653 // only check for the character code. The regexp above made already
657 if (digitsPart.codeUnitAt(i) > maxCharCode) { 654 // sure that the string does not contain anything but digits or
658 return handleError(source); 655 // letters.
659 } 656 // "a".codeUnitAt(0) + (radix - 10) - 1;
660 } 657 maxCharCode = (0x61 - 10 - 1) + radix;
658 }
659 assert(match[digitsIndex] is String);
660 String digitsPart = JS('String', '#[#]', match, digitsIndex);
661 for (int i = 0; i < digitsPart.length; i++) {
662 int characterCode = digitsPart.codeUnitAt(i) | 0x20;
663 if (characterCode > maxCharCode) {
664 return _parseIntError(source, handleError);
661 } 665 }
662 } 666 }
663 } 667 }
664 if (match == null) return handleError(source); 668 // The above matching and checks ensures the source has at least one digits
665 return JS('num', r'parseInt(#, #)', source, radix); 669 // and all digits are suitable for the radix, so parseInt cannot return NaN.
670 return JS('int', r'parseInt(#, #)', source, radix);
671 }
672
673 @NoInline()
674 static double _parseDoubleError(String source,
675 double handleError(String source)) {
676 if (handleError == null) throw new FormatException(source);
677 return handleError(source);
666 } 678 }
667 679
668 static double parseDouble(String source, double handleError(String source)) { 680 static double parseDouble(String source, double handleError(String source)) {
669 checkString(source); 681 checkString(source);
670 if (handleError == null) handleError = _throwFormatException;
671 // Notice that JS parseFloat accepts garbage at the end of the string. 682 // Notice that JS parseFloat accepts garbage at the end of the string.
672 // Accept only: 683 // Accept only:
673 // - [+/-]NaN 684 // - [+/-]NaN
674 // - [+/-]Infinity 685 // - [+/-]Infinity
675 // - a Dart double literal 686 // - a Dart double literal
676 // We do allow leading or trailing whitespace. 687 // We do allow leading or trailing whitespace.
677 if (!JS('bool', 688 if (!JS('bool',
678 r'/^\s*[+-]?(?:Infinity|NaN|' 689 r'/^\s*[+-]?(?:Infinity|NaN|'
679 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', 690 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)',
680 source)) { 691 source)) {
681 return handleError(source); 692 return _parseDoubleError(source, handleError);
682 } 693 }
683 var result = JS('num', r'parseFloat(#)', source); 694 var result = JS('num', r'parseFloat(#)', source);
684 if (result.isNaN) { 695 if (result.isNaN) {
685 var trimmed = source.trim(); 696 var trimmed = source.trim();
686 if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') { 697 if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') {
687 return result; 698 return result;
688 } 699 }
689 return handleError(source); 700 return _parseDoubleError(source, handleError);
690 } 701 }
691 return result; 702 return result;
692 } 703 }
693 704
694 /** [: r"$".codeUnitAt(0) :] */ 705 /** [: r"$".codeUnitAt(0) :] */
695 static const int DOLLAR_CHAR_VALUE = 36; 706 static const int DOLLAR_CHAR_VALUE = 36;
696 707
697 /// Creates a string containing the complete type for the class [className] 708 /// Creates a string containing the complete type for the class [className]
698 /// with the given type arguments. 709 /// with the given type arguments.
699 /// 710 ///
(...skipping 3170 matching lines...) Expand 10 before | Expand all | Expand 10 after
3870 // This is a function that will return a helper function that does the 3881 // This is a function that will return a helper function that does the
3871 // iteration of the sync*. 3882 // iteration of the sync*.
3872 // 3883 //
3873 // Each invocation should give a body with fresh state. 3884 // Each invocation should give a body with fresh state.
3874 final dynamic /* js function */ _outerHelper; 3885 final dynamic /* js function */ _outerHelper;
3875 3886
3876 SyncStarIterable(this._outerHelper); 3887 SyncStarIterable(this._outerHelper);
3877 3888
3878 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); 3889 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper));
3879 } 3890 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/js_lib/js_number.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698