OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |