blob: 2fc25a6fb80f706916b405d3a7fa2ea9a99b107f [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#library('dart:_interceptors');
#import('dart:collection');
add$1(var receiver, var value) {
if (isJsArray(receiver)) {
checkGrowable(receiver, 'add');
JS('Object', r'#.push(#)', receiver, value);
return;
}
return UNINTERCEPTED(receiver.add(value));
}
removeAt$1(var receiver, var index) {
if (isJsArray(receiver)) {
if (index is !int) throw new ArgumentError(index);
if (index < 0 || index >= receiver.length) {
throw new RangeError.value(index);
}
checkGrowable(receiver, 'removeAt');
return JS("Object", r'#.splice(#, 1)[0]', receiver, index);
}
return UNINTERCEPTED(receiver.removeAt(index));
}
removeLast(var receiver) {
if (isJsArray(receiver)) {
checkGrowable(receiver, 'removeLast');
if (receiver.length == 0) throw new RangeError.value(-1);
return JS('Object', r'#.pop()', receiver);
}
return UNINTERCEPTED(receiver.removeLast());
}
filter(var receiver, var predicate) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.filter(predicate));
} else {
return Collections.filter(receiver, [], predicate);
}
}
get$length(var receiver) {
if (receiver is String || isJsArray(receiver)) {
return JS('num', r'#.length', receiver);
} else {
return UNINTERCEPTED(receiver.length);
}
}
set$length(receiver, newLength) {
if (isJsArray(receiver)) {
checkNull(newLength); // TODO(ahe): This is not specified but co19 tests it.
if (newLength is !int) throw new ArgumentError(newLength);
if (newLength < 0) throw new RangeError.value(newLength);
checkGrowable(receiver, 'set length');
JS('void', r'#.length = #', receiver, newLength);
} else {
UNINTERCEPTED(receiver.length = newLength);
}
return newLength;
}
toString(var value) {
if (JS('bool', r'typeof # == "object" && # !== null', value, value)) {
if (isJsArray(value)) {
return Collections.collectionToString(value);
} else {
return UNINTERCEPTED(value.toString());
}
}
if (JS('bool', r'# === 0 && (1 / #) < 0', value, value)) {
return '-0.0';
}
if (value == null) return 'null';
if (JS('bool', r'typeof # == "function"', value)) {
return 'Closure';
}
return JS('string', r'String(#)', value);
}
iterator(receiver) {
if (isJsArray(receiver)) {
return new ListIterator(receiver);
}
return UNINTERCEPTED(receiver.iterator());
}
charCodeAt(var receiver, int index) {
if (receiver is String) {
if (index is !num) throw new ArgumentError(index);
if (index < 0) throw new RangeError.value(index);
if (index >= receiver.length) throw new RangeError.value(index);
return JS('int', r'#.charCodeAt(#)', receiver, index);
} else {
return UNINTERCEPTED(receiver.charCodeAt(index));
}
}
get$isEmpty(receiver) {
if (receiver is String || isJsArray(receiver)) {
return JS('bool', r'#.length === 0', receiver);
}
return UNINTERCEPTED(receiver.isEmpty);
}
compareTo(a, b) {
if (checkNumbers(a, b)) {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else if (a == b) {
if (a == 0) {
bool aIsNegative = a.isNegative;
bool bIsNegative = b.isNegative;
if (aIsNegative == bIsNegative) return 0;
if (aIsNegative) return -1;
return 1;
}
return 0;
} else if (a.isNaN) {
if (b.isNaN) {
return 0;
}
return 1;
} else {
return -1;
}
} else if (a is String) {
if (b is !String) throw new ArgumentError(b);
return JS('bool', r'# == #', a, b) ? 0
: JS('bool', r'# < #', a, b) ? -1 : 1;
} else {
return UNINTERCEPTED(a.compareTo(b));
}
}
addAll(receiver, collection) {
if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.addAll(collection));
// TODO(ahe): Use for-in when it is implemented correctly.
var iterator = collection.iterator();
while (iterator.hasNext) {
receiver.add(iterator.next());
}
}
addLast(receiver, value) {
if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.addLast(value));
checkGrowable(receiver, 'addLast');
JS('Object', r'#.push(#)', receiver, value);
}
clear(receiver) {
if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.clear());
receiver.length = 0;
}
forEach(receiver, f) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.forEach(f));
} else {
return Collections.forEach(receiver, f);
}
}
map(receiver, f) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.map(f));
} else {
return Collections.map(receiver, [], f);
}
}
reduce(receiver, initialValue, f) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.reduce(initialValue, f));
} else {
return Collections.reduce(receiver, initialValue, f);
}
}
getRange(receiver, start, length) {
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 ArgumentError(start);
if (length is !int) throw new ArgumentError(length);
if (length < 0) throw new ArgumentError(length);
if (start < 0) throw new RangeError.value(start);
var end = start + length;
if (end > receiver.length) {
throw new RangeError.value(length);
}
if (length < 0) throw new ArgumentError(length);
return JS('Object', r'#.slice(#, #)', receiver, start, end);
}
indexOf$1(receiver, element) {
if (isJsArray(receiver)) {
var length = JS('num', r'#.length', receiver);
return Arrays.indexOf(receiver, element, 0, length);
} else if (receiver is String) {
checkNull(element);
if (element is !String) throw new ArgumentError(element);
return JS('int', r'#.indexOf(#)', receiver, element);
}
return UNINTERCEPTED(receiver.indexOf(element));
}
indexOf$2(receiver, element, start) {
if (isJsArray(receiver)) {
if (start is !int) throw new ArgumentError(start);
var length = JS('num', r'#.length', receiver);
return Arrays.indexOf(receiver, element, start, length);
} else if (receiver is String) {
checkNull(element);
if (start is !int) throw new ArgumentError(start);
if (element is !String) throw new ArgumentError(element);
if (start < 0) return -1; // TODO(ahe): Is this correct?
return JS('int', r'#.indexOf(#, #)', receiver, element, start);
}
return UNINTERCEPTED(receiver.indexOf(element, start));
}
insertRange$2(receiver, start, length) {
if (isJsArray(receiver)) {
return insertRange$3(receiver, start, length, null);
}
return UNINTERCEPTED(receiver.insertRange(start, length));
}
insertRange$3(receiver, start, length, initialValue) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.insertRange(start, length, initialValue));
}
return listInsertRange(receiver, start, length, initialValue);
}
get$last(receiver) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.last);
}
return receiver[receiver.length - 1];
}
lastIndexOf$1(receiver, element) {
if (isJsArray(receiver)) {
var start = JS('num', r'#.length', receiver);
return Arrays.lastIndexOf(receiver, element, start);
} else if (receiver is String) {
checkNull(element);
if (element is !String) throw new ArgumentError(element);
return JS('int', r'#.lastIndexOf(#)', receiver, element);
}
return UNINTERCEPTED(receiver.lastIndexOf(element));
}
lastIndexOf$2(receiver, element, start) {
if (isJsArray(receiver)) {
return Arrays.lastIndexOf(receiver, element, start);
} else if (receiver is String) {
checkNull(element);
if (element is !String) throw new ArgumentError(element);
if (start != null) {
if (start is !num) throw new ArgumentError(start);
if (start < 0) return -1;
if (start >= receiver.length) {
if (element == "") return receiver.length;
start = receiver.length - 1;
}
}
return stringLastIndexOfUnchecked(receiver, element, start);
}
return UNINTERCEPTED(receiver.lastIndexOf(element, start));
}
removeRange(receiver, start, length) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.removeRange(start, length));
}
checkGrowable(receiver, 'removeRange');
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 ArgumentError(start);
if (length is !int) throw new ArgumentError(length);
if (length < 0) throw new ArgumentError(length);
var receiverLength = JS('num', r'#.length', receiver);
if (start < 0 || start >= receiverLength) {
throw new RangeError.value(start);
}
if (start + length > receiverLength) {
throw new RangeError.value(start + length);
}
Arrays.copy(receiver,
start + length,
receiver,
start,
receiverLength - length - start);
receiver.length = receiverLength - length;
}
setRange$3(receiver, start, length, from) {
if (isJsArray(receiver)) {
return setRange$4(receiver, start, length, from, 0);
}
return UNINTERCEPTED(receiver.setRange(start, length, from));
}
setRange$4(receiver, start, length, from, startFrom) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.setRange(start, length, from, startFrom));
}
checkMutable(receiver, 'indexed set');
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 ArgumentError(start);
if (length is !int) throw new ArgumentError(length);
if (startFrom is !int) throw new ArgumentError(startFrom);
if (length < 0) throw new ArgumentError(length);
if (start < 0) throw new RangeError.value(start);
if (start + length > receiver.length) {
throw new RangeError.value(start + length);
}
Arrays.copy(from, startFrom, receiver, start, length);
}
some(receiver, f) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.some(f));
} else {
return Collections.some(receiver, f);
}
}
every(receiver, f) {
if (!isJsArray(receiver)) {
return UNINTERCEPTED(receiver.every(f));
} else {
return Collections.every(receiver, f);
}
}
// TODO(ngeoffray): Make it possible to have just one "sort" function ends
// an optional parameter.
sort$0(receiver) {
if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.sort());
checkMutable(receiver, 'sort');
coreSort(receiver, Comparable.compare);
}
sort$1(receiver, compare) {
if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.sort(compare));
checkMutable(receiver, 'sort');
coreSort(receiver, compare);
}
get$isNegative(receiver) {
if (receiver is num) {
return (receiver == 0) ? (1 / receiver) < 0 : receiver < 0;
} else {
return UNINTERCEPTED(receiver.isNegative);
}
}
get$isNaN(receiver) {
if (receiver is num) {
return JS('bool', r'isNaN(#)', receiver);
} else {
return UNINTERCEPTED(receiver.isNaN);
}
}
remainder(a, b) {
if (checkNumbers(a, b)) {
return JS('num', r'# % #', a, b);
} else {
return UNINTERCEPTED(a.remainder(b));
}
}
abs(receiver) {
if (receiver is !num) return UNINTERCEPTED(receiver.abs());
return JS('num', r'Math.abs(#)', receiver);
}
toInt(receiver) {
if (receiver is !num) return UNINTERCEPTED(receiver.toInt());
if (receiver.isNaN) throw new FormatException('NaN');
if (receiver.isInfinite) throw new FormatException('Infinity');
var truncated = receiver.truncate();
return JS('bool', r'# == -0.0', truncated) ? 0 : truncated;
}
ceil(receiver) {
if (receiver is !num) return UNINTERCEPTED(receiver.ceil());
return JS('num', r'Math.ceil(#)', receiver);
}
floor(receiver) {
if (receiver is !num) return UNINTERCEPTED(receiver.floor());
return JS('num', r'Math.floor(#)', receiver);
}
get$isInfinite(receiver) {
if (receiver is !num) return UNINTERCEPTED(receiver.isInfinite);
return JS('bool', r'# == Infinity', receiver)
|| JS('bool', r'# == -Infinity', receiver);
}
round(receiver) {
if (receiver is !num) return UNINTERCEPTED(receiver.round());
if (JS('bool', r'# < 0', receiver)) {
return JS('num', r'-Math.round(-#)', receiver);
} else {
return JS('num', r'Math.round(#)', receiver);
}
}
toDouble(receiver) {
if (receiver is !num) return UNINTERCEPTED(receiver.toDouble());
return receiver;
}
truncate(receiver) {
if (receiver is !num) return UNINTERCEPTED(receiver.truncate());
return receiver < 0 ? receiver.ceil() : receiver.floor();
}
toStringAsFixed(receiver, fractionDigits) {
if (receiver is !num) {
return UNINTERCEPTED(receiver.toStringAsFixed(fractionDigits));
}
checkNum(fractionDigits);
String result = JS('String', r'#.toFixed(#)', receiver, fractionDigits);
if (receiver == 0 && receiver.isNegative) return "-$result";
return result;
}
toStringAsExponential(receiver, fractionDigits) {
if (receiver is !num) {
return UNINTERCEPTED(receiver.toStringAsExponential(fractionDigits));
}
String result;
if (fractionDigits != null) {
checkNum(fractionDigits);
result = JS('String', r'#.toExponential(#)', receiver, fractionDigits);
} else {
result = JS('String', r'#.toExponential()', receiver);
}
if (receiver == 0 && receiver.isNegative) return "-$result";
return result;
}
toStringAsPrecision(receiver, fractionDigits) {
if (receiver is !num) {
return UNINTERCEPTED(receiver.toStringAsPrecision(fractionDigits));
}
checkNum(fractionDigits);
String result = JS('String', r'#.toPrecision(#)',
receiver, fractionDigits);
if (receiver == 0 && receiver.isNegative) return "-$result";
return result;
}
toRadixString(receiver, radix) {
if (receiver is !num) {
return UNINTERCEPTED(receiver.toRadixString(radix));
}
checkNum(radix);
if (radix < 2 || radix > 36) throw new ArgumentError(radix);
return JS('String', r'#.toString(#)', receiver, radix);
}
allMatches(receiver, str) {
if (receiver is !String) return UNINTERCEPTED(receiver.allMatches(str));
checkString(str);
return allMatchesInStringUnchecked(receiver, str);
}
concat(receiver, other) {
if (receiver is !String) return UNINTERCEPTED(receiver.concat(other));
if (other is !String) throw new ArgumentError(other);
return JS('String', r'# + #', receiver, other);
}
contains$1(receiver, other) {
if (receiver is String) {
return contains$2(receiver, other, 0);
} else if (isJsArray(receiver)) {
for (int i = 0; i < receiver.length; i++) {
if (other == receiver[i]) return true;
}
return false;
}
return UNINTERCEPTED(receiver.contains(other));
}
contains$2(receiver, other, startIndex) {
if (receiver is !String) {
return UNINTERCEPTED(receiver.contains(other, startIndex));
}
checkNull(other);
return stringContainsUnchecked(receiver, other, startIndex);
}
endsWith(receiver, other) {
if (receiver is !String) return UNINTERCEPTED(receiver.endsWith(other));
checkString(other);
int receiverLength = receiver.length;
int otherLength = other.length;
if (otherLength > receiverLength) return false;
return other == receiver.substring(receiverLength - otherLength);
}
replaceAll(receiver, from, to) {
if (receiver is !String) return UNINTERCEPTED(receiver.replaceAll(from, to));
checkString(to);
return stringReplaceAllUnchecked(receiver, from, to);
}
replaceFirst(receiver, from, to) {
if (receiver is !String) {
return UNINTERCEPTED(receiver.replaceFirst(from, to));
}
checkString(to);
return stringReplaceFirstUnchecked(receiver, from, to);
}
split(receiver, pattern) {
if (receiver is !String) return UNINTERCEPTED(receiver.split(pattern));
checkNull(pattern);
return stringSplitUnchecked(receiver, pattern);
}
splitChars(receiver) {
if (receiver is !String) return UNINTERCEPTED(receiver.splitChars());
return JS('List', r'#.split("")', receiver);
}
startsWith(receiver, other) {
if (receiver is !String) return UNINTERCEPTED(receiver.startsWith(other));
checkString(other);
int length = other.length;
if (length > receiver.length) return false;
return JS('bool', r'# == #', other,
JS('String', r'#.substring(0, #)', receiver, length));
}
substring$1(receiver, startIndex) {
if (receiver is !String) return UNINTERCEPTED(receiver.substring(startIndex));
return substring$2(receiver, startIndex, null);
}
substring$2(receiver, startIndex, endIndex) {
if (receiver is !String) {
return UNINTERCEPTED(receiver.substring(startIndex, endIndex));
}
checkNum(startIndex);
var length = receiver.length;
if (endIndex == null) endIndex = length;
checkNum(endIndex);
if (startIndex < 0 ) throw new RangeError.value(startIndex);
if (startIndex > endIndex) throw new RangeError.value(startIndex);
if (endIndex > length) throw new RangeError.value(endIndex);
return substringUnchecked(receiver, startIndex, endIndex);
}
toLowerCase(receiver) {
if (receiver is !String) return UNINTERCEPTED(receiver.toLowerCase());
return JS('String', r'#.toLowerCase()', receiver);
}
toUpperCase(receiver) {
if (receiver is !String) return UNINTERCEPTED(receiver.toUpperCase());
return JS('String', r'#.toUpperCase()', receiver);
}
trim(receiver) {
if (receiver is !String) return UNINTERCEPTED(receiver.trim());
return JS('String', r'#.trim()', receiver);
}
/**
* This is the [Jenkins hash function][1] but using masking to keep
* values in SMI range.
*
* [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function
*/
get$hashCode(receiver) {
// TODO(ahe): This method shouldn't have to use JS. Update when our
// optimizations are smarter.
if (receiver == null) return 0;
if (receiver is num) return JS('int', r'# & 0x1FFFFFFF', receiver);
if (receiver is bool) return receiver ? 0x40377024 : 0xc18c0076;
if (isJsArray(receiver)) return Primitives.objectHashCode(receiver);
if (receiver is !String) return UNINTERCEPTED(receiver.hashCode);
int hash = 0;
int length = JS('int', r'#.length', receiver);
for (int i = 0; i < length; i++) {
hash = 0x1fffffff & (hash + JS('int', r'#.charCodeAt(#)', receiver, i));
hash = 0x1fffffff & (hash + JS('int', r'# << #', 0x0007ffff & hash, 10));
hash ^= hash >> 6;
}
hash = 0x1fffffff & (hash + JS('int', r'# << #', 0x03ffffff & hash, 3));
hash ^= hash >> 11;
return 0x1fffffff & (hash + JS('int', r'# << #', 0x00003fff & hash, 15));
}
get$charCodes(receiver) {
if (receiver is !String) return UNINTERCEPTED(receiver.charCodes);
int len = receiver.length;
List<int> result = new List<int>(len);
for (int i = 0; i < len; i++) {
result[i] = receiver.charCodeAt(i);
}
return result;
}
get$isEven(receiver) {
if (receiver is !int) return UNINTERCEPTED(receiver.isEven);
return (receiver & 1) == 0;
}
get$isOdd(receiver) {
if (receiver is !int) return UNINTERCEPTED(receiver.isOdd);
return (receiver & 1) == 1;
}
get$runtimeType(receiver) {
if (receiver is int) {
return getOrCreateCachedRuntimeType('int');
} else if (receiver is String) {
return getOrCreateCachedRuntimeType('String');
} else if (receiver is double) {
return getOrCreateCachedRuntimeType('double');
} else if (receiver is bool) {
return getOrCreateCachedRuntimeType('bool');
} else if (receiver == null) {
return getOrCreateCachedRuntimeType('Null');
} else if (isJsArray(receiver)) {
return getOrCreateCachedRuntimeType('List');
} else {
return UNINTERCEPTED(receiver.runtimeType);
}
}
// TODO(lrn): These getters should be generated automatically for all
// intercepted methods.
get$toString(receiver) => () => toString(receiver);