OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 #library('js_helper'); | |
6 | |
7 #import('coreimpl.dart'); | |
8 | |
9 #source('constant_map.dart'); | |
10 #source('native_helper.dart'); | |
11 #source('regexp_helper.dart'); | |
12 #source('string_helper.dart'); | |
13 | |
14 /** | |
15 * Returns true if both arguments are numbers. | |
16 * | |
17 * If only the first argument is a number, an | |
18 * [IllegalArgumentException] with the other argument is thrown. | |
19 */ | |
20 bool checkNumbers(var a, var b) { | |
21 if (a is num) { | |
22 if (b is num) { | |
23 return true; | |
24 } else { | |
25 checkNull(b); | |
26 throw new IllegalArgumentException(b); | |
27 } | |
28 } | |
29 return false; | |
30 } | |
31 | |
32 | |
33 bool isJsArray(var value) { | |
34 return value !== null && JS('bool', @'#.constructor === Array', value); | |
35 } | |
36 | |
37 | |
38 add(var a, var b) { | |
39 if (checkNumbers(a, b)) { | |
40 return JS('num', @'# + #', a, b); | |
41 } else if (a is String) { | |
42 // TODO(lrn): Remove when we disable String.operator+ | |
43 b = b.toString(); | |
44 if (b is String) { | |
45 return JS('String', @'# + #', a, b); | |
46 } | |
47 checkNull(b); | |
48 throw new IllegalArgumentException(b); | |
49 } | |
50 return UNINTERCEPTED(a + b); | |
51 } | |
52 | |
53 div(var a, var b) { | |
54 if (checkNumbers(a, b)) { | |
55 return JS('num', @'# / #', a, b); | |
56 } | |
57 return UNINTERCEPTED(a / b); | |
58 } | |
59 | |
60 mul(var a, var b) { | |
61 if (checkNumbers(a, b)) { | |
62 return JS('num', @'# * #', a, b); | |
63 } | |
64 return UNINTERCEPTED(a * b); | |
65 } | |
66 | |
67 sub(var a, var b) { | |
68 if (checkNumbers(a, b)) { | |
69 return JS('num', @'# - #', a, b); | |
70 } | |
71 return UNINTERCEPTED(a - b); | |
72 } | |
73 | |
74 mod(var a, var b) { | |
75 if (checkNumbers(a, b)) { | |
76 // Euclidean Modulo. | |
77 int result = JS('num', @'# % #', a, b); | |
78 if (result == 0) return 0; // Make sure we don't return -0.0. | |
79 if (result > 0) return result; | |
80 if (b < 0) { | |
81 return result - b; | |
82 } else { | |
83 return result + b; | |
84 } | |
85 } | |
86 return UNINTERCEPTED(a % b); | |
87 } | |
88 | |
89 tdiv(var a, var b) { | |
90 if (checkNumbers(a, b)) { | |
91 return (a / b).truncate(); | |
92 } | |
93 return UNINTERCEPTED(a ~/ b); | |
94 } | |
95 | |
96 eq(var a, var b) { | |
97 if (JS('bool', @'typeof # === "object"', a)) { | |
98 if (JS_HAS_EQUALS(a)) { | |
99 return UNINTERCEPTED(a == b) === true; | |
100 } else { | |
101 return JS('bool', @'# === #', a, b); | |
102 } | |
103 } | |
104 // TODO(lrn): is NaN === NaN ? Is -0.0 === 0.0 ? | |
105 return JS('bool', @'# === #', a, b); | |
106 } | |
107 | |
108 eqq(var a, var b) { | |
109 return JS('bool', @'# === #', a, b); | |
110 } | |
111 | |
112 eqNull(var a) { | |
113 if (JS('bool', @'typeof # === "object"', a)) { | |
114 if (JS_HAS_EQUALS(a)) { | |
115 return UNINTERCEPTED(a == null) === true; | |
116 } else { | |
117 return false; | |
118 } | |
119 } else { | |
120 return JS('bool', @'typeof # === "undefined"', a); | |
121 } | |
122 } | |
123 | |
124 gt(var a, var b) { | |
125 if (checkNumbers(a, b)) { | |
126 return JS('bool', @'# > #', a, b); | |
127 } | |
128 return UNINTERCEPTED(a > b); | |
129 } | |
130 | |
131 ge(var a, var b) { | |
132 if (checkNumbers(a, b)) { | |
133 return JS('bool', @'# >= #', a, b); | |
134 } | |
135 return UNINTERCEPTED(a >= b); | |
136 } | |
137 | |
138 lt(var a, var b) { | |
139 if (checkNumbers(a, b)) { | |
140 return JS('bool', @'# < #', a, b); | |
141 } | |
142 return UNINTERCEPTED(a < b); | |
143 } | |
144 | |
145 le(var a, var b) { | |
146 if (checkNumbers(a, b)) { | |
147 return JS('bool', @'# <= #', a, b); | |
148 } | |
149 return UNINTERCEPTED(a <= b); | |
150 } | |
151 | |
152 shl(var a, var b) { | |
153 // TODO(floitsch): inputs must be integers. | |
154 if (checkNumbers(a, b)) { | |
155 if (b < 0) throw new IllegalArgumentException(b); | |
156 return JS('num', @'# << #', a, b); | |
157 } | |
158 return UNINTERCEPTED(a << b); | |
159 } | |
160 | |
161 shr(var a, var b) { | |
162 // TODO(floitsch): inputs must be integers. | |
163 if (checkNumbers(a, b)) { | |
164 if (b < 0) throw new IllegalArgumentException(b); | |
165 return JS('num', @'# >> #', a, b); | |
166 } | |
167 return UNINTERCEPTED(a >> b); | |
168 } | |
169 | |
170 and(var a, var b) { | |
171 // TODO(floitsch): inputs must be integers. | |
172 if (checkNumbers(a, b)) { | |
173 return JS('num', @'# & #', a, b); | |
174 } | |
175 return UNINTERCEPTED(a & b); | |
176 } | |
177 | |
178 or(var a, var b) { | |
179 // TODO(floitsch): inputs must be integers. | |
180 if (checkNumbers(a, b)) { | |
181 return JS('num', @'# | #', a, b); | |
182 } | |
183 return UNINTERCEPTED(a | b); | |
184 } | |
185 | |
186 xor(var a, var b) { | |
187 // TODO(floitsch): inputs must be integers. | |
188 if (checkNumbers(a, b)) { | |
189 return JS('num', @'# ^ #', a, b); | |
190 } | |
191 return UNINTERCEPTED(a ^ b); | |
192 } | |
193 | |
194 not(var a) { | |
195 if (JS('bool', @'typeof # === "number"', a)) return JS('num', @'~#', a); | |
196 return UNINTERCEPTED(~a); | |
197 } | |
198 | |
199 neg(var a) { | |
200 if (JS('bool', @'typeof # === "number"', a)) return JS('num', @'-#', a); | |
201 return UNINTERCEPTED(-a); | |
202 } | |
203 | |
204 index(var a, var index) { | |
205 if (a is String || isJsArray(a)) { | |
206 if (index is !int) { | |
207 if (index is !num) throw new IllegalArgumentException(index); | |
208 if (index.truncate() !== index) throw new IllegalArgumentException(index); | |
209 } | |
210 if (index < 0 || index >= a.length) { | |
211 throw new IndexOutOfRangeException(index); | |
212 } | |
213 return JS('Object', @'#[#]', a, index); | |
214 } | |
215 return UNINTERCEPTED(a[index]); | |
216 } | |
217 | |
218 void indexSet(var a, var index, var value) { | |
219 if (isJsArray(a)) { | |
220 if (!(index is int)) { | |
221 throw new IllegalArgumentException(index); | |
222 } | |
223 if (index < 0 || index >= a.length) { | |
224 throw new IndexOutOfRangeException(index); | |
225 } | |
226 checkMutable(a, 'indexed set'); | |
227 JS('Object', @'#[#] = #', a, index, value); | |
228 return; | |
229 } | |
230 UNINTERCEPTED(a[index] = value); | |
231 } | |
232 | |
233 checkMutable(list, reason) { | |
234 if (JS('bool', @'!!(#.immutable$list)', list)) { | |
235 throw new UnsupportedOperationException(reason); | |
236 } | |
237 } | |
238 | |
239 builtin$add$1(var receiver, var value) { | |
240 if (isJsArray(receiver)) { | |
241 checkGrowable(receiver, 'add'); | |
242 JS('Object', @'#.push(#)', receiver, value); | |
243 return; | |
244 } | |
245 return UNINTERCEPTED(receiver.add(value)); | |
246 } | |
247 | |
248 builtin$removeLast$0(var receiver) { | |
249 if (isJsArray(receiver)) { | |
250 checkGrowable(receiver, 'removeLast'); | |
251 if (receiver.length === 0) throw new IndexOutOfRangeException(-1); | |
252 return JS('Object', @'#.pop()', receiver); | |
253 } | |
254 return UNINTERCEPTED(receiver.removeLast()); | |
255 } | |
256 | |
257 builtin$filter$1(var receiver, var predicate) { | |
258 if (!isJsArray(receiver)) { | |
259 return UNINTERCEPTED(receiver.filter(predicate)); | |
260 } else { | |
261 return Collections.filter(receiver, [], predicate); | |
262 } | |
263 } | |
264 | |
265 | |
266 builtin$get$length(var receiver) { | |
267 if (receiver is String || isJsArray(receiver)) { | |
268 return JS('num', @'#.length', receiver); | |
269 } else { | |
270 return UNINTERCEPTED(receiver.length); | |
271 } | |
272 } | |
273 | |
274 builtin$set$length(receiver, newLength) { | |
275 if (isJsArray(receiver)) { | |
276 checkNull(newLength); // TODO(ahe): This is not specified but co19 tests it. | |
277 if (newLength is !int) throw new IllegalArgumentException(newLength); | |
278 if (newLength < 0) throw new IndexOutOfRangeException(newLength); | |
279 checkGrowable(receiver, 'set length'); | |
280 JS('void', @'#.length = #', receiver, newLength); | |
281 } else { | |
282 UNINTERCEPTED(receiver.length = newLength); | |
283 } | |
284 return newLength; | |
285 } | |
286 | |
287 checkGrowable(list, reason) { | |
288 if (JS('bool', @'!!(#.fixed$length)', list)) { | |
289 throw new UnsupportedOperationException(reason); | |
290 } | |
291 } | |
292 | |
293 builtin$toString$0(var value) { | |
294 if (JS('bool', @'typeof # == "object"', value)) { | |
295 if (isJsArray(value)) { | |
296 return Collections.collectionToString(value); | |
297 } else { | |
298 return UNINTERCEPTED(value.toString()); | |
299 } | |
300 } | |
301 if (JS('bool', @'# === 0 && (1 / #) < 0', value, value)) { | |
302 return '-0.0'; | |
303 } | |
304 if (value === null) return 'null'; | |
305 if (JS('bool', @'typeof # == "function"', value)) { | |
306 return 'Closure'; | |
307 } | |
308 return JS('string', @'String(#)', value); | |
309 } | |
310 | |
311 | |
312 builtin$iterator$0(receiver) { | |
313 if (isJsArray(receiver)) { | |
314 return new ListIterator(receiver); | |
315 } | |
316 return UNINTERCEPTED(receiver.iterator()); | |
317 } | |
318 | |
319 class ListIterator<T> implements Iterator<T> { | |
320 int i; | |
321 List<T> list; | |
322 ListIterator(List<T> this.list) : i = 0; | |
323 bool hasNext() => i < JS('int', @'#.length', list); | |
324 T next() { | |
325 if (!hasNext()) throw new NoMoreElementsException(); | |
326 var value = JS('Object', @'#[#]', list, i); | |
327 i += 1; | |
328 return value; | |
329 } | |
330 } | |
331 | |
332 builtin$charCodeAt$1(var receiver, int index) { | |
333 if (receiver is String) { | |
334 if (index is !num) throw new IllegalArgumentException(index); | |
335 if (index < 0) throw new IndexOutOfRangeException(index); | |
336 if (index >= receiver.length) throw new IndexOutOfRangeException(index); | |
337 return JS('int', @'#.charCodeAt(#)', receiver, index); | |
338 } else { | |
339 return UNINTERCEPTED(receiver.charCodeAt(index)); | |
340 } | |
341 } | |
342 | |
343 builtin$isEmpty$0(receiver) { | |
344 if (receiver is String || isJsArray(receiver)) { | |
345 return JS('bool', @'#.length === 0', receiver); | |
346 } | |
347 return UNINTERCEPTED(receiver.isEmpty()); | |
348 } | |
349 | |
350 class Primitives { | |
351 static void printString(String string) { | |
352 var hasConsole = JS('bool', @'typeof console == "object"'); | |
353 if (hasConsole) { | |
354 JS('void', @'console.log(#)', string); | |
355 } else { | |
356 JS('void', @'write(#)', string); | |
357 JS('void', @'write("\n")'); | |
358 } | |
359 } | |
360 | |
361 /** [: @"$".charCodeAt(0) :] */ | |
362 static final int DOLLAR_CHAR_VALUE = 36; | |
363 | |
364 static String objectToString(Object object) { | |
365 String name = JS('String', @'#.constructor.name', object); | |
366 if (name === null) { | |
367 name = JS('String', @'#.match(/^\s*function\s*\$?(\S*)\s*\(/)[1]', | |
368 JS('String', @'#.constructor.toString()', object)); | |
369 } else { | |
370 if (name.charCodeAt(0) === DOLLAR_CHAR_VALUE) name = name.substring(1); | |
371 } | |
372 return "Instance of '$name'"; | |
373 } | |
374 | |
375 static List newList(length) { | |
376 if (length === null) return JS('Object', @'new Array()'); | |
377 if ((length is !int) || (length < 0)) { | |
378 throw new IllegalArgumentException(length); | |
379 } | |
380 var result = JS('Object', @'new Array(#)', length); | |
381 JS('void', @'#.fixed$length = #', result, true); | |
382 return result; | |
383 } | |
384 | |
385 static num dateNow() => JS('num', @'Date.now()'); | |
386 | |
387 static String stringFromCharCodes(charCodes) { | |
388 for (var i in charCodes) { | |
389 if (i is !int) throw new IllegalArgumentException(i); | |
390 } | |
391 return JS('String', @'String.fromCharCode.apply(#, #)', null, charCodes); | |
392 } | |
393 | |
394 static valueFromDecomposedDate(years, month, day, hours, minutes, seconds, | |
395 milliseconds, isUtc) { | |
396 checkInt(years); | |
397 checkInt(month); | |
398 if (month < 1 || 12 < month) throw new IllegalArgumentException(month); | |
399 checkInt(day); | |
400 if (day < 1 || 31 < day) throw new IllegalArgumentException(day); | |
401 checkInt(hours); | |
402 if (hours < 0 || 24 < hours) throw new IllegalArgumentException(hours); | |
403 checkInt(minutes); | |
404 if (minutes < 0 || 59 < minutes) { | |
405 throw new IllegalArgumentException(minutes); | |
406 } | |
407 checkInt(seconds); | |
408 if (seconds < 0 || 59 < seconds) { | |
409 // TODO(ahe): Leap seconds? | |
410 throw new IllegalArgumentException(seconds); | |
411 } | |
412 checkInt(milliseconds); | |
413 if (milliseconds < 0 || 999 < milliseconds) { | |
414 throw new IllegalArgumentException(milliseconds); | |
415 } | |
416 checkBool(isUtc); | |
417 var jsMonth = month - 1; | |
418 var value; | |
419 if (isUtc) { | |
420 value = JS('num', @'Date.UTC(#, #, #, #, #, #, #)', | |
421 years, jsMonth, day, hours, minutes, seconds, milliseconds); | |
422 } else { | |
423 value = JS('num', @'new Date(#, #, #, #, #, #, #).valueOf()', | |
424 years, jsMonth, day, hours, minutes, seconds, milliseconds); | |
425 } | |
426 if (value.isNaN()) throw new IllegalArgumentException(''); | |
427 if (years <= 0 || years < 100) return patchUpY2K(value, years, isUtc); | |
428 return value; | |
429 } | |
430 | |
431 static patchUpY2K(value, years, isUtc) { | |
432 var date = JS('Object', @'new Date(#)', value); | |
433 if (isUtc) { | |
434 JS('num', @'#.setUTCFullYear(#)', date, years); | |
435 } else { | |
436 JS('num', @'#.setFullYear(#)', date, years); | |
437 } | |
438 return JS('num', @'#.valueOf()', date); | |
439 } | |
440 | |
441 // Lazily keep a JS Date stored in the JS object. | |
442 static lazyAsJsDate(receiver) { | |
443 if (JS('bool', @'#.date === (void 0)', receiver)) { | |
444 JS('void', @'#.date = new Date(#)', receiver, receiver.value); | |
445 } | |
446 return JS('Date', @'#.date', receiver); | |
447 } | |
448 | |
449 static getYear(receiver) { | |
450 return (receiver.timeZone.isUtc) | |
451 ? JS('int', @'#.getUTCFullYear()', lazyAsJsDate(receiver)) | |
452 : JS('int', @'#.getFullYear()', lazyAsJsDate(receiver)); | |
453 } | |
454 | |
455 static getMonth(receiver) { | |
456 return (receiver.timeZone.isUtc) | |
457 ? JS('int', @'#.getUTCMonth()', lazyAsJsDate(receiver)) + 1 | |
458 : JS('int', @'#.getMonth()', lazyAsJsDate(receiver)) + 1; | |
459 } | |
460 | |
461 static getDay(receiver) { | |
462 return (receiver.timeZone.isUtc) | |
463 ? JS('int', @'#.getUTCDate()', lazyAsJsDate(receiver)) | |
464 : JS('int', @'#.getDate()', lazyAsJsDate(receiver)); | |
465 } | |
466 | |
467 static getHours(receiver) { | |
468 return (receiver.timeZone.isUtc) | |
469 ? JS('int', @'#.getUTCHours()', lazyAsJsDate(receiver)) | |
470 : JS('int', @'#.getHours()', lazyAsJsDate(receiver)); | |
471 } | |
472 | |
473 static getMinutes(receiver) { | |
474 return (receiver.timeZone.isUtc) | |
475 ? JS('int', @'#.getUTCMinutes()', lazyAsJsDate(receiver)) | |
476 : JS('int', @'#.getMinutes()', lazyAsJsDate(receiver)); | |
477 } | |
478 | |
479 static getSeconds(receiver) { | |
480 return (receiver.timeZone.isUtc) | |
481 ? JS('int', @'#.getUTCSeconds()', lazyAsJsDate(receiver)) | |
482 : JS('int', @'#.getSeconds()', lazyAsJsDate(receiver)); | |
483 } | |
484 | |
485 static getMilliseconds(receiver) { | |
486 return (receiver.timeZone.isUtc) | |
487 ? JS('int', @'#.getUTCMilliseconds()', lazyAsJsDate(receiver)) | |
488 : JS('int', @'#.getMilliseconds()', lazyAsJsDate(receiver)); | |
489 } | |
490 | |
491 static getWeekday(receiver) { | |
492 return (receiver.timeZone.isUtc) | |
493 ? JS('int', @'#.getUTCDay()', lazyAsJsDate(receiver)) | |
494 : JS('int', @'#.getDay()', lazyAsJsDate(receiver)); | |
495 } | |
496 | |
497 static valueFromDateString(str) { | |
498 checkNull(str); | |
499 if (str is !String) throw new IllegalArgumentException(str); | |
500 var value = JS('num', @'Date.parse(#)', str); | |
501 if (value.isNaN()) throw new IllegalArgumentException(str); | |
502 return value; | |
503 } | |
504 } | |
505 | |
506 builtin$compareTo$1(a, b) { | |
507 if (checkNumbers(a, b)) { | |
508 if (a < b) { | |
509 return -1; | |
510 } else if (a > b) { | |
511 return 1; | |
512 } else if (a == b) { | |
513 if (a == 0) { | |
514 bool aIsNegative = a.isNegative(); | |
515 bool bIsNegative = b.isNegative(); | |
516 if (aIsNegative == bIsNegative) return 0; | |
517 if (aIsNegative) return -1; | |
518 return 1; | |
519 } | |
520 return 0; | |
521 } else if (a.isNaN()) { | |
522 if (b.isNaN()) { | |
523 return 0; | |
524 } | |
525 return 1; | |
526 } else { | |
527 return -1; | |
528 } | |
529 } else if (a is String) { | |
530 if (b is !String) throw new IllegalArgumentException(b); | |
531 return JS('bool', @'# == #', a, b) ? 0 | |
532 : JS('bool', @'# < #', a, b) ? -1 : 1; | |
533 } else { | |
534 return UNINTERCEPTED(a.compareTo(b)); | |
535 } | |
536 } | |
537 | |
538 /** | |
539 * Called by generated code to throw an illegal-argument exception, | |
540 * for example, if a non-integer index is given to an optimized | |
541 * indexed access. | |
542 */ | |
543 iae(argument) { | |
544 throw new IllegalArgumentException(argument); | |
545 } | |
546 | |
547 /** | |
548 * Called by generated code to throw an index-out-of-range exception, | |
549 * for example, if a bounds check fails in an optimized indexed | |
550 * access. | |
551 */ | |
552 ioore(index) { | |
553 throw new IndexOutOfRangeException(index); | |
554 } | |
555 | |
556 builtin$addAll$1(receiver, collection) { | |
557 if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.addAll(collection)); | |
558 | |
559 // TODO(ahe): Use for-in when it is implemented correctly. | |
560 var iterator = collection.iterator(); | |
561 while (iterator.hasNext()) { | |
562 receiver.add(iterator.next()); | |
563 } | |
564 } | |
565 | |
566 builtin$addLast$1(receiver, value) { | |
567 if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.addLast(value)); | |
568 | |
569 checkGrowable(receiver, 'addLast'); | |
570 JS('Object', @'#.push(#)', receiver, value); | |
571 } | |
572 | |
573 builtin$clear$0(receiver) { | |
574 if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.clear()); | |
575 receiver.length = 0; | |
576 } | |
577 | |
578 builtin$forEach$1(receiver, f) { | |
579 if (!isJsArray(receiver)) { | |
580 return UNINTERCEPTED(receiver.forEach(f)); | |
581 } else { | |
582 return Collections.forEach(receiver, f); | |
583 } | |
584 } | |
585 | |
586 builtin$map$1(receiver, f) { | |
587 if (!isJsArray(receiver)) { | |
588 return UNINTERCEPTED(receiver.map(f)); | |
589 } else { | |
590 return Collections.map(receiver, [], f); | |
591 } | |
592 } | |
593 | |
594 builtin$getRange$2(receiver, start, length) { | |
595 if (!isJsArray(receiver)) { | |
596 return UNINTERCEPTED(receiver.getRange(start, length)); | |
597 } | |
598 if (0 === length) return []; | |
599 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. | |
600 checkNull(length); // TODO(ahe): This is not specified but co19 tests it. | |
601 if (start is !int) throw new IllegalArgumentException(start); | |
602 if (length is !int) throw new IllegalArgumentException(length); | |
603 if (length < 0) throw new IllegalArgumentException(length); | |
604 if (start < 0) throw new IndexOutOfRangeException(start); | |
605 var end = start + length; | |
606 if (end > receiver.length) { | |
607 throw new IndexOutOfRangeException(length); | |
608 } | |
609 if (length < 0) throw new IllegalArgumentException(length); | |
610 return JS('Object', @'#.slice(#, #)', receiver, start, end); | |
611 } | |
612 | |
613 builtin$indexOf$1(receiver, element) { | |
614 if (isJsArray(receiver) || receiver is String) { | |
615 return builtin$indexOf$2(receiver, element, 0); | |
616 } | |
617 return UNINTERCEPTED(receiver.indexOf(element)); | |
618 } | |
619 | |
620 builtin$indexOf$2(receiver, element, start) { | |
621 if (isJsArray(receiver)) { | |
622 if (start is !int) throw new IllegalArgumentException(start); | |
623 var length = JS('num', @'#.length', receiver); | |
624 return Arrays.indexOf(receiver, element, start, length); | |
625 } else if (receiver is String) { | |
626 checkNull(element); | |
627 if (start is !int) throw new IllegalArgumentException(start); | |
628 if (element is !String) throw new IllegalArgumentException(element); | |
629 if (start < 0) return -1; // TODO(ahe): Is this correct? | |
630 return JS('int', @'#.indexOf(#, #)', receiver, element, start); | |
631 } | |
632 return UNINTERCEPTED(receiver.indexOf(element, start)); | |
633 } | |
634 | |
635 builtin$insertRange$2(receiver, start, length) { | |
636 if (isJsArray(receiver)) { | |
637 return builtin$insertRange$3(receiver, start, length, null); | |
638 } | |
639 return UNINTERCEPTED(receiver.insertRange(start, length)); | |
640 } | |
641 | |
642 builtin$insertRange$3(receiver, start, length, initialValue) { | |
643 if (!isJsArray(receiver)) { | |
644 return UNINTERCEPTED(receiver.insertRange(start, length, initialValue)); | |
645 } | |
646 return listInsertRange(receiver, start, length, initialValue); | |
647 } | |
648 | |
649 listInsertRange(receiver, start, length, initialValue) { | |
650 if (length === 0) { | |
651 return; | |
652 } | |
653 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. | |
654 checkNull(length); // TODO(ahe): This is not specified but co19 tests it. | |
655 if (length is !int) throw new IllegalArgumentException(length); | |
656 if (length < 0) throw new IllegalArgumentException(length); | |
657 if (start is !int) throw new IllegalArgumentException(start); | |
658 | |
659 var receiverLength = JS('num', @'#.length', receiver); | |
660 if (start < 0 || start > receiverLength) { | |
661 throw new IndexOutOfRangeException(start); | |
662 } | |
663 receiver.length = receiverLength + length; | |
664 Arrays.copy(receiver, | |
665 start, | |
666 receiver, | |
667 start + length, | |
668 receiverLength - start); | |
669 if (initialValue !== null) { | |
670 for (int i = start; i < start + length; i++) { | |
671 receiver[i] = initialValue; | |
672 } | |
673 } | |
674 receiver.length = receiverLength + length; | |
675 } | |
676 | |
677 builtin$last$0(receiver) { | |
678 if (!isJsArray(receiver)) { | |
679 return UNINTERCEPTED(receiver.last()); | |
680 } | |
681 return receiver[receiver.length - 1]; | |
682 } | |
683 | |
684 builtin$lastIndexOf$1(receiver, element) { | |
685 if (isJsArray(receiver)) { | |
686 var start = JS('num', @'#.length', receiver); | |
687 return Arrays.lastIndexOf(receiver, element, start); | |
688 } else if (receiver is String) { | |
689 checkNull(element); | |
690 if (element is !String) throw new IllegalArgumentException(element); | |
691 return JS('int', @'#.lastIndexOf(#)', receiver, element); | |
692 } | |
693 return UNINTERCEPTED(receiver.lastIndexOf(element)); | |
694 } | |
695 | |
696 builtin$lastIndexOf$2(receiver, element, start) { | |
697 if (isJsArray(receiver)) { | |
698 return Arrays.lastIndexOf(receiver, element, start); | |
699 } else if (receiver is String) { | |
700 checkNull(element); | |
701 if (element is !String) throw new IllegalArgumentException(element); | |
702 if (start !== null) { | |
703 if (start is !num) throw new IllegalArgumentException(start); | |
704 if (start < 0) return -1; | |
705 if (start >= receiver.length) { | |
706 if (element == "") return receiver.length; | |
707 start = receiver.length - 1; | |
708 } | |
709 } | |
710 return stringLastIndexOfUnchecked(receiver, element, start); | |
711 } | |
712 return UNINTERCEPTED(receiver.lastIndexOf(element, start)); | |
713 } | |
714 | |
715 stringLastIndexOfUnchecked(receiver, element, start) | |
716 => JS('int', @'#.lastIndexOf(#, #)', receiver, element, start); | |
717 | |
718 builtin$removeRange$2(receiver, start, length) { | |
719 if (!isJsArray(receiver)) { | |
720 return UNINTERCEPTED(receiver.removeRange(start, length)); | |
721 } | |
722 checkGrowable(receiver, 'removeRange'); | |
723 if (length == 0) { | |
724 return; | |
725 } | |
726 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. | |
727 checkNull(length); // TODO(ahe): This is not specified but co19 tests it. | |
728 if (start is !int) throw new IllegalArgumentException(start); | |
729 if (length is !int) throw new IllegalArgumentException(length); | |
730 if (length < 0) throw new IllegalArgumentException(length); | |
731 var receiverLength = JS('num', @'#.length', receiver); | |
732 if (start < 0 || start >= receiverLength) { | |
733 throw new IndexOutOfRangeException(start); | |
734 } | |
735 if (start + length > receiverLength) { | |
736 throw new IndexOutOfRangeException(start + length); | |
737 } | |
738 Arrays.copy(receiver, | |
739 start + length, | |
740 receiver, | |
741 start, | |
742 receiverLength - length - start); | |
743 receiver.length = receiverLength - length; | |
744 } | |
745 | |
746 builtin$setRange$3(receiver, start, length, from) { | |
747 if (isJsArray(receiver)) { | |
748 return builtin$setRange$4(receiver, start, length, from, 0); | |
749 } | |
750 return UNINTERCEPTED(receiver.setRange(start, length, from)); | |
751 } | |
752 | |
753 builtin$setRange$4(receiver, start, length, from, startFrom) { | |
754 if (!isJsArray(receiver)) { | |
755 return UNINTERCEPTED(receiver.setRange(start, length, from, startFrom)); | |
756 } | |
757 | |
758 checkMutable(receiver, 'indexed set'); | |
759 if (length === 0) return; | |
760 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. | |
761 checkNull(length); // TODO(ahe): This is not specified but co19 tests it. | |
762 checkNull(from); // TODO(ahe): This is not specified but co19 tests it. | |
763 checkNull(startFrom); // TODO(ahe): This is not specified but co19 tests it. | |
764 if (start is !int) throw new IllegalArgumentException(start); | |
765 if (length is !int) throw new IllegalArgumentException(length); | |
766 if (startFrom is !int) throw new IllegalArgumentException(startFrom); | |
767 if (length < 0) throw new IllegalArgumentException(length); | |
768 if (start < 0) throw new IndexOutOfRangeException(start); | |
769 if (start + length > receiver.length) { | |
770 throw new IndexOutOfRangeException(start + length); | |
771 } | |
772 | |
773 Arrays.copy(from, startFrom, receiver, start, length); | |
774 } | |
775 | |
776 builtin$some$1(receiver, f) { | |
777 if (!isJsArray(receiver)) { | |
778 return UNINTERCEPTED(receiver.some(f)); | |
779 } else { | |
780 return Collections.some(receiver, f); | |
781 } | |
782 } | |
783 | |
784 builtin$every$1(receiver, f) { | |
785 if (!isJsArray(receiver)) { | |
786 return UNINTERCEPTED(receiver.every(f)); | |
787 } else { | |
788 return Collections.every(receiver, f); | |
789 } | |
790 } | |
791 | |
792 builtin$sort$1(receiver, compare) { | |
793 if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.sort(compare)); | |
794 | |
795 checkMutable(receiver, 'sort'); | |
796 DualPivotQuicksort.sort(receiver, compare); | |
797 } | |
798 | |
799 checkNull(object) { | |
800 if (object === null) throw new NullPointerException(); | |
801 return object; | |
802 } | |
803 | |
804 checkNum(value) { | |
805 if (value is !num) { | |
806 checkNull(value); | |
807 throw new IllegalArgumentException(value); | |
808 } | |
809 return value; | |
810 } | |
811 | |
812 checkInt(value) { | |
813 if (value is !int) { | |
814 checkNull(value); | |
815 throw new IllegalArgumentException(value); | |
816 } | |
817 return value; | |
818 } | |
819 | |
820 checkBool(value) { | |
821 if (value is !bool) { | |
822 checkNull(value); | |
823 throw new IllegalArgumentException(value); | |
824 } | |
825 return value; | |
826 } | |
827 | |
828 checkString(value) { | |
829 if (value is !String) { | |
830 checkNull(value); | |
831 throw new IllegalArgumentException(value); | |
832 } | |
833 return value; | |
834 } | |
835 | |
836 builtin$isNegative$0(receiver) { | |
837 if (receiver is num) { | |
838 return (receiver === 0) ? (1 / receiver) < 0 : receiver < 0; | |
839 } else { | |
840 return UNINTERCEPTED(receiver.isNegative()); | |
841 } | |
842 } | |
843 | |
844 builtin$isNaN$0(receiver) { | |
845 if (receiver is num) { | |
846 return JS('bool', @'isNaN(#)', receiver); | |
847 } else { | |
848 return UNINTERCEPTED(receiver.isNegative()); | |
849 } | |
850 } | |
851 | |
852 builtin$remainder$1(a, b) { | |
853 if (checkNumbers(a, b)) { | |
854 return JS('num', @'# % #', a, b); | |
855 } else { | |
856 return UNINTERCEPTED(a.remainder(b)); | |
857 } | |
858 } | |
859 | |
860 builtin$abs$0(receiver) { | |
861 if (receiver is !num) return UNINTERCEPTED(receiver.abs()); | |
862 | |
863 return JS('num', @'Math.abs(#)', receiver); | |
864 } | |
865 | |
866 builtin$toInt$0(receiver) { | |
867 if (receiver is !num) return UNINTERCEPTED(receiver.toInt()); | |
868 | |
869 if (receiver.isNaN()) throw new BadNumberFormatException('NaN'); | |
870 | |
871 if (receiver.isInfinite()) throw new BadNumberFormatException('Infinity'); | |
872 | |
873 var truncated = receiver.truncate(); | |
874 return JS('bool', @'# == -0.0', truncated) ? 0 : truncated; | |
875 } | |
876 | |
877 builtin$ceil$0(receiver) { | |
878 if (receiver is !num) return UNINTERCEPTED(receiver.ceil()); | |
879 | |
880 return JS('num', @'Math.ceil(#)', receiver); | |
881 } | |
882 | |
883 builtin$floor$0(receiver) { | |
884 if (receiver is !num) return UNINTERCEPTED(receiver.floor()); | |
885 | |
886 return JS('num', @'Math.floor(#)', receiver); | |
887 } | |
888 | |
889 builtin$isInfinite$0(receiver) { | |
890 if (receiver is !num) return UNINTERCEPTED(receiver.isInfinite()); | |
891 | |
892 return JS('bool', @'# == Infinity', receiver) | |
893 || JS('bool', @'# == -Infinity', receiver); | |
894 } | |
895 | |
896 builtin$negate$0(receiver) { | |
897 if (receiver is !num) return UNINTERCEPTED(receiver.negate()); | |
898 | |
899 return JS('num', @'-#', receiver); | |
900 } | |
901 | |
902 builtin$round$0(receiver) { | |
903 if (receiver is !num) return UNINTERCEPTED(receiver.round()); | |
904 | |
905 if (JS('bool', @'# < 0', receiver)) { | |
906 return JS('num', @'-Math.round(-#)', receiver); | |
907 } else { | |
908 return JS('num', @'Math.round(#)', receiver); | |
909 } | |
910 } | |
911 | |
912 builtin$toDouble$0(receiver) { | |
913 if (receiver is !num) return UNINTERCEPTED(receiver.toDouble()); | |
914 | |
915 // TODO(ahe): Just return receiver? | |
916 return JS('double', @'# + 0', receiver); | |
917 } | |
918 | |
919 builtin$truncate$0(receiver) { | |
920 if (receiver is !num) return UNINTERCEPTED(receiver.truncate()); | |
921 | |
922 return receiver < 0 ? receiver.ceil() : receiver.floor(); | |
923 } | |
924 | |
925 builtin$toStringAsFixed$1(receiver, fractionDigits) { | |
926 if (receiver is !num) { | |
927 return UNINTERCEPTED(receiver.toStringAsFixed(fractionDigits)); | |
928 } | |
929 checkNum(fractionDigits); | |
930 | |
931 String result = JS('String', @'#.toFixed(#)', receiver, fractionDigits); | |
932 if (receiver == 0 && receiver.isNegative()) return "-$result"; | |
933 return result; | |
934 } | |
935 | |
936 builtin$toStringAsExponential$1(receiver, fractionDigits) { | |
937 if (receiver is !num) { | |
938 return UNINTERCEPTED(receiver.toStringAsExponential(fractionDigits)); | |
939 } | |
940 if (fractionDigits !== null) checkNum(fractionDigits); | |
941 | |
942 String result = JS('String', @'#.toExponential(#)', | |
943 receiver, fractionDigits); | |
944 if (receiver == 0 && receiver.isNegative()) return "-$result"; | |
945 return result; | |
946 } | |
947 | |
948 builtin$toStringAsPrecision$1(receiver, fractionDigits) { | |
949 if (receiver is !num) { | |
950 return UNINTERCEPTED(receiver.toStringAsPrecision(fractionDigits)); | |
951 } | |
952 checkNum(fractionDigits); | |
953 | |
954 String result = JS('String', @'#.toPrecision(#)', | |
955 receiver, fractionDigits); | |
956 if (receiver == 0 && receiver.isNegative()) return "-$result"; | |
957 return result; | |
958 } | |
959 | |
960 builtin$toRadixString$1(receiver, radix) { | |
961 if (receiver is !num) { | |
962 return UNINTERCEPTED(receiver.toRadixString(radix)); | |
963 } | |
964 checkNum(radix); | |
965 | |
966 return JS('String', @'#.toString(#)', receiver, radix); | |
967 } | |
968 | |
969 builtin$allMatches$1(receiver, str) { | |
970 if (receiver is !String) return UNINTERCEPTED(receiver.allMatches(str)); | |
971 checkString(str); | |
972 return allMatchesInStringUnchecked(receiver, str); | |
973 } | |
974 | |
975 builtin$concat$1(receiver, other) { | |
976 if (receiver is !String) return UNINTERCEPTED(receiver.concat(other)); | |
977 | |
978 if (other is !String) throw new IllegalArgumentException(other); | |
979 return JS('String', @'# + #', receiver, other); | |
980 } | |
981 | |
982 builtin$contains$1(receiver, other) { | |
983 if (receiver is !String) { | |
984 return UNINTERCEPTED(receiver.contains(other)); | |
985 } | |
986 return builtin$contains$2(receiver, other, 0); | |
987 } | |
988 | |
989 builtin$contains$2(receiver, other, startIndex) { | |
990 if (receiver is !String) { | |
991 return UNINTERCEPTED(receiver.contains(other, startIndex)); | |
992 } | |
993 checkNull(other); | |
994 return stringContainsUnchecked(receiver, other, startIndex); | |
995 } | |
996 | |
997 builtin$endsWith$1(receiver, other) { | |
998 if (receiver is !String) return UNINTERCEPTED(receiver.endsWith(other)); | |
999 | |
1000 checkString(other); | |
1001 int receiverLength = receiver.length; | |
1002 int otherLength = other.length; | |
1003 if (otherLength > receiverLength) return false; | |
1004 return other == receiver.substring(receiverLength - otherLength); | |
1005 } | |
1006 | |
1007 builtin$replaceAll$2(receiver, from, to) { | |
1008 if (receiver is !String) return UNINTERCEPTED(receiver.replaceAll(from, to)); | |
1009 | |
1010 checkString(to); | |
1011 return stringReplaceAllUnchecked(receiver, from, to); | |
1012 } | |
1013 | |
1014 builtin$replaceFirst$2(receiver, from, to) { | |
1015 if (receiver is !String) { | |
1016 return UNINTERCEPTED(receiver.replaceFirst(from, to)); | |
1017 } | |
1018 checkString(to); | |
1019 return stringReplaceFirstUnchecked(receiver, from, to); | |
1020 } | |
1021 | |
1022 builtin$split$1(receiver, pattern) { | |
1023 if (receiver is !String) return UNINTERCEPTED(receiver.split(pattern)); | |
1024 checkNull(pattern); | |
1025 return stringSplitUnchecked(receiver, pattern); | |
1026 } | |
1027 | |
1028 builtin$splitChars$0(receiver) { | |
1029 if (receiver is !String) return UNINTERCEPTED(receiver.splitChars()); | |
1030 | |
1031 return JS('List', @'#.split("")', receiver); | |
1032 } | |
1033 | |
1034 builtin$startsWith$1(receiver, other) { | |
1035 if (receiver is !String) return UNINTERCEPTED(receiver.startsWith(other)); | |
1036 checkString(other); | |
1037 | |
1038 int length = other.length; | |
1039 if (length > receiver.length) return false; | |
1040 return JS('bool', @'# == #', other, | |
1041 JS('String', @'#.substring(0, #)', receiver, length)); | |
1042 } | |
1043 | |
1044 builtin$substring$1(receiver, startIndex) { | |
1045 if (receiver is !String) return UNINTERCEPTED(receiver.substring(startIndex)); | |
1046 | |
1047 return builtin$substring$2(receiver, startIndex, null); | |
1048 } | |
1049 | |
1050 builtin$substring$2(receiver, startIndex, endIndex) { | |
1051 if (receiver is !String) { | |
1052 return UNINTERCEPTED(receiver.substring(startIndex, endIndex)); | |
1053 } | |
1054 checkNum(startIndex); | |
1055 var length = receiver.length; | |
1056 if (endIndex === null) endIndex = length; | |
1057 checkNum(endIndex); | |
1058 if (startIndex < 0 ) throw new IndexOutOfRangeException(startIndex); | |
1059 if (startIndex > endIndex) throw new IndexOutOfRangeException(startIndex); | |
1060 if (endIndex > length) throw new IndexOutOfRangeException(endIndex); | |
1061 return substringUnchecked(receiver, startIndex, endIndex); | |
1062 } | |
1063 | |
1064 substringUnchecked(receiver, startIndex, endIndex) | |
1065 => JS('String', @'#.substring(#, #)', receiver, startIndex, endIndex); | |
1066 | |
1067 | |
1068 builtin$toLowerCase$0(receiver) { | |
1069 if (receiver is !String) return UNINTERCEPTED(receiver.toLowerCase()); | |
1070 | |
1071 return JS('String', @'#.toLowerCase()', receiver); | |
1072 } | |
1073 | |
1074 builtin$toUpperCase$0(receiver) { | |
1075 if (receiver is !String) return UNINTERCEPTED(receiver.toUpperCase()); | |
1076 | |
1077 return JS('String', @'#.toUpperCase()', receiver); | |
1078 } | |
1079 | |
1080 builtin$trim$0(receiver) { | |
1081 if (receiver is !String) return UNINTERCEPTED(receiver.trim()); | |
1082 | |
1083 return JS('String', @'#.trim()', receiver); | |
1084 } | |
1085 | |
1086 class MathNatives { | |
1087 static int parseInt(str) { | |
1088 checkString(str); | |
1089 if (!JS('bool', | |
1090 @'/^\s*[+-]?(?:0[xX][abcdefABCDEF0-9]+|\d+)\s*$/.test(#)', | |
1091 str)) { | |
1092 throw new BadNumberFormatException(str); | |
1093 } | |
1094 var trimmed = str.trim(); | |
1095 var base = 10;; | |
1096 if ((trimmed.length > 2 && (trimmed[1] == 'x' || trimmed[1] == 'X')) || | |
1097 (trimmed.length > 3 && (trimmed[2] == 'x' || trimmed[2] == 'X'))) { | |
1098 base = 16; | |
1099 } | |
1100 var ret = JS('num', @'parseInt(#, #)', trimmed, base); | |
1101 if (ret.isNaN()) throw new BadNumberFormatException(str); | |
1102 return ret; | |
1103 } | |
1104 | |
1105 static double parseDouble(String str) { | |
1106 checkString(str); | |
1107 var ret = JS('num', @'parseFloat(#)', str); | |
1108 if (ret == 0 && (str.startsWith("0x") || str.startsWith("0X"))) { | |
1109 // TODO(ahe): This is unspecified, but tested by co19. | |
1110 ret = JS('num', @'parseInt(#)', str); | |
1111 } | |
1112 if (ret.isNaN() && str != 'NaN' && str != '-NaN') { | |
1113 throw new BadNumberFormatException(str); | |
1114 } | |
1115 return ret; | |
1116 } | |
1117 | |
1118 static double sqrt(num value) | |
1119 => JS('double', @'Math.sqrt(#)', checkNum(value)); | |
1120 | |
1121 static double sin(num value) | |
1122 => JS('double', @'Math.sin(#)', checkNum(value)); | |
1123 | |
1124 static double cos(num value) | |
1125 => JS('double', @'Math.cos(#)', checkNum(value)); | |
1126 | |
1127 static double tan(num value) | |
1128 => JS('double', @'Math.tan(#)', checkNum(value)); | |
1129 | |
1130 static double acos(num value) | |
1131 => JS('double', @'Math.acos(#)', checkNum(value)); | |
1132 | |
1133 static double asin(num value) | |
1134 => JS('double', @'Math.asin(#)', checkNum(value)); | |
1135 | |
1136 static double atan(num value) | |
1137 => JS('double', @'Math.atan(#)', checkNum(value)); | |
1138 | |
1139 static double atan2(num a, num b) | |
1140 => JS('double', @'Math.atan2(#, #)', checkNum(a), checkNum(b)); | |
1141 | |
1142 static double exp(num value) | |
1143 => JS('double', @'Math.exp(#)', checkNum(value)); | |
1144 | |
1145 static double log(num value) | |
1146 => JS('double', @'Math.log(#)', checkNum(value)); | |
1147 | |
1148 static num pow(num value, num exponent) { | |
1149 checkNum(value); | |
1150 checkNum(exponent); | |
1151 return JS('num', @'Math.pow(#, #)', value, exponent); | |
1152 } | |
1153 | |
1154 static double random() => JS('double', @'Math.random()'); | |
1155 } | |
1156 | |
1157 /** | |
1158 * This is the [Jenkins hash function][1] but using masking to keep | |
1159 * values in SMI range. This was inspired by jmesserly's work in | |
1160 * Frog. | |
1161 * | |
1162 * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function | |
1163 */ | |
1164 builtin$hashCode$0(receiver) { | |
1165 // TODO(ahe): This method shouldn't have to use JS. Update when our | |
1166 // optimizations are smarter. | |
1167 if (receiver is num) return JS('int', @'# & 0x1FFFFFFF', receiver); | |
1168 if (receiver is !String) return UNINTERCEPTED(receiver.hashCode()); | |
1169 int hash = 0; | |
1170 int length = JS('int', @'#.length', receiver); | |
1171 for (int i = 0; i < length; i++) { | |
1172 hash = 0x1fffffff & (hash + JS('int', @'#.charCodeAt(#)', receiver, i)); | |
1173 hash = 0x1fffffff & (hash + JS('int', @'# << #', 0x0007ffff & hash, 10)); | |
1174 hash ^= hash >> 6; | |
1175 } | |
1176 hash = 0x1fffffff & (hash + JS('int', @'# << #', 0x03ffffff & hash, 3)); | |
1177 hash ^= hash >> 11; | |
1178 return 0x1fffffff & (hash + JS('int', @'# << #', 0x00003fff & hash, 15)); | |
1179 } | |
1180 | |
1181 // TODO(ahe): Dynamic may be overridden. | |
1182 builtin$get$dynamic(receiver) => receiver; | |
1183 | |
1184 /** | |
1185 * Called by generated code to capture the stacktrace before throwing | |
1186 * an exception. | |
1187 */ | |
1188 captureStackTrace(ex) { | |
1189 var jsError = JS('Object', @'new Error()'); | |
1190 JS('void', @'#.dartException = #', jsError, ex); | |
1191 JS('void', @'''#.toString = #''', jsError, DART_CLOSURE_TO_JS(toStringWrapper)
); | |
1192 return jsError; | |
1193 } | |
1194 | |
1195 /** | |
1196 * This method is installed as JavaScript toString method on exception | |
1197 * objects in [captureStackTrace]. So JavaScript 'this' binds to an | |
1198 * instance of JavaScript Error to which we have added a property | |
1199 * 'dartException' which holds a Dart object. | |
1200 */ | |
1201 toStringWrapper() => JS('Object', @'this.dartException').toString(); | |
1202 | |
1203 builtin$charCodes$0(receiver) { | |
1204 if (receiver is !String) return UNINTERCEPTED(receiver.charCodes()); | |
1205 int len = receiver.length; | |
1206 List<int> result = new List<int>(len); | |
1207 for (int i = 0; i < len; i++) { | |
1208 result[i] = receiver.charCodeAt(i); | |
1209 } | |
1210 return result; | |
1211 } | |
1212 | |
1213 makeLiteralListConst(list) { | |
1214 JS('bool', @'#.immutable$list = #', list, true); | |
1215 JS('bool', @'#.fixed$length = #', list, true); | |
1216 return list; | |
1217 } | |
1218 | |
1219 /** | |
1220 * Called from catch blocks in generated code to extract the Dart | |
1221 * exception from the thrown value. The thrown value may have been | |
1222 * created by [captureStackTrace] or it may be a 'native' JS | |
1223 * exception. | |
1224 * | |
1225 * Some native exceptions are mapped to new Dart instances, others are | |
1226 * returned unmodified. | |
1227 */ | |
1228 unwrapException(ex) { | |
1229 // Note that we are checking if the object has the property. If it | |
1230 // has, it could be set to null if the thrown value is null. | |
1231 if (JS('bool', @'"dartException" in #', ex)) { | |
1232 return JS('Object', @'#.dartException', ex); | |
1233 } else if (JS('bool', @'# instanceof TypeError', ex)) { | |
1234 // TODO(ahe): ex.type is Chrome specific. | |
1235 var type = JS('String', @'#.type', ex); | |
1236 var jsArguments = JS('Object', @'#.arguments', ex); | |
1237 var name = jsArguments[0]; | |
1238 if (type == 'property_not_function' || | |
1239 type == 'called_non_callable' || | |
1240 type == 'non_object_property_call' || | |
1241 type == 'non_object_property_load') { | |
1242 if (name !== null && name.startsWith(@'$call$')) { | |
1243 return new ObjectNotClosureException(); | |
1244 } else { | |
1245 return new NullPointerException(); | |
1246 } | |
1247 } else if (type == 'undefined_method') { | |
1248 if (name is String && name.startsWith(@'$call$')) { | |
1249 return new ObjectNotClosureException(); | |
1250 } else { | |
1251 return new NoSuchMethodException('', name, []); | |
1252 } | |
1253 } | |
1254 } else if (JS('bool', @'# instanceof RangeError', ex)) { | |
1255 var message = JS('String', @'#.message', ex); | |
1256 if (message.contains('call stack')) { | |
1257 return new StackOverflowException(); | |
1258 } | |
1259 } | |
1260 return ex; | |
1261 } | |
1262 | |
1263 /** | |
1264 * Called by generated code to fetch the stack trace from an | |
1265 * exception. | |
1266 */ | |
1267 StackTrace getTraceFromException(exception) { | |
1268 return new StackTrace(JS("var", @"#.stack", exception)); | |
1269 } | |
1270 | |
1271 class StackTrace { | |
1272 var stack; | |
1273 StackTrace(this.stack); | |
1274 String toString() => stack != null ? stack : ''; | |
1275 } | |
1276 | |
1277 | |
1278 /** | |
1279 * Called by generated code to build a map literal. [keyValuePairs] is | |
1280 * a list of key, value, key, value, ..., etc. | |
1281 */ | |
1282 makeLiteralMap(List keyValuePairs) { | |
1283 Iterator iterator = keyValuePairs.iterator(); | |
1284 Map result = new LinkedHashMap(); | |
1285 while (iterator.hasNext()) { | |
1286 String key = iterator.next(); | |
1287 var value = iterator.next(); | |
1288 result[key] = value; | |
1289 } | |
1290 return result; | |
1291 } | |
1292 | |
1293 invokeClosure(Function closure, | |
1294 var isolate, | |
1295 int numberOfArguments, | |
1296 var arg1, | |
1297 var arg2) { | |
1298 if (numberOfArguments == 0) { | |
1299 return JS_CALL_IN_ISOLATE(isolate, () => closure()); | |
1300 } else if (numberOfArguments == 1) { | |
1301 return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1)); | |
1302 } else if (numberOfArguments == 2) { | |
1303 return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2)); | |
1304 } else { | |
1305 throw new Exception( | |
1306 'Unsupported number of arguments for wrapped closure'); | |
1307 } | |
1308 } | |
1309 | |
1310 /** | |
1311 * Called by generated code to convert a Dart closure to a JS | |
1312 * closure when the Dart closure is passed to the DOM. | |
1313 */ | |
1314 convertDartClosureToJS(closure) { | |
1315 if (closure === null) return null; | |
1316 var function = JS('var', @'#.$identity', closure); | |
1317 if (JS('bool', @'!!#', function)) return function; | |
1318 | |
1319 function = JS("var", @"""function() { | |
1320 return #(#, #, arguments.length, arguments[0], arguments[1]); | |
1321 }""", | |
1322 DART_CLOSURE_TO_JS(invokeClosure), | |
1323 closure, | |
1324 JS_CURRENT_ISOLATE()); | |
1325 | |
1326 JS('void', @'#.$identity = #', closure, function); | |
1327 return function; | |
1328 } | |
1329 | |
1330 /** | |
1331 * Super class for Dart closures. | |
1332 */ | |
1333 class Closure implements Function { | |
1334 String toString() => "Closure"; | |
1335 } | |
1336 | |
1337 bool jsHasOwnProperty(var jsObject, String property) { | |
1338 return JS('bool', @'#.hasOwnProperty(#)', jsObject, property); | |
1339 } | |
1340 | |
1341 jsPropertyAccess(var jsObject, String property) { | |
1342 return JS('var', @'#[#]', jsObject, property); | |
1343 } | |
1344 | |
1345 /** | |
1346 * Called at the end of unaborted switch cases to get the singleton | |
1347 * FallThroughError exception that will be thrown. | |
1348 */ | |
1349 getFallThroughError() => const FallThroughError(); | |
1350 | |
1351 builtin$isEven$0(receiver) { | |
1352 if (receiver is !int) return UNINTERCEPTED(receiver.isEven()); | |
1353 return (receiver & 1) === 0; | |
1354 } | |
1355 | |
1356 builtin$isOdd$0(receiver) { | |
1357 if (receiver is !int) return UNINTERCEPTED(receiver.isOdd()); | |
1358 return (receiver & 1) === 1; | |
1359 } | |
1360 | |
1361 /** | |
1362 * Represents the type Dynamic. The compiler treats this specially. | |
1363 */ | |
1364 interface Dynamic { | |
1365 } | |
1366 | |
1367 /** | |
1368 * Represents the type of Null. The compiler treats this specially. | |
1369 */ | |
1370 class Null { | |
1371 factory Null() { | |
1372 throw new UnsupportedOperationException('new Null()'); | |
1373 } | |
1374 } | |
1375 | |
1376 builtin$get$toString(receiver) => () => builtin$toString$0(receiver); | |
OLD | NEW |