| 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;
|
| + }
|
| +}
|
|
|