| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 /// This library defines runtime operations on objects used by the code | 5 /// This library defines runtime operations on objects used by the code |
| 6 /// generator. | 6 /// generator. |
| 7 part of dart._runtime; | 7 part of dart._runtime; |
| 8 | 8 |
| 9 _canonicalFieldName(obj, name, args, displayName) => JS('', '''(() => { | 9 class _Invocation extends Invocation { |
| 10 $name = $canonicalMember($obj, $name); | 10 final Symbol memberName; |
| 11 if ($name) return $name; | 11 final List positionalArguments; |
| 12 // TODO(jmesserly): in the future we might have types that "overlay" Dart | 12 final Map<Symbol, dynamic> namedArguments; |
| 13 // methods while also exposing the full native API, e.g. dart:html vs | 13 final bool isMethod; |
| 14 // dart:dom. To support that we'd need to fall back to the normal name | 14 final bool isGetter; |
| 15 // if an extension method wasn't found. | 15 final bool isSetter; |
| 16 $throwNoSuchMethodFunc($obj, $displayName, $args); | |
| 17 })()'''); | |
| 18 | 16 |
| 19 dload(obj, field) => JS('', '''(() => { | 17 _Invocation(String memberName, this.positionalArguments, |
| 20 $field = $_canonicalFieldName($obj, $field, [], $field); | 18 {namedArguments, |
| 21 $_trackCall($obj, $field); | 19 this.isMethod: false, |
| 22 if ($hasMethod($obj, $field)) { | 20 this.isGetter: false, |
| 23 return $bind($obj, $field); | 21 this.isSetter: false}) |
| 22 : memberName = _dartSymbol(memberName), |
| 23 namedArguments = _namedArgsToSymbols(namedArguments); |
| 24 |
| 25 static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { |
| 26 if (namedArgs == null) return {}; |
| 27 return new Map.fromIterable( |
| 28 getOwnPropertyNames(namedArgs), |
| 29 key: _dartSymbol, |
| 30 value: (k) => JS('', '#[#]', namedArgs, k)); |
| 24 } | 31 } |
| 25 // TODO(vsm): Implement NSM robustly. An 'in' check breaks on certain | 32 } |
| 26 // types. hasOwnProperty doesn't chase the proto chain. | |
| 27 // Also, do we want an NSM on regular JS objects? | |
| 28 // See: https://github.com/dart-lang/dev_compiler/issues/169 | |
| 29 let result = $obj[$field]; | |
| 30 return result; | |
| 31 })()'''); | |
| 32 | 33 |
| 33 dput(obj, field, value) => JS('', '''(() => { | 34 dload(obj, field) { |
| 34 $field = $_canonicalFieldName($obj, $field, [$value], $field); | 35 var f = _canonicalMember(obj, field); |
| 35 $_trackCall($obj, $field); | 36 _trackCall(obj, f); |
| 37 if (f != null) { |
| 38 if (hasMethod(obj, f)) return bind(obj, f, JS('', 'void 0')); |
| 39 return JS('', '#[#]', obj, f); |
| 40 } |
| 41 return noSuchMethod(obj, |
| 42 new _Invocation(field, JS('', '[]'), isGetter: true)); |
| 43 } |
| 36 | 44 |
| 37 // TODO(vsm): Implement NSM and type checks. | 45 dput(obj, field, value) { |
| 38 // See: https://github.com/dart-lang/dev_compiler/issues/170 | 46 var f = _canonicalMember(obj, field); |
| 39 $obj[$field] = $value; | 47 _trackCall(obj, f); |
| 40 return $value; | 48 if (f != null) { |
| 41 })()'''); | 49 return JS('', '#[#] = #', obj, f, value); |
| 50 } |
| 51 return noSuchMethod(obj, |
| 52 new _Invocation(field, JS('', '[#]', value), isSetter: true)); |
| 53 } |
| 42 | 54 |
| 43 /// Check that a function of a given type can be applied to | 55 /// Check that a function of a given type can be applied to |
| 44 /// actuals. | 56 /// actuals. |
| 45 _checkApply(type, actuals) => JS('', '''(() => { | 57 _checkApply(type, actuals) => JS('', '''(() => { |
| 46 if ($actuals.length < $type.args.length) return false; | 58 if ($actuals.length < $type.args.length) return false; |
| 47 let index = 0; | 59 let index = 0; |
| 48 for(let i = 0; i < $type.args.length; ++i) { | 60 for(let i = 0; i < $type.args.length; ++i) { |
| 49 if (!$instanceOfOrNull($actuals[i], $type.args[i])) return false; | 61 if (!$instanceOfOrNull($actuals[i], $type.args[i])) return false; |
| 50 ++index; | 62 ++index; |
| 51 } | 63 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 70 if (names.length == 0) return false; | 82 if (names.length == 0) return false; |
| 71 for (var name of names) { | 83 for (var name of names) { |
| 72 if (!($hasOwnProperty.call($type.named, name))) { | 84 if (!($hasOwnProperty.call($type.named, name))) { |
| 73 return false; | 85 return false; |
| 74 } | 86 } |
| 75 if (!$instanceOfOrNull(opts[name], $type.named[name])) return false; | 87 if (!$instanceOfOrNull(opts[name], $type.named[name])) return false; |
| 76 } | 88 } |
| 77 return true; | 89 return true; |
| 78 })()'''); | 90 })()'''); |
| 79 | 91 |
| 80 _dartSymbol(name) => JS('', ''' | 92 Symbol _dartSymbol(name) => |
| 81 $const_($Symbol.new($name.toString())) | 93 JS('', '#(#.new(#.toString()))', const_, Symbol, name); |
| 82 '''); | |
| 83 | 94 |
| 84 throwNoSuchMethod(obj, name, pArgs, nArgs, extras) => JS('', '''(() => { | 95 // TODO(jmesserly): we need to handle named arguments better. |
| 85 $throw_(new $NoSuchMethodError($obj, $_dartSymbol($name), $pArgs, $nArgs, $ext
ras)); | |
| 86 })()'''); | |
| 87 | |
| 88 throwNoSuchMethodFunc(obj, name, pArgs, opt_func) => JS('', '''(() => { | |
| 89 if ($obj === void 0) $obj = $opt_func; | |
| 90 $throwNoSuchMethod($obj, $name, $pArgs); | |
| 91 })()'''); | |
| 92 | |
| 93 _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => { | 96 _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => { |
| 94 $_trackCall($obj, $name); | 97 $_trackCall($obj, $name); |
| 95 | 98 |
| 96 let originalFunction = $f; | 99 let originalTarget = obj === void 0 ? f : obj; |
| 100 |
| 101 function callNSM() { |
| 102 let namedArgs = null; |
| 103 if (args.length > 0 && |
| 104 args[args.length - 1].__proto__ == Object.prototype) { |
| 105 namedArgs = args.pop(); |
| 106 } |
| 107 return $noSuchMethod(originalTarget, new $_Invocation( |
| 108 $name, $args, {namedArguments: namedArgs, isMethod: true})); |
| 109 } |
| 97 if (!($f instanceof Function)) { | 110 if (!($f instanceof Function)) { |
| 98 // We're not a function (and hence not a method either) | 111 // We're not a function (and hence not a method either) |
| 99 // Grab the `call` method if it's not a function. | 112 // Grab the `call` method if it's not a function. |
| 100 if ($f != null) { | 113 if ($f != null) { |
| 101 $ftype = $getMethodType($f, 'call'); | 114 $ftype = $getMethodType($f, 'call'); |
| 102 $f = $f.call; | 115 $f = $f.call; |
| 103 } | 116 } |
| 104 if (!($f instanceof Function)) { | 117 if (!($f instanceof Function)) { |
| 105 $throwNoSuchMethodFunc($obj, $name, $args, originalFunction); | 118 return callNSM(); |
| 106 } | 119 } |
| 107 } | 120 } |
| 108 // If f is a function, but not a method (no method type) | 121 // If f is a function, but not a method (no method type) |
| 109 // then it should have been a function valued field, so | 122 // then it should have been a function valued field, so |
| 110 // get the type from the function. | 123 // get the type from the function. |
| 111 if ($ftype === void 0) { | 124 if ($ftype === void 0) { |
| 112 $ftype = $_getRuntimeType($f); | 125 $ftype = $_getRuntimeType($f); |
| 113 } | 126 } |
| 114 | 127 |
| 115 if (!$ftype) { | 128 if (!$ftype) { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 145 if ($_checkApply($ftype, $args)) { | 158 if ($_checkApply($ftype, $args)) { |
| 146 if ($typeArgs != null) { | 159 if ($typeArgs != null) { |
| 147 return $f.apply($obj, $typeArgs).apply($obj, $args); | 160 return $f.apply($obj, $typeArgs).apply($obj, $args); |
| 148 } | 161 } |
| 149 return $f.apply($obj, $args); | 162 return $f.apply($obj, $args); |
| 150 } | 163 } |
| 151 | 164 |
| 152 // TODO(leafp): throw a type error (rather than NSM) | 165 // TODO(leafp): throw a type error (rather than NSM) |
| 153 // if the arity matches but the types are wrong. | 166 // if the arity matches but the types are wrong. |
| 154 // TODO(jmesserly): nSM should include type args? | 167 // TODO(jmesserly): nSM should include type args? |
| 155 $throwNoSuchMethodFunc($obj, $name, $args, originalFunction); | 168 return callNSM(); |
| 156 })()'''); | 169 })()'''); |
| 157 | 170 |
| 158 dcall(f, @rest args) => _checkAndCall( | 171 dcall(f, @rest args) => _checkAndCall( |
| 159 f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); | 172 f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); |
| 160 | 173 |
| 161 | |
| 162 dgcall(f, typeArgs, @rest args) => _checkAndCall( | 174 dgcall(f, typeArgs, @rest args) => _checkAndCall( |
| 163 f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); | 175 f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); |
| 164 | 176 |
| 165 Map<String, int> _callMethodStats = new Map(); | 177 Map<String, int> _callMethodStats = new Map(); |
| 166 | 178 |
| 167 List<List<Object>> getDynamicStats() { | 179 List<List<Object>> getDynamicStats() { |
| 168 List<List<Object>> ret = []; | 180 List<List<Object>> ret = []; |
| 169 | 181 |
| 170 var keys = _callMethodStats.keys.toList(); | 182 var keys = _callMethodStats.keys.toList(); |
| 171 | 183 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 202 name = "${typeName(actual)}.$name <$src>"; | 214 name = "${typeName(actual)}.$name <$src>"; |
| 203 if (_callMethodStats.containsKey(name)) { | 215 if (_callMethodStats.containsKey(name)) { |
| 204 _callMethodStats[name] = _callMethodStats[name] + 1; | 216 _callMethodStats[name] = _callMethodStats[name] + 1; |
| 205 } else { | 217 } else { |
| 206 _callMethodStats[name] = 1; | 218 _callMethodStats[name] = 1; |
| 207 } | 219 } |
| 208 } | 220 } |
| 209 | 221 |
| 210 /// Shared code for dsend, dindex, and dsetindex. | 222 /// Shared code for dsend, dindex, and dsetindex. |
| 211 _callMethod(obj, name, typeArgs, args, displayName) { | 223 _callMethod(obj, name, typeArgs, args, displayName) { |
| 212 var symbol = _canonicalFieldName(obj, name, args, displayName); | 224 var symbol = _canonicalMember(obj, name); |
| 225 if (symbol == null) { |
| 226 return noSuchMethod(obj, |
| 227 new _Invocation(displayName, args, isMethod: true)); |
| 228 } |
| 213 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; | 229 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; |
| 214 var ftype = getMethodType(obj, symbol); | 230 var ftype = getMethodType(obj, symbol); |
| 215 return _checkAndCall(f, ftype, obj, typeArgs, args, displayName); | 231 return _checkAndCall(f, ftype, obj, typeArgs, args, displayName); |
| 216 } | 232 } |
| 217 | 233 |
| 218 dsend(obj, method, @rest args) => _callMethod(obj, method, null, args, method); | 234 dsend(obj, method, @rest args) => _callMethod(obj, method, null, args, method); |
| 219 | 235 |
| 220 dgsend(obj, typeArgs, method, @rest args) => | 236 dgsend(obj, typeArgs, method, @rest args) => |
| 221 _callMethod(obj, method, typeArgs, args, method); | 237 _callMethod(obj, method, typeArgs, args, method); |
| 222 | 238 |
| (...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 604 // the compiler. | 620 // the compiler. |
| 605 hashCode(obj) { | 621 hashCode(obj) { |
| 606 if (obj == null) return 0; | 622 if (obj == null) return 0; |
| 607 | 623 |
| 608 switch (JS('String', 'typeof #', obj)) { | 624 switch (JS('String', 'typeof #', obj)) { |
| 609 case "number": | 625 case "number": |
| 610 return JS('','# & 0x1FFFFFFF', obj); | 626 return JS('','# & 0x1FFFFFFF', obj); |
| 611 case "boolean": | 627 case "boolean": |
| 612 // From JSBool.hashCode, see comment there. | 628 // From JSBool.hashCode, see comment there. |
| 613 return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); | 629 return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); |
| 630 case "function": |
| 631 // TODO(jmesserly): this doesn't work for method tear-offs. |
| 632 return Primitives.objectHashCode(obj); |
| 614 } | 633 } |
| 615 | 634 |
| 616 var extension = getExtensionType(obj); | 635 var extension = getExtensionType(obj); |
| 617 if (extension != null) { | 636 if (extension != null) { |
| 618 return JS('', '#[dartx.hashCode]', obj); | 637 return JS('', '#[dartx.hashCode]', obj); |
| 619 } | 638 } |
| 620 return JS('', '#.hashCode', obj); | 639 return JS('', '#.hashCode', obj); |
| 621 } | 640 } |
| 622 | 641 |
| 623 @JSExportName('toString') | 642 @JSExportName('toString') |
| 624 String _toString(obj) { | 643 String _toString(obj) { |
| 625 if (obj == null) return "null"; | 644 if (obj == null) return "null"; |
| 626 | 645 |
| 627 var extension = getExtensionType(obj); | 646 var extension = getExtensionType(obj); |
| 628 if (extension != null) { | 647 if (extension != null) { |
| 629 return JS('String', '#[dartx.toString]()', obj); | 648 return JS('String', '#[dartx.toString]()', obj); |
| 630 } | 649 } |
| 650 if (JS('bool', 'typeof # == "function"', obj)) { |
| 651 return JS('String', r'"Closure: " + # + " from: " + #', |
| 652 getReifiedType(obj), obj); |
| 653 } |
| 631 // TODO(jmesserly): restore this faster path once ES Symbol is treated as | 654 // TODO(jmesserly): restore this faster path once ES Symbol is treated as |
| 632 // an extension type (and thus hits the above code path). | 655 // an extension type (and thus hits the above code path). |
| 633 // See https://github.com/dart-lang/dev_compiler/issues/578. | 656 // See https://github.com/dart-lang/dev_compiler/issues/578. |
| 634 // return JS('', '"" + #', obj); | 657 // return JS('', '"" + #', obj); |
| 635 return JS('String', '#.toString()', obj); | 658 return JS('String', '#.toString()', obj); |
| 636 } | 659 } |
| 637 | 660 |
| 638 // TODO(jmesserly): is the argument type verified statically? | 661 // TODO(jmesserly): is the argument type verified statically? |
| 639 noSuchMethod(obj, Invocation invocation) { | 662 noSuchMethod(obj, Invocation invocation) { |
| 640 if (obj == null) { | 663 if (obj == null || JS('bool', 'typeof # == "function"', obj)) { |
| 641 throw new NoSuchMethodError( | 664 throw new NoSuchMethodError( |
| 642 null, | 665 obj, |
| 643 invocation.memberName, | 666 invocation.memberName, |
| 644 invocation.positionalArguments, | 667 invocation.positionalArguments, |
| 645 invocation.namedArguments); | 668 invocation.namedArguments); |
| 646 } | 669 } |
| 647 // Delegate to the (possibly user-defined) method on the object. | 670 // Delegate to the (possibly user-defined) method on the object. |
| 648 var extension = getExtensionType(obj); | 671 var extension = getExtensionType(obj); |
| 649 if (extension != null) { | 672 if (extension != null) { |
| 650 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); | 673 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); |
| 651 } | 674 } |
| 652 return JS('', '#.noSuchMethod(#)', obj, invocation); | 675 return JS('', '#.noSuchMethod(#)', obj, invocation); |
| 653 } | 676 } |
| 654 | 677 |
| 655 constFn(x) => JS('', '() => x'); | 678 constFn(x) => JS('', '() => x'); |
| 656 | 679 |
| 657 runtimeType(obj) { | 680 runtimeType(obj) { |
| 658 // Handle primitives where the method isn't on the object. | 681 // Handle primitives where the method isn't on the object. |
| 659 var result = _checkPrimitiveType(obj); | 682 var result = _checkPrimitiveType(obj); |
| 660 if (result != null) return wrapType(result); | 683 if (result != null) return wrapType(result); |
| 661 | 684 |
| 662 // Delegate to the (possibly user-defined) method on the object. | 685 // Delegate to the (possibly user-defined) method on the object. |
| 663 var extension = getExtensionType(obj); | 686 var extension = getExtensionType(obj); |
| 664 if (extension != null) { | 687 if (extension != null) { |
| 665 return JS('', '#[dartx.runtimeType]', obj); | 688 return JS('', '#[dartx.runtimeType]', obj); |
| 666 } | 689 } |
| 690 if (JS('bool', 'typeof # == "function"', obj)) { |
| 691 return wrapType(getReifiedType(obj)); |
| 692 } |
| 667 return JS('', '#.runtimeType', obj); | 693 return JS('', '#.runtimeType', obj); |
| 668 } | 694 } |
| 669 | 695 |
| 670 /// Implements Dart's interpolated strings as ES2015 tagged template literals. | 696 /// Implements Dart's interpolated strings as ES2015 tagged template literals. |
| 671 /// | 697 /// |
| 672 /// For example: dart.str`hello ${name}` | 698 /// For example: dart.str`hello ${name}` |
| 673 String str(strings, @rest values) => JS('', '''(() => { | 699 String str(strings, @rest values) => JS('', '''(() => { |
| 674 let s = $strings[0]; | 700 let s = $strings[0]; |
| 675 for (let i = 0, len = $values.length; i < len; ) { | 701 for (let i = 0, len = $values.length; i < len; ) { |
| 676 s += $notNull($_toString($values[i])) + $strings[++i]; | 702 s += $notNull($_toString($values[i])) + $strings[++i]; |
| 677 } | 703 } |
| 678 return s; | 704 return s; |
| 679 })()'''); | 705 })()'''); |
| 680 | 706 |
| 681 | 707 |
| 682 final JsIterator = JS('', ''' | 708 final JsIterator = JS('', ''' |
| 683 class JsIterator { | 709 class JsIterator { |
| 684 constructor(dartIterator) { | 710 constructor(dartIterator) { |
| 685 this.dartIterator = dartIterator; | 711 this.dartIterator = dartIterator; |
| 686 } | 712 } |
| 687 next() { | 713 next() { |
| 688 let i = this.dartIterator; | 714 let i = this.dartIterator; |
| 689 let done = !i.moveNext(); | 715 let done = !i.moveNext(); |
| 690 return { done: done, value: done ? void 0 : i.current }; | 716 return { done: done, value: done ? void 0 : i.current }; |
| 691 } | 717 } |
| 692 } | 718 } |
| 693 '''); | 719 '''); |
| 720 |
| 721 _canonicalMember(obj, name) { |
| 722 // Private names are symbols and are already canonical. |
| 723 if (JS('bool', 'typeof # === "symbol"', name)) return name; |
| 724 |
| 725 if (obj != null && getExtensionType(obj) != null) { |
| 726 return JS('', 'dartx.#', name); |
| 727 } |
| 728 |
| 729 // Check for certain names that we can't use in JS |
| 730 if (name == 'constructor' || name == 'prototype') { |
| 731 name = '+' + name; |
| 732 } |
| 733 return name; |
| 734 } |
| OLD | NEW |