| 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 |