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

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/core/int.dart » ('j') | sdk/lib/core/int.dart » ('J')
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 JsGetName, 10 JsGetName,
(...skipping 568 matching lines...) Expand 10 before | Expand all | Expand 10 after
579 579
580 static int objectHashCode(object) { 580 static int objectHashCode(object) {
581 int hash = JS('int|Null', r'#.$identityHash', object); 581 int hash = JS('int|Null', r'#.$identityHash', object);
582 if (hash == null) { 582 if (hash == null) {
583 hash = JS('int', '(Math.random() * 0x3fffffff) | 0'); 583 hash = JS('int', '(Math.random() * 0x3fffffff) | 0');
584 JS('void', r'#.$identityHash = #', object, hash); 584 JS('void', r'#.$identityHash = #', object, hash);
585 } 585 }
586 return JS('int', '#', hash); 586 return JS('int', '#', hash);
587 } 587 }
588 588
589 static _throwFormatException(String string) { 589 @NoInline()
590 throw new FormatException(string); 590 static int _parseError(String source, int handleError(String source)) {
591 if (handleError == null) throw new FormatException(source);
592 return handleError(source);
591 } 593 }
592 594
593 static int parseInt(String source, 595 static int parseInt(String source,
594 int radix, 596 int radix,
595 int handleError(String source)) { 597 int handleError(String source)) {
596 if (handleError == null) handleError = _throwFormatException;
597
598 checkString(source); 598 checkString(source);
599 var match = JS('JSExtendableArray|Null', 599 var re = JS('', r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i');
600 r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(#)', 600 var match = JS('JSExtendableArray|Null', '#.exec(#)', re, source);
601 source);
602 int digitsIndex = 1; 601 int digitsIndex = 1;
603 int hexIndex = 2; 602 int hexIndex = 2;
604 int decimalIndex = 3; 603 int decimalIndex = 3;
605 int nonDecimalHexIndex = 4; 604 int nonDecimalHexIndex = 4;
605 if (match == null) {
606 // TODO(sra): It might be that the match failed due to unrecognized U+0085
607 // spaces. We could replace them with U+0020 spaces and try matching
608 // again.
Lasse Reichstein Nielsen 2015/03/06 13:06:51 If \s doesn't always include U+0085, maybe change
sra1 2015/03/06 21:38:38 In that case, JavaScript's parseInt fails too.
609 return _parseError(source, handleError);
610 }
606 if (radix == null) { 611 if (radix == null) {
607 radix = 10; 612 if (match[decimalIndex] != null) {
608 if (match != null) { 613 // Cannot fail because we know that the digits are all decimal.
609 if (match[hexIndex] != null) { 614 return JS('int', r'parseInt(#, 10)', source);
610 // Cannot fail because we know that the digits are all hex.
611 return JS('num', r'parseInt(#, 16)', source);
612 }
613 if (match[decimalIndex] != null) {
614 // Cannot fail because we know that the digits are all decimal.
615 return JS('num', r'parseInt(#, 10)', source);
616 }
617 return handleError(source);
618 } 615 }
619 } else { 616 if (match[hexIndex] != null) {
620 if (radix is! int) throw new ArgumentError("Radix is not an integer"); 617 // Cannot fail because we know that the digits are all hex.
621 if (radix < 2 || radix > 36) { 618 return JS('int', r'parseInt(#, 16)', source);
622 throw new RangeError("Radix $radix not in range 2..36");
623 } 619 }
624 if (match != null) { 620 return _parseError(source, handleError);
625 if (radix == 10 && match[decimalIndex] != null) { 621 }
626 // Cannot fail because we know that the digits are all decimal. 622
627 return JS('num', r'parseInt(#, 10)', source); 623 if (radix is! int) throw new ArgumentError("Radix is not an integer");
628 } 624 if (radix < 2 || radix > 36) {
629 if (radix < 10 || match[decimalIndex] == null) { 625 throw new RangeError("Radix $radix not in range 2..36");
Lasse Reichstein Nielsen 2015/03/06 13:06:51 You can change this one to: throw new RangeError
sra1 2015/03/06 21:38:39 Done.
630 // We know that the characters must be ASCII as otherwise the 626 }
631 // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus 627 if (radix == 10 && match[decimalIndex] != null) {
632 // guaranteed to be a safe operation, since it preserves digits 628 // Cannot fail because we know that the digits are all decimal.
633 // and lower-cases ASCII letters. 629 return JS('int', r'parseInt(#, 10)', source);
634 int maxCharCode; 630 }
635 if (radix <= 10) { 631 if (radix < 10 || match[decimalIndex] == null) {
636 // Allow all digits less than the radix. For example 0, 1, 2 for 632 // We know that the characters must be ASCII as otherwise the
637 // radix 3. 633 // regexp wouldn't have matched. Lowercasing by doing `| 0x20` is thus
638 // "0".codeUnitAt(0) + radix - 1; 634 // guaranteed to be a safe operation, since it preserves digits
639 maxCharCode = 0x30 + radix - 1; 635 // and lower-cases ASCII letters.
Lasse Reichstein Nielsen 2015/03/06 13:06:51 How about doing parseInt first, and only scanning
sra1 2015/03/06 21:38:38 Another idea is to have a table of per-radix regex
640 } else { 636 int maxCharCode;
641 // Letters are located after the digits in ASCII. Therefore we 637 if (radix <= 10) {
642 // only check for the character code. The regexp above made already 638 // Allow all digits less than the radix. For example 0, 1, 2 for
643 // sure that the string does not contain anything but digits or 639 // radix 3.
644 // letters. 640 // "0".codeUnitAt(0) + radix - 1;
645 // "a".codeUnitAt(0) + (radix - 10) - 1; 641 maxCharCode = 0x30 + radix - 1;
Lasse Reichstein Nielsen 2015/03/06 13:06:51 Maybe: 0x30 - 1 + radix to constant fold (0x30-1)
sra1 2015/03/06 21:38:38 Done.
646 maxCharCode = 0x61 + radix - 10 - 1; 642 } else {
647 } 643 // Letters are located after the digits in ASCII. Therefore we
648 String digitsPart = match[digitsIndex]; 644 // only check for the character code. The regexp above made already
649 for (int i = 0; i < digitsPart.length; i++) { 645 // sure that the string does not contain anything but digits or
650 int characterCode = digitsPart.codeUnitAt(0) | 0x20; 646 // letters.
651 if (digitsPart.codeUnitAt(i) > maxCharCode) { 647 // "a".codeUnitAt(0) + (radix - 10) - 1;
652 return handleError(source); 648 maxCharCode = 0x61 + radix - 10 - 1;
Lasse Reichstein Nielsen 2015/03/06 13:06:51 Similar constant folding possible.
sra1 2015/03/06 21:38:38 Done.
653 } 649 }
654 } 650 assert(match[digitsIndex] is String);
651 String digitsPart = JS('String', '#[#]', match, digitsIndex);
652 for (int i = 0; i < digitsPart.length; i++) {
653 int characterCode = digitsPart.codeUnitAt(i) | 0x20;
654 if (characterCode > maxCharCode) {
655 return _parseError(source, handleError);
655 } 656 }
656 } 657 }
657 } 658 }
658 if (match == null) return handleError(source); 659 return JS('int', r'parseInt(#, #)', source, radix);
Lasse Reichstein Nielsen 2015/03/06 13:06:51 Make a comment that the checks above guarantee tha
sra1 2015/03/06 21:38:39 Done.
659 return JS('num', r'parseInt(#, #)', source, radix);
660 } 660 }
661 661
662 static double parseDouble(String source, double handleError(String source)) { 662 static double parseDouble(String source, double handleError(String source)) {
663 checkString(source); 663 checkString(source);
664 if (handleError == null) handleError = _throwFormatException;
665 // Notice that JS parseFloat accepts garbage at the end of the string. 664 // Notice that JS parseFloat accepts garbage at the end of the string.
666 // Accept only: 665 // Accept only:
667 // - [+/-]NaN 666 // - [+/-]NaN
668 // - [+/-]Infinity 667 // - [+/-]Infinity
669 // - a Dart double literal 668 // - a Dart double literal
670 // We do allow leading or trailing whitespace. 669 // We do allow leading or trailing whitespace.
671 if (!JS('bool', 670 if (!JS('bool',
672 r'/^\s*[+-]?(?:Infinity|NaN|' 671 r'/^\s*[+-]?(?:Infinity|NaN|'
673 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', 672 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)',
674 source)) { 673 source)) {
675 return handleError(source); 674 return _parseError(source, handleError);
676 } 675 }
677 var result = JS('num', r'parseFloat(#)', source); 676 var result = JS('num', r'parseFloat(#)', source);
678 if (result.isNaN) { 677 if (result.isNaN) {
679 var trimmed = source.trim(); 678 var trimmed = source.trim();
680 if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') { 679 if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') {
681 return result; 680 return result;
682 } 681 }
683 return handleError(source); 682 return _parseError(source, handleError);
684 } 683 }
685 return result; 684 return result;
686 } 685 }
687 686
688 /** [: r"$".codeUnitAt(0) :] */ 687 /** [: r"$".codeUnitAt(0) :] */
689 static const int DOLLAR_CHAR_VALUE = 36; 688 static const int DOLLAR_CHAR_VALUE = 36;
690 689
691 /// Creates a string containing the complete type for the class [className] 690 /// Creates a string containing the complete type for the class [className]
692 /// with the given type arguments. 691 /// with the given type arguments.
693 /// 692 ///
(...skipping 3169 matching lines...) Expand 10 before | Expand all | Expand 10 after
3863 // This is a function that will return a helper function that does the 3862 // This is a function that will return a helper function that does the
3864 // iteration of the sync*. 3863 // iteration of the sync*.
3865 // 3864 //
3866 // Each invocation should give a body with fresh state. 3865 // Each invocation should give a body with fresh state.
3867 final dynamic /* js function */ _outerHelper; 3866 final dynamic /* js function */ _outerHelper;
3868 3867
3869 SyncStarIterable(this._outerHelper); 3868 SyncStarIterable(this._outerHelper);
3870 3869
3871 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper)); 3870 Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper));
3872 } 3871 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/core/int.dart » ('j') | sdk/lib/core/int.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698