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 class InvocationImpl extends Invocation { | 9 class InvocationImpl extends Invocation { |
10 final Symbol memberName; | 10 final Symbol memberName; |
11 final List positionalArguments; | 11 final List positionalArguments; |
12 final Map<Symbol, dynamic> namedArguments; | 12 final Map<Symbol, dynamic> namedArguments; |
13 final bool isMethod; | 13 final bool isMethod; |
14 final bool isGetter; | 14 final bool isGetter; |
15 final bool isSetter; | 15 final bool isSetter; |
16 | 16 |
17 InvocationImpl(String memberName, this.positionalArguments, | 17 InvocationImpl(memberName, this.positionalArguments, |
18 {namedArguments, | 18 {namedArguments, |
19 this.isMethod: false, | 19 this.isMethod: false, |
20 this.isGetter: false, | 20 this.isGetter: false, |
21 this.isSetter: false}) | 21 this.isSetter: false}) |
22 : memberName = _dartSymbol(memberName), | 22 : memberName = _dartSymbol(memberName), |
23 namedArguments = _namedArgsToSymbols(namedArguments); | 23 namedArguments = _namedArgsToSymbols(namedArguments); |
24 | 24 |
25 static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { | 25 static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { |
26 if (namedArgs == null) return {}; | 26 if (namedArgs == null) return {}; |
27 return new Map.fromIterable(getOwnPropertyNames(namedArgs), | 27 return new Map.fromIterable(getOwnPropertyNames(namedArgs), |
28 key: _dartSymbol, value: (k) => JS('', '#[#]', namedArgs, k)); | 28 key: _dartSymbol, value: (k) => JS('', '#[#]', namedArgs, k)); |
29 } | 29 } |
30 } | 30 } |
31 | 31 |
| 32 // Warning: dload, dput, and dsend assume they are never called on methods |
| 33 // implemented by the Object base class as those methods can always be |
| 34 // statically resolved. |
32 dload(obj, field) { | 35 dload(obj, field) { |
33 var f = _canonicalMember(obj, field); | 36 var f = _canonicalMember(obj, field); |
| 37 |
34 _trackCall(obj); | 38 _trackCall(obj); |
35 if (f != null) { | 39 if (f != null) { |
36 if (hasMethod(obj, f)) return bind(obj, f, JS('', 'void 0')); | 40 // XXX verify it is a Dart type first |
37 return JS('', '#[#]', obj, f); | 41 var type = getType(obj); |
| 42 |
| 43 if (hasField(type, f) || hasGetter(type, f)) return JS('', '#[#]', obj, f); |
| 44 if (hasMethod(type, f)) return bind(obj, f, JS('', 'void 0')); |
38 } | 45 } |
39 return noSuchMethod( | 46 return noSuchMethod( |
40 obj, new InvocationImpl(field, JS('', '[]'), isGetter: true)); | 47 obj, new InvocationImpl(field, JS('', '[]'), isGetter: true)); |
41 } | 48 } |
42 | 49 |
43 dput(obj, field, value) { | 50 dput(obj, field, value) { |
44 var f = _canonicalMember(obj, field); | 51 var f = _canonicalMember(obj, field); |
45 _trackCall(obj); | 52 _trackCall(obj); |
46 if (f != null) { | 53 if (f != null) { |
47 return JS('', '#[#] = #', obj, f, value); | 54 // XXX verify it is a Dart type first |
| 55 var type = getType(obj); |
| 56 var setter = getSetterType(type, f); |
| 57 if (JS('bool', '# != void 0', setter)) { |
| 58 // TODO(jacobr): throw a type error instead of a NoSuchMethodError if |
| 59 // the type of the setter doesn't match. |
| 60 if (instanceOfOrNull(value, JS('', '#.args[0]', setter))) { |
| 61 return JS('', '#[#] = #', obj, f, value); |
| 62 } |
| 63 } else { |
| 64 var field = getFieldType(type, f); |
| 65 // TODO(jacobr): propogate metadata tracking which fields are private. |
| 66 if (JS('bool', '# != void 0', field)) { |
| 67 // TODO(jacobr): throw a type error instead of a NoSuchMethodError if |
| 68 // the type of the field doesn't match. |
| 69 if (instanceOfOrNull(value, field)) { |
| 70 return JS('', '#[#] = #', obj, f, value); |
| 71 } |
| 72 } |
| 73 } |
48 } | 74 } |
49 return noSuchMethod( | 75 return noSuchMethod( |
50 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); | 76 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); |
51 } | 77 } |
52 | 78 |
53 /// Check that a function of a given type can be applied to | 79 /// Check that a function of a given type can be applied to |
54 /// actuals. | 80 /// actuals. |
55 _checkApply(type, actuals) => JS( | 81 _checkApply(type, actuals) => JS( |
56 '', | 82 '', |
57 '''(() => { | 83 '''(() => { |
(...skipping 24 matching lines...) Expand all Loading... |
82 if (names.length == 0) return false; | 108 if (names.length == 0) return false; |
83 for (var name of names) { | 109 for (var name of names) { |
84 if (!($hasOwnProperty.call($type.named, name))) { | 110 if (!($hasOwnProperty.call($type.named, name))) { |
85 return false; | 111 return false; |
86 } | 112 } |
87 $check(opts[name], $type.named[name]); | 113 $check(opts[name], $type.named[name]); |
88 } | 114 } |
89 return true; | 115 return true; |
90 })()'''); | 116 })()'''); |
91 | 117 |
92 Symbol _dartSymbol(name) => | 118 _toSymbolName(symbol) => JS( |
93 JS('', '#(#.new(#.toString()))', const_, Symbol, name); | 119 '', |
| 120 '''(() => { |
| 121 let str = $symbol.toString(); |
| 122 // Strip leading 'Symbol(' and trailing ')' |
| 123 return str.substring(7, str.length-1); |
| 124 })()'''); |
| 125 |
| 126 _toDisplayName(name) => JS( |
| 127 '', |
| 128 '''(() => { |
| 129 // Names starting with _ are escaped names used to disambiguate Dart and |
| 130 // JS names. |
| 131 if ($name[0] === '_') { |
| 132 // Inverse of |
| 133 switch($name) { |
| 134 case '_get': |
| 135 return '[]'; |
| 136 case '_set': |
| 137 return '[]='; |
| 138 case '_negate': |
| 139 return 'unary-'; |
| 140 case '_constructor': |
| 141 case '_prototype': |
| 142 return $name.substring(1); |
| 143 } |
| 144 } |
| 145 return $name; |
| 146 })()'''); |
| 147 |
| 148 Symbol _dartSymbol(name) { |
| 149 return (JS('bool', 'typeof # === "symbol"', name)) |
| 150 ? JS('', '#(new #.es6(#, #))', const_, _internal.Symbol, |
| 151 _toSymbolName(name), name) |
| 152 : JS('', '#(#.new(#))', const_, Symbol, _toDisplayName(name)); |
| 153 } |
94 | 154 |
95 /// Extracts the named argument array from a list of arguments, and returns it. | 155 /// Extracts the named argument array from a list of arguments, and returns it. |
96 // TODO(jmesserly): we need to handle named arguments better. | 156 // TODO(jmesserly): we need to handle named arguments better. |
97 extractNamedArgs(args) { | 157 extractNamedArgs(args) { |
98 if (JS('bool', '#.length > 0', args)) { | 158 if (JS('bool', '#.length > 0', args)) { |
99 var last = JS('', '#[#.length - 1]', args, args); | 159 var last = JS('', '#[#.length - 1]', args, args); |
100 if (JS( | 160 if (JS( |
101 'bool', '# != null && #.__proto__ === Object.prototype', last, last)) { | 161 'bool', '# != null && #.__proto__ === Object.prototype', last, last)) { |
102 return JS('', '#.pop()', args); | 162 return JS('', '#.pop()', args); |
103 } | 163 } |
(...skipping 10 matching lines...) Expand all Loading... |
114 | 174 |
115 function callNSM() { | 175 function callNSM() { |
116 return $noSuchMethod(originalTarget, new $InvocationImpl( | 176 return $noSuchMethod(originalTarget, new $InvocationImpl( |
117 $name, $args, | 177 $name, $args, |
118 {namedArguments: $extractNamedArgs($args), isMethod: true})); | 178 {namedArguments: $extractNamedArgs($args), isMethod: true})); |
119 } | 179 } |
120 if (!($f instanceof Function)) { | 180 if (!($f instanceof Function)) { |
121 // We're not a function (and hence not a method either) | 181 // We're not a function (and hence not a method either) |
122 // Grab the `call` method if it's not a function. | 182 // Grab the `call` method if it's not a function. |
123 if ($f != null) { | 183 if ($f != null) { |
124 $ftype = $getMethodType($f, 'call'); | 184 $ftype = $getMethodType($getType($f), 'call'); |
125 $f = $f.call; | 185 $f = $f.call; |
126 } | 186 } |
127 if (!($f instanceof Function)) { | 187 if (!($f instanceof Function)) { |
128 return callNSM(); | 188 return callNSM(); |
129 } | 189 } |
130 } | 190 } |
131 // If f is a function, but not a method (no method type) | 191 // If f is a function, but not a method (no method type) |
132 // then it should have been a function valued field, so | 192 // then it should have been a function valued field, so |
133 // get the type from the function. | 193 // get the type from the function. |
134 if ($ftype === void 0) { | 194 if ($ftype === void 0) { |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
296 } | 356 } |
297 | 357 |
298 /// Shared code for dsend, dindex, and dsetindex. | 358 /// Shared code for dsend, dindex, and dsetindex. |
299 _callMethod(obj, name, typeArgs, args, displayName) { | 359 _callMethod(obj, name, typeArgs, args, displayName) { |
300 var symbol = _canonicalMember(obj, name); | 360 var symbol = _canonicalMember(obj, name); |
301 if (symbol == null) { | 361 if (symbol == null) { |
302 return noSuchMethod( | 362 return noSuchMethod( |
303 obj, new InvocationImpl(displayName, args, isMethod: true)); | 363 obj, new InvocationImpl(displayName, args, isMethod: true)); |
304 } | 364 } |
305 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; | 365 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; |
306 var ftype = getMethodType(obj, symbol); | 366 var type = getType(obj); |
| 367 var ftype = getMethodType(type, symbol); |
| 368 // No such method if dart object and ftype is missing. |
307 return _checkAndCall(f, ftype, obj, typeArgs, args, displayName); | 369 return _checkAndCall(f, ftype, obj, typeArgs, args, displayName); |
308 } | 370 } |
309 | 371 |
310 dsend(obj, method, @rest args) => _callMethod(obj, method, null, args, method); | 372 dsend(obj, method, @rest args) => _callMethod(obj, method, null, args, method); |
311 | 373 |
312 dgsend(obj, typeArgs, method, @rest args) => | 374 dgsend(obj, typeArgs, method, @rest args) => |
313 _callMethod(obj, method, typeArgs, args, method); | 375 _callMethod(obj, method, typeArgs, args, method); |
314 | 376 |
315 dindex(obj, index) => | 377 dindex(obj, index) => |
316 _callMethod(obj, '_get', null, JS('', '[#]', index), '[]'); | 378 _callMethod(obj, '_get', null, JS('', '[#]', index), '[]'); |
(...skipping 513 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
830 | 892 |
831 _canonicalMember(obj, name) { | 893 _canonicalMember(obj, name) { |
832 // Private names are symbols and are already canonical. | 894 // Private names are symbols and are already canonical. |
833 if (JS('bool', 'typeof # === "symbol"', name)) return name; | 895 if (JS('bool', 'typeof # === "symbol"', name)) return name; |
834 | 896 |
835 if (obj != null && getExtensionType(obj) != null) { | 897 if (obj != null && getExtensionType(obj) != null) { |
836 return JS('', 'dartx.#', name); | 898 return JS('', 'dartx.#', name); |
837 } | 899 } |
838 | 900 |
839 // Check for certain names that we can't use in JS | 901 // Check for certain names that we can't use in JS |
840 if (name == 'constructor' || name == 'prototype') { | 902 /*(if (name == 'constructor' || name == 'prototype') { |
841 name = '+' + name; | 903 name = '+' + name; |
842 } | 904 }*/ |
| 905 if (JS('bool', '# instanceof #', obj, Object)) { |
| 906 // This is a Dart type not a JS interop type. |
| 907 |
| 908 } else {} |
843 return name; | 909 return name; |
844 } | 910 } |
845 | 911 |
846 /// Emulates the implicit "loadLibrary" function provided by a deferred library. | 912 /// Emulates the implicit "loadLibrary" function provided by a deferred library. |
847 /// | 913 /// |
848 /// Libraries are not actually deferred in DDC, so this just returns a future | 914 /// Libraries are not actually deferred in DDC, so this just returns a future |
849 /// that completes immediately. | 915 /// that completes immediately. |
850 Future loadLibrary() => new Future.value(); | 916 Future loadLibrary() => new Future.value(); |
OLD | NEW |