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(String 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( | 27 return new Map.fromIterable(getOwnPropertyNames(namedArgs), |
28 getOwnPropertyNames(namedArgs), | 28 key: _dartSymbol, value: (k) => JS('', '#[#]', namedArgs, k)); |
29 key: _dartSymbol, | |
30 value: (k) => JS('', '#[#]', namedArgs, k)); | |
31 } | 29 } |
32 } | 30 } |
33 | 31 |
34 dload(obj, field) { | 32 dload(obj, field) { |
35 var f = _canonicalMember(obj, field); | 33 var f = _canonicalMember(obj, field); |
36 _trackCall(obj); | 34 _trackCall(obj); |
37 if (f != null) { | 35 if (f != null) { |
38 if (hasMethod(obj, f)) return bind(obj, f, JS('', 'void 0')); | 36 if (hasMethod(obj, f)) return bind(obj, f, JS('', 'void 0')); |
39 return JS('', '#[#]', obj, f); | 37 return JS('', '#[#]', obj, f); |
40 } | 38 } |
41 return noSuchMethod(obj, | 39 return noSuchMethod( |
42 new InvocationImpl(field, JS('', '[]'), isGetter: true)); | 40 obj, new InvocationImpl(field, JS('', '[]'), isGetter: true)); |
43 } | 41 } |
44 | 42 |
45 dput(obj, field, value) { | 43 dput(obj, field, value) { |
46 var f = _canonicalMember(obj, field); | 44 var f = _canonicalMember(obj, field); |
47 _trackCall(obj); | 45 _trackCall(obj); |
48 if (f != null) { | 46 if (f != null) { |
49 return JS('', '#[#] = #', obj, f, value); | 47 return JS('', '#[#] = #', obj, f, value); |
50 } | 48 } |
51 return noSuchMethod(obj, | 49 return noSuchMethod( |
52 new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); | 50 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); |
53 } | 51 } |
54 | 52 |
55 /// Check that a function of a given type can be applied to | 53 /// Check that a function of a given type can be applied to |
56 /// actuals. | 54 /// actuals. |
57 _checkApply(type, actuals) => JS('', '''(() => { | 55 _checkApply(type, actuals) => JS( |
| 56 '', |
| 57 '''(() => { |
58 if ($actuals.length < $type.args.length) return false; | 58 if ($actuals.length < $type.args.length) return false; |
59 let index = 0; | 59 let index = 0; |
60 for(let i = 0; i < $type.args.length; ++i) { | 60 for(let i = 0; i < $type.args.length; ++i) { |
61 if (!$instanceOfOrNull($actuals[i], $type.args[i])) return false; | 61 if (!$instanceOfOrNull($actuals[i], $type.args[i])) return false; |
62 ++index; | 62 ++index; |
63 } | 63 } |
64 if ($actuals.length == $type.args.length) return true; | 64 if ($actuals.length == $type.args.length) return true; |
65 let extras = $actuals.length - $type.args.length; | 65 let extras = $actuals.length - $type.args.length; |
66 if ($type.optionals.length > 0) { | 66 if ($type.optionals.length > 0) { |
67 if (extras > $type.optionals.length) return false; | 67 if (extras > $type.optionals.length) return false; |
(...skipping 22 matching lines...) Expand all Loading... |
90 })()'''); | 90 })()'''); |
91 | 91 |
92 Symbol _dartSymbol(name) => | 92 Symbol _dartSymbol(name) => |
93 JS('', '#(#.new(#.toString()))', const_, Symbol, name); | 93 JS('', '#(#.new(#.toString()))', const_, Symbol, name); |
94 | 94 |
95 /// Extracts the named argument array from a list of arguments, and returns it. | 95 /// Extracts the named argument array from a list of arguments, and returns it. |
96 // TODO(jmesserly): we need to handle named arguments better. | 96 // TODO(jmesserly): we need to handle named arguments better. |
97 extractNamedArgs(args) { | 97 extractNamedArgs(args) { |
98 if (JS('bool', '#.length > 0', args)) { | 98 if (JS('bool', '#.length > 0', args)) { |
99 var last = JS('', '#[#.length - 1]', args, args); | 99 var last = JS('', '#[#.length - 1]', args, args); |
100 if (JS('bool', '# != null && #.__proto__ === Object.prototype', | 100 if (JS( |
101 last, last)) { | 101 'bool', '# != null && #.__proto__ === Object.prototype', last, last)) { |
102 return JS('', '#.pop()', args); | 102 return JS('', '#.pop()', args); |
103 } | 103 } |
104 } | 104 } |
105 return null; | 105 return null; |
106 } | 106 } |
107 | 107 |
108 _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => { | 108 _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS( |
| 109 '', |
| 110 '''(() => { |
109 $_trackCall($obj); | 111 $_trackCall($obj); |
110 | 112 |
111 let originalTarget = obj === void 0 ? f : obj; | 113 let originalTarget = obj === void 0 ? f : obj; |
112 | 114 |
113 function callNSM() { | 115 function callNSM() { |
114 return $noSuchMethod(originalTarget, new $InvocationImpl( | 116 return $noSuchMethod(originalTarget, new $InvocationImpl( |
115 $name, $args, | 117 $name, $args, |
116 {namedArguments: $extractNamedArgs($args), isMethod: true})); | 118 {namedArguments: $extractNamedArgs($args), isMethod: true})); |
117 } | 119 } |
118 if (!($f instanceof Function)) { | 120 if (!($f instanceof Function)) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 } | 171 } |
170 return $f.apply($obj, $args); | 172 return $f.apply($obj, $args); |
171 } | 173 } |
172 | 174 |
173 // TODO(leafp): throw a type error (rather than NSM) | 175 // TODO(leafp): throw a type error (rather than NSM) |
174 // if the arity matches but the types are wrong. | 176 // if the arity matches but the types are wrong. |
175 // TODO(jmesserly): nSM should include type args? | 177 // TODO(jmesserly): nSM should include type args? |
176 return callNSM(); | 178 return callNSM(); |
177 })()'''); | 179 })()'''); |
178 | 180 |
179 dcall(f, @rest args) => _checkAndCall( | 181 dcall(f, @rest args) => |
180 f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); | 182 _checkAndCall(f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); |
181 | 183 |
182 dgcall(f, typeArgs, @rest args) => _checkAndCall( | 184 dgcall(f, typeArgs, @rest args) => _checkAndCall( |
183 f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); | 185 f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); |
184 | 186 |
| 187 /// Helper for REPL dynamic invocation variants that make a best effort to |
| 188 /// enable accessing private members across library boundaries. |
| 189 _dhelperRepl(object, field, callback) => JS( |
| 190 '', |
| 191 '''(() => { |
| 192 let rawField = $field; |
| 193 if (typeof(field) == 'symbol') { |
| 194 // test if the specified field exists in which case it is safe to use it. |
| 195 if ($field in $object) return $callback($field); |
| 196 |
| 197 // Symbol is from a different library. Make a best effort to |
| 198 $field = $field.toString(); |
| 199 $field = $field.substring('Symbol('.length, field.length - 1); |
| 200 |
| 201 } else if ($field.charAt(0) != '_') { |
| 202 // Not a private member so default call path is safe. |
| 203 return $callback($field); |
| 204 } |
| 205 |
| 206 // If the exact field name is present, invoke callback with it. |
| 207 if ($field in $object) return $callback($field); |
| 208 |
| 209 // TODO(jacobr): warn if there are multiple private members with the same |
| 210 // name which could happen if super classes in different libraries have |
| 211 // the same private member name. |
| 212 let proto = $object; |
| 213 while (proto !== null) { |
| 214 // Private field (indicated with "_"). |
| 215 let symbols = Object.getOwnPropertySymbols(proto); |
| 216 let target = 'Symbol(' + $field + ')'; |
| 217 |
| 218 for (let s = 0; s < symbols.length; s++) { |
| 219 let sym = symbols[s]; |
| 220 if (target == sym.toString()) return $callback(sym); |
| 221 } |
| 222 proto = proto.__proto__; |
| 223 } |
| 224 // We didn't find a plausible alternate private symbol so just fall back |
| 225 // to the regular field. |
| 226 return $callback(rawField); |
| 227 })()'''); |
| 228 |
| 229 dloadRepl(obj, field) => |
| 230 _dhelperRepl(obj, field, (resolvedField) => dload(obj, resolvedField)); |
| 231 |
| 232 dputRepl(obj, field, value) => _dhelperRepl( |
| 233 obj, field, (resolvedField) => dput(obj, resolvedField, value)); |
| 234 |
| 235 _callMethodRepl(obj, method, typeArgs, args) => _dhelperRepl(obj, method, |
| 236 (resolvedField) => _callMethod(obj, resolvedField, typeArgs, args, method)); |
| 237 |
| 238 dsendRepl(obj, method, @rest args) => _callMethodRepl(obj, method, null, args); |
| 239 |
| 240 dgsendRepl(obj, typeArgs, method, @rest args) => |
| 241 _callMethodRepl(obj, method, typeArgs, args); |
| 242 |
185 class _MethodStats { | 243 class _MethodStats { |
186 final String typeName; | 244 final String typeName; |
187 final String frame; | 245 final String frame; |
188 int count; | 246 int count; |
189 | 247 |
190 _MethodStats(this.typeName, this.frame) { | 248 _MethodStats(this.typeName, this.frame) { |
191 count = 0; | 249 count = 0; |
192 } | 250 } |
193 } | 251 } |
194 | 252 |
195 Map<String, _MethodStats> _callMethodStats = new Map(); | 253 Map<String, _MethodStats> _callMethodStats = new Map(); |
196 | 254 |
197 List<List<Object>> getDynamicStats() { | 255 List<List<Object>> getDynamicStats() { |
198 List<List<Object>> ret = []; | 256 List<List<Object>> ret = []; |
199 | 257 |
200 var keys = _callMethodStats.keys.toList(); | 258 var keys = _callMethodStats.keys.toList(); |
201 | 259 |
202 keys.sort((a, b) => _callMethodStats[b].count.compareTo( | 260 keys.sort( |
203 _callMethodStats[a].count)); | 261 (a, b) => _callMethodStats[b].count.compareTo(_callMethodStats[a].count)); |
204 for (var key in keys) { | 262 for (var key in keys) { |
205 var stats = _callMethodStats[key]; | 263 var stats = _callMethodStats[key]; |
206 ret.add([stats.typeName, stats.frame, stats.count]); | 264 ret.add([stats.typeName, stats.frame, stats.count]); |
207 } | 265 } |
208 | 266 |
209 return ret; | 267 return ret; |
210 } | 268 } |
211 | 269 |
212 clearDynamicStats() { | 270 clearDynamicStats() { |
213 _callMethodStats.clear(); | 271 _callMethodStats.clear(); |
(...skipping 10 matching lines...) Expand all Loading... |
224 var src = ''; | 282 var src = ''; |
225 for (int i = 2; i < stack.length; ++i) { | 283 for (int i = 2; i < stack.length; ++i) { |
226 var frame = stack[i]; | 284 var frame = stack[i]; |
227 if (!frame.contains('dart_sdk.js')) { | 285 if (!frame.contains('dart_sdk.js')) { |
228 src = frame; | 286 src = frame; |
229 break; | 287 break; |
230 } | 288 } |
231 } | 289 } |
232 | 290 |
233 var actualTypeName = typeName(actual); | 291 var actualTypeName = typeName(actual); |
234 _callMethodStats.putIfAbsent("$actualTypeName <$src>", | 292 _callMethodStats |
235 () => new _MethodStats(actualTypeName, src)).count++; | 293 .putIfAbsent( |
| 294 "$actualTypeName <$src>", () => new _MethodStats(actualTypeName, src)) |
| 295 .count++; |
236 } | 296 } |
237 | 297 |
238 /// Shared code for dsend, dindex, and dsetindex. | 298 /// Shared code for dsend, dindex, and dsetindex. |
239 _callMethod(obj, name, typeArgs, args, displayName) { | 299 _callMethod(obj, name, typeArgs, args, displayName) { |
240 var symbol = _canonicalMember(obj, name); | 300 var symbol = _canonicalMember(obj, name); |
241 if (symbol == null) { | 301 if (symbol == null) { |
242 return noSuchMethod(obj, | 302 return noSuchMethod( |
243 new InvocationImpl(displayName, args, isMethod: true)); | 303 obj, new InvocationImpl(displayName, args, isMethod: true)); |
244 } | 304 } |
245 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; | 305 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; |
246 var ftype = getMethodType(obj, symbol); | 306 var ftype = getMethodType(obj, symbol); |
247 return _checkAndCall(f, ftype, obj, typeArgs, args, displayName); | 307 return _checkAndCall(f, ftype, obj, typeArgs, args, displayName); |
248 } | 308 } |
249 | 309 |
250 dsend(obj, method, @rest args) => _callMethod(obj, method, null, args, method); | 310 dsend(obj, method, @rest args) => _callMethod(obj, method, null, args, method); |
251 | 311 |
252 dgsend(obj, typeArgs, method, @rest args) => | 312 dgsend(obj, typeArgs, method, @rest args) => |
253 _callMethod(obj, method, typeArgs, args, method); | 313 _callMethod(obj, method, typeArgs, args, method); |
254 | 314 |
255 dindex(obj, index) => _callMethod(obj, 'get', null, JS('', '[#]', index), '[]'); | 315 dindex(obj, index) => _callMethod(obj, 'get', null, JS('', '[#]', index), '[]'); |
256 | 316 |
257 dsetindex(obj, index, value) => | 317 dsetindex(obj, index, value) => |
258 _callMethod(obj, 'set', null, JS('', '[#, #]', index, value), '[]='); | 318 _callMethod(obj, 'set', null, JS('', '[#, #]', index, value), '[]='); |
259 | 319 |
260 /// TODO(leafp): This duplicates code in types.dart. | 320 /// TODO(leafp): This duplicates code in types.dart. |
261 /// I haven't found a way to factor it out that makes the | 321 /// I haven't found a way to factor it out that makes the |
262 /// code generator happy though. | 322 /// code generator happy though. |
263 _ignoreMemo(f) => JS('', '''(() => { | 323 _ignoreMemo(f) => JS( |
| 324 '', |
| 325 '''(() => { |
264 let memo = new Map(); | 326 let memo = new Map(); |
265 return (t1, t2) => { | 327 return (t1, t2) => { |
266 let map = memo.get(t1); | 328 let map = memo.get(t1); |
267 let result; | 329 let result; |
268 if (map) { | 330 if (map) { |
269 result = map.get(t2); | 331 result = map.get(t2); |
270 if (result !== void 0) return result; | 332 if (result !== void 0) return result; |
271 } else { | 333 } else { |
272 memo.set(t1, map = new Map()); | 334 memo.set(t1, map = new Map()); |
273 } | 335 } |
274 result = $f(t1, t2); | 336 result = $f(t1, t2); |
275 map.set(t2, result); | 337 map.set(t2, result); |
276 return result; | 338 return result; |
277 }; | 339 }; |
278 })()'''); | 340 })()'''); |
279 | 341 |
280 final _ignoreTypeFailure = JS('', '''(() => { | 342 final _ignoreTypeFailure = JS( |
| 343 '', |
| 344 '''(() => { |
281 return $_ignoreMemo((actual, type) => { | 345 return $_ignoreMemo((actual, type) => { |
282 // TODO(vsm): Remove this hack ... | 346 // TODO(vsm): Remove this hack ... |
283 // This is primarily due to the lack of generic methods, | 347 // This is primarily due to the lack of generic methods, |
284 // but we need to triage all the types. | 348 // but we need to triage all the types. |
285 if (!!$isSubtype(type, $Iterable) && !!$isSubtype(actual, $Iterable) || | 349 if (!!$isSubtype(type, $Iterable) && !!$isSubtype(actual, $Iterable) || |
286 !!$isSubtype(type, $Future) && !!$isSubtype(actual, $Future) || | 350 !!$isSubtype(type, $Future) && !!$isSubtype(actual, $Future) || |
287 !!$isSubtype(type, $Map) && !!$isSubtype(actual, $Map) || | 351 !!$isSubtype(type, $Map) && !!$isSubtype(actual, $Map) || |
288 $isFunctionType(type) && $isFunctionType(actual) || | 352 $isFunctionType(type) && $isFunctionType(actual) || |
289 !!$isSubtype(type, $Stream) && !!$isSubtype(actual, $Stream) || | 353 !!$isSubtype(type, $Stream) && !!$isSubtype(actual, $Stream) || |
290 !!$isSubtype(type, $StreamSubscription) && | 354 !!$isSubtype(type, $StreamSubscription) && |
291 !!$isSubtype(actual, $StreamSubscription)) { | 355 !!$isSubtype(actual, $StreamSubscription)) { |
292 console.warn('Ignoring cast fail from ' + $typeName(actual) + | 356 console.warn('Ignoring cast fail from ' + $typeName(actual) + |
293 ' to ' + $typeName(type)); | 357 ' to ' + $typeName(type)); |
294 return true; | 358 return true; |
295 } | 359 } |
296 return false; | 360 return false; |
297 }); | 361 }); |
298 })()'''); | 362 })()'''); |
299 | 363 |
300 /// Returns true if [obj] is an instance of [type] | 364 /// Returns true if [obj] is an instance of [type] |
301 /// Returns false if [obj] is not an instance of [type] in both spec | 365 /// Returns false if [obj] is not an instance of [type] in both spec |
302 /// and strong mode | 366 /// and strong mode |
303 /// Returns null if [obj] is not an instance of [type] in strong mode | 367 /// Returns null if [obj] is not an instance of [type] in strong mode |
304 /// but might be in spec mode | 368 /// but might be in spec mode |
305 bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS('', '''(() => { | 369 bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS( |
| 370 '', |
| 371 '''(() => { |
306 let actual = $getReifiedType($obj); | 372 let actual = $getReifiedType($obj); |
307 let result = $isSubtype(actual, $type); | 373 let result = $isSubtype(actual, $type); |
308 if (result || actual == $jsobject || | 374 if (result || actual == $jsobject || |
309 actual == $int && type == $double) return true; | 375 actual == $int && type == $double) return true; |
310 if (result === false) return false; | 376 if (result === false) return false; |
311 if ($ignoreFromWhiteList == void 0) return result; | 377 if ($ignoreFromWhiteList == void 0) return result; |
312 if ($_ignoreTypeFailure(actual, $type)) return true; | 378 if ($_ignoreTypeFailure(actual, $type)) return true; |
313 return result; | 379 return result; |
314 })()'''); | 380 })()'''); |
315 | 381 |
316 /// Returns true if [obj] is null or an instance of [type] | 382 /// Returns true if [obj] is null or an instance of [type] |
317 /// Returns false if [obj] is non-null and not an instance of [type] | 383 /// Returns false if [obj] is non-null and not an instance of [type] |
318 /// in strong mode | 384 /// in strong mode |
319 instanceOfOrNull(obj, type) => JS('', '''(() => { | 385 instanceOfOrNull(obj, type) => JS( |
| 386 '', |
| 387 '''(() => { |
320 // If strongInstanceOf returns null, convert to false here. | 388 // If strongInstanceOf returns null, convert to false here. |
321 if (($obj == null) || $strongInstanceOf($obj, $type, true)) return true; | 389 if (($obj == null) || $strongInstanceOf($obj, $type, true)) return true; |
322 return false; | 390 return false; |
323 })()'''); | 391 })()'''); |
324 | 392 |
325 @JSExportName('is') | 393 @JSExportName('is') |
326 instanceOf(obj, type) => JS('', '''(() => { | 394 instanceOf(obj, type) => JS( |
| 395 '', |
| 396 '''(() => { |
327 let result = $strongInstanceOf($obj, $type); | 397 let result = $strongInstanceOf($obj, $type); |
328 if (result !== null) return result; | 398 if (result !== null) return result; |
329 let actual = $getReifiedType($obj); | 399 let actual = $getReifiedType($obj); |
330 $throwStrongModeError('Strong mode is-check failure: ' + | 400 $throwStrongModeError('Strong mode is-check failure: ' + |
331 $typeName(actual) + ' does not soundly subtype ' + | 401 $typeName(actual) + ' does not soundly subtype ' + |
332 $typeName($type)); | 402 $typeName($type)); |
333 })()'''); | 403 })()'''); |
334 | 404 |
335 @JSExportName('as') | 405 @JSExportName('as') |
336 cast(obj, type) { | 406 cast(obj, type) { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 | 452 |
383 if (JS('bool', 'Math.floor(#) != #', obj, obj)) { | 453 if (JS('bool', 'Math.floor(#) != #', obj, obj)) { |
384 throwCastError(obj, getReifiedType(obj), JS('', '#', int)); | 454 throwCastError(obj, getReifiedType(obj), JS('', '#', int)); |
385 } | 455 } |
386 return obj; | 456 return obj; |
387 } | 457 } |
388 | 458 |
389 /// Adds type type test predicates to a constructor for a non-parameterized | 459 /// Adds type type test predicates to a constructor for a non-parameterized |
390 /// type. Non-parameterized types can use `instanceof` for subclass checks and | 460 /// type. Non-parameterized types can use `instanceof` for subclass checks and |
391 /// fall through to a helper for subtype tests. | 461 /// fall through to a helper for subtype tests. |
392 addSimpleTypeTests(ctor) => JS('', '''(() => { | 462 addSimpleTypeTests(ctor) => JS( |
| 463 '', |
| 464 '''(() => { |
393 $ctor.is = function is_C(object) { | 465 $ctor.is = function is_C(object) { |
394 // This is incorrect for classes [Null] and [Object], so we do not use | 466 // This is incorrect for classes [Null] and [Object], so we do not use |
395 // [addSimpleTypeTests] for these classes. | 467 // [addSimpleTypeTests] for these classes. |
396 if (object instanceof this) return true; | 468 if (object instanceof this) return true; |
397 return dart.is(object, this); | 469 return dart.is(object, this); |
398 }; | 470 }; |
399 $ctor.as = function as_C(object) { | 471 $ctor.as = function as_C(object) { |
400 if (object instanceof this) return object; | 472 if (object instanceof this) return object; |
401 return dart.as(object, this); | 473 return dart.as(object, this); |
402 }; | 474 }; |
403 $ctor._check = function check_C(object) { | 475 $ctor._check = function check_C(object) { |
404 if (object instanceof this) return object; | 476 if (object instanceof this) return object; |
405 return dart.check(object, this); | 477 return dart.check(object, this); |
406 }; | 478 }; |
407 })()'''); | 479 })()'''); |
408 | 480 |
409 /// Adds type type test predicates to a constructor. Used for parmeterized | 481 /// Adds type type test predicates to a constructor. Used for parmeterized |
410 /// types. We avoid `instanceof` for, e.g. `x is ListQueue` since there is | 482 /// types. We avoid `instanceof` for, e.g. `x is ListQueue` since there is |
411 /// no common class for `ListQueue<int>` and `ListQueue<String>`. | 483 /// no common class for `ListQueue<int>` and `ListQueue<String>`. |
412 addTypeTests(ctor) => JS('', '''(() => { | 484 addTypeTests(ctor) => JS( |
| 485 '', |
| 486 '''(() => { |
413 $ctor.as = function as_G(object) { | 487 $ctor.as = function as_G(object) { |
414 return dart.as(object, this); | 488 return dart.as(object, this); |
415 }; | 489 }; |
416 $ctor.is = function is_G(object) { | 490 $ctor.is = function is_G(object) { |
417 return dart.is(object, this); | 491 return dart.is(object, this); |
418 }; | 492 }; |
419 $ctor._check = function check_G(object) { | 493 $ctor._check = function check_G(object) { |
420 return dart.check(object, this); | 494 return dart.check(object, this); |
421 }; | 495 }; |
422 })()'''); | 496 })()'''); |
423 | 497 |
424 equals(x, y) => JS('', '''(() => { | 498 equals(x, y) => JS( |
| 499 '', |
| 500 '''(() => { |
425 if ($x == null || $y == null) return $x == $y; | 501 if ($x == null || $y == null) return $x == $y; |
426 let eq = $x['==']; | 502 let eq = $x['==']; |
427 return eq ? eq.call($x, $y) : $x === $y; | 503 return eq ? eq.call($x, $y) : $x === $y; |
428 })()'''); | 504 })()'''); |
429 | 505 |
430 /// Checks that `x` is not null or undefined. */ | 506 /// Checks that `x` is not null or undefined. */ |
431 notNull(x) { | 507 notNull(x) { |
432 if (x == null) throwNullValueError(); | 508 if (x == null) throwNullValueError(); |
433 return x; | 509 return x; |
434 } | 510 } |
435 | 511 |
436 /// | 512 /// |
437 /// Creates a dart:collection LinkedHashMap. | 513 /// Creates a dart:collection LinkedHashMap. |
438 /// | 514 /// |
439 /// For a map with string keys an object literal can be used, for example | 515 /// For a map with string keys an object literal can be used, for example |
440 /// `map({'hi': 1, 'there': 2})`. | 516 /// `map({'hi': 1, 'there': 2})`. |
441 /// | 517 /// |
442 /// Otherwise an array should be used, for example `map([1, 2, 3, 4])` will | 518 /// Otherwise an array should be used, for example `map([1, 2, 3, 4])` will |
443 /// create a map with keys [1, 3] and values [2, 4]. Each key-value pair | 519 /// create a map with keys [1, 3] and values [2, 4]. Each key-value pair |
444 /// should be adjacent entries in the array. | 520 /// should be adjacent entries in the array. |
445 /// | 521 /// |
446 /// For a map with no keys the function can be called with no arguments, for | 522 /// For a map with no keys the function can be called with no arguments, for |
447 /// example `map()`. | 523 /// example `map()`. |
448 /// | 524 /// |
449 // TODO(jmesserly): this could be faster | 525 // TODO(jmesserly): this could be faster |
450 // TODO(jmesserly): we can use default values `= dynamic` once #417 is fixed. | 526 // TODO(jmesserly): we can use default values `= dynamic` once #417 is fixed. |
451 // TODO(jmesserly): move this to classes for consistentcy with list literals? | 527 // TODO(jmesserly): move this to classes for consistentcy with list literals? |
452 map(values, [K, V]) => JS('', '''(() => { | 528 map(values, [K, V]) => JS( |
| 529 '', |
| 530 '''(() => { |
453 if ($K == null) $K = $dynamic; | 531 if ($K == null) $K = $dynamic; |
454 if ($V == null) $V = $dynamic; | 532 if ($V == null) $V = $dynamic; |
455 let map = ${getGenericClass(LinkedHashMap)}($K, $V).new(); | 533 let map = ${getGenericClass(LinkedHashMap)}($K, $V).new(); |
456 if (Array.isArray($values)) { | 534 if (Array.isArray($values)) { |
457 for (let i = 0, end = $values.length - 1; i < end; i += 2) { | 535 for (let i = 0, end = $values.length - 1; i < end; i += 2) { |
458 let key = $values[i]; | 536 let key = $values[i]; |
459 let value = $values[i + 1]; | 537 let value = $values[i + 1]; |
460 map.set(key, value); | 538 map.set(key, value); |
461 } | 539 } |
462 } else if (typeof $values === 'object') { | 540 } else if (typeof $values === 'object') { |
463 for (let key of $getOwnPropertyNames($values)) { | 541 for (let key of $getOwnPropertyNames($values)) { |
464 map.set(key, $values[key]); | 542 map.set(key, $values[key]); |
465 } | 543 } |
466 } | 544 } |
467 return map; | 545 return map; |
468 })()'''); | 546 })()'''); |
469 | 547 |
470 @JSExportName('assert') | 548 @JSExportName('assert') |
471 assert_(condition) => JS('', '''(() => { | 549 assert_(condition) => JS( |
| 550 '', |
| 551 '''(() => { |
472 if (!$condition) $throwAssertionError(); | 552 if (!$condition) $throwAssertionError(); |
473 })()'''); | 553 })()'''); |
474 | 554 |
475 final _stack = JS('', 'new WeakMap()'); | 555 final _stack = JS('', 'new WeakMap()'); |
476 @JSExportName('throw') | 556 @JSExportName('throw') |
477 throw_(obj) => JS('', '''(() => { | 557 throw_(obj) => JS( |
| 558 '', |
| 559 '''(() => { |
478 if ($obj != null && (typeof $obj == 'object' || typeof $obj == 'function')) { | 560 if ($obj != null && (typeof $obj == 'object' || typeof $obj == 'function')) { |
479 // TODO(jmesserly): couldn't we store the most recent stack in a single | 561 // TODO(jmesserly): couldn't we store the most recent stack in a single |
480 // variable? There should only be one active stack trace. That would | 562 // variable? There should only be one active stack trace. That would |
481 // allow it to work for things like strings and numbers. | 563 // allow it to work for things like strings and numbers. |
482 $_stack.set($obj, new Error()); | 564 $_stack.set($obj, new Error()); |
483 } | 565 } |
484 throw $obj; | 566 throw $obj; |
485 })()'''); | 567 })()'''); |
486 | 568 |
487 getError(exception) => JS('', '''(() => { | 569 getError(exception) => JS( |
| 570 '', |
| 571 '''(() => { |
488 var stack = $_stack.get($exception); | 572 var stack = $_stack.get($exception); |
489 return stack !== void 0 ? stack : $exception; | 573 return stack !== void 0 ? stack : $exception; |
490 })()'''); | 574 })()'''); |
491 | 575 |
492 // This is a utility function: it is only intended to be called from dev | 576 // This is a utility function: it is only intended to be called from dev |
493 // tools. | 577 // tools. |
494 stackPrint(exception) => JS('', '''(() => { | 578 stackPrint(exception) => JS( |
| 579 '', |
| 580 '''(() => { |
495 var error = $getError($exception); | 581 var error = $getError($exception); |
496 console.log(error.stack ? error.stack : 'No stack trace for: ' + error); | 582 console.log(error.stack ? error.stack : 'No stack trace for: ' + error); |
497 })()'''); | 583 })()'''); |
498 | 584 |
499 stackTrace(exception) => JS('', '''(() => { | 585 stackTrace(exception) => JS( |
| 586 '', |
| 587 '''(() => { |
500 var error = $getError($exception); | 588 var error = $getError($exception); |
501 return $getTraceFromException(error); | 589 return $getTraceFromException(error); |
502 })()'''); | 590 })()'''); |
503 | 591 |
504 /// | 592 /// |
505 /// Implements a sequence of .? operations. | 593 /// Implements a sequence of .? operations. |
506 /// | 594 /// |
507 /// Will call each successive callback, unless one returns null, which stops | 595 /// Will call each successive callback, unless one returns null, which stops |
508 /// the sequence. | 596 /// the sequence. |
509 /// | 597 /// |
510 nullSafe(obj, @rest callbacks) => JS('', '''(() => { | 598 nullSafe(obj, @rest callbacks) => JS( |
| 599 '', |
| 600 '''(() => { |
511 if ($obj == null) return $obj; | 601 if ($obj == null) return $obj; |
512 for (let callback of $callbacks) { | 602 for (let callback of $callbacks) { |
513 $obj = callback($obj); | 603 $obj = callback($obj); |
514 if ($obj == null) break; | 604 if ($obj == null) break; |
515 } | 605 } |
516 return $obj; | 606 return $obj; |
517 })()'''); | 607 })()'''); |
518 | 608 |
519 final _value = JS('', 'Symbol("_value")'); | 609 final _value = JS('', 'Symbol("_value")'); |
| 610 |
520 /// | 611 /// |
521 /// Looks up a sequence of [keys] in [map], recursively, and | 612 /// Looks up a sequence of [keys] in [map], recursively, and |
522 /// returns the result. If the value is not found, [valueFn] will be called to | 613 /// returns the result. If the value is not found, [valueFn] will be called to |
523 /// add it. For example: | 614 /// add it. For example: |
524 /// | 615 /// |
525 /// let map = new Map(); | 616 /// let map = new Map(); |
526 /// putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world'); | 617 /// putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world'); |
527 /// | 618 /// |
528 /// ... will create a Map with a structure like: | 619 /// ... will create a Map with a structure like: |
529 /// | 620 /// |
530 /// { 1: { 2: { 'hi ': { 'there ': 'world' } } } } | 621 /// { 1: { 2: { 'hi ': { 'there ': 'world' } } } } |
531 /// | 622 /// |
532 multiKeyPutIfAbsent(map, keys, valueFn) => JS('', '''(() => { | 623 multiKeyPutIfAbsent(map, keys, valueFn) => JS( |
| 624 '', |
| 625 '''(() => { |
533 for (let k of $keys) { | 626 for (let k of $keys) { |
534 let value = $map.get(k); | 627 let value = $map.get(k); |
535 if (!value) { | 628 if (!value) { |
536 // TODO(jmesserly): most of these maps are very small (e.g. 1 item), | 629 // TODO(jmesserly): most of these maps are very small (e.g. 1 item), |
537 // so it may be worth optimizing for that. | 630 // so it may be worth optimizing for that. |
538 $map.set(k, value = new Map()); | 631 $map.set(k, value = new Map()); |
539 } | 632 } |
540 $map = value; | 633 $map = value; |
541 } | 634 } |
542 if ($map.has($_value)) return $map.get($_value); | 635 if ($map.has($_value)) return $map.get($_value); |
(...skipping 11 matching lines...) Expand all Loading... |
554 final constants = JS('', 'new Map()'); | 647 final constants = JS('', 'new Map()'); |
555 | 648 |
556 /// | 649 /// |
557 /// Canonicalize a constant object. | 650 /// Canonicalize a constant object. |
558 /// | 651 /// |
559 /// Preconditions: | 652 /// Preconditions: |
560 /// - `obj` is an objects or array, not a primitive. | 653 /// - `obj` is an objects or array, not a primitive. |
561 /// - nested values of the object are themselves already canonicalized. | 654 /// - nested values of the object are themselves already canonicalized. |
562 /// | 655 /// |
563 @JSExportName('const') | 656 @JSExportName('const') |
564 const_(obj) => JS('', '''(() => { | 657 const_(obj) => JS( |
| 658 '', |
| 659 '''(() => { |
565 // TODO(leafp): This table gets quite large in apps. | 660 // TODO(leafp): This table gets quite large in apps. |
566 // Keeping the paths is probably expensive. It would probably | 661 // Keeping the paths is probably expensive. It would probably |
567 // be more space efficient to just use a direct hash table with | 662 // be more space efficient to just use a direct hash table with |
568 // an appropriately defined structural equality function. | 663 // an appropriately defined structural equality function. |
569 function lookupNonTerminal(map, key) { | 664 function lookupNonTerminal(map, key) { |
570 let result = map.get(key); | 665 let result = map.get(key); |
571 if (result !== void 0) return result; | 666 if (result !== void 0) return result; |
572 map.set(key, result = new Map()); | 667 map.set(key, result = new Map()); |
573 return result; | 668 return result; |
574 }; | 669 }; |
(...skipping 30 matching lines...) Expand all Loading... |
605 /// This maps the number of elements in the list (n) | 700 /// This maps the number of elements in the list (n) |
606 /// to a path of length n of maps indexed by the value | 701 /// to a path of length n of maps indexed by the value |
607 /// of the field. The final map is indexed by the element | 702 /// of the field. The final map is indexed by the element |
608 /// type and contains the canonical version of the list. | 703 /// type and contains the canonical version of the list. |
609 final constantLists = JS('', 'new Map()'); | 704 final constantLists = JS('', 'new Map()'); |
610 | 705 |
611 /// | 706 /// |
612 /// Canonicalize a constant list | 707 /// Canonicalize a constant list |
613 /// | 708 /// |
614 @JSExportName('constList') | 709 @JSExportName('constList') |
615 constList_(elements, elementType) => JS('', '''(() => { | 710 constList_(elements, elementType) => JS( |
| 711 '', |
| 712 '''(() => { |
616 function lookupNonTerminal(map, key) { | 713 function lookupNonTerminal(map, key) { |
617 let result = map.get(key); | 714 let result = map.get(key); |
618 if (result !== void 0) return result; | 715 if (result !== void 0) return result; |
619 map.set(key, result = new Map()); | 716 map.set(key, result = new Map()); |
620 return result; | 717 return result; |
621 }; | 718 }; |
622 let count = $elements.length; | 719 let count = $elements.length; |
623 let map = lookupNonTerminal($constantLists, count); | 720 let map = lookupNonTerminal($constantLists, count); |
624 for (let i = 0; i < count; i++) { | 721 for (let i = 0; i < count; i++) { |
625 map = lookupNonTerminal(map, elements[i]); | 722 map = lookupNonTerminal(map, elements[i]); |
626 } | 723 } |
627 let value = map.get($elementType); | 724 let value = map.get($elementType); |
628 if (value) return value; | 725 if (value) return value; |
629 value = $list($elements, $elementType); | 726 value = $list($elements, $elementType); |
630 map.set($elementType, value); | 727 map.set($elementType, value); |
631 return value; | 728 return value; |
632 })()'''); | 729 })()'''); |
633 | 730 |
634 // The following are helpers for Object methods when the receiver | 731 // The following are helpers for Object methods when the receiver |
635 // may be null or primitive. These should only be generated by | 732 // may be null or primitive. These should only be generated by |
636 // the compiler. | 733 // the compiler. |
637 hashCode(obj) { | 734 hashCode(obj) { |
638 if (obj == null) return 0; | 735 if (obj == null) return 0; |
639 | 736 |
640 switch (JS('String', 'typeof #', obj)) { | 737 switch (JS('String', 'typeof #', obj)) { |
641 case "number": | 738 case "number": |
642 return JS('','# & 0x1FFFFFFF', obj); | 739 return JS('', '# & 0x1FFFFFFF', obj); |
643 case "boolean": | 740 case "boolean": |
644 // From JSBool.hashCode, see comment there. | 741 // From JSBool.hashCode, see comment there. |
645 return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); | 742 return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); |
646 case "function": | 743 case "function": |
647 // TODO(jmesserly): this doesn't work for method tear-offs. | 744 // TODO(jmesserly): this doesn't work for method tear-offs. |
648 return Primitives.objectHashCode(obj); | 745 return Primitives.objectHashCode(obj); |
649 } | 746 } |
650 | 747 |
651 var extension = getExtensionType(obj); | 748 var extension = getExtensionType(obj); |
652 if (extension != null) { | 749 if (extension != null) { |
653 return JS('', '#[dartx.hashCode]', obj); | 750 return JS('', '#[dartx.hashCode]', obj); |
654 } | 751 } |
655 return JS('', '#.hashCode', obj); | 752 return JS('', '#.hashCode', obj); |
656 } | 753 } |
657 | 754 |
658 @JSExportName('toString') | 755 @JSExportName('toString') |
659 String _toString(obj) { | 756 String _toString(obj) { |
660 if (obj == null) return "null"; | 757 if (obj == null) return "null"; |
661 | 758 |
662 var extension = getExtensionType(obj); | 759 var extension = getExtensionType(obj); |
663 if (extension != null) { | 760 if (extension != null) { |
664 return JS('String', '#[dartx.toString]()', obj); | 761 return JS('String', '#[dartx.toString]()', obj); |
665 } | 762 } |
666 if (JS('bool', 'typeof # == "function"', obj)) { | 763 if (JS('bool', 'typeof # == "function"', obj)) { |
667 return JS('String', r'"Closure: " + # + " from: " + #', | 764 return JS( |
668 getReifiedType(obj), obj); | 765 'String', r'"Closure: " + # + " from: " + #', getReifiedType(obj), obj); |
669 } | 766 } |
670 // TODO(jmesserly): restore this faster path once ES Symbol is treated as | 767 // TODO(jmesserly): restore this faster path once ES Symbol is treated as |
671 // an extension type (and thus hits the above code path). | 768 // an extension type (and thus hits the above code path). |
672 // See https://github.com/dart-lang/dev_compiler/issues/578. | 769 // See https://github.com/dart-lang/dev_compiler/issues/578. |
673 // return JS('', '"" + #', obj); | 770 // return JS('', '"" + #', obj); |
674 return JS('String', '#.toString()', obj); | 771 return JS('String', '#.toString()', obj); |
675 } | 772 } |
676 | 773 |
677 // TODO(jmesserly): is the argument type verified statically? | 774 // TODO(jmesserly): is the argument type verified statically? |
678 noSuchMethod(obj, Invocation invocation) { | 775 noSuchMethod(obj, Invocation invocation) { |
679 if (obj == null || JS('bool', 'typeof # == "function"', obj)) { | 776 if (obj == null || JS('bool', 'typeof # == "function"', obj)) { |
680 throw new NoSuchMethodError( | 777 throw new NoSuchMethodError(obj, invocation.memberName, |
681 obj, | 778 invocation.positionalArguments, invocation.namedArguments); |
682 invocation.memberName, | |
683 invocation.positionalArguments, | |
684 invocation.namedArguments); | |
685 } | 779 } |
686 // Delegate to the (possibly user-defined) method on the object. | 780 // Delegate to the (possibly user-defined) method on the object. |
687 var extension = getExtensionType(obj); | 781 var extension = getExtensionType(obj); |
688 if (extension != null) { | 782 if (extension != null) { |
689 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); | 783 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); |
690 } | 784 } |
691 return JS('', '#.noSuchMethod(#)', obj, invocation); | 785 return JS('', '#.noSuchMethod(#)', obj, invocation); |
692 } | 786 } |
693 | 787 |
694 constFn(x) => JS('', '() => x'); | 788 constFn(x) => JS('', '() => x'); |
(...skipping 10 matching lines...) Expand all Loading... |
705 } | 799 } |
706 if (JS('bool', 'typeof # == "function"', obj)) { | 800 if (JS('bool', 'typeof # == "function"', obj)) { |
707 return wrapType(getReifiedType(obj)); | 801 return wrapType(getReifiedType(obj)); |
708 } | 802 } |
709 return JS('', '#.runtimeType', obj); | 803 return JS('', '#.runtimeType', obj); |
710 } | 804 } |
711 | 805 |
712 /// Implements Dart's interpolated strings as ES2015 tagged template literals. | 806 /// Implements Dart's interpolated strings as ES2015 tagged template literals. |
713 /// | 807 /// |
714 /// For example: dart.str`hello ${name}` | 808 /// For example: dart.str`hello ${name}` |
715 String str(strings, @rest values) => JS('', '''(() => { | 809 String str(strings, @rest values) => JS( |
| 810 '', |
| 811 '''(() => { |
716 let s = $strings[0]; | 812 let s = $strings[0]; |
717 for (let i = 0, len = $values.length; i < len; ) { | 813 for (let i = 0, len = $values.length; i < len; ) { |
718 s += $notNull($_toString($values[i])) + $strings[++i]; | 814 s += $notNull($_toString($values[i])) + $strings[++i]; |
719 } | 815 } |
720 return s; | 816 return s; |
721 })()'''); | 817 })()'''); |
722 | 818 |
723 | 819 final JsIterator = JS( |
724 final JsIterator = JS('', ''' | 820 '', |
| 821 ''' |
725 class JsIterator { | 822 class JsIterator { |
726 constructor(dartIterator) { | 823 constructor(dartIterator) { |
727 this.dartIterator = dartIterator; | 824 this.dartIterator = dartIterator; |
728 } | 825 } |
729 next() { | 826 next() { |
730 let i = this.dartIterator; | 827 let i = this.dartIterator; |
731 let done = !i.moveNext(); | 828 let done = !i.moveNext(); |
732 return { done: done, value: done ? void 0 : i.current }; | 829 return { done: done, value: done ? void 0 : i.current }; |
733 } | 830 } |
734 } | 831 } |
735 '''); | 832 '''); |
736 | 833 |
737 _canonicalMember(obj, name) { | 834 _canonicalMember(obj, name) { |
738 // Private names are symbols and are already canonical. | 835 // Private names are symbols and are already canonical. |
739 if (JS('bool', 'typeof # === "symbol"', name)) return name; | 836 if (JS('bool', 'typeof # === "symbol"', name)) return name; |
740 | 837 |
741 if (obj != null && getExtensionType(obj) != null) { | 838 if (obj != null && getExtensionType(obj) != null) { |
742 return JS('', 'dartx.#', name); | 839 return JS('', 'dartx.#', name); |
743 } | 840 } |
744 | 841 |
745 // Check for certain names that we can't use in JS | 842 // Check for certain names that we can't use in JS |
746 if (name == 'constructor' || name == 'prototype') { | 843 if (name == 'constructor' || name == 'prototype') { |
747 name = '+' + name; | 844 name = '+' + name; |
748 } | 845 } |
749 return name; | 846 return name; |
750 } | 847 } |
OLD | NEW |