blob: e0d73cbdf1b37fcfe1894ef3fce6650ade0d171c [file] [log] [blame]
// 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.
/// 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;
/// The JS `typeof` value, if unambiguous.
String? get primitiveTypeOf => null;
static const jsBoolean = JSBoolean();
static const jsNumber = JSNumber();
static const jsNull = JSNull();
static const jsObject = JSObject();
static const jsString = JSString();
static const jsUnknown = JSUnknown();
// TODO(jmesserly): add JSFunction that maps to Dart's `Function` type.
}
/// Inhabited by booleans (including JSBool), null, and undefined
class JSBoolean extends JSType {
const JSBoolean();
@override
bool get isPrimitive => true;
@override
bool get isPrimitiveInJS => true;
@override
bool get isFalsey => true;
@override
String get primitiveTypeOf => 'boolean';
}
/// 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();
@override
bool get isPrimitive => true;
@override
bool get isPrimitiveInJS => true;
@override
bool get isFalsey => true;
@override
String get primitiveTypeOf => 'number';
}
/// Inhabited by null and undefined
class JSNull extends JSType {
const JSNull();
@override
bool get isPrimitive => false;
@override
bool get isPrimitiveInJS => false;
@override
bool get isFalsey => true;
}
/// Inhabited by objects, null, and undefined
class JSObject extends JSType {
const JSObject();
@override
bool get isPrimitive => false;
@override
bool get isPrimitiveInJS => false;
@override
bool get isFalsey => false;
}
/// Inhabited by strings (including JSString), null, and undefined
class JSString extends JSType {
const JSString();
@override
bool get isPrimitive => true;
@override
bool get isPrimitiveInJS => false;
@override
bool get isFalsey => true;
@override
String get primitiveTypeOf => 'string';
}
/// Inhabitance not statically known
class JSUnknown extends JSType {
const JSUnknown();
@override
bool get isPrimitive => false;
@override
bool get isPrimitiveInJS => false;
@override
bool get isFalsey => true;
}
abstract class SharedJSTypeRep<DartType> {
JSType typeFor(DartType type);
bool isNumber(DartType type) => typeFor(type) is JSNumber;
bool isBoolean(DartType type) => typeFor(type) is JSBoolean;
/// 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;
}
}