Chromium Code Reviews| Index: dart/frog/leg/lib/js_helper.dart |
| diff --git a/dart/frog/leg/lib/js_helper.dart b/dart/frog/leg/lib/js_helper.dart |
| index ede2331b27d894bed0bf9a6a788e4b51f5b9f6e4..254c9a2bc0fab605e7da30ac36931a0a9b882894 100644 |
| --- a/dart/frog/leg/lib/js_helper.dart |
| +++ b/dart/frog/leg/lib/js_helper.dart |
| @@ -4,6 +4,8 @@ |
| #library('js_helper'); |
| +#import('coreimpl.dart'); |
| + |
| /** |
| * Returns true if both arguments are numbers. |
| * If only the first argument is a number, throws the given message as |
| @@ -14,7 +16,7 @@ bool checkNumbers(var a, var b, var message) { |
| if (b is num) { |
| return true; |
| } else { |
| - throw message; |
| + throw new IllegalArgumentException(message); |
| } |
| } |
| return false; |
| @@ -64,6 +66,7 @@ sub(var a, var b) { |
| mod(var a, var b) { |
| if (checkNumbers(a, b, "int% expects an int as second operand.")) { |
| + if (b === 0) throw new IntegerDivisionByZeroException(); |
| // Euclidean Modulo. |
| int result = JS("num", @"$0 % $1", a, b); |
| if (result == 0) return 0; // Make sure we don't return -0.0. |
| @@ -150,6 +153,7 @@ le(var a, var b) { |
| shl(var a, var b) { |
| // TODO(floitsch): inputs must be integers. |
| if (checkNumbers(a, b, "int<< expects an int as second operand.")) { |
| + if (b < 0) throw new IllegalArgumentException(b); |
| return JS("num", @"$0 << $1", a, b); |
| } |
| return UNINTERCEPTED(a << b); |
| @@ -158,6 +162,7 @@ shl(var a, var b) { |
| shr(var a, var b) { |
| // TODO(floitsch): inputs must be integers. |
| if (checkNumbers(a, b, "int>> expects an int as second operand.")) { |
| + if (b < 0) throw new IllegalArgumentException(b); |
| return JS("num", @"$0 >> $1", a, b); |
| } |
| return UNINTERCEPTED(a >> b); |
| @@ -198,6 +203,7 @@ neg(var a) { |
| } |
| index(var a, var index) { |
| + checkNull(a); |
| if (a is String || isJSArray(a)) { |
| if (!(index is int)) { |
| throw new IllegalArgumentException(index); |
| @@ -211,6 +217,7 @@ index(var a, var index) { |
| } |
| indexSet(var a, var index, var value) { |
| + checkNull(a); |
| if (isJSArray(a)) { |
| if (!(index is int)) { |
| throw new IllegalArgumentException(index); |
| @@ -224,6 +231,7 @@ indexSet(var a, var index, var value) { |
| } |
| builtin$add$1(var receiver, var value) { |
| + checkNull(receiver); |
| if (isJSArray(receiver)) { |
| JS("Object", @"$0.push($1)", receiver, value); |
| return; |
| @@ -232,13 +240,16 @@ builtin$add$1(var receiver, var value) { |
| } |
| builtin$removeLast$0(var receiver) { |
| + checkNull(receiver); |
| if (isJSArray(receiver)) { |
| + if (receiver.length === 0) throw new IndexOutOfRangeException(-1); |
| return JS("Object", @"$0.pop()", receiver); |
| } |
| return UNINTERCEPTED(receiver.removeLast()); |
| } |
| builtin$filter$1(var receiver, var predicate) { |
| + checkNull(receiver); |
| if (isJSArray(receiver)) { |
| return JS("Object", @"$0.filter(function(v) { return $1(v) === true; })", |
| receiver, predicate); |
| @@ -248,12 +259,24 @@ builtin$filter$1(var receiver, var predicate) { |
| builtin$get$length(var receiver) { |
| + checkNull(receiver); |
| if (receiver is String || isJSArray(receiver)) { |
| return JS("num", @"$0.length", receiver); |
| } |
| return UNINTERCEPTED(receiver.length); |
| } |
| +builtin$set$length(receiver, newLength) { |
| + checkNull(receiver); |
| + if (isJSArray(receiver)) { |
| + checkNull(newLength); // TODO(ahe): This is not specified but co19 tests it. |
| + if (newLength is !int) throw new IllegalArgumentException(newLength); |
| + if (newLength < 0) throw new IndexOutOfRangeException(newLength); |
| + JS('void', @"$0.length = $1", receiver, newLength); |
| + } else { |
| + UNINTERCEPTED(receiver.length = newLength); |
| + } |
| +} |
| builtin$toString$0(var value) { |
| if (JS("bool", @"typeof $0 == 'object'", value)) { |
| @@ -273,7 +296,8 @@ builtin$toString$0(var value) { |
| } |
| -builtin$iterator$0(var receiver) { |
| +builtin$iterator$0(receiver) { |
| + checkNull(receiver); |
| if (isJSArray(receiver)) { |
| return new ListIterator(receiver); |
| } |
| @@ -286,6 +310,7 @@ class ListIterator<T> implements Iterator<T> { |
| ListIterator(List<T> this.list) : i = 0; |
| bool hasNext() => i < JS("int", @"$0.length", list); |
| T next() { |
| + if (!hasNext()) throw new NoMoreElementsException(); |
| var value = JS("Object", @"$0[$1]", list, i); |
| i += 1; |
| return value; |
| @@ -301,6 +326,7 @@ builtin$charCodeAt$1(var receiver, int index) { |
| } |
| builtin$isEmpty$0(var receiver) { |
|
kasperl
2012/02/21 07:16:43
Want to remove var here too?
ahe
2012/02/21 23:03:25
Done.
|
| + checkNull(receiver); |
| if (receiver is String || isJSArray(receiver)) { |
| return JS("bool", @"$0.length === 0", receiver); |
| } |
| @@ -327,10 +353,11 @@ class Primitives { |
| return "Instance of '$name'"; |
| } |
| - static List newList(int length) { |
| + static List newList(length) { |
| if (length == null) return JS("Object", @"new Array()"); |
| - if (!(length is int)) throw "Invalid argument"; |
| - if (length < 0) throw "Negative size"; |
| + if ((length is !int) || (length < 0)) { |
| + throw new IllegalArgumentException(length); |
| + } |
| return JS("Object", @"new Array($0)", length); |
| } |
| @@ -400,3 +427,436 @@ builtin$compareTo$1(a, b) { |
| return UNINTERCEPTED(a.compareTo(b)); |
| } |
| } |
| + |
| +iae(argument) { |
|
kasperl
2012/02/21 07:16:43
Maybe make it clear (through a comment at least) t
ahe
2012/02/21 23:03:25
Done.
|
| + throw new IllegalArgumentException(argument); |
| +} |
| + |
| +ioore(index) { |
| + throw new IndexOutOfRangeException(index); |
| +} |
| + |
| +builtin$addAll$1(receiver, collection) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) return UNINTERCEPTED(receiver.addAll(collection)); |
| + |
| + var iterator = collection.iterator(); |
|
ngeoffray
2012/02/21 10:41:45
Why not using for in?
ahe
2012/02/21 23:03:25
Because I thought it wasn't implemented and becaus
|
| + while (iterator.hasNext()) { |
| + receiver.add(iterator.next()); |
| + } |
| +} |
| + |
| +// TODO(ahe): Investigate why this method causes a compiler crash. |
| +XXX_builtin$addLast$1(receiver, value) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) return UNINTERCEPTED(receiver.addLast(value)); |
| + |
| + throw @'builtin$addLast$1 is not implemented'; |
| +} |
| + |
| +builtin$clear$0(receiver) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) return UNINTERCEPTED(receiver.clear()); |
| + receiver.length = 0; |
| +} |
| + |
| +// TODO(ahe): Investigate why this method causes a compiler crash. |
| +XXX_builtin$forEach$1(receiver, f) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) return UNINTERCEPTED(receiver.forEach(f)); |
| + |
| + throw @'builtin$forEach$1 is not implemented'; |
| +} |
| + |
| +builtin$getRange$2(receiver, start, length) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) { |
| + return UNINTERCEPTED(receiver.getRange(start, length)); |
| + } |
| + if (0 === length) return []; |
| + checkNull(start); // TODO(ahe): This is not specified but co19 tests it. |
| + checkNull(length); // TODO(ahe): This is not specified but co19 tests it. |
| + if (start is !int) throw new IllegalArgumentException(start); |
| + if (length is !int) throw new IllegalArgumentException(length); |
| + if (start < 0) throw new IndexOutOfRangeException(start); |
| + var end = start + length; |
| + if (end > receiver.length) { |
| + throw new IndexOutOfRangeException(length); |
| + } |
| + if (length < 0) throw new IllegalArgumentException(length); |
| + return JS("Object", @"$0.slice($1, $2)", receiver, start, end); |
| +} |
| + |
| +builtin$indexOf$1(receiver, element) { |
| + checkNull(receiver); |
| + if (isJSArray(receiver) || receiver is String) { |
| + return builtin$indexOf$2(receiver, element, 0); |
| + } |
| + return UNINTERCEPTED(receiver.indexOf(element)); |
| +} |
| + |
| +builtin$indexOf$2(receiver, element, start) { |
| + checkNull(receiver); |
| + if (isJSArray(receiver)) { |
| + if (start is !int) throw new IllegalArgumentException(start); |
| + var length = JS("num", @"$0.length", receiver); |
| + return Arrays.indexOf(receiver, element, start, length); |
| + } else if (receiver is String) { |
| + throw new NotImplementedException(); |
| + } |
| + return UNINTERCEPTED(receiver.indexOf(element, start)); |
| +} |
| + |
| +builtin$insertRange$2(receiver, start, length) { |
| + checkNull(receiver); |
| + if (isJSArray(receiver)) { |
| + return builtin$insertRange$3(receiver, start, length, null); |
| + } |
| + return UNINTERCEPTED(receiver.insertRange(start, length)); |
| +} |
| + |
| +builtin$insertRange$3(receiver, start, length, initialValue) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) { |
|
ngeoffray
2012/02/21 10:41:45
It would look consistent to have this check invert
ahe
2012/02/21 23:03:25
I'll do this in a follow-up CL. I have 17 CL build
|
| + return UNINTERCEPTED(receiver.insertRange(start, length, initialValue)); |
| + } |
| + return listInsertRange(receiver, start, length, initialValue); |
| +} |
| + |
| +listInsertRange(receiver, start, length, initialValue) { |
| + if (length === 0) { |
| + return; |
| + } |
| + checkNull(start); // TODO(ahe): This is not specified but co19 tests it. |
| + checkNull(length); // TODO(ahe): This is not specified but co19 tests it. |
| + if (length is !int) throw new IllegalArgumentException(length); |
| + if (length < 0) throw new IllegalArgumentException(length); |
| + if (start is !int) throw new IllegalArgumentException(start); |
| + |
| + var receiverLength = JS("num", @"$0.length", receiver); |
| + if (start < 0 || start > receiverLength) { |
| + throw new IndexOutOfRangeException(start); |
| + } |
| + receiver.length = receiverLength + length; |
| + Arrays.copy(receiver, |
| + start, |
| + receiver, |
| + start + length, |
| + receiverLength - start); |
| + if (initialValue !== null) { |
| + for (int i = start; i < start + length; i++) { |
| + receiver[i] = initialValue; |
| + } |
| + } |
| + receiver.length = receiverLength + length; |
| +} |
| + |
| +builtin$last$0(receiver) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) { |
| + return UNINTERCEPTED(receiver.last()); |
|
ngeoffray
2012/02/21 10:41:45
Ditto for consistency,
|
| + } |
| + return receiver[receiver.length - 1]; |
| +} |
| + |
| +builtin$lastIndexOf$1(receiver, element) { |
| + checkNull(receiver); |
| + if (isJSArray(receiver)) { |
| + var start = JS("num", @"$0.length", receiver); |
| + return Arrays.lastIndexOf(receiver, element, start); |
| + } else if (receiver is String) { |
| + throw new NotImplementedException(); |
| + } |
| + return UNINTERCEPTED(receiver.lastIndexOf(element)); |
| +} |
| + |
| +builtin$lastIndexOf$2(receiver, element, start) { |
| + checkNull(receiver); |
| + if (isJSArray(receiver)) { |
| + return Arrays.lastIndexOf(receiver, element, start); |
| + } else if (receiver is String) { |
| + throw new NotImplementedException(); |
| + } |
| + return UNINTERCEPTED(receiver.lastIndexOf(element, start)); |
| +} |
| + |
| +builtin$removeRange$2(receiver, start, length) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) { |
| + return UNINTERCEPTED(receiver.removeRange(start, length)); |
|
ngeoffray
2012/02/21 10:41:45
Ditto.
|
| + } |
| + if (length == 0) { |
| + return; |
| + } |
| + checkNull(start); // TODO(ahe): This is not specified but co19 tests it. |
| + checkNull(length); // TODO(ahe): This is not specified but co19 tests it. |
| + if (start is !int) throw new IllegalArgumentException(start); |
| + if (length is !int) throw new IllegalArgumentException(length); |
| + if (length < 0) throw new IllegalArgumentException(length); |
| + var receiverLength = JS("num", @"$0.length", receiver); |
| + if (start < 0 || start >= receiverLength) { |
| + throw new IndexOutOfRangeException(start); |
| + } |
| + if (start + length > receiverLength) { |
| + throw new IndexOutOfRangeException(start + length); |
| + } |
| + Arrays.copy(receiver, |
| + start + length, |
| + receiver, |
| + start, |
| + receiverLength - length - start); |
| + receiver.length = receiverLength - length; |
| +} |
| + |
| +builtin$setRange$3(receiver, start, length, from) { |
| + checkNull(receiver); |
| + if (isJSArray(receiver)) { |
| + return builtin$setRange$4(receiver, start, length, from, 0); |
| + } |
| + return UNINTERCEPTED(receiver.setRange(start, length, from)); |
| +} |
| + |
| +builtin$setRange$4(receiver, start, length, from, startFrom) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) { |
| + return UNINTERCEPTED(receiver.setRange(start, length, from, startFrom)); |
| + } |
| + |
| + if (length === 0) return; |
| + checkNull(start); // TODO(ahe): This is not specified but co19 tests it. |
| + checkNull(length); // TODO(ahe): This is not specified but co19 tests it. |
| + checkNull(from); // TODO(ahe): This is not specified but co19 tests it. |
| + checkNull(startFrom); // TODO(ahe): This is not specified but co19 tests it. |
| + if (start is !int) throw new IllegalArgumentException(start); |
| + if (length is !int) throw new IllegalArgumentException(length); |
| + if (startFrom is !int) throw new IllegalArgumentException(startFrom); |
| + if (length < 0) throw new IllegalArgumentException(length); |
| + if (start < 0) throw new IndexOutOfRangeException(start); |
| + if (start + length > receiver.length) { |
| + throw new IndexOutOfRangeException(start + length); |
| + } |
| + |
| + Arrays.copy(from, startFrom, receiver, start, length); |
| +} |
| + |
| +builtin$some$1(receiver, f) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) return UNINTERCEPTED(receiver.some(f)); |
| + |
| + return Collections.some(receiver, f); |
| +} |
| + |
| +builtin$sort$1(receiver, compare) { |
| + checkNull(receiver); |
| + if (!isJSArray(receiver)) return UNINTERCEPTED(receiver.sort(compare)); |
| + |
| + DualPivotQuicksort.sort(receiver, compare); |
| +} |
| + |
| +checkNull(object) { |
| + if (object === null) throw new NullPointerException(); |
| +} |
| + |
| +builtin$isNegative$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is num) { |
| + return (receiver === 0) ? (1 / receiver) < 0 : receiver < 0; |
| + } else { |
| + return UNINTERCEPTED(receiver.isNegative()); |
| + } |
| +} |
| + |
| +builtin$isNaN$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is num) { |
| + return JS("bool", @"isNaN($0)", receiver); |
| + } else { |
| + return UNINTERCEPTED(receiver.isNegative()); |
| + } |
| +} |
| + |
| +builtin$remainder$1(a, b) { |
| + checkNull(a); |
| + if (checkNumbers(a, b, "num.remainder expects a number as second operand.")) { |
| + throw new NotImplementedException(); |
| + } else { |
| + return UNINTERCEPTED(a.remainder(b)); |
| + } |
| +} |
| + |
| +builtin$abs$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.abs()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$toInt$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.toInt()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$ceil$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.ceil()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$floor$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.floor()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$isInfinite$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.isInfinite()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$negate$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.negate()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$round$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.round()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$toDouble$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.toDouble()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$truncate$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !num) return UNINTERCEPTED(receiver.truncate()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| + |
| +builtin$toStringAsFixed$1(receiver, fractionDigits) { |
| + checkNull(receiver); |
| + if (receiver is !num) { |
| + return UNINTERCEPTED(receiver.toStringAsFixed(fractionDigits)); |
| + } |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$allMatches$1(receiver, str) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.allMatches(str)); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$concat$1(receiver, other) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.concat(other)); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$contains$2(receiver, other, startIndex) { |
| + checkNull(receiver); |
| + if (receiver is !String) { |
| + return UNINTERCEPTED(receiver.contains(other, startIndex)); |
| + } |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$endsWith$1(receiver, other) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.endsWith(other)); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$replaceAll$2(receiver, from, to) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.replaceAll(from, to)); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$replaceFirst$2(receiver, from, to) { |
| + checkNull(receiver); |
| + if (receiver is !String) { |
| + return UNINTERCEPTED(receiver.replaceFirst(from, to)); |
| + } |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$split$1(receiver, pattern) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.split(pattern)); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$splitChars$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.splitChars()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$startsWith$1(receiver, other) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.startsWith(other)); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$substring$1(receiver, startIndex) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.substring(startIndex)); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$substring$2(receiver, startIndex, endIndex) { |
| + checkNull(receiver); |
| + if (receiver is !String) { |
| + return UNINTERCEPTED(receiver.substring(startIndex, endIndex)); |
| + } |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$toLowerCase$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.toLowerCase()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$toUpperCase$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.toUpperCase()); |
| + |
| + throw new NotImplementedException(); |
| +} |
| + |
| +builtin$trim$0(receiver) { |
| + checkNull(receiver); |
| + if (receiver is !String) return UNINTERCEPTED(receiver.trim()); |
| + |
| + throw new NotImplementedException(); |
| +} |