| 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 |