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..2d73cb049eff6cfe33703d074e961817514c9fce |
--- /dev/null |
+++ b/pkg/dev_compiler/lib/src/compiler/js_typerep.dart |
@@ -0,0 +1,154 @@ |
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
+// 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/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 { |
+ 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 as TypeParameterType).element.bound; |
+ } |
+ if (type == null) return JSType.jsUnknown; |
+ if (type.isDartCoreNull) return JSType.jsNull; |
+ // Note that this should be changed if Dart gets non-nullable types |
+ if (type.isBottom) return JSType.jsNull; |
+ 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) => isNumber(t) ? types.numType : 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; |
+ } |
+ |
+ bool isUnknown(DartType t) => typeFor(t) is JSUnknown; |
+ |
+ 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; |
+ } |
+} |