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 |