OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library _js_helper; | 5 library _js_helper; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 | 8 |
9 part 'constant_map.dart'; | 9 part 'constant_map.dart'; |
10 part 'native_helper.dart'; | 10 part 'native_helper.dart'; |
(...skipping 321 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
332 throw new UnsupportedError(reason); | 332 throw new UnsupportedError(reason); |
333 } | 333 } |
334 } | 334 } |
335 | 335 |
336 String S(value) { | 336 String S(value) { |
337 var res = value.toString(); | 337 var res = value.toString(); |
338 if (res is !String) throw new ArgumentError(value); | 338 if (res is !String) throw new ArgumentError(value); |
339 return res; | 339 return res; |
340 } | 340 } |
341 | 341 |
342 class ListIterator<T> implements Iterator<T> { | |
343 int i; | |
344 List<T> list; | |
345 ListIterator(List<T> this.list) : i = 0; | |
346 bool get hasNext => i < JS('int', r'#.length', list); | |
347 T next() { | |
348 if (!hasNext) throw new StateError("No more elements"); | |
349 var value = JS('', r'#[#]', list, i); | |
350 i += 1; | |
351 return value; | |
352 } | |
353 } | |
354 | |
355 createInvocationMirror(name, internalName, type, arguments, argumentNames) => | 342 createInvocationMirror(name, internalName, type, arguments, argumentNames) => |
356 new JSInvocationMirror(name, internalName, type, arguments, argumentNames); | 343 new JSInvocationMirror(name, internalName, type, arguments, argumentNames); |
357 | 344 |
358 class JSInvocationMirror implements InvocationMirror { | 345 class JSInvocationMirror implements InvocationMirror { |
359 static const METHOD = 0; | 346 static const METHOD = 0; |
360 static const GETTER = 1; | 347 static const GETTER = 1; |
361 static const SETTER = 2; | 348 static const SETTER = 2; |
362 | 349 |
363 final String memberName; | 350 final String memberName; |
364 final String _internalName; | 351 final String _internalName; |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
454 JS('void', r'print(#)', string); | 441 JS('void', r'print(#)', string); |
455 return; | 442 return; |
456 } | 443 } |
457 | 444 |
458 // This is somewhat nasty, but we don't want to drag in a bunch of | 445 // This is somewhat nasty, but we don't want to drag in a bunch of |
459 // dependencies to handle a situation that cannot happen. So we | 446 // dependencies to handle a situation that cannot happen. So we |
460 // avoid using Dart [:throw:] and Dart [toString]. | 447 // avoid using Dart [:throw:] and Dart [toString]. |
461 JS('void', "throw 'Unable to print message: ' + String(#)", string); | 448 JS('void', "throw 'Unable to print message: ' + String(#)", string); |
462 } | 449 } |
463 | 450 |
464 static int parseInt(String string) { | 451 static void _throwFormatException(String string) { |
465 checkString(string); | 452 throw new FormatException(string); |
466 var match = JS('=List|Null', | |
467 r'/^\s*[+-]?(?:0(x)[a-f0-9]+|\d+)\s*$/i.exec(#)', | |
468 string); | |
469 if (match == null) { | |
470 throw new FormatException(string); | |
471 } | |
472 var base = 10; | |
473 if (match[1] != null) base = 16; | |
474 var result = JS('num', r'parseInt(#, #)', string, base); | |
475 if (result.isNaN) throw new FormatException(string); | |
476 return result; | |
477 } | 453 } |
478 | 454 |
479 static double parseDouble(String string) { | 455 static int parseInt(String source, |
480 checkString(string); | 456 int radix, |
| 457 int handleError(String source)) { |
| 458 if (handleError == null) handleError = _throwFormatException; |
| 459 |
| 460 checkString(source); |
| 461 var match = JS('=List|Null', |
| 462 r'/^\s*[+-]?((0x[a-f0-9]+)|(\d+)|([a-z0-9]+))\s*$/i.exec(#)', |
| 463 source); |
| 464 int digitsIndex = 1; |
| 465 int hexIndex = 2; |
| 466 int decimalIndex = 3; |
| 467 int nonDecimalHexIndex = 4; |
| 468 if (radix == null) { |
| 469 radix = 10; |
| 470 if (match != null) { |
| 471 if (match[hexIndex] != null) { |
| 472 // Cannot fail because we know that the digits are all hex. |
| 473 return JS('num', r'parseInt(#, 16)', source); |
| 474 } |
| 475 if (match[decimalIndex] != null) { |
| 476 // Cannot fail because we know that the digits are all decimal. |
| 477 return JS('num', r'parseInt(#, 10)', source); |
| 478 } |
| 479 return handleError(source); |
| 480 } |
| 481 } else { |
| 482 if (radix is! int) throw new ArgumentError("Radix is not an integer"); |
| 483 if (radix < 2 || radix > 36) { |
| 484 throw new RangeError("Radix $radix not in range 2..36"); |
| 485 } |
| 486 if (match != null) { |
| 487 if (radix == 10 && match[decimalIndex] != null) { |
| 488 // Cannot fail because we know that the digits are all decimal. |
| 489 return JS('num', r'parseInt(#, 10)', source); |
| 490 } |
| 491 if (radix < 10 || match[decimalIndex] == null) { |
| 492 // We know that the characters must be ASCII as otherwise the |
| 493 // regexp wouldn't have matched. Calling toLowerCase is thus |
| 494 // guaranteed to be a safe operation. If it wasn't ASCII, then |
| 495 // "İ" would become "i", and we would accept it for radices greater |
| 496 // than 18. |
| 497 int maxCharCode; |
| 498 if (radix <= 10) { |
| 499 // Allow all digits less than the radix. For example 0, 1, 2 for |
| 500 // radix 3. |
| 501 // "0".charCodeAt(0) + radix - 1; |
| 502 maxCharCode = 0x30 + radix - 1; |
| 503 } else { |
| 504 // Characters are located after the digits in ASCII. Therefore we |
| 505 // only check for the character code. The regexp above made already |
| 506 // sure that the string does not contain anything but digits or |
| 507 // characters. |
| 508 // "0".charCodeAt(0) + radix - 1; |
| 509 maxCharCode = 0x61 + radix - 10 - 1; |
| 510 } |
| 511 String digitsPart = match[digitsIndex].toLowerCase(); |
| 512 for (int i = 0; i < digitsPart.length; i++) { |
| 513 if (digitsPart.charCodeAt(i) > maxCharCode) { |
| 514 return handleError(source); |
| 515 } |
| 516 } |
| 517 } |
| 518 } |
| 519 } |
| 520 if (match == null) return handleError(source); |
| 521 return JS('num', r'parseInt(#, #)', source, radix); |
| 522 } |
| 523 |
| 524 static double parseDouble(String source, int handleError(String source)) { |
| 525 checkString(source); |
| 526 if (handleError == null) handleError = _throwFormatException; |
481 // Notice that JS parseFloat accepts garbage at the end of the string. | 527 // Notice that JS parseFloat accepts garbage at the end of the string. |
482 // Accept, ignoring leading and trailing whitespace: | 528 // Accept only: |
483 // - NaN | 529 // - NaN |
484 // - [+/-]Infinity | 530 // - [+/-]Infinity |
485 // - a Dart double literal | 531 // - a Dart double literal |
| 532 // We do not allow leading or trailing whitespace. |
486 if (!JS('bool', | 533 if (!JS('bool', |
487 r'/^\s*(?:NaN|[+-]?(?:Infinity|' | 534 r'/^\s*(?:NaN|[+-]?(?:Infinity|' |
488 r'(?:\.\d+|\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?))\s*$/.test(#)', | 535 r'(?:\.\d+|\d+(?:\.\d+)?)(?:[eE][+-]?\d+)?))\s*$/.test(#)', |
489 string)) { | 536 source)) { |
490 throw new FormatException(string); | 537 return handleError(source); |
491 } | 538 } |
492 var result = JS('num', r'parseFloat(#)', string); | 539 var result = JS('num', r'parseFloat(#)', source); |
493 if (result.isNaN && string != 'NaN') { | 540 if (result.isNaN && source != 'NaN') { |
494 throw new FormatException(string); | 541 return handleError(source); |
495 } | 542 } |
496 return result; | 543 return result; |
497 } | 544 } |
498 | 545 |
499 /** [: r"$".charCodeAt(0) :] */ | 546 /** [: r"$".charCodeAt(0) :] */ |
500 static const int DOLLAR_CHAR_VALUE = 36; | 547 static const int DOLLAR_CHAR_VALUE = 36; |
501 | 548 |
502 static String objectTypeName(Object object) { | 549 static String objectTypeName(Object object) { |
503 String name = constructorNameFallback(object); | 550 String name = constructorNameFallback(object); |
504 if (name == 'Object') { | 551 if (name == 'Object') { |
505 // Try to decompile the constructor by turning it into a string | 552 // Try to decompile the constructor by turning it into a string |
506 // and get the name out of that. If the decompiled name is a | 553 // and get the name out of that. If the decompiled name is a |
507 // string, we use that instead of the very generic 'Object'. | 554 // string, we use that instead of the very generic 'Object'. |
508 var decompiled = JS('var', r'#.match(/^\s*function\s*(\S*)\s*\(/)[1]', | 555 var decompiled = JS('var', r'#.match(/^\s*function\s*(\S*)\s*\(/)[1]', |
509 JS('var', r'String(#.constructor)', object)); | 556 JS('var', r'String(#.constructor)', object)); |
510 if (decompiled is String) name = decompiled; | 557 if (decompiled is String) name = decompiled; |
511 } | 558 } |
512 // TODO(kasperl): If the namer gave us a fresh global name, we may | 559 // TODO(kasperl): If the namer gave us a fresh global name, we may |
513 // want to remove the numeric suffix that makes it unique too. | 560 // want to remove the numeric suffix that makes it unique too. |
514 if (identical(name.charCodeAt(0), DOLLAR_CHAR_VALUE)) name = name.substring(
1); | 561 if (identical(name.charCodeAt(0), DOLLAR_CHAR_VALUE)) name = name.substring(
1); |
515 return name; | 562 return name; |
516 } | 563 } |
517 | 564 |
518 static String objectToString(Object object) { | 565 static String objectToString(Object object) { |
519 String name = objectTypeName(object); | 566 String name = objectTypeName(object); |
520 return "Instance of '$name'"; | 567 return "Instance of '$name'"; |
521 } | 568 } |
522 | 569 |
523 static List newList(length) { | 570 static List newGrowableList(length) { |
524 // TODO(sra): For good concrete type analysis we need the JS-type to | 571 // TODO(sra): For good concrete type analysis we need the JS-type to |
525 // specifically name the JavaScript Array implementation. 'List' matches | 572 // specifically name the JavaScript Array implementation. 'List' matches |
526 // all the dart:html types that implement List<T>. | 573 // all the dart:html types that implement List<T>. |
527 if (length == null) return JS('=List', r'new Array()'); | 574 return JS('Object', r'new Array(#)', length); |
528 if ((length is !int) || (length < 0)) { | 575 } |
529 throw new ArgumentError(length); | 576 |
530 } | 577 static List newFixedList(length) { |
| 578 // TODO(sra): For good concrete type analysis we need the JS-type to |
| 579 // specifically name the JavaScript Array implementation. 'List' matches |
| 580 // all the dart:html types that implement List<T>. |
531 var result = JS('=List', r'new Array(#)', length); | 581 var result = JS('=List', r'new Array(#)', length); |
532 JS('void', r'#.fixed$length = #', result, true); | 582 JS('void', r'#.fixed$length = #', result, true); |
533 return result; | 583 return result; |
534 } | 584 } |
535 | 585 |
536 static num dateNow() => JS('num', r'Date.now()'); | 586 static num dateNow() => JS('num', r'Date.now()'); |
537 | 587 |
538 static num numMicroseconds() { | 588 static num numMicroseconds() { |
539 if (JS('bool', 'typeof window != "undefined" && window !== null')) { | 589 if (JS('bool', 'typeof window != "undefined" && window !== null')) { |
540 var performance = JS('var', 'window.performance'); | 590 var performance = JS('var', 'window.performance'); |
(...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
848 } | 898 } |
849 | 899 |
850 checkString(value) { | 900 checkString(value) { |
851 if (value is !String) { | 901 if (value is !String) { |
852 throw new ArgumentError(value); | 902 throw new ArgumentError(value); |
853 } | 903 } |
854 return value; | 904 return value; |
855 } | 905 } |
856 | 906 |
857 class MathNatives { | 907 class MathNatives { |
858 static int parseInt(str) { | |
859 checkString(str); | |
860 if (!JS('bool', | |
861 r'/^\s*[+-]?(?:0[xX][abcdefABCDEF0-9]+|\d+)\s*$/.test(#)', | |
862 str)) { | |
863 throw new FormatException(str); | |
864 } | |
865 var trimmed = str.trim(); | |
866 var base = 10;; | |
867 if ((trimmed.length > 2 && (trimmed[1] == 'x' || trimmed[1] == 'X')) || | |
868 (trimmed.length > 3 && (trimmed[2] == 'x' || trimmed[2] == 'X'))) { | |
869 base = 16; | |
870 } | |
871 var ret = JS('num', r'parseInt(#, #)', trimmed, base); | |
872 if (ret.isNaN) throw new FormatException(str); | |
873 return ret; | |
874 } | |
875 | |
876 static double parseDouble(String str) { | |
877 checkString(str); | |
878 var ret = JS('num', r'parseFloat(#)', str); | |
879 if (ret == 0 && (str.startsWith("0x") || str.startsWith("0X"))) { | |
880 // TODO(ahe): This is unspecified, but tested by co19. | |
881 ret = JS('num', r'parseInt(#)', str); | |
882 } | |
883 if (ret.isNaN && str != 'NaN' && str != '-NaN') { | |
884 throw new FormatException(str); | |
885 } | |
886 return ret; | |
887 } | |
888 | |
889 static double sqrt(num value) | 908 static double sqrt(num value) |
890 => JS('double', r'Math.sqrt(#)', checkNum(value)); | 909 => JS('double', r'Math.sqrt(#)', checkNum(value)); |
891 | 910 |
892 static double sin(num value) | 911 static double sin(num value) |
893 => JS('double', r'Math.sin(#)', checkNum(value)); | 912 => JS('double', r'Math.sin(#)', checkNum(value)); |
894 | 913 |
895 static double cos(num value) | 914 static double cos(num value) |
896 => JS('double', r'Math.cos(#)', checkNum(value)); | 915 => JS('double', r'Math.cos(#)', checkNum(value)); |
897 | 916 |
898 static double tan(num value) | 917 static double tan(num value) |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1077 StackTrace(this.stack); | 1096 StackTrace(this.stack); |
1078 String toString() => stack != null ? stack : ''; | 1097 String toString() => stack != null ? stack : ''; |
1079 } | 1098 } |
1080 | 1099 |
1081 | 1100 |
1082 /** | 1101 /** |
1083 * Called by generated code to build a map literal. [keyValuePairs] is | 1102 * Called by generated code to build a map literal. [keyValuePairs] is |
1084 * a list of key, value, key, value, ..., etc. | 1103 * a list of key, value, key, value, ..., etc. |
1085 */ | 1104 */ |
1086 makeLiteralMap(List keyValuePairs) { | 1105 makeLiteralMap(List keyValuePairs) { |
1087 Iterator iterator = keyValuePairs.iterator(); | 1106 Iterator iterator = keyValuePairs.iterator; |
1088 Map result = new LinkedHashMap(); | 1107 Map result = new LinkedHashMap(); |
1089 while (iterator.hasNext) { | 1108 while (iterator.moveNext()) { |
1090 String key = iterator.next(); | 1109 String key = iterator.current; |
1091 var value = iterator.next(); | 1110 iterator.moveNext(); |
| 1111 var value = iterator.current; |
1092 result[key] = value; | 1112 result[key] = value; |
1093 } | 1113 } |
1094 return result; | 1114 return result; |
1095 } | 1115 } |
1096 | 1116 |
1097 invokeClosure(Function closure, | 1117 invokeClosure(Function closure, |
1098 var isolate, | 1118 var isolate, |
1099 int numberOfArguments, | 1119 int numberOfArguments, |
1100 var arg1, | 1120 var arg1, |
1101 var arg2) { | 1121 var arg2) { |
(...skipping 606 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1708 if (len != t.length) return false; | 1728 if (len != t.length) return false; |
1709 for (int i = 1; i < len; i++) { | 1729 for (int i = 1; i < len; i++) { |
1710 if (!isSubtype(s[i], t[i])) { | 1730 if (!isSubtype(s[i], t[i])) { |
1711 return false; | 1731 return false; |
1712 } | 1732 } |
1713 } | 1733 } |
1714 return true; | 1734 return true; |
1715 } | 1735 } |
1716 | 1736 |
1717 createRuntimeType(String name) => new TypeImpl(name); | 1737 createRuntimeType(String name) => new TypeImpl(name); |
OLD | NEW |