| 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; |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 // behavior for JS types. | 147 // behavior for JS types. |
| 148 // TODO(jacobr): remove the type checking rules workaround when mirrors based | 148 // TODO(jacobr): remove the type checking rules workaround when mirrors based |
| 149 // PageLoader code can generate the correct reified generic types. | 149 // PageLoader code can generate the correct reified generic types. |
| 150 dputMirror(obj, field, value) { | 150 dputMirror(obj, field, value) { |
| 151 var f = _canonicalMember(obj, field); | 151 var f = _canonicalMember(obj, field); |
| 152 _trackCall(obj); | 152 _trackCall(obj); |
| 153 if (f != null) { | 153 if (f != null) { |
| 154 var setterType = getSetterType(getType(obj), f); | 154 var setterType = getSetterType(getType(obj), f); |
| 155 if (setterType != null) { | 155 if (setterType != null) { |
| 156 setterType = _stripGenericArguments(setterType); | 156 setterType = _stripGenericArguments(setterType); |
| 157 return JS('', '#[#] = #', obj, f, check(value, setterType)); | 157 return JS('', '#[#] = #._check(#)', obj, f, setterType, value); |
| 158 } | 158 } |
| 159 } | 159 } |
| 160 return noSuchMethod( | 160 return noSuchMethod( |
| 161 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); | 161 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); |
| 162 } | 162 } |
| 163 | 163 |
| 164 dput(obj, field, value) { | 164 dput(obj, field, value) { |
| 165 var f = _canonicalMember(obj, field); | 165 var f = _canonicalMember(obj, field); |
| 166 _trackCall(obj); | 166 _trackCall(obj); |
| 167 if (f != null) { | 167 if (f != null) { |
| 168 var setterType = getSetterType(getType(obj), f); | 168 var setterType = getSetterType(getType(obj), f); |
| 169 if (setterType != null) { | 169 if (setterType != null) { |
| 170 return JS('', '#[#] = #', obj, f, check(value, setterType)); | 170 return JS('', '#[#] = #._check(#)', obj, f, setterType, value); |
| 171 } | 171 } |
| 172 // Always allow for JS interop objects. | 172 // Always allow for JS interop objects. |
| 173 if (isJsInterop(obj)) { | 173 if (isJsInterop(obj)) { |
| 174 return JS('', '#[#] = #', obj, f, value); | 174 return JS('', '#[#] = #', obj, f, value); |
| 175 } | 175 } |
| 176 } | 176 } |
| 177 return noSuchMethod( | 177 return noSuchMethod( |
| 178 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); | 178 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); |
| 179 } | 179 } |
| 180 | 180 |
| 181 /// 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 |
| 182 /// actuals. | 182 /// actuals. |
| 183 _checkApply(type, actuals) => JS('', '''(() => { | 183 _checkApply(type, actuals) => JS('', '''(() => { |
| 184 // TODO(vsm): Remove when we no longer need mirrors metadata. | 184 // TODO(vsm): Remove when we no longer need mirrors metadata. |
| 185 // An array is used to encode annotations attached to the type. | 185 // An array is used to encode annotations attached to the type. |
| 186 if ($type instanceof Array) { | 186 if ($type instanceof Array) { |
| 187 $type = type[0]; | 187 $type = type[0]; |
| 188 } | 188 } |
| 189 if ($actuals.length < $type.args.length) return false; | 189 if ($actuals.length < $type.args.length) return false; |
| 190 let index = 0; | 190 let index = 0; |
| 191 for(let i = 0; i < $type.args.length; ++i) { | 191 for(let i = 0; i < $type.args.length; ++i) { |
| 192 $check($actuals[i], $type.args[i]); | 192 $type.args[i]._check($actuals[i]); |
| 193 ++index; | 193 ++index; |
| 194 } | 194 } |
| 195 if ($actuals.length == $type.args.length) return true; | 195 if ($actuals.length == $type.args.length) return true; |
| 196 let extras = $actuals.length - $type.args.length; | 196 let extras = $actuals.length - $type.args.length; |
| 197 if ($type.optionals.length > 0) { | 197 if ($type.optionals.length > 0) { |
| 198 if (extras > $type.optionals.length) return false; | 198 if (extras > $type.optionals.length) return false; |
| 199 for(let i = 0, j=index; i < extras; ++i, ++j) { | 199 for(let i = 0, j=index; i < extras; ++i, ++j) { |
| 200 $check($actuals[j], $type.optionals[i]); | 200 $type.optionals[i]._check($actuals[j]); |
| 201 } | 201 } |
| 202 return true; | 202 return true; |
| 203 } | 203 } |
| 204 // TODO(leafp): We can't tell when someone might be calling | 204 // TODO(leafp): We can't tell when someone might be calling |
| 205 // something expecting an optional argument with named arguments | 205 // something expecting an optional argument with named arguments |
| 206 | 206 |
| 207 if (extras != 1) return false; | 207 if (extras != 1) return false; |
| 208 // An empty named list means no named arguments | 208 // An empty named list means no named arguments |
| 209 if ($getOwnPropertyNames($type.named).length == 0) return false; | 209 if ($getOwnPropertyNames($type.named).length == 0) return false; |
| 210 let opts = $actuals[index]; | 210 let opts = $actuals[index]; |
| 211 let names = $getOwnPropertyNames(opts); | 211 let names = $getOwnPropertyNames(opts); |
| 212 // Type is something other than a map | 212 // Type is something other than a map |
| 213 if (names.length == 0) return false; | 213 if (names.length == 0) return false; |
| 214 for (var name of names) { | 214 for (var name of names) { |
| 215 if (!($hasOwnProperty.call($type.named, name))) { | 215 if (!($hasOwnProperty.call($type.named, name))) { |
| 216 return false; | 216 return false; |
| 217 } | 217 } |
| 218 $check(opts[name], $type.named[name]); | 218 $type.named[name]._check(opts[name]); |
| 219 } | 219 } |
| 220 return true; | 220 return true; |
| 221 })()'''); | 221 })()'''); |
| 222 | 222 |
| 223 _toSymbolName(symbol) => JS('', '''(() => { | 223 _toSymbolName(symbol) => JS('', '''(() => { |
| 224 let str = $symbol.toString(); | 224 let str = $symbol.toString(); |
| 225 // Strip leading 'Symbol(' and trailing ')' | 225 // Strip leading 'Symbol(' and trailing ')' |
| 226 return str.substring(7, str.length-1); | 226 return str.substring(7, str.length-1); |
| 227 })()'''); | 227 })()'''); |
| 228 | 228 |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 475 !!$isSubtype(type, $StreamSubscription) && | 475 !!$isSubtype(type, $StreamSubscription) && |
| 476 !!$isSubtype(actual, $StreamSubscription)) { | 476 !!$isSubtype(actual, $StreamSubscription)) { |
| 477 console.warn('Ignoring cast fail from ' + $typeName(actual) + | 477 console.warn('Ignoring cast fail from ' + $typeName(actual) + |
| 478 ' to ' + $typeName(type)); | 478 ' to ' + $typeName(type)); |
| 479 return true; | 479 return true; |
| 480 } | 480 } |
| 481 return false; | 481 return false; |
| 482 }); | 482 }); |
| 483 })()'''); | 483 })()'''); |
| 484 | 484 |
| 485 /// Returns true if [obj] is an instance of [type] in strong mode, otherwise | |
| 486 /// false. | |
| 487 /// | |
| 488 /// This also allows arbitrary JS function objects to be subtypes of every Dart | |
| 489 /// function types. | |
| 490 bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS('', '''(() => { | |
| 491 let actual = $getReifiedType($obj); | |
| 492 let result = $isSubtype(actual, $type); | |
| 493 if (result || | |
| 494 (actual == $int && $isSubtype($double, $type)) || | |
| 495 (actual == $jsobject && $_isFunctionType(type) && | |
| 496 typeof(obj) === 'function')) { | |
| 497 return true; | |
| 498 } | |
| 499 if (result === null && | |
| 500 dart.__ignoreWhitelistedErrors && | |
| 501 $ignoreFromWhiteList && | |
| 502 $_ignoreTypeFailure(actual, $type)) { | |
| 503 return true; | |
| 504 } | |
| 505 return false; | |
| 506 })()'''); | |
| 507 | |
| 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] | |
| 510 /// in strong mode | |
| 511 bool instanceOfOrNull(obj, type) { | |
| 512 // If strongInstanceOf returns null, convert to false here. | |
| 513 return obj == null || JS('bool', '#', strongInstanceOf(obj, type, true)); | |
| 514 } | |
| 515 | |
| 516 @JSExportName('is') | 485 @JSExportName('is') |
| 517 bool instanceOf(obj, type) { | 486 bool instanceOf(obj, type) { |
| 518 if (obj == null) { | 487 if (obj == null) { |
| 519 return JS('bool', '# == # || #', type, Null, _isTop(type)); | 488 return JS('bool', '# == # || #', type, Null, _isTop(type)); |
| 520 } | 489 } |
| 521 return strongInstanceOf(obj, type, false); | 490 return JS('#', '!!#', isSubtype(getReifiedType(obj), type)); |
| 522 } | 491 } |
| 523 | 492 |
| 524 @JSExportName('as') | 493 @JSExportName('as') |
| 525 cast(obj, type) { | 494 cast(obj, type, bool typeError) { |
| 526 if (JS('bool', '# == #', type, dynamic) || obj == null) return obj; | 495 if (obj == null) return obj; |
| 527 bool result = strongInstanceOf(obj, type, true); | 496 var actual = getReifiedType(obj); |
| 528 if (JS('bool', '#', result)) return obj; | 497 var result = isSubtype(actual, type); |
| 529 if (JS('bool', '!dart.__ignoreAllErrors')) { | 498 if (JS( |
| 530 _throwCastError(obj, type, result); | 499 'bool', |
| 500 '# === true || # === null && dart.__ignoreWhitelistedErrors && #(#, #)', |
| 501 result, |
| 502 result, |
| 503 _ignoreTypeFailure, |
| 504 actual, |
| 505 type)) { |
| 506 return obj; |
| 531 } | 507 } |
| 532 JS('', 'console.error(#)', | 508 return castError(obj, type, typeError); |
| 533 'Actual: ${typeName(getReifiedType(obj))} Expected: ${typeName(type)}'); | |
| 534 return obj; | |
| 535 } | |
| 536 | |
| 537 check(obj, type) { | |
| 538 if (JS('bool', '# == #', type, dynamic) || obj == null) return obj; | |
| 539 bool result = strongInstanceOf(obj, type, true); | |
| 540 if (JS('bool', '#', result)) return obj; | |
| 541 if (JS('bool', '!dart.__ignoreAllErrors')) { | |
| 542 _throwTypeError(obj, type, result); | |
| 543 } | |
| 544 JS('', 'console.error(#)', | |
| 545 'Actual: ${typeName(getReifiedType(obj))} Expected: ${typeName(type)}'); | |
| 546 return obj; | |
| 547 } | 509 } |
| 548 | 510 |
| 549 bool test(bool obj) { | 511 bool test(bool obj) { |
| 550 if (obj == null) _throwBooleanConversionError(); | 512 if (obj == null) _throwBooleanConversionError(); |
| 551 return obj; | 513 return obj; |
| 552 } | 514 } |
| 553 | 515 |
| 554 bool dtest(obj) { | 516 bool dtest(obj) { |
| 555 if (obj is! bool) booleanConversionFailed(obj); | 517 if (obj is! bool) booleanConversionFailed(obj); |
| 556 return obj; | 518 return obj; |
| 557 } | 519 } |
| 558 | 520 |
| 559 void _throwBooleanConversionError() => | 521 void _throwBooleanConversionError() => |
| 560 throw new BooleanConversionAssertionError(); | 522 throw new BooleanConversionAssertionError(); |
| 561 | 523 |
| 562 void booleanConversionFailed(obj) { | 524 void booleanConversionFailed(obj) { |
| 563 if (obj == null) { | 525 if (obj == null) { |
| 564 _throwBooleanConversionError(); | 526 _throwBooleanConversionError(); |
| 565 } | 527 } |
| 566 var actual = getReifiedType(obj); | 528 var actual = getReifiedType(obj); |
| 567 var expected = JS('', '#', bool); | 529 var expected = JS('', '#', bool); |
| 568 throw new TypeErrorImplementation.fromMessage( | 530 throw new TypeErrorImplementation.fromMessage( |
| 569 "type '${typeName(actual)}' is not a subtype of " | 531 "type '${typeName(actual)}' is not a subtype of " |
| 570 "type '${typeName(expected)}' in boolean expression"); | 532 "type '${typeName(expected)}' in boolean expression"); |
| 571 } | 533 } |
| 572 | 534 |
| 573 void _throwCastError(obj, type, bool result) { | 535 castError(obj, type, bool typeError) { |
| 574 var actual = getReifiedType(obj); | 536 var objType = getReifiedType(obj); |
| 575 if (result == false) throwCastError(obj, actual, type); | 537 if (JS('bool', '!dart.__ignoreAllErrors')) { |
| 538 var errorInStrongMode = isSubtype(objType, type) == null; |
| 576 | 539 |
| 577 throwStrongModeCastError(obj, actual, type); | 540 var actual = typeName(objType); |
| 578 } | 541 var expected = typeName(type); |
| 542 if (JS('bool', 'dart.__trapRuntimeErrors')) JS('', 'debugger'); |
| 579 | 543 |
| 580 void _throwTypeError(obj, type, bool result) { | 544 var error = JS('bool', '#', typeError) |
| 581 var actual = getReifiedType(obj); | 545 ? new TypeErrorImplementation(obj, actual, expected, errorInStrongMode) |
| 582 if (result == false) throwTypeError(obj, actual, type); | 546 : new CastErrorImplementation(obj, actual, expected, errorInStrongMode); |
| 583 | 547 throw error; |
| 584 throwStrongModeTypeError(obj, actual, type); | 548 } |
| 549 JS('', 'console.error(#)', |
| 550 'Actual: ${typeName(objType)} Expected: ${typeName(type)}'); |
| 551 return obj; |
| 585 } | 552 } |
| 586 | 553 |
| 587 asInt(obj) { | 554 asInt(obj) { |
| 588 if (obj == null) return null; | 555 if (obj == null) return null; |
| 589 | 556 |
| 590 if (JS('bool', 'Math.floor(#) != #', obj, obj)) { | 557 if (JS('bool', 'Math.floor(#) != #', obj, obj)) { |
| 591 throwCastError(obj, getReifiedType(obj), JS('', '#', int)); | 558 castError(obj, JS('', '#', int), false); |
| 592 } | 559 } |
| 593 return obj; | 560 return obj; |
| 594 } | 561 } |
| 595 | 562 |
| 596 /// Adds type type test predicates to a constructor for a non-parameterized | |
| 597 /// type. Non-parameterized types can use `instanceof` for subclass checks and | |
| 598 /// fall through to a helper for subtype tests. | |
| 599 addSimpleTypeTests(ctor) => JS('', '''(() => { | |
| 600 $ctor.is = function is_C(object) { | |
| 601 // This is incorrect for classes [Null] and [Object], so we do not use | |
| 602 // [addSimpleTypeTests] for these classes. | |
| 603 if (object instanceof this) return true; | |
| 604 return dart.is(object, this); | |
| 605 }; | |
| 606 $ctor.as = function as_C(object) { | |
| 607 if (object instanceof this) return object; | |
| 608 return dart.as(object, this); | |
| 609 }; | |
| 610 $ctor._check = function check_C(object) { | |
| 611 if (object instanceof this) return object; | |
| 612 return dart.check(object, this); | |
| 613 }; | |
| 614 })()'''); | |
| 615 | |
| 616 /// Adds type type test predicates to a constructor. Used for parmeterized | |
| 617 /// types. We avoid `instanceof` for, e.g. `x is ListQueue` since there is | |
| 618 /// no common class for `ListQueue<int>` and `ListQueue<String>`. | |
| 619 addTypeTests(ctor) => JS('', '''(() => { | |
| 620 $ctor.as = function as_G(object) { | |
| 621 return dart.as(object, this); | |
| 622 }; | |
| 623 $ctor.is = function is_G(object) { | |
| 624 return dart.is(object, this); | |
| 625 }; | |
| 626 $ctor._check = function check_G(object) { | |
| 627 return dart.check(object, this); | |
| 628 }; | |
| 629 })()'''); | |
| 630 | |
| 631 // TODO(vsm): Consider optimizing this. We may be able to statically | 563 // TODO(vsm): Consider optimizing this. We may be able to statically |
| 632 // determine which == operation to invoke given the static types. | 564 // determine which == operation to invoke given the static types. |
| 633 equals(x, y) => JS('', '''(() => { | 565 equals(x, y) => JS('', '''(() => { |
| 634 if ($x == null || $y == null) return $x == $y; | 566 if ($x == null || $y == null) return $x == $y; |
| 635 let eq = $x[dartx['==']] || $x['==']; | 567 let eq = $x[dartx['==']] || $x['==']; |
| 636 return eq ? eq.call($x, $y) : $x === $y; | 568 return eq ? eq.call($x, $y) : $x === $y; |
| 637 })()'''); | 569 })()'''); |
| 638 | 570 |
| 639 /// Checks that `x` is not null or undefined. */ | 571 /// Checks that `x` is not null or undefined. */ |
| 640 notNull(x) { | 572 notNull(x) { |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 926 var extension = getExtensionType(obj); | 858 var extension = getExtensionType(obj); |
| 927 if (extension != null) { | 859 if (extension != null) { |
| 928 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); | 860 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); |
| 929 } | 861 } |
| 930 return JS('', '#.noSuchMethod(#)', obj, invocation); | 862 return JS('', '#.noSuchMethod(#)', obj, invocation); |
| 931 } | 863 } |
| 932 | 864 |
| 933 constFn(x) => JS('', '() => x'); | 865 constFn(x) => JS('', '() => x'); |
| 934 | 866 |
| 935 runtimeType(obj) { | 867 runtimeType(obj) { |
| 936 // Handle primitives where the method isn't on the object. | 868 if (obj == null) return Null; |
| 937 var result = _checkPrimitiveType(obj); | 869 if (JS('bool', '# instanceof #', obj, Object)) { |
| 938 if (result != null) return wrapType(result); | 870 // A normal Dart object: get `obj.runtimeType` |
| 939 | 871 // (callable Dart classes are also handled here) |
| 940 // Delegate to the (possibly user-defined) method on the object. | 872 return JS('', '#.runtimeType', obj); |
| 941 var extension = getExtensionType(obj); | |
| 942 if (extension != null) { | |
| 943 result = JS('', '#[dartx.runtimeType]', obj); | |
| 944 // If extension doesn't override runtimeType, return the extension type. | |
| 945 return result ?? wrapType(extension); | |
| 946 } | 873 } |
| 947 if (JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { | 874 if (JS('bool', 'typeof obj == "object"')) { |
| 948 return wrapType(getReifiedType(obj)); | 875 // Some other kind of JS object. |
| 876 var extensionType = JS('', '#[#]', obj, _extensionType); |
| 877 if (extensionType != null) { |
| 878 // An extension type: get `obj[dartx.runtimeType]` |
| 879 var result = JS('', '#[dartx.runtimeType]', obj); |
| 880 // If the extension doesn't override runtimeType, handle that. |
| 881 // TODO(jmesserly): is this still possible? Object members should always |
| 882 // be defined on extension types. |
| 883 if (result != null) return result; |
| 884 } else { |
| 885 extensionType = jsobject; |
| 886 } |
| 887 return wrapType(extensionType); |
| 949 } | 888 } |
| 950 return JS('', '#.runtimeType', obj); | 889 // All other types: fall back to `getReifiedType` |
| 890 return wrapType(getReifiedType(obj)); |
| 951 } | 891 } |
| 952 | 892 |
| 953 /// Implements Dart's interpolated strings as ES2015 tagged template literals. | 893 /// Implements Dart's interpolated strings as ES2015 tagged template literals. |
| 954 /// | 894 /// |
| 955 /// For example: dart.str`hello ${name}` | 895 /// For example: dart.str`hello ${name}` |
| 956 String str(strings, @rest values) => JS('', '''(() => { | 896 String str(strings, @rest values) => JS('', '''(() => { |
| 957 let s = $strings[0]; | 897 let s = $strings[0]; |
| 958 for (let i = 0, len = $values.length; i < len; ) { | 898 for (let i = 0, len = $values.length; i < len; ) { |
| 959 s += $notNull($_toString($values[i])) + $strings[++i]; | 899 s += $notNull($_toString($values[i])) + $strings[++i]; |
| 960 } | 900 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 994 /// Libraries are not actually deferred in DDC, so this just returns a future | 934 /// Libraries are not actually deferred in DDC, so this just returns a future |
| 995 /// that completes immediately. | 935 /// that completes immediately. |
| 996 Future loadLibrary() => new Future.value(); | 936 Future loadLibrary() => new Future.value(); |
| 997 | 937 |
| 998 /// Defines lazy statics. | 938 /// Defines lazy statics. |
| 999 void defineLazy(to, from) { | 939 void defineLazy(to, from) { |
| 1000 for (var name in getOwnNamesAndSymbols(from)) { | 940 for (var name in getOwnNamesAndSymbols(from)) { |
| 1001 defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); | 941 defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); |
| 1002 } | 942 } |
| 1003 } | 943 } |
| OLD | NEW |