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 |