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 |