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

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

Issue 3003613002: improve DDC's type checks (Closed)
Patch Set: format Created 3 years, 4 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;
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698