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 List<Type> typeArguments; |
13 final bool isMethod; | 14 final bool isMethod; |
14 final bool isGetter; | 15 final bool isGetter; |
15 final bool isSetter; | 16 final bool isSetter; |
16 | 17 |
17 InvocationImpl(memberName, this.positionalArguments, | 18 InvocationImpl(memberName, this.positionalArguments, |
18 {namedArguments, | 19 {namedArguments, |
| 20 List typeArguments, |
19 this.isMethod: false, | 21 this.isMethod: false, |
20 this.isGetter: false, | 22 this.isGetter: false, |
21 this.isSetter: false}) | 23 this.isSetter: false}) |
22 : memberName = _dartSymbol(memberName), | 24 : memberName = |
23 namedArguments = _namedArgsToSymbols(namedArguments); | 25 isSetter ? _setterSymbol(memberName) : _dartSymbol(memberName), |
| 26 namedArguments = _namedArgsToSymbols(namedArguments), |
| 27 typeArguments = typeArguments == null |
| 28 ? const [] |
| 29 : typeArguments.map(wrapType).toList(); |
24 | 30 |
25 static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { | 31 static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { |
26 if (namedArgs == null) return {}; | 32 if (namedArgs == null) return {}; |
27 return new Map.fromIterable(getOwnPropertyNames(namedArgs), | 33 return new Map.fromIterable(getOwnPropertyNames(namedArgs), |
28 key: _dartSymbol, value: (k) => JS('', '#[#]', namedArgs, k)); | 34 key: _dartSymbol, value: (k) => JS('', '#[#]', namedArgs, k)); |
29 } | 35 } |
30 } | 36 } |
31 | 37 |
32 /// Given an object and a method name, tear off the method. | 38 /// Given an object and a method name, tear off the method. |
33 /// Sets the runtime type of the torn off method appropriately, | 39 /// Sets the runtime type of the torn off method appropriately, |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 if (isJsInterop(obj)) { | 173 if (isJsInterop(obj)) { |
168 return JS('', '#[#] = #', obj, f, value); | 174 return JS('', '#[#] = #', obj, f, value); |
169 } | 175 } |
170 } | 176 } |
171 return noSuchMethod( | 177 return noSuchMethod( |
172 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); | 178 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); |
173 } | 179 } |
174 | 180 |
175 /// Check that a function of a given type can be applied to | 181 /// Check that a function of a given type can be applied to |
176 /// actuals. | 182 /// actuals. |
177 _checkApply(type, actuals) => JS( | 183 _checkApply(type, actuals) => JS('', '''(() => { |
178 '', | |
179 '''(() => { | |
180 // TODO(vsm): Remove when we no longer need mirrors metadata. | 184 // TODO(vsm): Remove when we no longer need mirrors metadata. |
181 // An array is used to encode annotations attached to the type. | 185 // An array is used to encode annotations attached to the type. |
182 if ($type instanceof Array) { | 186 if ($type instanceof Array) { |
183 $type = type[0]; | 187 $type = type[0]; |
184 } | 188 } |
185 if ($actuals.length < $type.args.length) return false; | 189 if ($actuals.length < $type.args.length) return false; |
186 let index = 0; | 190 let index = 0; |
187 for(let i = 0; i < $type.args.length; ++i) { | 191 for(let i = 0; i < $type.args.length; ++i) { |
188 $check($actuals[i], $type.args[i]); | 192 $check($actuals[i], $type.args[i]); |
189 ++index; | 193 ++index; |
(...skipping 19 matching lines...) Expand all Loading... |
209 if (names.length == 0) return false; | 213 if (names.length == 0) return false; |
210 for (var name of names) { | 214 for (var name of names) { |
211 if (!($hasOwnProperty.call($type.named, name))) { | 215 if (!($hasOwnProperty.call($type.named, name))) { |
212 return false; | 216 return false; |
213 } | 217 } |
214 $check(opts[name], $type.named[name]); | 218 $check(opts[name], $type.named[name]); |
215 } | 219 } |
216 return true; | 220 return true; |
217 })()'''); | 221 })()'''); |
218 | 222 |
219 _toSymbolName(symbol) => JS( | 223 _toSymbolName(symbol) => JS('', '''(() => { |
220 '', | |
221 '''(() => { | |
222 let str = $symbol.toString(); | 224 let str = $symbol.toString(); |
223 // Strip leading 'Symbol(' and trailing ')' | 225 // Strip leading 'Symbol(' and trailing ')' |
224 return str.substring(7, str.length-1); | 226 return str.substring(7, str.length-1); |
225 })()'''); | 227 })()'''); |
226 | 228 |
227 _toDisplayName(name) => JS( | 229 _toDisplayName(name) => JS('', '''(() => { |
228 '', | |
229 '''(() => { | |
230 // Names starting with _ are escaped names used to disambiguate Dart and | 230 // Names starting with _ are escaped names used to disambiguate Dart and |
231 // JS names. | 231 // JS names. |
232 if ($name[0] === '_') { | 232 if ($name[0] === '_') { |
233 // Inverse of | 233 // Inverse of |
234 switch($name) { | 234 switch($name) { |
235 case '_get': | 235 case '_get': |
236 return '[]'; | 236 return '[]'; |
237 case '_set': | 237 case '_set': |
238 return '[]='; | 238 return '[]='; |
239 case '_negate': | 239 case '_negate': |
240 return 'unary-'; | 240 return 'unary-'; |
241 case '_constructor': | 241 case '_constructor': |
242 case '_prototype': | 242 case '_prototype': |
243 return $name.substring(1); | 243 return $name.substring(1); |
244 } | 244 } |
245 } | 245 } |
246 return $name; | 246 return $name; |
247 })()'''); | 247 })()'''); |
248 | 248 |
249 Symbol _dartSymbol(name) { | 249 Symbol _dartSymbol(name) { |
250 return (JS('bool', 'typeof # === "symbol"', name)) | 250 return (JS('bool', 'typeof # === "symbol"', name)) |
251 ? JS('Symbol', '#(new #.new(#, #))', const_, _internal.PrivateSymbol, | 251 ? JS('Symbol', '#(new #.new(#, #))', const_, _internal.PrivateSymbol, |
252 _toSymbolName(name), name) | 252 _toSymbolName(name), name) |
253 : JS('Symbol', '#(#.new(#))', const_, Symbol, _toDisplayName(name)); | 253 : JS('Symbol', '#(#.new(#))', const_, Symbol, _toDisplayName(name)); |
254 } | 254 } |
255 | 255 |
| 256 Symbol _setterSymbol(name) { |
| 257 return (JS('bool', 'typeof # === "symbol"', name)) |
| 258 ? JS('Symbol', '#(new #.new(# + "=", #))', const_, |
| 259 _internal.PrivateSymbol, _toSymbolName(name), name) |
| 260 : JS('Symbol', '#(#.new(# + "="))', const_, Symbol, _toDisplayName(name)); |
| 261 } |
| 262 |
256 /// Extracts the named argument array from a list of arguments, and returns it. | 263 /// Extracts the named argument array from a list of arguments, and returns it. |
257 // TODO(jmesserly): we need to handle named arguments better. | 264 // TODO(jmesserly): we need to handle named arguments better. |
258 extractNamedArgs(args) { | 265 extractNamedArgs(args) { |
259 if (JS('bool', '#.length > 0', args)) { | 266 if (JS('bool', '#.length > 0', args)) { |
260 var last = JS('', '#[#.length - 1]', args, args); | 267 var last = JS('', '#[#.length - 1]', args, args); |
261 if (JS( | 268 if (JS( |
262 'bool', '# != null && #.__proto__ === Object.prototype', last, last)) { | 269 'bool', '# != null && #.__proto__ === Object.prototype', last, last)) { |
263 return JS('', '#.pop()', args); | 270 return JS('', '#.pop()', args); |
264 } | 271 } |
265 } | 272 } |
266 return null; | 273 return null; |
267 } | 274 } |
268 | 275 |
269 _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS( | 276 _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => { |
270 '', | |
271 '''(() => { | |
272 $_trackCall($obj); | 277 $_trackCall($obj); |
273 | 278 |
274 let originalTarget = obj === void 0 ? f : obj; | 279 let originalTarget = obj === void 0 ? f : obj; |
275 | 280 |
276 function callNSM() { | 281 function callNSM() { |
277 return $noSuchMethod(originalTarget, new $InvocationImpl.new( | 282 return $noSuchMethod(originalTarget, new $InvocationImpl.new( |
278 $name, $args, | 283 $name, $args, { |
279 {namedArguments: $extractNamedArgs($args), isMethod: true})); | 284 namedArguments: $extractNamedArgs($args), |
| 285 typeArguments: $typeArgs, |
| 286 isMethod: true |
| 287 })); |
280 } | 288 } |
281 if (!($f instanceof Function)) { | 289 if (!($f instanceof Function)) { |
282 // We're not a function (and hence not a method either) | 290 // We're not a function (and hence not a method either) |
283 // Grab the `call` method if it's not a function. | 291 // Grab the `call` method if it's not a function. |
284 if ($f != null) { | 292 if ($f != null) { |
285 $ftype = $getMethodType($getType($f), 'call'); | 293 $ftype = $getMethodType($getType($f), 'call'); |
286 $f = f.call ? $bind($f, 'call') : void 0; | 294 $f = f.call ? $bind($f, 'call') : void 0; |
287 } | 295 } |
288 if (!($f instanceof Function)) { | 296 if (!($f instanceof Function)) { |
289 return callNSM(); | 297 return callNSM(); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 })()'''); | 350 })()'''); |
343 | 351 |
344 dcall(f, @rest args) => | 352 dcall(f, @rest args) => |
345 _checkAndCall(f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); | 353 _checkAndCall(f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); |
346 | 354 |
347 dgcall(f, typeArgs, @rest args) => _checkAndCall( | 355 dgcall(f, typeArgs, @rest args) => _checkAndCall( |
348 f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); | 356 f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); |
349 | 357 |
350 /// Helper for REPL dynamic invocation variants that make a best effort to | 358 /// Helper for REPL dynamic invocation variants that make a best effort to |
351 /// enable accessing private members across library boundaries. | 359 /// enable accessing private members across library boundaries. |
352 _dhelperRepl(object, field, callback) => JS( | 360 _dhelperRepl(object, field, callback) => JS('', '''(() => { |
353 '', | |
354 '''(() => { | |
355 let rawField = $field; | 361 let rawField = $field; |
356 if (typeof(field) == 'symbol') { | 362 if (typeof(field) == 'symbol') { |
357 // test if the specified field exists in which case it is safe to use it. | 363 // test if the specified field exists in which case it is safe to use it. |
358 if ($field in $object) return $callback($field); | 364 if ($field in $object) return $callback($field); |
359 | 365 |
360 // Symbol is from a different library. Make a best effort to | 366 // Symbol is from a different library. Make a best effort to |
361 $field = $field.toString(); | 367 $field = $field.toString(); |
362 $field = $field.substring('Symbol('.length, field.length - 1); | 368 $field = $field.substring('Symbol('.length, field.length - 1); |
363 | 369 |
364 } else if ($field.charAt(0) != '_') { | 370 } else if ($field.charAt(0) != '_') { |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 | 430 |
425 dindex(obj, index) => | 431 dindex(obj, index) => |
426 _callMethod(obj, '_get', null, JS('', '[#]', index), '[]'); | 432 _callMethod(obj, '_get', null, JS('', '[#]', index), '[]'); |
427 | 433 |
428 dsetindex(obj, index, value) => | 434 dsetindex(obj, index, value) => |
429 _callMethod(obj, '_set', null, JS('', '[#, #]', index, value), '[]='); | 435 _callMethod(obj, '_set', null, JS('', '[#, #]', index, value), '[]='); |
430 | 436 |
431 /// TODO(leafp): This duplicates code in types.dart. | 437 /// TODO(leafp): This duplicates code in types.dart. |
432 /// I haven't found a way to factor it out that makes the | 438 /// I haven't found a way to factor it out that makes the |
433 /// code generator happy though. | 439 /// code generator happy though. |
434 _ignoreMemo(f) => JS( | 440 _ignoreMemo(f) => JS('', '''(() => { |
435 '', | |
436 '''(() => { | |
437 let memo = new Map(); | 441 let memo = new Map(); |
438 return (t1, t2) => { | 442 return (t1, t2) => { |
439 let map = memo.get(t1); | 443 let map = memo.get(t1); |
440 let result; | 444 let result; |
441 if (map) { | 445 if (map) { |
442 result = map.get(t2); | 446 result = map.get(t2); |
443 if (result !== void 0) return result; | 447 if (result !== void 0) return result; |
444 } else { | 448 } else { |
445 memo.set(t1, map = new Map()); | 449 memo.set(t1, map = new Map()); |
446 } | 450 } |
447 result = $f(t1, t2); | 451 result = $f(t1, t2); |
448 map.set(t2, result); | 452 map.set(t2, result); |
449 return result; | 453 return result; |
450 }; | 454 }; |
451 })()'''); | 455 })()'''); |
452 | 456 |
453 final _ignoreTypeFailure = JS( | 457 final _ignoreTypeFailure = JS('', '''(() => { |
454 '', | |
455 '''(() => { | |
456 return $_ignoreMemo((actual, type) => { | 458 return $_ignoreMemo((actual, type) => { |
457 // TODO(vsm): Remove this hack ... | 459 // TODO(vsm): Remove this hack ... |
458 // This is primarily due to the lack of generic methods, | 460 // This is primarily due to the lack of generic methods, |
459 // but we need to triage all the types. | 461 // but we need to triage all the types. |
460 if ($_isFutureOr(type)) { | 462 if ($_isFutureOr(type)) { |
461 // Ignore if we would ignore either side of union. | 463 // Ignore if we would ignore either side of union. |
462 let typeArg = $getGenericArgs(type)[0]; | 464 let typeArg = $getGenericArgs(type)[0]; |
463 let typeFuture = ${getGenericClass(Future)}(typeArg); | 465 let typeFuture = ${getGenericClass(Future)}(typeArg); |
464 return $_ignoreTypeFailure(actual, typeFuture) || | 466 return $_ignoreTypeFailure(actual, typeFuture) || |
465 $_ignoreTypeFailure(actual, typeArg); | 467 $_ignoreTypeFailure(actual, typeArg); |
(...skipping 13 matching lines...) Expand all Loading... |
479 return false; | 481 return false; |
480 }); | 482 }); |
481 })()'''); | 483 })()'''); |
482 | 484 |
483 /// Returns true if [obj] is an instance of [type] | 485 /// Returns true if [obj] is an instance of [type] |
484 /// Returns true if [obj] is a JS function and [type] is a function type | 486 /// Returns true if [obj] is a JS function and [type] is a function type |
485 /// Returns false if [obj] is not an instance of [type] in both spec | 487 /// Returns false if [obj] is not an instance of [type] in both spec |
486 /// and strong mode | 488 /// and strong mode |
487 /// Returns null if [obj] is not an instance of [type] in strong mode | 489 /// Returns null if [obj] is not an instance of [type] in strong mode |
488 /// but might be in spec mode | 490 /// but might be in spec mode |
489 bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS( | 491 bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS('', '''(() => { |
490 '', | |
491 '''(() => { | |
492 let actual = $getReifiedType($obj); | 492 let actual = $getReifiedType($obj); |
493 let result = $isSubtype(actual, $type); | 493 let result = $isSubtype(actual, $type); |
494 if (result || (actual == $int && $isSubtype($double, $type))) return true; | 494 if (result || (actual == $int && $isSubtype($double, $type))) return true; |
495 if (actual == $jsobject && $_isFunctionType(type) && | 495 if (actual == $jsobject && $_isFunctionType(type) && |
496 typeof(obj) === 'function') { | 496 typeof(obj) === 'function') { |
497 return true; | 497 return true; |
498 } | 498 } |
499 if (result === false) return false; | 499 if (result === false) return false; |
500 if (!dart.__ignoreWhitelistedErrors || | 500 if (!dart.__ignoreWhitelistedErrors || |
501 ($ignoreFromWhiteList == void 0)) { | 501 ($ignoreFromWhiteList == void 0)) { |
502 return result; | 502 return result; |
503 } | 503 } |
504 if ($_ignoreTypeFailure(actual, $type)) return true; | 504 if ($_ignoreTypeFailure(actual, $type)) return true; |
505 return result; | 505 return result; |
506 })()'''); | 506 })()'''); |
507 | 507 |
508 /// Returns true if [obj] is null or an instance of [type] | 508 /// Returns true if [obj] is null or an instance of [type] |
509 /// Returns false if [obj] is non-null and not an instance of [type] | 509 /// Returns false if [obj] is non-null and not an instance of [type] |
510 /// in strong mode | 510 /// in strong mode |
511 instanceOfOrNull(obj, type) => JS( | 511 instanceOfOrNull(obj, type) => JS('', '''(() => { |
512 '', | |
513 '''(() => { | |
514 // If strongInstanceOf returns null, convert to false here. | 512 // If strongInstanceOf returns null, convert to false here. |
515 if (($obj == null) || $strongInstanceOf($obj, $type, true)) return true; | 513 if (($obj == null) || $strongInstanceOf($obj, $type, true)) return true; |
516 return false; | 514 return false; |
517 })()'''); | 515 })()'''); |
518 | 516 |
519 @JSExportName('is') | 517 @JSExportName('is') |
520 bool instanceOf(obj, type) => JS( | 518 bool instanceOf(obj, type) => JS('', '''(() => { |
521 '', | |
522 '''(() => { | |
523 if ($obj == null) { | 519 if ($obj == null) { |
524 return $type == $Null || $_isTop($type); | 520 return $type == $Null || $_isTop($type); |
525 } | 521 } |
526 let result = $strongInstanceOf($obj, $type); | 522 let result = $strongInstanceOf($obj, $type); |
527 if (result !== null) return result; | 523 if (result !== null) return result; |
528 if (!dart.__failForWeakModeIsChecks) return false; | 524 if (!dart.__failForWeakModeIsChecks) return false; |
529 let actual = $getReifiedType($obj); | 525 let actual = $getReifiedType($obj); |
530 let message = 'Strong mode is-check failure: ' + | 526 let message = 'Strong mode is-check failure: ' + |
531 $typeName(actual) + ' does not soundly subtype ' + | 527 $typeName(actual) + ' does not soundly subtype ' + |
532 $typeName($type); | 528 $typeName($type); |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
605 | 601 |
606 if (JS('bool', 'Math.floor(#) != #', obj, obj)) { | 602 if (JS('bool', 'Math.floor(#) != #', obj, obj)) { |
607 throwCastError(obj, getReifiedType(obj), JS('', '#', int)); | 603 throwCastError(obj, getReifiedType(obj), JS('', '#', int)); |
608 } | 604 } |
609 return obj; | 605 return obj; |
610 } | 606 } |
611 | 607 |
612 /// Adds type type test predicates to a constructor for a non-parameterized | 608 /// Adds type type test predicates to a constructor for a non-parameterized |
613 /// type. Non-parameterized types can use `instanceof` for subclass checks and | 609 /// type. Non-parameterized types can use `instanceof` for subclass checks and |
614 /// fall through to a helper for subtype tests. | 610 /// fall through to a helper for subtype tests. |
615 addSimpleTypeTests(ctor) => JS( | 611 addSimpleTypeTests(ctor) => JS('', '''(() => { |
616 '', | |
617 '''(() => { | |
618 $ctor.is = function is_C(object) { | 612 $ctor.is = function is_C(object) { |
619 // This is incorrect for classes [Null] and [Object], so we do not use | 613 // This is incorrect for classes [Null] and [Object], so we do not use |
620 // [addSimpleTypeTests] for these classes. | 614 // [addSimpleTypeTests] for these classes. |
621 if (object instanceof this) return true; | 615 if (object instanceof this) return true; |
622 return dart.is(object, this); | 616 return dart.is(object, this); |
623 }; | 617 }; |
624 $ctor.as = function as_C(object) { | 618 $ctor.as = function as_C(object) { |
625 if (object instanceof this) return object; | 619 if (object instanceof this) return object; |
626 return dart.as(object, this); | 620 return dart.as(object, this); |
627 }; | 621 }; |
628 $ctor._check = function check_C(object) { | 622 $ctor._check = function check_C(object) { |
629 if (object instanceof this) return object; | 623 if (object instanceof this) return object; |
630 return dart.check(object, this); | 624 return dart.check(object, this); |
631 }; | 625 }; |
632 })()'''); | 626 })()'''); |
633 | 627 |
634 /// Adds type type test predicates to a constructor. Used for parmeterized | 628 /// Adds type type test predicates to a constructor. Used for parmeterized |
635 /// types. We avoid `instanceof` for, e.g. `x is ListQueue` since there is | 629 /// types. We avoid `instanceof` for, e.g. `x is ListQueue` since there is |
636 /// no common class for `ListQueue<int>` and `ListQueue<String>`. | 630 /// no common class for `ListQueue<int>` and `ListQueue<String>`. |
637 addTypeTests(ctor) => JS( | 631 addTypeTests(ctor) => JS('', '''(() => { |
638 '', | |
639 '''(() => { | |
640 $ctor.as = function as_G(object) { | 632 $ctor.as = function as_G(object) { |
641 return dart.as(object, this); | 633 return dart.as(object, this); |
642 }; | 634 }; |
643 $ctor.is = function is_G(object) { | 635 $ctor.is = function is_G(object) { |
644 return dart.is(object, this); | 636 return dart.is(object, this); |
645 }; | 637 }; |
646 $ctor._check = function check_G(object) { | 638 $ctor._check = function check_G(object) { |
647 return dart.check(object, this); | 639 return dart.check(object, this); |
648 }; | 640 }; |
649 })()'''); | 641 })()'''); |
650 | 642 |
651 // TODO(vsm): Consider optimizing this. We may be able to statically | 643 // TODO(vsm): Consider optimizing this. We may be able to statically |
652 // determine which == operation to invoke given the static types. | 644 // determine which == operation to invoke given the static types. |
653 equals(x, y) => JS( | 645 equals(x, y) => JS('', '''(() => { |
654 '', | |
655 '''(() => { | |
656 if ($x == null || $y == null) return $x == $y; | 646 if ($x == null || $y == null) return $x == $y; |
657 let eq = $x[dartx['==']] || $x['==']; | 647 let eq = $x[dartx['==']] || $x['==']; |
658 return eq ? eq.call($x, $y) : $x === $y; | 648 return eq ? eq.call($x, $y) : $x === $y; |
659 })()'''); | 649 })()'''); |
660 | 650 |
661 /// Checks that `x` is not null or undefined. */ | 651 /// Checks that `x` is not null or undefined. */ |
662 notNull(x) { | 652 notNull(x) { |
663 if (x == null) throwNullValueError(); | 653 if (x == null) throwNullValueError(); |
664 return x; | 654 return x; |
665 } | 655 } |
666 | 656 |
667 /// | 657 /// |
668 /// Creates a dart:collection LinkedHashMap. | 658 /// Creates a dart:collection LinkedHashMap. |
669 /// | 659 /// |
670 /// For a map with string keys an object literal can be used, for example | 660 /// For a map with string keys an object literal can be used, for example |
671 /// `map({'hi': 1, 'there': 2})`. | 661 /// `map({'hi': 1, 'there': 2})`. |
672 /// | 662 /// |
673 /// Otherwise an array should be used, for example `map([1, 2, 3, 4])` will | 663 /// Otherwise an array should be used, for example `map([1, 2, 3, 4])` will |
674 /// create a map with keys [1, 3] and values [2, 4]. Each key-value pair | 664 /// create a map with keys [1, 3] and values [2, 4]. Each key-value pair |
675 /// should be adjacent entries in the array. | 665 /// should be adjacent entries in the array. |
676 /// | 666 /// |
677 /// For a map with no keys the function can be called with no arguments, for | 667 /// For a map with no keys the function can be called with no arguments, for |
678 /// example `map()`. | 668 /// example `map()`. |
679 /// | 669 /// |
680 // TODO(jmesserly): this could be faster | 670 // TODO(jmesserly): this could be faster |
681 // TODO(jmesserly): we can use default values `= dynamic` once #417 is fixed. | 671 // TODO(jmesserly): we can use default values `= dynamic` once #417 is fixed. |
682 // TODO(jmesserly): move this to classes for consistency with list literals? | 672 // TODO(jmesserly): move this to classes for consistency with list literals? |
683 map(values, [K, V]) => JS( | 673 map(values, [K, V]) => JS('', '''(() => { |
684 '', | |
685 '''(() => { | |
686 if ($K == null) $K = $dynamic; | 674 if ($K == null) $K = $dynamic; |
687 if ($V == null) $V = $dynamic; | 675 if ($V == null) $V = $dynamic; |
688 let map = ${getGenericClass(LinkedHashMap)}($K, $V).new(); | 676 let map = ${getGenericClass(LinkedHashMap)}($K, $V).new(); |
689 if (Array.isArray($values)) { | 677 if (Array.isArray($values)) { |
690 for (let i = 0, end = $values.length - 1; i < end; i += 2) { | 678 for (let i = 0, end = $values.length - 1; i < end; i += 2) { |
691 let key = $values[i]; | 679 let key = $values[i]; |
692 let value = $values[i + 1]; | 680 let value = $values[i + 1]; |
693 map._set(key, value); | 681 map._set(key, value); |
694 } | 682 } |
695 } else if (typeof $values === 'object') { | 683 } else if (typeof $values === 'object') { |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
788 /// returns the result. If the value is not found, [valueFn] will be called to | 776 /// returns the result. If the value is not found, [valueFn] will be called to |
789 /// add it. For example: | 777 /// add it. For example: |
790 /// | 778 /// |
791 /// let map = new Map(); | 779 /// let map = new Map(); |
792 /// putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world'); | 780 /// putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world'); |
793 /// | 781 /// |
794 /// ... will create a Map with a structure like: | 782 /// ... will create a Map with a structure like: |
795 /// | 783 /// |
796 /// { 1: { 2: { 'hi ': { 'there ': 'world' } } } } | 784 /// { 1: { 2: { 'hi ': { 'there ': 'world' } } } } |
797 /// | 785 /// |
798 multiKeyPutIfAbsent(map, keys, valueFn) => JS( | 786 multiKeyPutIfAbsent(map, keys, valueFn) => JS('', '''(() => { |
799 '', | |
800 '''(() => { | |
801 for (let k of $keys) { | 787 for (let k of $keys) { |
802 let value = $map.get(k); | 788 let value = $map.get(k); |
803 if (!value) { | 789 if (!value) { |
804 // TODO(jmesserly): most of these maps are very small (e.g. 1 item), | 790 // TODO(jmesserly): most of these maps are very small (e.g. 1 item), |
805 // so it may be worth optimizing for that. | 791 // so it may be worth optimizing for that. |
806 $map.set(k, value = new Map()); | 792 $map.set(k, value = new Map()); |
807 } | 793 } |
808 $map = value; | 794 $map = value; |
809 } | 795 } |
810 if ($map.has($_value)) return $map.get($_value); | 796 if ($map.has($_value)) return $map.get($_value); |
(...skipping 11 matching lines...) Expand all Loading... |
822 final constants = JS('', 'new Map()'); | 808 final constants = JS('', 'new Map()'); |
823 | 809 |
824 /// | 810 /// |
825 /// Canonicalize a constant object. | 811 /// Canonicalize a constant object. |
826 /// | 812 /// |
827 /// Preconditions: | 813 /// Preconditions: |
828 /// - `obj` is an objects or array, not a primitive. | 814 /// - `obj` is an objects or array, not a primitive. |
829 /// - nested values of the object are themselves already canonicalized. | 815 /// - nested values of the object are themselves already canonicalized. |
830 /// | 816 /// |
831 @JSExportName('const') | 817 @JSExportName('const') |
832 const_(obj) => JS( | 818 const_(obj) => JS('', '''(() => { |
833 '', | |
834 '''(() => { | |
835 // TODO(leafp): This table gets quite large in apps. | 819 // TODO(leafp): This table gets quite large in apps. |
836 // Keeping the paths is probably expensive. It would probably | 820 // Keeping the paths is probably expensive. It would probably |
837 // be more space efficient to just use a direct hash table with | 821 // be more space efficient to just use a direct hash table with |
838 // an appropriately defined structural equality function. | 822 // an appropriately defined structural equality function. |
839 function lookupNonTerminal(map, key) { | 823 function lookupNonTerminal(map, key) { |
840 let result = map.get(key); | 824 let result = map.get(key); |
841 if (result !== void 0) return result; | 825 if (result !== void 0) return result; |
842 map.set(key, result = new Map()); | 826 map.set(key, result = new Map()); |
843 return result; | 827 return result; |
844 }; | 828 }; |
(...skipping 26 matching lines...) Expand all Loading... |
871 return $obj; | 855 return $obj; |
872 })()'''); | 856 })()'''); |
873 | 857 |
874 /// The global constant list table. | 858 /// The global constant list table. |
875 /// This maps the number of elements in the list (n) | 859 /// This maps the number of elements in the list (n) |
876 /// to a path of length n of maps indexed by the value | 860 /// to a path of length n of maps indexed by the value |
877 /// of the field. The final map is indexed by the element | 861 /// of the field. The final map is indexed by the element |
878 /// type and contains the canonical version of the list. | 862 /// type and contains the canonical version of the list. |
879 final constantLists = JS('', 'new Map()'); | 863 final constantLists = JS('', 'new Map()'); |
880 | 864 |
881 /// | |
882 /// Canonicalize a constant list | 865 /// Canonicalize a constant list |
883 /// | 866 constList(elements, elementType) => JS('', '''(() => { |
884 constList(elements, elementType) => JS( | |
885 '', | |
886 '''(() => { | |
887 function lookupNonTerminal(map, key) { | 867 function lookupNonTerminal(map, key) { |
888 let result = map.get(key); | 868 let result = map.get(key); |
889 if (result !== void 0) return result; | 869 if (result !== void 0) return result; |
890 map.set(key, result = new Map()); | 870 map.set(key, result = new Map()); |
891 return result; | 871 return result; |
892 }; | 872 }; |
893 let count = $elements.length; | 873 let count = $elements.length; |
894 let map = lookupNonTerminal($constantLists, count); | 874 let map = lookupNonTerminal($constantLists, count); |
895 for (let i = 0; i < count; i++) { | 875 for (let i = 0; i < count; i++) { |
896 map = lookupNonTerminal(map, elements[i]); | 876 map = lookupNonTerminal(map, elements[i]); |
897 } | 877 } |
898 let value = map.get($elementType); | 878 let value = map.get($elementType); |
899 if (value) return value; | 879 if (value) return value; |
900 value = $list($elements, $elementType); | 880 |
| 881 value = $setType($elements, ${getGenericClass(JSArray)}($elementType)); |
901 map.set($elementType, value); | 882 map.set($elementType, value); |
902 return value; | 883 return value; |
903 })()'''); | 884 })()'''); |
904 | 885 |
905 // The following are helpers for Object methods when the receiver | 886 // The following are helpers for Object methods when the receiver |
906 // may be null or primitive. These should only be generated by | 887 // may be null or primitive. These should only be generated by |
907 // the compiler. | 888 // the compiler. |
908 hashCode(obj) { | 889 hashCode(obj) { |
909 if (obj == null) return 0; | 890 if (obj == null) return 0; |
910 | 891 |
911 switch (JS('String', 'typeof #', obj)) { | 892 switch (JS('String', 'typeof #', obj)) { |
912 case "number": | 893 case "number": |
913 return JS('', '# & 0x1FFFFFFF', obj); | 894 return JS('', '# & 0x1FFFFFFF', obj); |
914 case "boolean": | 895 case "boolean": |
915 // From JSBool.hashCode, see comment there. | 896 // From JSBool.hashCode, see comment there. |
916 return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); | 897 return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); |
917 case "function": | 898 case "function": |
918 var hashFn = JS('', '#[#]', obj, _tearoffHashcode); | 899 if (JS('bool', '# instanceof Function', obj)) { |
919 if (hashFn != null) return JS('int', '#()', hashFn); | 900 var hashFn = JS('', '#[#]', obj, _tearoffHashcode); |
920 return Primitives.objectHashCode(obj); | 901 if (hashFn != null) return JS('int', '#()', hashFn); |
| 902 return Primitives.objectHashCode(obj); |
| 903 } |
921 } | 904 } |
922 | 905 |
923 var extension = getExtensionType(obj); | 906 var extension = getExtensionType(obj); |
924 if (extension != null) { | 907 if (extension != null) { |
925 return JS('', '#[dartx.hashCode]', obj); | 908 return JS('', '#[dartx.hashCode]', obj); |
926 } | 909 } |
927 return JS('', '#.hashCode', obj); | 910 return JS('', '#.hashCode', obj); |
928 } | 911 } |
929 | 912 |
930 @JSExportName('toString') | 913 @JSExportName('toString') |
931 String _toString(obj) { | 914 String _toString(obj) { |
932 if (obj == null) return "null"; | 915 if (obj == null) return "null"; |
933 | 916 |
934 var extension = getExtensionType(obj); | 917 var extension = getExtensionType(obj); |
935 if (extension != null) { | 918 if (extension != null) { |
936 return JS('String', '#[dartx.toString]()', obj); | 919 return JS('String', '#[dartx.toString]()', obj); |
937 } | 920 } |
938 if (JS('bool', 'typeof # == "function"', obj)) { | 921 if (JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
939 // If the function is a Type object, we should just display the type name. | 922 // If the function is a Type object, we should just display the type name. |
940 // Regular Dart code should typically get wrapped type objects instead of | 923 // Regular Dart code should typically get wrapped type objects instead of |
941 // raw type (aka JS constructor) objects however raw type objects can be | 924 // raw type (aka JS constructor) objects however raw type objects can be |
942 // exposed to Dart code via JS interop or debugging tools. | 925 // exposed to Dart code via JS interop or debugging tools. |
943 if (isType(obj)) return typeName(obj); | 926 if (isType(obj)) return typeName(obj); |
944 | 927 |
945 return JS( | 928 return JS( |
946 'String', r'"Closure: " + # + " from: " + #', getReifiedType(obj), obj); | 929 'String', r'"Closure: " + # + " from: " + #', getReifiedType(obj), obj); |
947 } | 930 } |
948 // TODO(jmesserly): restore this faster path once ES Symbol is treated as | 931 // TODO(jmesserly): restore this faster path once ES Symbol is treated as |
949 // an extension type (and thus hits the above code path). | 932 // an extension type (and thus hits the above code path). |
950 // See https://github.com/dart-lang/sdk/issues/28323 | 933 // See https://github.com/dart-lang/sdk/issues/28323 |
951 // return JS('', '"" + #', obj); | 934 // return JS('', '"" + #', obj); |
952 return JS('String', '#.toString()', obj); | 935 return JS('String', '#.toString()', obj); |
953 } | 936 } |
954 | 937 |
955 // TODO(jmesserly): is the argument type verified statically? | 938 // TODO(jmesserly): is the argument type verified statically? |
956 noSuchMethod(obj, Invocation invocation) { | 939 noSuchMethod(obj, Invocation invocation) { |
957 if (obj == null || JS('bool', 'typeof # == "function"', obj)) { | 940 if (obj == null || |
| 941 JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
958 throwNoSuchMethodError(obj, invocation.memberName, | 942 throwNoSuchMethodError(obj, invocation.memberName, |
959 invocation.positionalArguments, invocation.namedArguments); | 943 invocation.positionalArguments, invocation.namedArguments); |
960 } | 944 } |
961 // Delegate to the (possibly user-defined) method on the object. | 945 // Delegate to the (possibly user-defined) method on the object. |
962 var extension = getExtensionType(obj); | 946 var extension = getExtensionType(obj); |
963 if (extension != null) { | 947 if (extension != null) { |
964 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); | 948 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); |
965 } | 949 } |
966 return JS('', '#.noSuchMethod(#)', obj, invocation); | 950 return JS('', '#.noSuchMethod(#)', obj, invocation); |
967 } | 951 } |
968 | 952 |
969 constFn(x) => JS('', '() => x'); | 953 constFn(x) => JS('', '() => x'); |
970 | 954 |
971 runtimeType(obj) { | 955 runtimeType(obj) { |
972 // Handle primitives where the method isn't on the object. | 956 // Handle primitives where the method isn't on the object. |
973 var result = _checkPrimitiveType(obj); | 957 var result = _checkPrimitiveType(obj); |
974 if (result != null) return wrapType(result); | 958 if (result != null) return wrapType(result); |
975 | 959 |
976 // Delegate to the (possibly user-defined) method on the object. | 960 // Delegate to the (possibly user-defined) method on the object. |
977 var extension = getExtensionType(obj); | 961 var extension = getExtensionType(obj); |
978 if (extension != null) { | 962 if (extension != null) { |
979 result = JS('', '#[dartx.runtimeType]', obj); | 963 result = JS('', '#[dartx.runtimeType]', obj); |
980 // If extension doesn't override runtimeType, return the extension type. | 964 // If extension doesn't override runtimeType, return the extension type. |
981 return result ?? wrapType(extension); | 965 return result ?? wrapType(extension); |
982 } | 966 } |
983 if (JS('bool', 'typeof # == "function"', obj)) { | 967 if (JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
984 return wrapType(getReifiedType(obj)); | 968 return wrapType(getReifiedType(obj)); |
985 } | 969 } |
986 return JS('', '#.runtimeType', obj); | 970 return JS('', '#.runtimeType', obj); |
987 } | 971 } |
988 | 972 |
989 /// Implements Dart's interpolated strings as ES2015 tagged template literals. | 973 /// Implements Dart's interpolated strings as ES2015 tagged template literals. |
990 /// | 974 /// |
991 /// For example: dart.str`hello ${name}` | 975 /// For example: dart.str`hello ${name}` |
992 String str(strings, @rest values) => JS( | 976 String str(strings, @rest values) => JS('', '''(() => { |
993 '', | |
994 '''(() => { | |
995 let s = $strings[0]; | 977 let s = $strings[0]; |
996 for (let i = 0, len = $values.length; i < len; ) { | 978 for (let i = 0, len = $values.length; i < len; ) { |
997 s += $notNull($_toString($values[i])) + $strings[++i]; | 979 s += $notNull($_toString($values[i])) + $strings[++i]; |
998 } | 980 } |
999 return s; | 981 return s; |
1000 })()'''); | 982 })()'''); |
1001 | 983 |
1002 final JsIterator = JS( | 984 final JsIterator = JS('', ''' |
1003 '', | |
1004 ''' | |
1005 class JsIterator { | 985 class JsIterator { |
1006 constructor(dartIterator) { | 986 constructor(dartIterator) { |
1007 this.dartIterator = dartIterator; | 987 this.dartIterator = dartIterator; |
1008 } | 988 } |
1009 next() { | 989 next() { |
1010 let i = this.dartIterator; | 990 let i = this.dartIterator; |
1011 let done = !i.moveNext(); | 991 let done = !i.moveNext(); |
1012 return { done: done, value: done ? void 0 : i.current }; | 992 return { done: done, value: done ? void 0 : i.current }; |
1013 } | 993 } |
1014 } | 994 } |
(...skipping 19 matching lines...) Expand all Loading... |
1034 /// Libraries are not actually deferred in DDC, so this just returns a future | 1014 /// Libraries are not actually deferred in DDC, so this just returns a future |
1035 /// that completes immediately. | 1015 /// that completes immediately. |
1036 Future loadLibrary() => new Future.value(); | 1016 Future loadLibrary() => new Future.value(); |
1037 | 1017 |
1038 /// Defines lazy statics. | 1018 /// Defines lazy statics. |
1039 void defineLazy(to, from) { | 1019 void defineLazy(to, from) { |
1040 for (var name in getOwnNamesAndSymbols(from)) { | 1020 for (var name in getOwnNamesAndSymbols(from)) { |
1041 defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); | 1021 defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); |
1042 } | 1022 } |
1043 } | 1023 } |
OLD | NEW |