Chromium Code Reviews| 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..9b9e0ff419415a621f037645627e018de08174f0 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; |
| +} |
| + |
| +_lazyJSType_is(o, LazyJSType t) { |
|
Jennifer Messerly
2016/11/22 18:18:19
style guide naming suggestion:
_isInstanceOfLazyJ
|
| + 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); |
| +} |
| + |
| +_lazyJSType_as(o, LazyJSType t) { |
|
Jennifer Messerly
2016/11/22 18:18:19
same here:
_castToLazyJSType
(or _asInstanceOfLa
|
| + 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 $_lazyJSType_is(object, this); |
| + }; |
| + $LazyJSType.prototype.as = function as_T(object) { |
| + return $_lazyJSType_as(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) { |