| 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 dart._js_helper; | 5 library dart._js_helper; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'dart:_debugger' show stackTraceMapper; | 9 import 'dart:_debugger' show stackTraceMapper; |
| 10 | 10 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 66 return JS('int', '#', hash); | 66 return JS('int', '#', hash); |
| 67 } | 67 } |
| 68 | 68 |
| 69 @NoInline() | 69 @NoInline() |
| 70 static int _parseIntError(String source, int handleError(String source)) { | 70 static int _parseIntError(String source, int handleError(String source)) { |
| 71 if (handleError == null) throw new FormatException(source); | 71 if (handleError == null) throw new FormatException(source); |
| 72 return handleError(source); | 72 return handleError(source); |
| 73 } | 73 } |
| 74 | 74 |
| 75 static int parseInt( | 75 static int parseInt( |
| 76 String source, int radix, int handleError(String source)) { | 76 @nullCheck String source, int _radix, int handleError(String source)) { |
| 77 checkString(source); | |
| 78 var re = JS('', r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i'); | 77 var re = JS('', r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i'); |
| 79 var/*=JSArray<String>*/ match = | 78 var/*=JSArray<String>*/ match = |
| 80 JS('JSExtendableArray|Null', '#.exec(#)', re, source); | 79 JS('JSExtendableArray|Null', '#.exec(#)', re, source); |
| 81 int digitsIndex = 1; | 80 int digitsIndex = 1; |
| 82 int hexIndex = 2; | 81 int hexIndex = 2; |
| 83 int decimalIndex = 3; | 82 int decimalIndex = 3; |
| 84 int nonDecimalHexIndex = 4; | 83 int nonDecimalHexIndex = 4; |
| 85 if (match == null) { | 84 if (match == null) { |
| 86 // TODO(sra): It might be that the match failed due to unrecognized U+0085 | 85 // TODO(sra): It might be that the match failed due to unrecognized U+0085 |
| 87 // spaces. We could replace them with U+0020 spaces and try matching | 86 // spaces. We could replace them with U+0020 spaces and try matching |
| 88 // again. | 87 // again. |
| 89 return _parseIntError(source, handleError); | 88 return _parseIntError(source, handleError); |
| 90 } | 89 } |
| 91 String decimalMatch = match[decimalIndex]; | 90 String decimalMatch = match[decimalIndex]; |
| 92 if (radix == null) { | 91 if (_radix == null) { |
| 93 if (decimalMatch != null) { | 92 if (decimalMatch != null) { |
| 94 // Cannot fail because we know that the digits are all decimal. | 93 // Cannot fail because we know that the digits are all decimal. |
| 95 return JS('int', r'parseInt(#, 10)', source); | 94 return JS('int', r'parseInt(#, 10)', source); |
| 96 } | 95 } |
| 97 if (match[hexIndex] != null) { | 96 if (match[hexIndex] != null) { |
| 98 // Cannot fail because we know that the digits are all hex. | 97 // Cannot fail because we know that the digits are all hex. |
| 99 return JS('int', r'parseInt(#, 16)', source); | 98 return JS('int', r'parseInt(#, 16)', source); |
| 100 } | 99 } |
| 101 return _parseIntError(source, handleError); | 100 return _parseIntError(source, handleError); |
| 102 } | 101 } |
| 103 | 102 @notNull var radix = _radix; |
| 104 if (radix is! int) { | |
| 105 throw new ArgumentError.value(radix, 'radix', 'is not an integer'); | |
| 106 } | |
| 107 if (radix < 2 || radix > 36) { | 103 if (radix < 2 || radix > 36) { |
| 108 throw new RangeError.range(radix, 2, 36, 'radix'); | 104 throw new RangeError.range(radix, 2, 36, 'radix'); |
| 109 } | 105 } |
| 110 if (radix == 10 && decimalMatch != null) { | 106 if (radix == 10 && decimalMatch != null) { |
| 111 // Cannot fail because we know that the digits are all decimal. | 107 // Cannot fail because we know that the digits are all decimal. |
| 112 return JS('int', r'parseInt(#, 10)', source); | 108 return JS('int', r'parseInt(#, 10)', source); |
| 113 } | 109 } |
| 114 // If radix >= 10 and we have only decimal digits the string is safe. | 110 // If radix >= 10 and we have only decimal digits the string is safe. |
| 115 // Otherwise we need to check the digits. | 111 // Otherwise we need to check the digits. |
| 116 if (radix < 10 || decimalMatch == null) { | 112 if (radix < 10 || decimalMatch == null) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 | 144 |
| 149 @NoInline() | 145 @NoInline() |
| 150 static double _parseDoubleError( | 146 static double _parseDoubleError( |
| 151 String source, double handleError(String source)) { | 147 String source, double handleError(String source)) { |
| 152 if (handleError == null) { | 148 if (handleError == null) { |
| 153 throw new FormatException('Invalid double', source); | 149 throw new FormatException('Invalid double', source); |
| 154 } | 150 } |
| 155 return handleError(source); | 151 return handleError(source); |
| 156 } | 152 } |
| 157 | 153 |
| 158 static double parseDouble(String source, double handleError(String source)) { | 154 static double parseDouble(@nullCheck String source, double handleError(String
source)) { |
| 159 checkString(source); | |
| 160 // Notice that JS parseFloat accepts garbage at the end of the string. | 155 // Notice that JS parseFloat accepts garbage at the end of the string. |
| 161 // Accept only: | 156 // Accept only: |
| 162 // - [+/-]NaN | 157 // - [+/-]NaN |
| 163 // - [+/-]Infinity | 158 // - [+/-]Infinity |
| 164 // - a Dart double literal | 159 // - a Dart double literal |
| 165 // We do allow leading or trailing whitespace. | 160 // We do allow leading or trailing whitespace. |
| 166 if (!JS( | 161 if (!JS( |
| 167 'bool', | 162 'bool', |
| 168 r'/^\s*[+-]?(?:Infinity|NaN|' | 163 r'/^\s*[+-]?(?:Infinity|NaN|' |
| 169 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', | 164 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 // In a browser return self.location.href. | 220 // In a browser return self.location.href. |
| 226 if (JS('bool', '!!self.location')) { | 221 if (JS('bool', '!!self.location')) { |
| 227 return JS('String', 'self.location.href'); | 222 return JS('String', 'self.location.href'); |
| 228 } | 223 } |
| 229 | 224 |
| 230 return null; | 225 return null; |
| 231 } | 226 } |
| 232 | 227 |
| 233 // This is to avoid stack overflows due to very large argument arrays in | 228 // This is to avoid stack overflows due to very large argument arrays in |
| 234 // apply(). It fixes http://dartbug.com/6919 | 229 // apply(). It fixes http://dartbug.com/6919 |
| 235 static String _fromCharCodeApply(List<int> array) { | 230 @notNull static String _fromCharCodeApply(List<int> array) { |
| 236 const kMaxApply = 500; | 231 const kMaxApply = 500; |
| 237 int end = array.length; | 232 @nullCheck int end = array.length; |
| 238 if (end <= kMaxApply) { | 233 if (end <= kMaxApply) { |
| 239 return JS('String', r'String.fromCharCode.apply(null, #)', array); | 234 return JS('String', r'String.fromCharCode.apply(null, #)', array); |
| 240 } | 235 } |
| 241 String result = ''; | 236 String result = ''; |
| 242 for (int i = 0; i < end; i += kMaxApply) { | 237 for (int i = 0; i < end; i += kMaxApply) { |
| 243 int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; | 238 int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; |
| 244 result = JS( | 239 result = JS( |
| 245 'String', | 240 'String', |
| 246 r'# + String.fromCharCode.apply(null, #.slice(#, #))', | 241 r'# + String.fromCharCode.apply(null, #.slice(#, #))', |
| 247 result, | 242 result, |
| 248 array, | 243 array, |
| 249 i, | 244 i, |
| 250 chunkEnd); | 245 chunkEnd); |
| 251 } | 246 } |
| 252 return result; | 247 return result; |
| 253 } | 248 } |
| 254 | 249 |
| 255 static String stringFromCodePoints(/*=JSArray<int>*/ codePoints) { | 250 @notNull static String stringFromCodePoints(JSArray<int> codePoints) { |
| 256 List<int> a = <int>[]; | 251 List<int> a = <int>[]; |
| 257 for (var i in codePoints) { | 252 for (@nullCheck var i in codePoints) { |
| 258 if (i is! int) throw argumentErrorValue(i); | |
| 259 if (i <= 0xffff) { | 253 if (i <= 0xffff) { |
| 260 a.add(i); | 254 a.add(i); |
| 261 } else if (i <= 0x10ffff) { | 255 } else if (i <= 0x10ffff) { |
| 262 a.add(0xd800 + ((((i - 0x10000) >> 10) & 0x3ff))); | 256 a.add(0xd800 + ((((i - 0x10000) >> 10) & 0x3ff))); |
| 263 a.add(0xdc00 + (i & 0x3ff)); | 257 a.add(0xdc00 + (i & 0x3ff)); |
| 264 } else { | 258 } else { |
| 265 throw argumentErrorValue(i); | 259 throw argumentErrorValue(i); |
| 266 } | 260 } |
| 267 } | 261 } |
| 268 return _fromCharCodeApply(a); | 262 return _fromCharCodeApply(a); |
| 269 } | 263 } |
| 270 | 264 |
| 271 static String stringFromCharCodes(/*=JSArray<int>*/ charCodes) { | 265 @notNull static String stringFromCharCodes(JSArray<int> charCodes) { |
| 272 for (var i in charCodes) { | 266 for (@nullCheck var i in charCodes) { |
| 273 if (i is! int) throw argumentErrorValue(i); | |
| 274 if (i < 0) throw argumentErrorValue(i); | 267 if (i < 0) throw argumentErrorValue(i); |
| 275 if (i > 0xffff) return stringFromCodePoints(charCodes); | 268 if (i > 0xffff) return stringFromCodePoints(charCodes); |
| 276 } | 269 } |
| 277 return _fromCharCodeApply(charCodes); | 270 return _fromCharCodeApply(charCodes); |
| 278 } | 271 } |
| 279 | 272 |
| 280 // [start] and [end] are validated. | 273 // [start] and [end] are validated. |
| 281 static String stringFromNativeUint8List( | 274 @notNull static String stringFromNativeUint8List( |
| 282 NativeUint8List charCodes, int start, int end) { | 275 NativeUint8List charCodes, @nullCheck int start, @nullCheck int end) { |
| 283 const kMaxApply = 500; | 276 const kMaxApply = 500; |
| 284 if (end <= kMaxApply && start == 0 && end == charCodes.length) { | 277 if (end <= kMaxApply && start == 0 && end == charCodes.length) { |
| 285 return JS('String', r'String.fromCharCode.apply(null, #)', charCodes); | 278 return JS('String', r'String.fromCharCode.apply(null, #)', charCodes); |
| 286 } | 279 } |
| 287 String result = ''; | 280 String result = ''; |
| 288 for (int i = start; i < end; i += kMaxApply) { | 281 for (int i = start; i < end; i += kMaxApply) { |
| 289 int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; | 282 int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; |
| 290 result = JS( | 283 result = JS( |
| 291 'String', | 284 'String', |
| 292 r'# + String.fromCharCode.apply(null, #.subarray(#, #))', | 285 r'# + String.fromCharCode.apply(null, #.subarray(#, #))', |
| 293 result, | 286 result, |
| 294 charCodes, | 287 charCodes, |
| 295 i, | 288 i, |
| 296 chunkEnd); | 289 chunkEnd); |
| 297 } | 290 } |
| 298 return result; | 291 return result; |
| 299 } | 292 } |
| 300 | 293 |
| 301 static String stringFromCharCode(int charCode) { | 294 @notNull static String stringFromCharCode(@nullCheck int charCode) { |
| 302 if (0 <= charCode) { | 295 if (0 <= charCode) { |
| 303 if (charCode <= 0xffff) { | 296 if (charCode <= 0xffff) { |
| 304 return JS('String', 'String.fromCharCode(#)', charCode); | 297 return JS('String', 'String.fromCharCode(#)', charCode); |
| 305 } | 298 } |
| 306 if (charCode <= 0x10ffff) { | 299 if (charCode <= 0x10ffff) { |
| 307 var bits = charCode - 0x10000; | 300 var bits = charCode - 0x10000; |
| 308 var low = 0xDC00 | (bits & 0x3ff); | 301 var low = 0xDC00 | (bits & 0x3ff); |
| 309 var high = 0xD800 | (bits >> 10); | 302 var high = 0xD800 | (bits >> 10); |
| 310 return JS('String', 'String.fromCharCode(#, #)', high, low); | 303 return JS('String', 'String.fromCharCode(#, #)', high, low); |
| 311 } | 304 } |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 354 match = JS('JSArray|Null', r'/(?:GMT|UTC)[+-]\d{4}/.exec(#.toString())', d); | 347 match = JS('JSArray|Null', r'/(?:GMT|UTC)[+-]\d{4}/.exec(#.toString())', d); |
| 355 if (match != null) return match[0]; | 348 if (match != null) return match[0]; |
| 356 return ""; | 349 return ""; |
| 357 } | 350 } |
| 358 | 351 |
| 359 static int getTimeZoneOffsetInMinutes(DateTime receiver) { | 352 static int getTimeZoneOffsetInMinutes(DateTime receiver) { |
| 360 // Note that JS and Dart disagree on the sign of the offset. | 353 // Note that JS and Dart disagree on the sign of the offset. |
| 361 return -JS('int', r'#.getTimezoneOffset()', lazyAsJsDate(receiver)); | 354 return -JS('int', r'#.getTimezoneOffset()', lazyAsJsDate(receiver)); |
| 362 } | 355 } |
| 363 | 356 |
| 364 static num valueFromDecomposedDate(int years, int month, int day, int hours, | 357 static num valueFromDecomposedDate(@nullCheck int years, @nullCheck int month, |
| 365 int minutes, int seconds, int milliseconds, bool isUtc) { | 358 @nullCheck int day, @nullCheck int hours, |
| 359 @nullCheck int minutes, @nullCheck int seconds, @nullCheck int millisecond
s, |
| 360 @nullCheck bool isUtc) { |
| 366 final int MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000; | 361 final int MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000; |
| 367 checkInt(years); | |
| 368 checkInt(month); | |
| 369 checkInt(day); | |
| 370 checkInt(hours); | |
| 371 checkInt(minutes); | |
| 372 checkInt(seconds); | |
| 373 checkInt(milliseconds); | |
| 374 checkBool(isUtc); | |
| 375 var jsMonth = month - 1; | 362 var jsMonth = month - 1; |
| 376 num value; | 363 num value; |
| 377 if (isUtc) { | 364 if (isUtc) { |
| 378 value = JS('num', r'Date.UTC(#, #, #, #, #, #, #)', years, jsMonth, day, | 365 value = JS('num', r'Date.UTC(#, #, #, #, #, #, #)', years, jsMonth, day, |
| 379 hours, minutes, seconds, milliseconds); | 366 hours, minutes, seconds, milliseconds); |
| 380 } else { | 367 } else { |
| 381 value = JS('num', r'new Date(#, #, #, #, #, #, #).valueOf()', years, | 368 value = JS('num', r'new Date(#, #, #, #, #, #, #).valueOf()', years, |
| 382 jsMonth, day, hours, minutes, seconds, milliseconds); | 369 jsMonth, day, hours, minutes, seconds, milliseconds); |
| 383 } | 370 } |
| 384 if (value.isNaN || | 371 if (value.isNaN || |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 486 | 473 |
| 487 static StackTrace extractStackTrace(Error error) => | 474 static StackTrace extractStackTrace(Error error) => |
| 488 getTraceFromException(error); | 475 getTraceFromException(error); |
| 489 } | 476 } |
| 490 | 477 |
| 491 /** | 478 /** |
| 492 * Diagnoses an indexing error. Returns the ArgumentError or RangeError that | 479 * Diagnoses an indexing error. Returns the ArgumentError or RangeError that |
| 493 * describes the problem. | 480 * describes the problem. |
| 494 */ | 481 */ |
| 495 @NoInline() | 482 @NoInline() |
| 496 Error diagnoseIndexError(indexable, index) { | 483 Error diagnoseIndexError(indexable, int index) { |
| 497 if (index is! int) return new ArgumentError.value(index, 'index'); | |
| 498 int length = indexable.length; | 484 int length = indexable.length; |
| 499 // The following returns the same error that would be thrown by calling | 485 // The following returns the same error that would be thrown by calling |
| 500 // [RangeError.checkValidIndex] with no optional parameters provided. | 486 // [RangeError.checkValidIndex] with no optional parameters provided. |
| 501 if (index < 0 || index >= length) { | 487 if (index < 0 || index >= length) { |
| 502 return new RangeError.index(index, indexable, 'index', null, length); | 488 return new RangeError.index(index, indexable, 'index', null, length); |
| 503 } | 489 } |
| 504 // The above should always match, but if it does not, use the following. | 490 // The above should always match, but if it does not, use the following. |
| 505 return new RangeError.value(index, 'index'); | 491 return new RangeError.value(index, 'index'); |
| 506 } | 492 } |
| 507 | 493 |
| 508 /** | 494 /** |
| 509 * Diagnoses a range error. Returns the ArgumentError or RangeError that | 495 * Diagnoses a range error. Returns the ArgumentError or RangeError that |
| 510 * describes the problem. | 496 * describes the problem. |
| 511 */ | 497 */ |
| 512 @NoInline() | 498 @NoInline() |
| 513 Error diagnoseRangeError(start, end, length) { | 499 Error diagnoseRangeError(int start, int end, int length) { |
| 514 if (start is! int) { | 500 if (start == null) { |
| 515 return new ArgumentError.value(start, 'start'); | 501 return new ArgumentError.value(start, 'start'); |
| 516 } | 502 } |
| 517 if (start < 0 || start > length) { | 503 if (start < 0 || start > length) { |
| 518 return new RangeError.range(start, 0, length, 'start'); | 504 return new RangeError.range(start, 0, length, 'start'); |
| 519 } | 505 } |
| 520 if (end != null) { | 506 if (end != null) { |
| 521 if (end is! int) { | |
| 522 return new ArgumentError.value(end, 'end'); | |
| 523 } | |
| 524 if (end < start || end > length) { | 507 if (end < start || end > length) { |
| 525 return new RangeError.range(end, start, length, 'end'); | 508 return new RangeError.range(end, start, length, 'end'); |
| 526 } | 509 } |
| 527 } | 510 } |
| 528 // The above should always match, but if it does not, use the following. | 511 // The above should always match, but if it does not, use the following. |
| 529 return new ArgumentError.value(end, "end"); | 512 return new ArgumentError.value(end, "end"); |
| 530 } | 513 } |
| 531 | 514 |
| 532 stringLastIndexOfUnchecked(receiver, element, start) => | 515 @notNull int stringLastIndexOfUnchecked(receiver, element, start) => |
| 533 JS('int', r'#.lastIndexOf(#, #)', receiver, element, start); | 516 JS('int', r'#.lastIndexOf(#, #)', receiver, element, start); |
| 534 | 517 |
| 535 /// 'factory' for constructing ArgumentError.value to keep the call sites small. | 518 /// 'factory' for constructing ArgumentError.value to keep the call sites small. |
| 536 @NoInline() | 519 @NoInline() |
| 537 ArgumentError argumentErrorValue(object) { | 520 ArgumentError argumentErrorValue(object) { |
| 538 return new ArgumentError.value(object); | 521 return new ArgumentError.value(object); |
| 539 } | 522 } |
| 540 | 523 |
| 541 checkNull(object) { | 524 void throwArgumentErrorValue(value) { |
| 542 if (object == null) throw argumentErrorValue(object); | 525 throw argumentErrorValue(value); |
| 543 return object; | |
| 544 } | |
| 545 | |
| 546 checkNum(value) { | |
| 547 if (value is! num) throw argumentErrorValue(value); | |
| 548 return value; | |
| 549 } | 526 } |
| 550 | 527 |
| 551 checkInt(value) { | 528 checkInt(value) { |
| 552 if (value is! int) throw argumentErrorValue(value); | 529 if (value is! int) throw argumentErrorValue(value); |
| 553 return value; | 530 return value; |
| 554 } | 531 } |
| 555 | 532 |
| 556 checkBool(value) { | |
| 557 if (value is! bool) throw argumentErrorValue(value); | |
| 558 return value; | |
| 559 } | |
| 560 | |
| 561 checkString(value) { | |
| 562 if (value is! String) throw argumentErrorValue(value); | |
| 563 return value; | |
| 564 } | |
| 565 | |
| 566 throwRuntimeError(message) { | 533 throwRuntimeError(message) { |
| 567 throw new RuntimeError(message); | 534 throw new RuntimeError(message); |
| 568 } | 535 } |
| 569 | 536 |
| 570 throwAbstractClassInstantiationError(className) { | 537 throwAbstractClassInstantiationError(className) { |
| 571 throw new AbstractClassInstantiationError(className); | 538 throw new AbstractClassInstantiationError(className); |
| 572 } | 539 } |
| 573 | 540 |
| 574 @NoInline() | 541 @NoInline() |
| 575 throwConcurrentModificationError(collection) { | 542 throwConcurrentModificationError(collection) { |
| (...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 914 if (dart.polyfill(object)) { | 881 if (dart.polyfill(object)) { |
| 915 dart.applyAllExtensions(object); | 882 dart.applyAllExtensions(object); |
| 916 } | 883 } |
| 917 } catch (e) { | 884 } catch (e) { |
| 918 // This may fail due to cross-origin errors. In that case, we shouldn't | 885 // This may fail due to cross-origin errors. In that case, we shouldn't |
| 919 // need to polyfill as we can't get objects from that frame. | 886 // need to polyfill as we can't get objects from that frame. |
| 920 | 887 |
| 921 // TODO(vsm): Detect this more robustly - ideally before we try to polyfill. | 888 // TODO(vsm): Detect this more robustly - ideally before we try to polyfill. |
| 922 } | 889 } |
| 923 } | 890 } |
| OLD | NEW |