Chromium Code Reviews| 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 |