Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart

Issue 2979353002: implement `Invocation.typeArguments` in DDC (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698