Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(182)

Side by Side Diff: frog/leg/lib/js_helper.dart

Issue 9873021: Move frog/leg to lib/compiler/implementation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frog/leg/lib/io.dart ('k') | frog/leg/lib/mock.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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);
OLDNEW
« no previous file with comments | « frog/leg/lib/io.dart ('k') | frog/leg/lib/mock.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698