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 DEFERRED_LIBRARY_URIS, | 10 DEFERRED_LIBRARY_URIS, |
(...skipping 574 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 Loading... |
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 } |
OLD | NEW |