Index: pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart |
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart |
index 0f34d0e5a975a9b7ff6980d3946c2eb80b60f4a3..fb3f9da9605ff299f00c8ec860c9a64f50fc46e2 100644 |
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart |
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/types.dart |
@@ -58,14 +58,15 @@ final _typeObject = JS('', 'Symbol("typeObject")'); |
class TypeRep implements Type { |
String get name => this.toString(); |
+ // TODO(jmesserly): these should never be reached. |
Leaf
2017/08/24 17:19:39
Are they here just to be defensive, or are there l
Jennifer Messerly
2017/08/24 18:26:37
legacy, basically. I want to investigate if they c
|
@JSExportName('is') |
bool is_T(object) => instanceOf(object, this); |
@JSExportName('as') |
- as_T(object) => cast(object, this); |
+ as_T(object) => cast(object, this, false); |
@JSExportName('_check') |
- check_T(object) => check(object, this); |
+ check_T(object) => cast(object, this, true); |
} |
class Dynamic extends TypeRep { |
@@ -97,13 +98,21 @@ class LazyJSType extends TypeRep { |
} |
@JSExportName('is') |
- bool is_T(obj) => instanceOf(obj, rawJSTypeForCheck()); |
+ bool is_T(obj) { |
+ return JS('bool', '# instanceof #', obj, rawJSTypeForCheck()); |
+ } |
@JSExportName('as') |
- as_T(obj) => cast(obj, rawJSTypeForCheck()); |
+ as_T(obj) => |
+ JS('bool', '# instanceof #', obj, rawJSTypeForCheck()) || obj == null |
+ ? obj |
+ : castError(obj, this, false); |
@JSExportName('_check') |
- check_T(obj) => check(obj, rawJSTypeForCheck()); |
+ check_T(obj) => |
+ JS('bool', '# instanceof #', obj, rawJSTypeForCheck()) || obj == null |
+ ? obj |
+ : castError(obj, this, true); |
} |
/// An anonymous JS type |
@@ -115,21 +124,25 @@ class AnonymousJSType extends TypeRep { |
toString() => _dartName; |
@JSExportName('is') |
- bool is_T(obj) => _isJSType(getReifiedType(obj)); |
+ bool is_T(obj) => JS('bool', '# === #', getReifiedType(obj), jsobject); |
@JSExportName('as') |
- as_T(obj) => is_T(obj) ? obj : cast(obj, this); |
+ as_T(obj) => |
+ JS('bool', '# == null || # === #', obj, getReifiedType(obj), jsobject) |
+ ? obj |
+ : castError(obj, this, false); |
@JSExportName('_check') |
- check_T(obj) => is_T(obj) ? obj : check(obj, this); |
+ check_T(obj) => |
Leaf
2017/09/06 23:21:19
I think this changes the behavior when you have an
Jennifer Messerly
2017/09/06 23:40:58
I have no idea whether it was intended behavior in
|
+ JS('bool', '# == null || # === #', obj, getReifiedType(obj), jsobject) |
+ ? obj |
+ : castError(obj, this, true); |
} |
void _warn(arg) { |
JS('void', 'console.warn(#)', arg); |
} |
-bool _isJSType(t) => JS('bool', '!#[dart._runtimeType]', t); |
- |
var _lazyJSTypes = JS('', 'new Map()'); |
var _anonymousJSTypes = JS('', 'new Map()'); |
@@ -211,9 +224,7 @@ final _fnTypeTypeMap = JS('', 'new Map()'); |
/// index path (if present) is the canonical function type. |
final List _fnTypeSmallMap = JS('', '[new Map(), new Map(), new Map()]'); |
-_memoizeArray(map, arr, create) => JS( |
- '', |
- '''(() => { |
+_memoizeArray(map, arr, create) => JS('', '''(() => { |
let len = $arr.length; |
$map = $_lookupNonTerminal($map, len); |
for (var i = 0; i < len-1; ++i) { |
@@ -229,9 +240,7 @@ _memoizeArray(map, arr, create) => JS( |
// we slice off the remaining meta-data and make |
// it the second element of a packet for processing |
// later on in the constructor. |
-_normalizeParameter(a) => JS( |
- '', |
- '''(() => { |
+_normalizeParameter(a) => JS('', '''(() => { |
if ($a instanceof Array) { |
let result = []; |
result.push(($a[0] == $dynamic) ? $bottom : $a[0]); |
@@ -241,9 +250,7 @@ _normalizeParameter(a) => JS( |
return ($a == $dynamic) ? $bottom : $a; |
})()'''); |
-List _canonicalizeArray(definite, array, map) => JS( |
- '', |
- '''(() => { |
+List _canonicalizeArray(definite, array, map) => JS('', '''(() => { |
let arr = ($definite) |
? $array |
: $array.map($_normalizeParameter); |
@@ -252,9 +259,7 @@ List _canonicalizeArray(definite, array, map) => JS( |
// TODO(leafp): This only canonicalizes of the names are |
// emitted in a consistent order. |
-_canonicalizeNamed(definite, named, map) => JS( |
- '', |
- '''(() => { |
+_canonicalizeNamed(definite, named, map) => JS('', '''(() => { |
let key = []; |
let names = $getOwnPropertyNames($named); |
let r = {}; |
@@ -269,9 +274,7 @@ _canonicalizeNamed(definite, named, map) => JS( |
return $_memoizeArray($map, key, () => $named); |
})()'''); |
-_lookupNonTerminal(map, key) => JS( |
- '', |
- '''(() => { |
+_lookupNonTerminal(map, key) => JS('', '''(() => { |
let result = $map.get($key); |
if (result !== void 0) return result; |
$map.set($key, result = new Map()); |
@@ -281,9 +284,7 @@ _lookupNonTerminal(map, key) => JS( |
// TODO(leafp): This handles some low hanging fruit, but |
// really we should make all of this faster, and also |
// handle more cases here. |
-_createSmall(count, definite, returnType, required) => JS( |
- '', |
- '''(() => { |
+_createSmall(count, definite, returnType, required) => JS('', '''(() => { |
let map = $_fnTypeSmallMap[$count]; |
let args = ($definite) ? $required |
: $required.map($_normalizeParameter); |
@@ -414,6 +415,39 @@ class FunctionType extends AbstractFunctionType { |
_stringValue = buffer; |
return buffer; |
} |
+ |
+ @JSExportName('is') |
+ bool is_T(obj) { |
+ if (JS('bool', 'typeof # == "function"', obj)) { |
+ var actual = JS('', '#[#]', obj, _runtimeType); |
+ // If there's no actual type, it's a JS function. |
+ // Allow them to subtype all Dart function types. |
+ return JS('bool', '# == null || !!#', actual, isSubtype(actual, this)); |
+ } |
+ return false; |
+ } |
+ |
+ @JSExportName('as') |
+ as_T(obj, [bool typeError]) { |
+ if (obj == null) return obj; |
+ if (JS('bool', 'typeof # == "function"', obj)) { |
+ var actual = JS('', '#[#]', obj, _runtimeType); |
+ // If there's no actual type, it's a JS function. |
+ // Allow them to subtype all Dart function types. |
+ if (actual == null) return obj; |
+ var result = isSubtype(actual, this); |
+ if (result == true) return obj; |
+ if (result == null && JS('bool', 'dart.__ignoreWhitelistedErrors')) { |
+ JS('', "console.warn(#)", |
+ 'Ignoring cast fail from ${typeName(actual)} to ${typeName(this)}'); |
+ return obj; |
+ } |
+ } |
+ return castError(obj, this, typeError); |
+ } |
+ |
+ @JSExportName('_check') |
+ check_T(obj) => as_T(obj, true); |
} |
class Typedef extends AbstractFunctionType { |
@@ -431,6 +465,15 @@ class Typedef extends AbstractFunctionType { |
var ft = _functionType; |
return ft == null ? _functionType = _closure() : ft; |
} |
+ |
+ @JSExportName('is') |
+ bool is_T(object) => functionType.is_T(object); |
+ |
+ @JSExportName('as') |
+ as_T(object) => functionType.as_T(object); |
+ |
+ @JSExportName('_check') |
+ check_T(object) => functionType.check_T(object); |
} |
/// A type variable, used by [GenericFunctionType] to represent a type formal. |
@@ -604,9 +647,29 @@ class GenericFunctionType extends AbstractFunctionType { |
'recursive generic bounds: ${typeName(this)}. ' |
'Try passing explicit type arguments.'); |
} |
- |
return defaults; |
} |
+ |
+ @JSExportName('is') |
+ bool is_T(obj) { |
+ if (JS('bool', 'typeof # == "function"', obj)) { |
+ var actual = JS('', '#[#]', obj, _runtimeType); |
+ return JS('bool', '# != null && !!#', actual, isSubtype(actual, this)); |
+ } |
+ return false; |
+ } |
+ |
+ @JSExportName('as') |
+ as_T(obj) { |
+ if (obj == null || JS('bool', '#', is_T(obj))) return obj; |
+ return castError(obj, this, false); |
+ } |
+ |
+ @JSExportName('_check') |
+ check_T(obj) { |
+ if (obj == null || JS('bool', '#', is_T(obj))) return obj; |
+ return castError(obj, this, true); |
+ } |
} |
typedef(name, AbstractFunctionType Function() closure) => |
@@ -653,9 +716,7 @@ getFunctionTypeMirror(AbstractFunctionType type) { |
bool isType(obj) => JS('', '# === #', _getRuntimeType(obj), Type); |
-String typeName(type) => JS( |
- '', |
- '''(() => { |
+String typeName(type) => JS('', '''(() => { |
if ($type === void 0) return "undefined type"; |
if ($type === null) return "null type"; |
// Non-instance types |
@@ -710,9 +771,7 @@ bool _isFunctionType(type) => JS('bool', '# instanceof # || # === #', type, |
/// If [isCovariant] is true, then we are checking subtyping in a covariant |
/// position, and hence the direction of the check for function types |
/// corresponds to the direction of the check according to the Dart spec. |
-isFunctionSubtype(ft1, ft2, isCovariant) => JS( |
- '', |
- '''(() => { |
+isFunctionSubtype(ft1, ft2, isCovariant) => JS('', '''(() => { |
if ($ft2 === $Function) { |
return true; |
} |
@@ -795,20 +854,22 @@ bool isSubtype(t1, t2) { |
// TODO(leafp): This duplicates code in operations.dart. |
// I haven't found a way to factor it out that makes the |
// code generator happy though. |
- var map = JS('', '#.get(#)', _memo, t1); |
+ var map; |
bool result; |
- if (JS('bool', '# !== void 0', map)) { |
- result = JS('bool', '#.get(#)', map, t2); |
- if (JS('bool', '# !== void 0', result)) return result; |
+ if (JS('bool', '!#.hasOwnProperty(#)', t1, _subtypeCache)) { |
+ JS('', '#[#] = # = new Map()', t1, _subtypeCache, map); |
} else { |
- JS('', '#.set(#, # = new Map())', _memo, t1, map); |
+ map = JS('', '#[#]', t1, _subtypeCache); |
+ result = JS('bool|Null', '#.get(#)', map, t2); |
+ if (JS('bool', '# !== void 0', result)) return result; |
} |
- result = JS('', '# === # || #(#, #, true)', t1, t2, _isSubtype, t1, t2); |
+ result = |
+ JS('bool|Null', '# === # || #(#, #, true)', t1, t2, _isSubtype, t1, t2); |
JS('', '#.set(#, #)', map, t2, result); |
return result; |
} |
-final _memo = JS('', 'new Map()'); |
+final _subtypeCache = JS('', 'Symbol("_subtypeCache")'); |
_isBottom(type) => JS('bool', '# == # || # == #', type, bottom, type, Null); |
@@ -823,9 +884,7 @@ _isTop(type) { |
bool _isFutureOr(type) => |
JS('bool', '# === #', getGenericClass(type), getGenericClass(FutureOr)); |
-bool _isSubtype(t1, t2, isCovariant) => JS( |
- '', |
- '''(() => { |
+bool _isSubtype(t1, t2, isCovariant) => JS('', '''(() => { |
if ($t1 === $t2) return true; |
// Trivially true. |
@@ -879,7 +938,7 @@ bool _isSubtype(t1, t2, isCovariant) => JS( |
if ($t2 instanceof $AnonymousJSType) { |
// All JS types are subtypes of anonymous JS types. |
- return $_isJSType($t1); |
+ return $t1 === $jsobject; |
} |
if ($t2 instanceof $LazyJSType) { |
return $_isSubtype($t1, $t2.rawJSTypeForCheck(), isCovariant); |
@@ -948,9 +1007,7 @@ bool _isSubtype(t1, t2, isCovariant) => JS( |
return false; |
})()'''); |
-isClassSubType(t1, t2, isCovariant) => JS( |
- '', |
- '''(() => { |
+isClassSubType(t1, t2, isCovariant) => JS('', '''(() => { |
// We support Dart's covariant generics with the caveat that we do not |
// substitute bottom for dynamic in subtyping rules. |
// I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: |