Index: sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
index 39e1c5c8f651b7254d2ac1d00b522d3925ff7525..fb7d66d84798eb3f5c4e20cc2409af6d10d53a32 100644 |
--- a/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
+++ b/sdk/lib/_internal/compiler/implementation/lib/js_helper.dart |
@@ -339,19 +339,6 @@ String S(value) { |
return res; |
} |
-class ListIterator<T> implements Iterator<T> { |
- int i; |
- List<T> list; |
- ListIterator(List<T> this.list) : i = 0; |
- bool get hasNext => i < JS('int', r'#.length', list); |
- T next() { |
- if (!hasNext) throw new StateError("No more elements"); |
- var value = JS('', r'#[#]', list, i); |
- i += 1; |
- return value; |
- } |
-} |
- |
createInvocationMirror(name, internalName, type, arguments, argumentNames) => |
new JSInvocationMirror(name, internalName, type, arguments, argumentNames); |
@@ -461,37 +448,97 @@ class Primitives { |
JS('void', "throw 'Unable to print message: ' + String(#)", string); |
} |
- static int parseInt(String string) { |
- checkString(string); |
+ static void _throwFormatException(String string) { |
+ throw new FormatException(string); |
+ } |
+ |
+ static int parseInt(String source, |
+ int radix, |
+ int handleError(String source)) { |
+ if (handleError == null) handleError = _throwFormatException; |
+ |
+ checkString(source); |
var match = JS('=List|Null', |
- r'/^\s*[+-]?(?:0(x)[a-f0-9]+|\d+)\s*$/i.exec(#)', |
- string); |
- if (match == null) { |
- throw new FormatException(string); |
+ r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(#)', |
+ source); |
+ int digitsIndex = 1; |
+ int hexIndex = 2; |
+ int decimalIndex = 3; |
+ int nonDecimalHexIndex = 4; |
+ if (radix == null) { |
+ radix = 10; |
+ if (match != null) { |
+ if (match[hexIndex] != null) { |
+ // Cannot fail because we know that the digits are all hex. |
+ return JS('num', r'parseInt(#, 16)', source); |
+ } |
+ if (match[decimalIndex] != null) { |
+ // Cannot fail because we know that the digits are all decimal. |
+ return JS('num', r'parseInt(#, 10)', source); |
+ } |
+ return handleError(source); |
+ } |
+ } else { |
+ if (radix is! int) throw new ArgumentError("Radix is not an integer"); |
+ if (radix < 2 || radix > 36) { |
+ throw new RangeError("Radix $radix not in range 2..36"); |
+ } |
+ if (match != null) { |
+ if (radix == 10 && match[decimalIndex] != null) { |
+ // Cannot fail because we know that the digits are all decimal. |
+ return JS('num', r'parseInt(#, 10)', source); |
+ } |
+ if (radix < 10 || match[decimalIndex] == null) { |
+ // We know that the characters must be ASCII as otherwise the |
+ // regexp wouldn't have matched. Calling toLowerCase is thus |
+ // guaranteed to be a safe operation. If it wasn't ASCII, then |
+ // "İ" would become "i", and we would accept it for radices greater |
+ // than 18. |
+ int maxCharCode; |
+ if (radix <= 10) { |
+ // Allow all digits less than the radix. For example 0, 1, 2 for |
+ // radix 3. |
+ // "0".charCodeAt(0) + radix - 1; |
+ maxCharCode = 0x30 + radix - 1; |
+ } else { |
+ // Characters are located after the digits in ASCII. Therefore we |
+ // only check for the character code. The regexp above made already |
+ // sure that the string does not contain anything but digits or |
+ // characters. |
+ // "0".charCodeAt(0) + radix - 1; |
+ maxCharCode = 0x61 + radix - 10 - 1; |
+ } |
+ String digitsPart = match[digitsIndex].toLowerCase(); |
+ for (int i = 0; i < digitsPart.length; i++) { |
+ if (digitsPart.charCodeAt(i) > maxCharCode) { |
+ return handleError(source); |
+ } |
+ } |
+ } |
+ } |
} |
- var base = 10; |
- if (match[1] != null) base = 16; |
- var result = JS('num', r'parseInt(#, #)', string, base); |
- if (result.isNaN) throw new FormatException(string); |
- return result; |
+ if (match == null) return handleError(source); |
+ return JS('num', r'parseInt(#, #)', source, radix); |
} |
- static double parseDouble(String string) { |
- checkString(string); |
+ static double parseDouble(String source, int handleError(String source)) { |
+ checkString(source); |
+ if (handleError == null) handleError = _throwFormatException; |
// Notice that JS parseFloat accepts garbage at the end of the string. |
- // Accept, ignoring leading and trailing whitespace: |
+ // Accept only: |
// - NaN |
// - [+/-]Infinity |
- // - a Dart double literal |
+ // - a Dart double literal |
+ // We do not allow leading or trailing whitespace. |
if (!JS('bool', |
r'/^\s*(?:NaN|[+-]?(?:Infinity|' |
r'(?:\.\d+|\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?))\s*$/.test(#)', |
- string)) { |
- throw new FormatException(string); |
+ source)) { |
+ return handleError(source); |
} |
- var result = JS('num', r'parseFloat(#)', string); |
- if (result.isNaN && string != 'NaN') { |
- throw new FormatException(string); |
+ var result = JS('num', r'parseFloat(#)', source); |
+ if (result.isNaN && source != 'NaN') { |
+ return handleError(source); |
} |
return result; |
} |
@@ -520,14 +567,17 @@ class Primitives { |
return "Instance of '$name'"; |
} |
- static List newList(length) { |
+ static List newGrowableList(length) { |
+ // TODO(sra): For good concrete type analysis we need the JS-type to |
+ // specifically name the JavaScript Array implementation. 'List' matches |
+ // all the dart:html types that implement List<T>. |
+ return JS('Object', r'new Array(#)', length); |
+ } |
+ |
+ static List newFixedList(length) { |
// TODO(sra): For good concrete type analysis we need the JS-type to |
// specifically name the JavaScript Array implementation. 'List' matches |
// all the dart:html types that implement List<T>. |
- if (length == null) return JS('=List', r'new Array()'); |
- if ((length is !int) || (length < 0)) { |
- throw new ArgumentError(length); |
- } |
var result = JS('=List', r'new Array(#)', length); |
JS('void', r'#.fixed$length = #', result, true); |
return result; |
@@ -855,37 +905,6 @@ checkString(value) { |
} |
class MathNatives { |
- static int parseInt(str) { |
- checkString(str); |
- if (!JS('bool', |
- r'/^\s*[+-]?(?:0[xX][abcdefABCDEF0-9]+|\d+)\s*$/.test(#)', |
- str)) { |
- throw new FormatException(str); |
- } |
- var trimmed = str.trim(); |
- var base = 10;; |
- if ((trimmed.length > 2 && (trimmed[1] == 'x' || trimmed[1] == 'X')) || |
- (trimmed.length > 3 && (trimmed[2] == 'x' || trimmed[2] == 'X'))) { |
- base = 16; |
- } |
- var ret = JS('num', r'parseInt(#, #)', trimmed, base); |
- if (ret.isNaN) throw new FormatException(str); |
- return ret; |
- } |
- |
- static double parseDouble(String str) { |
- checkString(str); |
- var ret = JS('num', r'parseFloat(#)', str); |
- if (ret == 0 && (str.startsWith("0x") || str.startsWith("0X"))) { |
- // TODO(ahe): This is unspecified, but tested by co19. |
- ret = JS('num', r'parseInt(#)', str); |
- } |
- if (ret.isNaN && str != 'NaN' && str != '-NaN') { |
- throw new FormatException(str); |
- } |
- return ret; |
- } |
- |
static double sqrt(num value) |
=> JS('double', r'Math.sqrt(#)', checkNum(value)); |
@@ -1084,11 +1103,12 @@ class StackTrace { |
* a list of key, value, key, value, ..., etc. |
*/ |
makeLiteralMap(List keyValuePairs) { |
- Iterator iterator = keyValuePairs.iterator(); |
+ Iterator iterator = keyValuePairs.iterator; |
Map result = new LinkedHashMap(); |
- while (iterator.hasNext) { |
- String key = iterator.next(); |
- var value = iterator.next(); |
+ while (iterator.moveNext()) { |
+ String key = iterator.current; |
+ iterator.moveNext(); |
+ var value = iterator.current; |
result[key] = value; |
} |
return result; |