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 1c1acb27bc294a45f533d0e58369833632fa0ffb..91a4ea82f883c5754c3557c915f098774700802a 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 |
@@ -56,7 +56,9 @@ final _typeObject = JS('', 'Symbol("typeObject")'); |
// TODO(jmesserly): we shouldn't implement Type here. It should be moved down |
// to AbstractFunctionType. |
class TypeRep implements Type { |
- TypeRep() { _initialize; } |
+ TypeRep() { |
+ _initialize; |
+ } |
String get name => this.toString(); |
} |
@@ -64,12 +66,46 @@ class Dynamic extends TypeRep { |
toString() => 'dynamic'; |
} |
+class LazyJSType implements Type { |
+ final _jsTypeCallback; |
+ final _dartName; |
+ |
+ LazyJSType(this._jsTypeCallback, this._dartName); |
+ |
+ get _rawJSType => JS('', '#()', _jsTypeCallback); |
+ |
+ toString() => _jsTypeCallback != null ? typeName(_rawJSType) : _dartName; |
+} |
+ |
+_isInstanceOfLazyJSType(o, LazyJSType t) { |
+ if (t._jsTypeCallback != null) { |
+ return JS('bool', 'dart.is(#, #)', o, t._rawJSType); |
+ } |
+ if (o == null) return false; |
+ // Anonymous case: match any JS type. |
+ return _isJSObject(o); |
+} |
+ |
+_asInstanceOfLazyJSType(o, LazyJSType t) { |
+ if (t._jsTypeCallback != null) { |
+ return JS('bool', 'dart.as(#, #)', o, t._rawJSType); |
+ } |
+ // Anonymous case: allow any JS type. |
+ if (o == null) return null; |
+ if (!_isJSObject(o)) _throwCastError(o, t, true); |
+ return o; |
+} |
+ |
+bool _isJSObject(o) => JS('bool', '!dart.getReifiedType(o)[dart._runtimeType]'); |
+ |
@JSExportName('dynamic') |
final _dynamic = new Dynamic(); |
final _initialize = _initialize2(); |
-_initialize2() => JS('', '''(() => { |
+_initialize2() => JS( |
+ '', |
+ '''(() => { |
// JavaScript API forwards to runtime library. |
$TypeRep.prototype.is = function is_T(object) { |
return dart.is(object, this); |
@@ -91,6 +127,13 @@ _initialize2() => JS('', '''(() => { |
$Dynamic.prototype._check = function check_Dynamic(object) { |
return object; |
}; |
+ |
+ $LazyJSType.prototype.is = function is_T(object) { |
+ return $_isInstanceOfLazyJSType(object, this); |
+ }; |
+ $LazyJSType.prototype.as = function as_T(object) { |
+ return $_asInstanceOfLazyJSType(object, this); |
+ }; |
})()'''); |
class Void extends TypeRep { |
@@ -103,6 +146,7 @@ final _void = new Void(); |
class Bottom extends TypeRep { |
toString() => 'bottom'; |
} |
+ |
final bottom = new Bottom(); |
class JSObject extends TypeRep { |
@@ -117,7 +161,9 @@ class WrappedType extends Type { |
toString() => typeName(_wrappedType); |
} |
-final AbstractFunctionType = JS('', ''' |
+final AbstractFunctionType = JS( |
+ '', |
+ ''' |
class AbstractFunctionType extends $TypeRep { |
constructor() { |
super(); |
@@ -172,6 +218,7 @@ final AbstractFunctionType = JS('', ''' |
/// reached via this path (if any) is the canonical representative |
/// for this packet. |
final _fnTypeNamedArgMap = JS('', 'new Map()'); |
+ |
/// Memo table for positional argument groups. A positional argument |
/// packet [type1, ..., typen] (required or optional) corresponds to |
/// the path n, type1, ...., typen. The element reached via |
@@ -179,11 +226,13 @@ final _fnTypeNamedArgMap = JS('', 'new Map()'); |
/// packet. Note that required and optional parameters packages |
/// may have the same canonical representation. |
final _fnTypeArrayArgMap = JS('', 'new Map()'); |
+ |
/// Memo table for function types. The index path consists of the |
/// path length - 1, the returnType, the canonical positional argument |
/// packet, and if present, the canonical optional or named argument |
/// packet. A level of indirection could be avoided here if desired. |
final _fnTypeTypeMap = JS('', 'new Map()'); |
+ |
/// Memo table for small function types with no optional or named |
/// arguments and less than a fixed n (currently 3) number of |
/// required arguments. Indexing into this table by the number |
@@ -192,7 +241,9 @@ final _fnTypeTypeMap = JS('', 'new Map()'); |
/// index path (if present) is the canonical function type. |
final _fnTypeSmallMap = JS('', '[new Map(), new Map(), new Map()]'); |
-final FunctionType = JS('', ''' |
+final FunctionType = JS( |
+ '', |
+ ''' |
class FunctionType extends $AbstractFunctionType { |
static _memoizeArray(map, arr, create) { |
let len = arr.length; |
@@ -346,8 +397,9 @@ final FunctionType = JS('', ''' |
} |
'''); |
- |
-final Typedef = JS('', ''' |
+final Typedef = JS( |
+ '', |
+ ''' |
class Typedef extends $AbstractFunctionType { |
constructor(name, closure) { |
super(); |
@@ -391,7 +443,9 @@ final Typedef = JS('', ''' |
final _typeFormalCount = JS('', 'Symbol("_typeFormalCount")'); |
-_functionType(definite, returnType, args, extra) => JS('', '''(() => { |
+_functionType(definite, returnType, args, extra) => JS( |
+ '', |
+ '''(() => { |
// TODO(jmesserly): this is a bit of a retrofit, to easily fit |
// generic functions into all of the existing ways we generate function |
// signatures. Given `(T) => [T, [T]]` we'll return a function that does |
@@ -427,7 +481,9 @@ definiteFunctionType(returnType, args, extra) => |
typedef(name, closure) => JS('', 'new #(#, #)', Typedef, name, closure); |
-typeName(type) => JS('', '''(() => { |
+typeName(type) => JS( |
+ '', |
+ '''(() => { |
if ($type === void 0) return "undefined type"; |
if ($type === null) return "null type"; |
// Non-instance types |
@@ -480,9 +536,16 @@ getImplicitFunctionType(type) { |
return getMethodTypeFromType(type, 'call'); |
} |
-bool isFunctionType(type) => |
- JS('bool', '# instanceof # || # === #', |
- type, AbstractFunctionType, type, Function); |
+bool isFunctionType(type) => JS('bool', '# instanceof # || # === #', type, |
+ AbstractFunctionType, type, Function); |
+ |
+isLazyJSSubtype(LazyJSType t1, LazyJSType t2, covariant) { |
+ if (t1 == t2) return true; |
+ |
+ // All anonymous JS types are subtypes of each other. |
+ if (t1._jsTypeCallback == null || t2._jsTypeCallback == null) return true; |
+ return isClassSubType(t1._rawJSType, t2._rawJSType, covariant); |
+} |
/// Returns true if [ft1] <: [ft2]. |
/// Returns false if [ft1] </: [ft2] in both spec and strong mode |
@@ -491,7 +554,9 @@ bool isFunctionType(type) => |
/// If [covariant] 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, covariant) => JS('', '''(() => { |
+isFunctionSubtype(ft1, ft2, covariant) => JS( |
+ '', |
+ '''(() => { |
if ($ft2 === $Function) { |
return true; |
} |
@@ -571,7 +636,9 @@ isFunctionSubtype(ft1, ft2, covariant) => JS('', '''(() => { |
/// 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. |
-_subtypeMemo(f) => JS('', '''(() => { |
+_subtypeMemo(f) => JS( |
+ '', |
+ '''(() => { |
let memo = new Map(); |
return (t1, t2) => { |
let map = memo.get(t1); |
@@ -592,14 +659,16 @@ _subtypeMemo(f) => JS('', '''(() => { |
/// Returns false if [t1] </: [t2] in both spec and strong mode |
/// Returns undefined if [t1] </: [t2] in strong mode, but spec |
/// mode may differ |
-final isSubtype = |
- JS('', '$_subtypeMemo((t1, t2) => (t1 === t2) || $_isSubtype(t1, t2, true))'); |
+final isSubtype = JS( |
+ '', '$_subtypeMemo((t1, t2) => (t1 === t2) || $_isSubtype(t1, t2, true))'); |
_isBottom(type) => JS('bool', '# == #', type, bottom); |
_isTop(type) => JS('bool', '# == # || # == #', type, Object, type, dynamic); |
-_isSubtype(t1, t2, covariant) => JS('', '''(() => { |
+_isSubtype(t1, t2, covariant) => JS( |
+ '', |
+ '''(() => { |
if ($t1 === $t2) return true; |
// Trivially true. |
@@ -633,10 +702,17 @@ _isSubtype(t1, t2, covariant) => JS('', '''(() => { |
if ($isFunctionType($t1) && $isFunctionType($t2)) { |
return $isFunctionSubtype($t1, $t2, $covariant); |
} |
+ |
+ if ($t1 instanceof $LazyJSType && $t2 instanceof $LazyJSType) { |
+ return $isLazyJSSubtype($t1, $t2, $covariant); |
+ } |
+ |
return false; |
})()'''); |
-isClassSubType(t1, t2, covariant) => JS('', '''(() => { |
+isClassSubType(t1, t2, covariant) => 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: |
@@ -717,7 +793,9 @@ isClassSubType(t1, t2, covariant) => JS('', '''(() => { |
// TODO(jmesserly): this isn't currently used, but it could be if we want |
// `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile |
// time. |
-isGroundType(type) => JS('', '''(() => { |
+isGroundType(type) => JS( |
+ '', |
+ '''(() => { |
// TODO(vsm): Cache this if we start using it at runtime. |
if ($type instanceof $AbstractFunctionType) { |