Chromium Code Reviews| Index: pkg/dev_compiler/lib/src/compiler/js_typerep.dart |
| diff --git a/pkg/dev_compiler/lib/src/compiler/js_typerep.dart b/pkg/dev_compiler/lib/src/compiler/js_typerep.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0173f1d2895a72deb19d1714e4d42babd5c541a4 |
| --- /dev/null |
| +++ b/pkg/dev_compiler/lib/src/compiler/js_typerep.dart |
| @@ -0,0 +1,165 @@ |
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
|
Jennifer Messerly
2017/06/26 22:42:58
2017
Leaf
2017/06/30 21:55:32
I always feel vaguely guilty copy and pasting a co
|
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +import 'package:analyzer/dart/element/element.dart'; |
| +import 'package:analyzer/dart/element/type.dart'; |
| +import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| +import 'package:analyzer/src/generated/type_system.dart' |
| + show StrongTypeSystemImpl; |
| + |
| +/// An abstraction of the JS types |
| +abstract class JSType { |
|
Jennifer Messerly
2017/06/26 22:42:58
this is wonderful!!! thanks for putting this abstr
|
| + const JSType(); |
| + |
| + /// True if this type is built-in to JS, and we use the values unwrapped. |
| + /// For these types we generate a calling convention via static |
| + /// "extension methods". This allows types to be extended without adding |
| + /// extensions directly on the prototype. |
| + bool get isPrimitive; |
| + |
| + /// Is this type known to be definitively primitive |
| + /// (using the JS notion of primitive) |
| + bool get isPrimitiveInJS; |
| + |
| + /// Can a non-null element of this type potentially be interpreted |
| + /// as false in JS. |
| + bool get isFalsey; |
| + |
| + static const jsBoolean = const JSBoolean(); |
| + static const jsNumber = const JSNumber(); |
| + static const jsNull = const JSNull(); |
| + static const jsObject = const JSObject(); |
| + static const jsString = const JSString(); |
| + static const jsUnknown = const JSUnknown(); |
| +} |
| + |
| +/// Inhabited by booleans (including JSBool), null, and undefined |
| +class JSBoolean extends JSType { |
| + const JSBoolean(); |
| + bool get isPrimitive => true; |
| + bool get isPrimitiveInJS => true; |
| + bool get isFalsey => true; |
| +} |
| + |
| +/// Inhabited by numbers, null, and undefined |
| +/// In practice, this is 4 types: num, int, double, and _interceptors.JSNumber. |
| +/// |
| +/// _interceptors.JSNumber is the type that actually "implements" all numbers, |
| +/// hence it's a subtype of int and double (and num). |
| +/// It's defined in our "dart:_interceptors". |
| +class JSNumber extends JSType { |
| + const JSNumber(); |
| + bool get isPrimitive => true; |
| + bool get isPrimitiveInJS => true; |
| + bool get isFalsey => true; |
| +} |
| + |
| +/// Inhabited by null and undefined |
| +class JSNull extends JSType { |
| + const JSNull(); |
| + bool get isPrimitive => false; |
| + bool get isPrimitiveInJS => false; |
| + bool get isFalsey => true; |
| +} |
| + |
| +/// Inhabited by objects, null, and undefined |
| +class JSObject extends JSType { |
| + const JSObject(); |
| + bool get isPrimitive => false; |
| + bool get isPrimitiveInJS => false; |
| + bool get isFalsey => false; |
| +} |
| + |
| +/// Inhabited by strings (including JSString), null, and undefined |
| +class JSString extends JSType { |
| + const JSString(); |
| + bool get isPrimitive => true; |
| + bool get isPrimitiveInJS => false; |
| + bool get isFalsey => true; |
| +} |
| + |
| +/// Inhabitance not statically known |
| +class JSUnknown extends JSType { |
| + const JSUnknown(); |
| + bool get isPrimitive => false; |
| + bool get isPrimitiveInJS => false; |
| + bool get isFalsey => true; |
| +} |
| + |
| +class JSTypeRep { |
| + final StrongTypeSystemImpl rules; |
| + final TypeProvider types; |
| + |
| + JSTypeRep(this.rules, this.types); |
| + |
| + JSType typeFor(DartType type) { |
| + while (type is TypeParameterType) { |
| + type = (type.element as TypeParameterElement).bound; |
|
Jennifer Messerly
2017/06/26 22:42:59
small suggestion:
(type as TypeParameterType)
Leaf
2017/06/30 21:55:32
Done.
Leaf
2017/06/30 21:55:32
Done.
|
| + } |
| + if (type == null) return JSType.jsUnknown; |
| + if (type.isDartCoreNull) return JSType.jsNull; |
| + if (type.isBottom) return JSType.jsNull; |
|
Jennifer Messerly
2017/06/26 22:42:58
worth a TODO that this needs changing if we get no
Leaf
2017/06/30 21:55:32
Done.
Leaf
2017/06/30 21:55:32
Done.
|
| + if (rules.isSubtypeOf(type, types.numType)) return JSType.jsNumber; |
| + if (rules.isSubtypeOf(type, types.boolType)) return JSType.jsBoolean; |
| + if (rules.isSubtypeOf(type, types.stringType)) return JSType.jsString; |
| + if (type.isDartAsyncFutureOr) { |
| + var argument = (type as InterfaceType).typeArguments[0]; |
| + var argumentRep = typeFor(argument); |
| + if (argumentRep is JSObject || argumentRep is JSNull) { |
| + return JSType.jsObject; |
| + } |
| + return JSType.jsUnknown; |
| + } |
| + if (type.isDynamic || type.isObject || type.isVoid) return JSType.jsUnknown; |
| + return JSType.jsObject; |
| + } |
| + |
| + /// If the type [t] is [int] or [double], or a type parameter |
| + /// bounded by [int], [double] or [num] returns [num]. |
| + /// Otherwise returns [t]. |
| + DartType canonicalizeNumTypes(DartType t) { |
|
Jennifer Messerly
2017/06/26 22:42:58
perhaps:
=> isNumber(t) ? types.numType : t;
Leaf
2017/06/30 21:55:32
Done.
Leaf
2017/06/30 21:55:32
Done.
|
| + if (isNumber(t)) return types.numType; |
| + return t; |
| + } |
| + |
| + bool isNumber(DartType type) => typeFor(type) is JSNumber; |
| + |
| + /// Is this type known to be represented as Object or Null in JS. |
| + bool isObjectOrNull(DartType t) { |
| + var rep = typeFor(t); |
| + return rep is JSObject || rep is JSNull; |
| + } |
| + |
| + /// Do non-Null instances of this type definitely have the Dart Object methods |
|
Jennifer Messerly
2017/06/26 22:42:58
(this may be moot based on another comment below t
|
| + /// inserted directly on the prototype (albeit potentially with symbolized |
| + /// names). |
| + bool objectMethodsOnPrototype(DartType t) { |
|
Jennifer Messerly
2017/06/26 22:42:58
I think this method won't be needed.
fwiw, almost
Leaf
2017/06/30 21:55:32
Done.
|
| + var rep = typeFor(t); |
| + if (rep is! JSObject) return false; |
| + // Functions may not have the object methods on their prototypes. |
|
Jennifer Messerly
2017/06/26 22:42:58
FWIW, we could fix this if we wanted to (adding th
Leaf
2017/06/30 21:55:32
Acknowledged.
|
| + return t is InterfaceType; |
| + } |
| + |
| + bool isPrimitive(DartType t) => typeFor(t).isPrimitive; |
| + |
| + bool isPrimitiveInJS(DartType t) => typeFor(t).isPrimitiveInJS; |
| + |
| + bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
| + isPrimitiveInJS(leftT) && isPrimitiveInJS(rightT); |
| + |
| + bool unaryOperationIsPrimitive(DartType t) => isPrimitiveInJS(t); |
| + |
| + /// True if the JS double equals (`==`) comparison on the representation |
| + /// of these two types could potentially cause a conversion. |
| + bool equalityMayConvert(DartType t0, DartType t1) => !equalOrNull(t0, t1); |
| + |
| + // Are t0 and t1 known to either be represented by the same type |
| + // or else one or both of them is represented by Null |
| + bool equalOrNull(DartType t0, DartType t1) { |
| + var rep0 = typeFor(t0); |
| + var rep1 = typeFor(t1); |
| + if (rep0 is JSNull || rep1 is JSNull) return true; |
| + return rep0 == rep1 && rep0 is! JSUnknown; |
| + } |
| +} |