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