| 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 |
| 11 import 'dart:_foreign_helper' show | 11 import 'dart:_foreign_helper' show JS, JS_STRING_CONCAT; |
| 12 JS, | |
| 13 JS_STRING_CONCAT; | |
| 14 | 12 |
| 15 import 'dart:_interceptors'; | 13 import 'dart:_interceptors'; |
| 16 import 'dart:_internal' show | 14 import 'dart:_internal' |
| 17 EfficientLengthIterable, | 15 show EfficientLengthIterable, MappedIterable, IterableElementError; |
| 18 MappedIterable, | |
| 19 IterableElementError; | |
| 20 | 16 |
| 21 import 'dart:_native_typed_data'; | 17 import 'dart:_native_typed_data'; |
| 22 | 18 |
| 23 part 'annotations.dart'; | 19 part 'annotations.dart'; |
| 24 part 'linked_hash_map.dart'; | 20 part 'linked_hash_map.dart'; |
| 25 part 'native_helper.dart'; | 21 part 'native_helper.dart'; |
| 26 part 'regexp_helper.dart'; | 22 part 'regexp_helper.dart'; |
| 27 part 'string_helper.dart'; | 23 part 'string_helper.dart'; |
| 28 part 'js_rti.dart'; | 24 part 'js_rti.dart'; |
| 29 | 25 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 } | 64 } |
| 69 return JS('int', '#', hash); | 65 return JS('int', '#', hash); |
| 70 } | 66 } |
| 71 | 67 |
| 72 @NoInline() | 68 @NoInline() |
| 73 static int _parseIntError(String source, int handleError(String source)) { | 69 static int _parseIntError(String source, int handleError(String source)) { |
| 74 if (handleError == null) throw new FormatException(source); | 70 if (handleError == null) throw new FormatException(source); |
| 75 return handleError(source); | 71 return handleError(source); |
| 76 } | 72 } |
| 77 | 73 |
| 78 static int parseInt(String source, | 74 static int parseInt( |
| 79 int radix, | 75 String source, int radix, int handleError(String source)) { |
| 80 int handleError(String source)) { | |
| 81 checkString(source); | 76 checkString(source); |
| 82 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'); |
| 83 var/*=JSArray<String>*/ match = JS('JSExtendableArray|Null', '#.exec(#)', re
, source); | 78 var/*=JSArray<String>*/ match = |
| 79 JS('JSExtendableArray|Null', '#.exec(#)', re, source); |
| 84 int digitsIndex = 1; | 80 int digitsIndex = 1; |
| 85 int hexIndex = 2; | 81 int hexIndex = 2; |
| 86 int decimalIndex = 3; | 82 int decimalIndex = 3; |
| 87 int nonDecimalHexIndex = 4; | 83 int nonDecimalHexIndex = 4; |
| 88 if (match == null) { | 84 if (match == null) { |
| 89 // 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 |
| 90 // 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 |
| 91 // again. | 87 // again. |
| 92 return _parseIntError(source, handleError); | 88 return _parseIntError(source, handleError); |
| 93 } | 89 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 143 return _parseIntError(source, handleError); | 139 return _parseIntError(source, handleError); |
| 144 } | 140 } |
| 145 } | 141 } |
| 146 } | 142 } |
| 147 // The above matching and checks ensures the source has at least one digits | 143 // The above matching and checks ensures the source has at least one digits |
| 148 // and all digits are suitable for the radix, so parseInt cannot return NaN. | 144 // and all digits are suitable for the radix, so parseInt cannot return NaN. |
| 149 return JS('int', r'parseInt(#, #)', source, radix); | 145 return JS('int', r'parseInt(#, #)', source, radix); |
| 150 } | 146 } |
| 151 | 147 |
| 152 @NoInline() | 148 @NoInline() |
| 153 static double _parseDoubleError(String source, | 149 static double _parseDoubleError( |
| 154 double handleError(String source)) { | 150 String source, double handleError(String source)) { |
| 155 if (handleError == null) { | 151 if (handleError == null) { |
| 156 throw new FormatException('Invalid double', source); | 152 throw new FormatException('Invalid double', source); |
| 157 } | 153 } |
| 158 return handleError(source); | 154 return handleError(source); |
| 159 } | 155 } |
| 160 | 156 |
| 161 static double parseDouble(String source, double handleError(String source)) { | 157 static double parseDouble(String source, double handleError(String source)) { |
| 162 checkString(source); | 158 checkString(source); |
| 163 // Notice that JS parseFloat accepts garbage at the end of the string. | 159 // Notice that JS parseFloat accepts garbage at the end of the string. |
| 164 // Accept only: | 160 // Accept only: |
| 165 // - [+/-]NaN | 161 // - [+/-]NaN |
| 166 // - [+/-]Infinity | 162 // - [+/-]Infinity |
| 167 // - a Dart double literal | 163 // - a Dart double literal |
| 168 // We do allow leading or trailing whitespace. | 164 // We do allow leading or trailing whitespace. |
| 169 if (!JS('bool', | 165 if (!JS( |
| 170 r'/^\s*[+-]?(?:Infinity|NaN|' | 166 'bool', |
| 171 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', | 167 r'/^\s*[+-]?(?:Infinity|NaN|' |
| 172 source)) { | 168 r'(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(#)', |
| 169 source)) { |
| 173 return _parseDoubleError(source, handleError); | 170 return _parseDoubleError(source, handleError); |
| 174 } | 171 } |
| 175 var result = JS('num', r'parseFloat(#)', source); | 172 var result = JS('num', r'parseFloat(#)', source); |
| 176 if (result.isNaN) { | 173 if (result.isNaN) { |
| 177 var trimmed = source.trim(); | 174 var trimmed = source.trim(); |
| 178 if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') { | 175 if (trimmed == 'NaN' || trimmed == '+NaN' || trimmed == '-NaN') { |
| 179 return result; | 176 return result; |
| 180 } | 177 } |
| 181 return _parseDoubleError(source, handleError); | 178 return _parseDoubleError(source, handleError); |
| 182 } | 179 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 if (performance == null) return; | 211 if (performance == null) return; |
| 215 if (JS('bool', 'typeof #.now != "function"', performance)) return; | 212 if (JS('bool', 'typeof #.now != "function"', performance)) return; |
| 216 timerFrequency = 1000000; | 213 timerFrequency = 1000000; |
| 217 timerTicks = () => (1000 * JS('num', '#.now()', performance)).floor(); | 214 timerTicks = () => (1000 * JS('num', '#.now()', performance)).floor(); |
| 218 } | 215 } |
| 219 | 216 |
| 220 static int timerFrequency; | 217 static int timerFrequency; |
| 221 static Function timerTicks; | 218 static Function timerTicks; |
| 222 | 219 |
| 223 static bool get isD8 { | 220 static bool get isD8 { |
| 224 return JS('bool', | 221 return JS( |
| 225 'typeof version == "function"' | 222 'bool', |
| 226 ' && typeof os == "object" && "system" in os'); | 223 'typeof version == "function"' |
| 224 ' && typeof os == "object" && "system" in os'); |
| 227 } | 225 } |
| 228 | 226 |
| 229 static bool get isJsshell { | 227 static bool get isJsshell { |
| 230 return JS('bool', | 228 return JS( |
| 231 'typeof version == "function" && typeof system == "function"'); | 229 'bool', 'typeof version == "function" && typeof system == "function"'); |
| 232 } | 230 } |
| 233 | 231 |
| 234 static String currentUri() { | 232 static String currentUri() { |
| 235 // In a browser return self.location.href. | 233 // In a browser return self.location.href. |
| 236 if (JS('bool', '!!self.location')) { | 234 if (JS('bool', '!!self.location')) { |
| 237 return JS('String', 'self.location.href'); | 235 return JS('String', 'self.location.href'); |
| 238 } | 236 } |
| 239 | 237 |
| 240 return null; | 238 return null; |
| 241 } | 239 } |
| 242 | 240 |
| 243 // This is to avoid stack overflows due to very large argument arrays in | 241 // This is to avoid stack overflows due to very large argument arrays in |
| 244 // apply(). It fixes http://dartbug.com/6919 | 242 // apply(). It fixes http://dartbug.com/6919 |
| 245 static String _fromCharCodeApply(List<int> array) { | 243 static String _fromCharCodeApply(List<int> array) { |
| 246 const kMaxApply = 500; | 244 const kMaxApply = 500; |
| 247 int end = array.length; | 245 int end = array.length; |
| 248 if (end <= kMaxApply) { | 246 if (end <= kMaxApply) { |
| 249 return JS('String', r'String.fromCharCode.apply(null, #)', array); | 247 return JS('String', r'String.fromCharCode.apply(null, #)', array); |
| 250 } | 248 } |
| 251 String result = ''; | 249 String result = ''; |
| 252 for (int i = 0; i < end; i += kMaxApply) { | 250 for (int i = 0; i < end; i += kMaxApply) { |
| 253 int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; | 251 int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; |
| 254 result = JS('String', | 252 result = JS( |
| 253 'String', |
| 255 r'# + String.fromCharCode.apply(null, #.slice(#, #))', | 254 r'# + String.fromCharCode.apply(null, #.slice(#, #))', |
| 256 result, array, i, chunkEnd); | 255 result, |
| 256 array, |
| 257 i, |
| 258 chunkEnd); |
| 257 } | 259 } |
| 258 return result; | 260 return result; |
| 259 } | 261 } |
| 260 | 262 |
| 261 static String stringFromCodePoints(/*=JSArray<int>*/ codePoints) { | 263 static String stringFromCodePoints(/*=JSArray<int>*/ codePoints) { |
| 262 List<int> a = <int>[]; | 264 List<int> a = <int>[]; |
| 263 for (var i in codePoints) { | 265 for (var i in codePoints) { |
| 264 if (i is !int) throw argumentErrorValue(i); | 266 if (i is! int) throw argumentErrorValue(i); |
| 265 if (i <= 0xffff) { | 267 if (i <= 0xffff) { |
| 266 a.add(i); | 268 a.add(i); |
| 267 } else if (i <= 0x10ffff) { | 269 } else if (i <= 0x10ffff) { |
| 268 a.add(0xd800 + ((((i - 0x10000) >> 10) & 0x3ff))); | 270 a.add(0xd800 + ((((i - 0x10000) >> 10) & 0x3ff))); |
| 269 a.add(0xdc00 + (i & 0x3ff)); | 271 a.add(0xdc00 + (i & 0x3ff)); |
| 270 } else { | 272 } else { |
| 271 throw argumentErrorValue(i); | 273 throw argumentErrorValue(i); |
| 272 } | 274 } |
| 273 } | 275 } |
| 274 return _fromCharCodeApply(a); | 276 return _fromCharCodeApply(a); |
| 275 } | 277 } |
| 276 | 278 |
| 277 static String stringFromCharCodes(/*=JSArray<int>*/ charCodes) { | 279 static String stringFromCharCodes(/*=JSArray<int>*/ charCodes) { |
| 278 for (var i in charCodes) { | 280 for (var i in charCodes) { |
| 279 if (i is !int) throw argumentErrorValue(i); | 281 if (i is! int) throw argumentErrorValue(i); |
| 280 if (i < 0) throw argumentErrorValue(i); | 282 if (i < 0) throw argumentErrorValue(i); |
| 281 if (i > 0xffff) return stringFromCodePoints(charCodes); | 283 if (i > 0xffff) return stringFromCodePoints(charCodes); |
| 282 } | 284 } |
| 283 return _fromCharCodeApply(charCodes); | 285 return _fromCharCodeApply(charCodes); |
| 284 } | 286 } |
| 285 | 287 |
| 286 // [start] and [end] are validated. | 288 // [start] and [end] are validated. |
| 287 static String stringFromNativeUint8List( | 289 static String stringFromNativeUint8List( |
| 288 NativeUint8List charCodes, int start, int end) { | 290 NativeUint8List charCodes, int start, int end) { |
| 289 const kMaxApply = 500; | 291 const kMaxApply = 500; |
| 290 if (end <= kMaxApply && start == 0 && end == charCodes.length) { | 292 if (end <= kMaxApply && start == 0 && end == charCodes.length) { |
| 291 return JS('String', r'String.fromCharCode.apply(null, #)', charCodes); | 293 return JS('String', r'String.fromCharCode.apply(null, #)', charCodes); |
| 292 } | 294 } |
| 293 String result = ''; | 295 String result = ''; |
| 294 for (int i = start; i < end; i += kMaxApply) { | 296 for (int i = start; i < end; i += kMaxApply) { |
| 295 int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; | 297 int chunkEnd = (i + kMaxApply < end) ? i + kMaxApply : end; |
| 296 result = JS('String', | 298 result = JS( |
| 299 'String', |
| 297 r'# + String.fromCharCode.apply(null, #.subarray(#, #))', | 300 r'# + String.fromCharCode.apply(null, #.subarray(#, #))', |
| 298 result, charCodes, i, chunkEnd); | 301 result, |
| 302 charCodes, |
| 303 i, |
| 304 chunkEnd); |
| 299 } | 305 } |
| 300 return result; | 306 return result; |
| 301 } | 307 } |
| 302 | 308 |
| 303 | |
| 304 static String stringFromCharCode(int charCode) { | 309 static String stringFromCharCode(int charCode) { |
| 305 if (0 <= charCode) { | 310 if (0 <= charCode) { |
| 306 if (charCode <= 0xffff) { | 311 if (charCode <= 0xffff) { |
| 307 return JS('String', 'String.fromCharCode(#)', charCode); | 312 return JS('String', 'String.fromCharCode(#)', charCode); |
| 308 } | 313 } |
| 309 if (charCode <= 0x10ffff) { | 314 if (charCode <= 0x10ffff) { |
| 310 var bits = charCode - 0x10000; | 315 var bits = charCode - 0x10000; |
| 311 var low = 0xDC00 | (bits & 0x3ff); | 316 var low = 0xDC00 | (bits & 0x3ff); |
| 312 var high = 0xD800 | (bits >> 10); | 317 var high = 0xD800 | (bits >> 10); |
| 313 return JS('String', 'String.fromCharCode(#, #)', high, low); | 318 return JS('String', 'String.fromCharCode(#, #)', high, low); |
| 314 } | 319 } |
| 315 } | 320 } |
| 316 throw new RangeError.range(charCode, 0, 0x10ffff); | 321 throw new RangeError.range(charCode, 0, 0x10ffff); |
| 317 } | 322 } |
| 318 | 323 |
| 319 static String stringConcatUnchecked(String string1, String string2) { | 324 static String stringConcatUnchecked(String string1, String string2) { |
| 320 return JS_STRING_CONCAT(string1, string2); | 325 return JS_STRING_CONCAT(string1, string2); |
| 321 } | 326 } |
| 322 | 327 |
| 323 static String flattenString(String str) { | 328 static String flattenString(String str) { |
| 324 return JS('String', "#.charCodeAt(0) == 0 ? # : #", str, str, str); | 329 return JS('String', "#.charCodeAt(0) == 0 ? # : #", str, str, str); |
| 325 } | 330 } |
| 326 | 331 |
| 327 static String getTimeZoneName(DateTime receiver) { | 332 static String getTimeZoneName(DateTime receiver) { |
| 328 // Firefox and Chrome emit the timezone in parenthesis. | 333 // Firefox and Chrome emit the timezone in parenthesis. |
| 329 // Example: "Wed May 16 2012 21:13:00 GMT+0200 (CEST)". | 334 // Example: "Wed May 16 2012 21:13:00 GMT+0200 (CEST)". |
| 330 // We extract this name using a regexp. | 335 // We extract this name using a regexp. |
| 331 var d = lazyAsJsDate(receiver); | 336 var d = lazyAsJsDate(receiver); |
| 332 List match = JS('JSArray|Null', r'/\((.*)\)/.exec(#.toString())', d); | 337 List match = JS('JSArray|Null', r'/\((.*)\)/.exec(#.toString())', d); |
| 333 if (match != null) return match[1]; | 338 if (match != null) return match[1]; |
| 334 | 339 |
| 335 // Internet Explorer 10+ emits the zone name without parenthesis: | 340 // Internet Explorer 10+ emits the zone name without parenthesis: |
| 336 // Example: Thu Oct 31 14:07:44 PDT 2013 | 341 // Example: Thu Oct 31 14:07:44 PDT 2013 |
| 337 match = JS('JSArray|Null', | 342 match = JS( |
| 338 // Thu followed by a space. | 343 'JSArray|Null', |
| 339 r'/^[A-Z,a-z]{3}\s' | 344 // Thu followed by a space. |
| 340 // Oct 31 followed by space. | 345 r'/^[A-Z,a-z]{3}\s' |
| 341 r'[A-Z,a-z]{3}\s\d+\s' | 346 // Oct 31 followed by space. |
| 342 // Time followed by a space. | 347 r'[A-Z,a-z]{3}\s\d+\s' |
| 343 r'\d{2}:\d{2}:\d{2}\s' | 348 // Time followed by a space. |
| 344 // The time zone name followed by a space. | 349 r'\d{2}:\d{2}:\d{2}\s' |
| 345 r'([A-Z]{3,5})\s' | 350 // The time zone name followed by a space. |
| 346 // The year. | 351 r'([A-Z]{3,5})\s' |
| 347 r'\d{4}$/' | 352 // The year. |
| 348 '.exec(#.toString())', | 353 r'\d{4}$/' |
| 349 d); | 354 '.exec(#.toString())', |
| 355 d); |
| 350 if (match != null) return match[1]; | 356 if (match != null) return match[1]; |
| 351 | 357 |
| 352 // IE 9 and Opera don't provide the zone name. We fall back to emitting the | 358 // IE 9 and Opera don't provide the zone name. We fall back to emitting the |
| 353 // UTC/GMT offset. | 359 // UTC/GMT offset. |
| 354 // Example (IE9): Wed Nov 20 09:51:00 UTC+0100 2013 | 360 // Example (IE9): Wed Nov 20 09:51:00 UTC+0100 2013 |
| 355 // (Opera): Wed Nov 20 2013 11:03:38 GMT+0100 | 361 // (Opera): Wed Nov 20 2013 11:03:38 GMT+0100 |
| 356 match = JS('JSArray|Null', r'/(?:GMT|UTC)[+-]\d{4}/.exec(#.toString())', d); | 362 match = JS('JSArray|Null', r'/(?:GMT|UTC)[+-]\d{4}/.exec(#.toString())', d); |
| 357 if (match != null) return match[0]; | 363 if (match != null) return match[0]; |
| 358 return ""; | 364 return ""; |
| 359 } | 365 } |
| 360 | 366 |
| 361 static int getTimeZoneOffsetInMinutes(DateTime receiver) { | 367 static int getTimeZoneOffsetInMinutes(DateTime receiver) { |
| 362 // Note that JS and Dart disagree on the sign of the offset. | 368 // Note that JS and Dart disagree on the sign of the offset. |
| 363 return -JS('int', r'#.getTimezoneOffset()', lazyAsJsDate(receiver)); | 369 return -JS('int', r'#.getTimezoneOffset()', lazyAsJsDate(receiver)); |
| 364 } | 370 } |
| 365 | 371 |
| 366 static num valueFromDecomposedDate(int years, int month, int day, int hours, | 372 static num valueFromDecomposedDate(int years, int month, int day, int hours, |
| 367 int minutes, int seconds, int milliseconds, bool isUtc) { | 373 int minutes, int seconds, int milliseconds, bool isUtc) { |
| 368 final int MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000; | 374 final int MAX_MILLISECONDS_SINCE_EPOCH = 8640000000000000; |
| 369 checkInt(years); | 375 checkInt(years); |
| 370 checkInt(month); | 376 checkInt(month); |
| 371 checkInt(day); | 377 checkInt(day); |
| 372 checkInt(hours); | 378 checkInt(hours); |
| 373 checkInt(minutes); | 379 checkInt(minutes); |
| 374 checkInt(seconds); | 380 checkInt(seconds); |
| 375 checkInt(milliseconds); | 381 checkInt(milliseconds); |
| 376 checkBool(isUtc); | 382 checkBool(isUtc); |
| 377 var jsMonth = month - 1; | 383 var jsMonth = month - 1; |
| 378 num value; | 384 num value; |
| 379 if (isUtc) { | 385 if (isUtc) { |
| 380 value = JS('num', r'Date.UTC(#, #, #, #, #, #, #)', | 386 value = JS('num', r'Date.UTC(#, #, #, #, #, #, #)', years, jsMonth, day, |
| 381 years, jsMonth, day, hours, minutes, seconds, milliseconds); | 387 hours, minutes, seconds, milliseconds); |
| 382 } else { | 388 } else { |
| 383 value = JS('num', r'new Date(#, #, #, #, #, #, #).valueOf()', | 389 value = JS('num', r'new Date(#, #, #, #, #, #, #).valueOf()', years, |
| 384 years, jsMonth, day, hours, minutes, seconds, milliseconds); | 390 jsMonth, day, hours, minutes, seconds, milliseconds); |
| 385 } | 391 } |
| 386 if (value.isNaN || | 392 if (value.isNaN || |
| 387 value < -MAX_MILLISECONDS_SINCE_EPOCH || | 393 value < -MAX_MILLISECONDS_SINCE_EPOCH || |
| 388 value > MAX_MILLISECONDS_SINCE_EPOCH) { | 394 value > MAX_MILLISECONDS_SINCE_EPOCH) { |
| 389 return null; | 395 return null; |
| 390 } | 396 } |
| 391 if (years <= 0 || years < 100) return patchUpY2K(value, years, isUtc); | 397 if (years <= 0 || years < 100) return patchUpY2K(value, years, isUtc); |
| 392 return value; | 398 return value; |
| 393 } | 399 } |
| 394 | 400 |
| 395 static patchUpY2K(value, years, isUtc) { | 401 static patchUpY2K(value, years, isUtc) { |
| 396 var date = JS('', r'new Date(#)', value); | 402 var date = JS('', r'new Date(#)', value); |
| 397 if (isUtc) { | 403 if (isUtc) { |
| 398 JS('num', r'#.setUTCFullYear(#)', date, years); | 404 JS('num', r'#.setUTCFullYear(#)', date, years); |
| 399 } else { | 405 } else { |
| 400 JS('num', r'#.setFullYear(#)', date, years); | 406 JS('num', r'#.setFullYear(#)', date, years); |
| 401 } | 407 } |
| 402 return JS('num', r'#.valueOf()', date); | 408 return JS('num', r'#.valueOf()', date); |
| 403 } | 409 } |
| 404 | 410 |
| 405 // Lazily keep a JS Date stored in the JS object. | 411 // Lazily keep a JS Date stored in the JS object. |
| 406 static lazyAsJsDate(DateTime receiver) { | 412 static lazyAsJsDate(DateTime receiver) { |
| 407 if (JS('bool', r'#.date === (void 0)', receiver)) { | 413 if (JS('bool', r'#.date === (void 0)', receiver)) { |
| 408 JS('void', r'#.date = new Date(#)', receiver, | 414 JS('void', r'#.date = new Date(#)', receiver, |
| 409 receiver.millisecondsSinceEpoch); | 415 receiver.millisecondsSinceEpoch); |
| 410 } | 416 } |
| 411 return JS('var', r'#.date', receiver); | 417 return JS('var', r'#.date', receiver); |
| 412 } | 418 } |
| 413 | 419 |
| 414 // The getters for date and time parts below add a positive integer to ensure | 420 // The getters for date and time parts below add a positive integer to ensure |
| 415 // that the result is really an integer, because the JavaScript implementation | 421 // that the result is really an integer, because the JavaScript implementation |
| 416 // may return -0.0 instead of 0. | 422 // may return -0.0 instead of 0. |
| 417 | 423 |
| 418 static getYear(DateTime receiver) { | 424 static getYear(DateTime receiver) { |
| 419 return (receiver.isUtc) | 425 return (receiver.isUtc) |
| 420 ? JS('int', r'(#.getUTCFullYear() + 0)', lazyAsJsDate(receiver)) | 426 ? JS('int', r'(#.getUTCFullYear() + 0)', lazyAsJsDate(receiver)) |
| 421 : JS('int', r'(#.getFullYear() + 0)', lazyAsJsDate(receiver)); | 427 : JS('int', r'(#.getFullYear() + 0)', lazyAsJsDate(receiver)); |
| 422 } | 428 } |
| 423 | 429 |
| 424 static getMonth(DateTime receiver) { | 430 static getMonth(DateTime receiver) { |
| 425 return (receiver.isUtc) | 431 return (receiver.isUtc) |
| 426 ? JS('int', r'#.getUTCMonth() + 1', lazyAsJsDate(receiver)) | 432 ? JS('int', r'#.getUTCMonth() + 1', lazyAsJsDate(receiver)) |
| 427 : JS('int', r'#.getMonth() + 1', lazyAsJsDate(receiver)); | 433 : JS('int', r'#.getMonth() + 1', lazyAsJsDate(receiver)); |
| 428 } | 434 } |
| 429 | 435 |
| 430 static getDay(DateTime receiver) { | 436 static getDay(DateTime receiver) { |
| 431 return (receiver.isUtc) | 437 return (receiver.isUtc) |
| 432 ? JS('int', r'(#.getUTCDate() + 0)', lazyAsJsDate(receiver)) | 438 ? JS('int', r'(#.getUTCDate() + 0)', lazyAsJsDate(receiver)) |
| 433 : JS('int', r'(#.getDate() + 0)', lazyAsJsDate(receiver)); | 439 : JS('int', r'(#.getDate() + 0)', lazyAsJsDate(receiver)); |
| 434 } | 440 } |
| 435 | 441 |
| 436 static getHours(DateTime receiver) { | 442 static getHours(DateTime receiver) { |
| 437 return (receiver.isUtc) | 443 return (receiver.isUtc) |
| 438 ? JS('int', r'(#.getUTCHours() + 0)', lazyAsJsDate(receiver)) | 444 ? JS('int', r'(#.getUTCHours() + 0)', lazyAsJsDate(receiver)) |
| 439 : JS('int', r'(#.getHours() + 0)', lazyAsJsDate(receiver)); | 445 : JS('int', r'(#.getHours() + 0)', lazyAsJsDate(receiver)); |
| 440 } | 446 } |
| 441 | 447 |
| 442 static getMinutes(DateTime receiver) { | 448 static getMinutes(DateTime receiver) { |
| 443 return (receiver.isUtc) | 449 return (receiver.isUtc) |
| 444 ? JS('int', r'(#.getUTCMinutes() + 0)', lazyAsJsDate(receiver)) | 450 ? JS('int', r'(#.getUTCMinutes() + 0)', lazyAsJsDate(receiver)) |
| 445 : JS('int', r'(#.getMinutes() + 0)', lazyAsJsDate(receiver)); | 451 : JS('int', r'(#.getMinutes() + 0)', lazyAsJsDate(receiver)); |
| 446 } | 452 } |
| 447 | 453 |
| 448 static getSeconds(DateTime receiver) { | 454 static getSeconds(DateTime receiver) { |
| 449 return (receiver.isUtc) | 455 return (receiver.isUtc) |
| 450 ? JS('int', r'(#.getUTCSeconds() + 0)', lazyAsJsDate(receiver)) | 456 ? JS('int', r'(#.getUTCSeconds() + 0)', lazyAsJsDate(receiver)) |
| 451 : JS('int', r'(#.getSeconds() + 0)', lazyAsJsDate(receiver)); | 457 : JS('int', r'(#.getSeconds() + 0)', lazyAsJsDate(receiver)); |
| 452 } | 458 } |
| 453 | 459 |
| 454 static getMilliseconds(DateTime receiver) { | 460 static getMilliseconds(DateTime receiver) { |
| 455 return (receiver.isUtc) | 461 return (receiver.isUtc) |
| 456 ? JS('int', r'(#.getUTCMilliseconds() + 0)', lazyAsJsDate(receiver)) | 462 ? JS('int', r'(#.getUTCMilliseconds() + 0)', lazyAsJsDate(receiver)) |
| 457 : JS('int', r'(#.getMilliseconds() + 0)', lazyAsJsDate(receiver)); | 463 : JS('int', r'(#.getMilliseconds() + 0)', lazyAsJsDate(receiver)); |
| 458 } | 464 } |
| 459 | 465 |
| 460 static getWeekday(DateTime receiver) { | 466 static getWeekday(DateTime receiver) { |
| 461 int weekday = (receiver.isUtc) | 467 int weekday = (receiver.isUtc) |
| 462 ? JS('int', r'#.getUTCDay() + 0', lazyAsJsDate(receiver)) | 468 ? JS('int', r'#.getUTCDay() + 0', lazyAsJsDate(receiver)) |
| 463 : JS('int', r'#.getDay() + 0', lazyAsJsDate(receiver)); | 469 : JS('int', r'#.getDay() + 0', lazyAsJsDate(receiver)); |
| 464 // Adjust by one because JS weeks start on Sunday. | 470 // Adjust by one because JS weeks start on Sunday. |
| 465 return (weekday + 6) % 7 + 1; | 471 return (weekday + 6) % 7 + 1; |
| 466 } | 472 } |
| 467 | 473 |
| 468 static valueFromDateString(str) { | 474 static valueFromDateString(str) { |
| 469 if (str is !String) throw argumentErrorValue(str); | 475 if (str is! String) throw argumentErrorValue(str); |
| 470 var value = JS('num', r'Date.parse(#)', str); | 476 var value = JS('num', r'Date.parse(#)', str); |
| 471 if (value.isNaN) throw argumentErrorValue(str); | 477 if (value.isNaN) throw argumentErrorValue(str); |
| 472 return value; | 478 return value; |
| 473 } | 479 } |
| 474 | 480 |
| 475 static getProperty(object, key) { | 481 static getProperty(object, key) { |
| 476 if (object == null || object is bool || object is num || object is String) { | 482 if (object == null || object is bool || object is num || object is String) { |
| 477 throw argumentErrorValue(object); | 483 throw argumentErrorValue(object); |
| 478 } | 484 } |
| 479 return JS('var', '#[#]', object, key); | 485 return JS('var', '#[#]', object, key); |
| 480 } | 486 } |
| 481 | 487 |
| 482 static void setProperty(object, key, value) { | 488 static void setProperty(object, key, value) { |
| 483 if (object == null || object is bool || object is num || object is String) { | 489 if (object == null || object is bool || object is num || object is String) { |
| 484 throw argumentErrorValue(object); | 490 throw argumentErrorValue(object); |
| 485 } | 491 } |
| 486 JS('void', '#[#] = #', object, key, value); | 492 JS('void', '#[#] = #', object, key, value); |
| 487 } | 493 } |
| 488 | 494 |
| 489 static StackTrace extractStackTrace(Error error) { | 495 static StackTrace extractStackTrace(Error error) { |
| 490 return getTraceFromException(JS('', r'#.$thrownJsError', error)); | 496 return getTraceFromException(JS('', r'#.$thrownJsError', error)); |
| 491 } | 497 } |
| 492 } | 498 } |
| 499 |
| 493 /** | 500 /** |
| 494 * Diagnoses an indexing error. Returns the ArgumentError or RangeError that | 501 * Diagnoses an indexing error. Returns the ArgumentError or RangeError that |
| 495 * describes the problem. | 502 * describes the problem. |
| 496 */ | 503 */ |
| 497 @NoInline() | 504 @NoInline() |
| 498 Error diagnoseIndexError(indexable, index) { | 505 Error diagnoseIndexError(indexable, index) { |
| 499 if (index is !int) return new ArgumentError.value(index, 'index'); | 506 if (index is! int) return new ArgumentError.value(index, 'index'); |
| 500 int length = indexable.length; | 507 int length = indexable.length; |
| 501 // The following returns the same error that would be thrown by calling | 508 // The following returns the same error that would be thrown by calling |
| 502 // [RangeError.checkValidIndex] with no optional parameters provided. | 509 // [RangeError.checkValidIndex] with no optional parameters provided. |
| 503 if (index < 0 || index >= length) { | 510 if (index < 0 || index >= length) { |
| 504 return new RangeError.index(index, indexable, 'index', null, length); | 511 return new RangeError.index(index, indexable, 'index', null, length); |
| 505 } | 512 } |
| 506 // The above should always match, but if it does not, use the following. | 513 // The above should always match, but if it does not, use the following. |
| 507 return new RangeError.value(index, 'index'); | 514 return new RangeError.value(index, 'index'); |
| 508 } | 515 } |
| 509 | 516 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 524 return new ArgumentError.value(end, 'end'); | 531 return new ArgumentError.value(end, 'end'); |
| 525 } | 532 } |
| 526 if (end < start || end > length) { | 533 if (end < start || end > length) { |
| 527 return new RangeError.range(end, start, length, 'end'); | 534 return new RangeError.range(end, start, length, 'end'); |
| 528 } | 535 } |
| 529 } | 536 } |
| 530 // The above should always match, but if it does not, use the following. | 537 // The above should always match, but if it does not, use the following. |
| 531 return new ArgumentError.value(end, "end"); | 538 return new ArgumentError.value(end, "end"); |
| 532 } | 539 } |
| 533 | 540 |
| 534 stringLastIndexOfUnchecked(receiver, element, start) | 541 stringLastIndexOfUnchecked(receiver, element, start) => |
| 535 => JS('int', r'#.lastIndexOf(#, #)', receiver, element, start); | 542 JS('int', r'#.lastIndexOf(#, #)', receiver, element, start); |
| 536 | |
| 537 | 543 |
| 538 /// 'factory' for constructing ArgumentError.value to keep the call sites small. | 544 /// 'factory' for constructing ArgumentError.value to keep the call sites small. |
| 539 @NoInline() | 545 @NoInline() |
| 540 ArgumentError argumentErrorValue(object) { | 546 ArgumentError argumentErrorValue(object) { |
| 541 return new ArgumentError.value(object); | 547 return new ArgumentError.value(object); |
| 542 } | 548 } |
| 543 | 549 |
| 544 checkNull(object) { | 550 checkNull(object) { |
| 545 if (object == null) throw argumentErrorValue(object); | 551 if (object == null) throw argumentErrorValue(object); |
| 546 return object; | 552 return object; |
| 547 } | 553 } |
| 548 | 554 |
| 549 checkNum(value) { | 555 checkNum(value) { |
| 550 if (value is !num) throw argumentErrorValue(value); | 556 if (value is! num) throw argumentErrorValue(value); |
| 551 return value; | 557 return value; |
| 552 } | 558 } |
| 553 | 559 |
| 554 checkInt(value) { | 560 checkInt(value) { |
| 555 if (value is !int) throw argumentErrorValue(value); | 561 if (value is! int) throw argumentErrorValue(value); |
| 556 return value; | 562 return value; |
| 557 } | 563 } |
| 558 | 564 |
| 559 checkBool(value) { | 565 checkBool(value) { |
| 560 if (value is !bool) throw argumentErrorValue(value); | 566 if (value is! bool) throw argumentErrorValue(value); |
| 561 return value; | 567 return value; |
| 562 } | 568 } |
| 563 | 569 |
| 564 checkString(value) { | 570 checkString(value) { |
| 565 if (value is !String) throw argumentErrorValue(value); | 571 if (value is! String) throw argumentErrorValue(value); |
| 566 return value; | 572 return value; |
| 567 } | 573 } |
| 568 | 574 |
| 569 throwRuntimeError(message) { | 575 throwRuntimeError(message) { |
| 570 throw new RuntimeError(message); | 576 throw new RuntimeError(message); |
| 571 } | 577 } |
| 572 | 578 |
| 573 throwAbstractClassInstantiationError(className) { | 579 throwAbstractClassInstantiationError(className) { |
| 574 throw new AbstractClassInstantiationError(className); | 580 throw new AbstractClassInstantiationError(className); |
| 575 } | 581 } |
| 576 | 582 |
| 577 | |
| 578 @NoInline() | 583 @NoInline() |
| 579 throwConcurrentModificationError(collection) { | 584 throwConcurrentModificationError(collection) { |
| 580 throw new ConcurrentModificationError(collection); | 585 throw new ConcurrentModificationError(collection); |
| 581 } | 586 } |
| 582 | 587 |
| 583 class NullError extends Error implements NoSuchMethodError { | 588 class NullError extends Error implements NoSuchMethodError { |
| 584 final String _message; | 589 final String _message; |
| 585 final String _method; | 590 final String _method; |
| 586 | 591 |
| 587 NullError(this._message, match) | 592 NullError(this._message, match) |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 769 class JSName { | 774 class JSName { |
| 770 final String name; | 775 final String name; |
| 771 const JSName(this.name); | 776 const JSName(this.name); |
| 772 } | 777 } |
| 773 | 778 |
| 774 /** | 779 /** |
| 775 * Special interface recognized by the compiler and implemented by DOM | 780 * Special interface recognized by the compiler and implemented by DOM |
| 776 * objects that support integer indexing. This interface is not | 781 * objects that support integer indexing. This interface is not |
| 777 * visible to anyone, and is only injected into special libraries. | 782 * visible to anyone, and is only injected into special libraries. |
| 778 */ | 783 */ |
| 779 abstract class JavaScriptIndexingBehavior { | 784 abstract class JavaScriptIndexingBehavior {} |
| 780 } | |
| 781 | 785 |
| 782 // TODO(lrn): These exceptions should be implemented in core. | 786 // TODO(lrn): These exceptions should be implemented in core. |
| 783 // When they are, remove the 'Implementation' here. | 787 // When they are, remove the 'Implementation' here. |
| 784 | 788 |
| 785 /** Thrown by type assertions that fail. */ | 789 /** Thrown by type assertions that fail. */ |
| 786 class TypeErrorImplementation extends Error implements TypeError { | 790 class TypeErrorImplementation extends Error implements TypeError { |
| 787 final String message; | 791 final String message; |
| 788 | 792 |
| 789 /** | 793 /** |
| 790 * Normal type error caused by a failed subtype test. | 794 * Normal type error caused by a failed subtype test. |
| 791 */ | 795 */ |
| 792 // TODO(sra): Include [value] in message. | 796 // TODO(sra): Include [value] in message. |
| 793 TypeErrorImplementation(Object value, Object actualType, Object expectedType) | 797 TypeErrorImplementation(Object value, Object actualType, Object expectedType) |
| 794 : message = "Type '${actualType}' is not a subtype " | 798 : message = "Type '${actualType}' is not a subtype " |
| 795 "of type '${expectedType}'"; | 799 "of type '${expectedType}'"; |
| 796 | 800 |
| 797 TypeErrorImplementation.fromMessage(String this.message); | 801 TypeErrorImplementation.fromMessage(String this.message); |
| 798 | 802 |
| 799 String toString() => message; | 803 String toString() => message; |
| 800 } | 804 } |
| 801 | 805 |
| 802 /** Thrown by the 'as' operator if the cast isn't valid. */ | 806 /** Thrown by the 'as' operator if the cast isn't valid. */ |
| 803 class CastErrorImplementation extends Error implements CastError { | 807 class CastErrorImplementation extends Error implements CastError { |
| 804 // TODO(lrn): Rename to CastError (and move implementation into core). | 808 // TODO(lrn): Rename to CastError (and move implementation into core). |
| 805 final String message; | 809 final String message; |
| 806 | 810 |
| 807 /** | 811 /** |
| 808 * Normal cast error caused by a failed type cast. | 812 * Normal cast error caused by a failed type cast. |
| 809 */ | 813 */ |
| 810 // TODO(sra): Include [value] in message. | 814 // TODO(sra): Include [value] in message. |
| 811 CastErrorImplementation(Object value, Object actualType, Object expectedType) | 815 CastErrorImplementation(Object value, Object actualType, Object expectedType) |
| 812 : message = "CastError: Casting value of type '$actualType' to" | 816 : message = "CastError: Casting value of type '$actualType' to" |
| 813 " incompatible type '$expectedType'"; | 817 " incompatible type '$expectedType'"; |
| 814 | 818 |
| 815 String toString() => message; | 819 String toString() => message; |
| 816 } | 820 } |
| 817 | 821 |
| 818 /// Thrown by type assertions that fail in strong mode that would have passed in | 822 /// Thrown by type assertions that fail in strong mode that would have passed in |
| 819 /// standard Dart. | 823 /// standard Dart. |
| 820 class StrongModeTypeError extends Error implements TypeError, StrongModeError { | 824 class StrongModeTypeError extends Error implements TypeError, StrongModeError { |
| 821 final String message; | 825 final String message; |
| 822 // TODO(sra): Include [value] in message. | 826 // TODO(sra): Include [value] in message. |
| 823 StrongModeTypeError(Object value, Object actualType, Object expectedType) | 827 StrongModeTypeError(Object value, Object actualType, Object expectedType) |
| 824 : message = "Type '${actualType}' is not a subtype " | 828 : message = "Type '${actualType}' is not a subtype " |
| 825 "of type '${expectedType}' in strong mode"; | 829 "of type '${expectedType}' in strong mode"; |
| 826 String toString() => message; | 830 String toString() => message; |
| 827 } | 831 } |
| 828 | 832 |
| 829 /// Thrown by casts that fail in strong mode that would have passed in standard | 833 /// Thrown by casts that fail in strong mode that would have passed in standard |
| 830 /// Dart. | 834 /// Dart. |
| 831 class StrongModeCastError extends Error implements CastError, StrongModeError { | 835 class StrongModeCastError extends Error implements CastError, StrongModeError { |
| 832 final String message; | 836 final String message; |
| 833 // TODO(sra): Include [value] in message. | 837 // TODO(sra): Include [value] in message. |
| 834 StrongModeCastError(Object value, Object actualType, Object expectedType) | 838 StrongModeCastError(Object value, Object actualType, Object expectedType) |
| 835 : message = "CastError: Casting value of type '$actualType' to" | 839 : message = "CastError: Casting value of type '$actualType' to" |
| 836 " type '$expectedType' which is incompatible in strong mode"; | 840 " type '$expectedType' which is incompatible in strong mode"; |
| 837 String toString() => message; | 841 String toString() => message; |
| 838 } | 842 } |
| 839 | 843 |
| 840 /// Used for Strong-mode errors other than type assertions and casts. | 844 /// Used for Strong-mode errors other than type assertions and casts. |
| 841 class StrongModeErrorImplementation extends Error implements StrongModeError { | 845 class StrongModeErrorImplementation extends Error implements StrongModeError { |
| 842 final String message; | 846 final String message; |
| 843 StrongModeErrorImplementation(this.message); | 847 StrongModeErrorImplementation(this.message); |
| 844 String toString() => message; | 848 String toString() => message; |
| 845 } | 849 } |
| 846 | 850 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 878 // TODO(lrn): Use a secure random source. | 882 // TODO(lrn): Use a secure random source. |
| 879 int int32a = JS("int", "(Math.random() * 0x100000000) >>> 0"); | 883 int int32a = JS("int", "(Math.random() * 0x100000000) >>> 0"); |
| 880 int int32b = JS("int", "(Math.random() * 0x100000000) >>> 0"); | 884 int int32b = JS("int", "(Math.random() * 0x100000000) >>> 0"); |
| 881 return int32a + int32b * 0x100000000; | 885 return int32a + int32b * 0x100000000; |
| 882 } | 886 } |
| 883 | 887 |
| 884 String jsonEncodeNative(String string) { | 888 String jsonEncodeNative(String string) { |
| 885 return JS("String", "JSON.stringify(#)", string); | 889 return JS("String", "JSON.stringify(#)", string); |
| 886 } | 890 } |
| 887 | 891 |
| 888 | |
| 889 // TODO(jmesserly): this adapter is to work around | 892 // TODO(jmesserly): this adapter is to work around |
| 890 // https://github.com/dart-lang/sdk/issues/28320 | 893 // https://github.com/dart-lang/sdk/issues/28320 |
| 891 class SyncIterator<E> implements Iterator<E> { | 894 class SyncIterator<E> implements Iterator<E> { |
| 892 final dynamic _jsIterator; | 895 final dynamic _jsIterator; |
| 893 E _current; | 896 E _current; |
| 894 | 897 |
| 895 SyncIterator(this._jsIterator); | 898 SyncIterator(this._jsIterator); |
| 896 | 899 |
| 897 E get current => _current; | 900 E get current => _current; |
| 898 | 901 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 913 // we have no way of telling the compiler yet, so it will generate an extra | 916 // we have no way of telling the compiler yet, so it will generate an extra |
| 914 // layer of indirection that wraps the SyncIterator. | 917 // layer of indirection that wraps the SyncIterator. |
| 915 _jsIterator() => JS('', '#(...#)', _generator, _args); | 918 _jsIterator() => JS('', '#(...#)', _generator, _args); |
| 916 | 919 |
| 917 Iterator<E> get iterator => new SyncIterator<E>(_jsIterator()); | 920 Iterator<E> get iterator => new SyncIterator<E>(_jsIterator()); |
| 918 } | 921 } |
| 919 | 922 |
| 920 class BooleanConversionAssertionError extends AssertionError { | 923 class BooleanConversionAssertionError extends AssertionError { |
| 921 toString() => 'Failed assertion: boolean expression must not be null'; | 924 toString() => 'Failed assertion: boolean expression must not be null'; |
| 922 } | 925 } |
| OLD | NEW |