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 |