blob: 6217798b67908c8d31fef342e5f9a35992053981 [file] [log] [blame]
// Copyright (c) 2016, 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.
part of js_ast;
final _any = new AnyTypeRef._();
final _unknown = new UnknownTypeRef._();
final _null = new NullTypeRef();
/// JavaScript type reference, designed to support a subset of the type systems
/// of the Closure Compiler and TypeScript:
/// - https://developers.google.com/closure/compiler/docs/js-for-compiler#types
/// - https://github.com/Microsoft/TypeScript/blob/v1.8.0-beta/doc/spec.md#3
///
/// Note that some subtleties like "nullability" or "optionality" are handled
/// using unions (with a [NullTypeRef] or with an "undefined" named typeref).
/// Also, primitives aren't modeled differently than named / qualified types,
/// as it brings little value for now. Primitive-specific type formatting is
/// handled by the type printers (for instance, the knowledge that
/// `number|null` is just `number` in TypeScript, and is `number?` in Closure).
abstract class TypeRef extends Expression {
int get precedenceLevel => PRIMARY;
TypeRef();
factory TypeRef.any() => _any;
factory TypeRef.void_() => new TypeRef.named('void');
factory TypeRef.unknown() => _unknown;
factory TypeRef.generic(TypeRef rawType, Iterable<TypeRef> typeArgs) {
if (typeArgs.isEmpty) {
throw new ArgumentError.value(typeArgs, "typeArgs", "is empty");
}
return new GenericTypeRef(rawType, typeArgs.toList());
}
factory TypeRef.array([TypeRef elementType]) => new ArrayTypeRef(elementType);
factory TypeRef.object([TypeRef keyType, TypeRef valueType]) {
// TODO(ochafik): Roll out a dedicated ObjectTypeRef?
var rawType = new TypeRef.named('Object');
return keyType == null && valueType == null
? rawType
: new GenericTypeRef(rawType, [keyType ?? _any, valueType ?? _any]);
}
factory TypeRef.function(
[TypeRef returnType, Map<Identifier, TypeRef> paramTypes]) =>
new FunctionTypeRef(returnType, paramTypes);
factory TypeRef.record(Map<Identifier, TypeRef> types) =>
new RecordTypeRef(types);
factory TypeRef.string() => new TypeRef.named('string');
factory TypeRef.number() => new TypeRef.named('number');
factory TypeRef.undefined() => new TypeRef.named('undefined');
factory TypeRef.boolean() => new TypeRef.named('boolean');
factory TypeRef.qualified(List<Identifier> path) =>
new QualifiedTypeRef(path);
factory TypeRef.named(String name) =>
new TypeRef.qualified(<Identifier>[new Identifier(name)]);
bool get isAny => this is AnyTypeRef;
bool get isUnknown => this is UnknownTypeRef;
bool get isNull => this is NullTypeRef;
TypeRef or(TypeRef other) => new UnionTypeRef([this, other]);
TypeRef orUndefined() => or(new TypeRef.undefined());
TypeRef orNull() => or(_null);
TypeRef toOptional() => new OptionalTypeRef(this);
}
class AnyTypeRef extends TypeRef {
AnyTypeRef._() : super();
factory AnyTypeRef() => _any;
T accept<T>(NodeVisitor<T> visitor) => visitor.visitAnyTypeRef(this);
void visitChildren(NodeVisitor visitor) {}
_clone() => new AnyTypeRef();
}
class NullTypeRef extends QualifiedTypeRef {
NullTypeRef() : super([new Identifier("null")]);
_clone() => new NullTypeRef();
}
class UnknownTypeRef extends TypeRef {
UnknownTypeRef._() : super();
factory UnknownTypeRef() => _unknown;
T accept<T>(NodeVisitor<T> visitor) => visitor.visitUnknownTypeRef(this);
void visitChildren(NodeVisitor visitor) {}
_clone() => new UnknownTypeRef();
}
class QualifiedTypeRef extends TypeRef {
final List<Identifier> path;
QualifiedTypeRef(this.path);
T accept<T>(NodeVisitor<T> visitor) => visitor.visitQualifiedTypeRef(this);
void visitChildren(NodeVisitor visitor) =>
path.forEach((p) => p.accept(visitor));
_clone() => new QualifiedTypeRef(path);
}
class ArrayTypeRef extends TypeRef {
final TypeRef elementType;
ArrayTypeRef(this.elementType);
T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrayTypeRef(this);
void visitChildren(NodeVisitor visitor) {
elementType.accept(visitor);
}
_clone() => new ArrayTypeRef(elementType);
}
class GenericTypeRef extends TypeRef {
final TypeRef rawType;
final List<TypeRef> typeArgs;
GenericTypeRef(this.rawType, this.typeArgs);
T accept<T>(NodeVisitor<T> visitor) => visitor.visitGenericTypeRef(this);
void visitChildren(NodeVisitor visitor) {
rawType.accept(visitor);
typeArgs.forEach((p) => p.accept(visitor));
}
_clone() => new GenericTypeRef(rawType, typeArgs);
}
class UnionTypeRef extends TypeRef {
final List<TypeRef> types;
UnionTypeRef(this.types);
T accept<T>(NodeVisitor<T> visitor) => visitor.visitUnionTypeRef(this);
void visitChildren(NodeVisitor visitor) {
types.forEach((p) => p.accept(visitor));
}
_clone() => new UnionTypeRef(types);
@override
TypeRef or(TypeRef other) {
if (types.contains(other)) return this;
return new UnionTypeRef([]
..addAll(types)
..add(other));
}
}
class OptionalTypeRef extends TypeRef {
final TypeRef type;
OptionalTypeRef(this.type);
T accept<T>(NodeVisitor<T> visitor) => visitor.visitOptionalTypeRef(this);
void visitChildren(NodeVisitor visitor) {
type.accept(visitor);
}
_clone() => new OptionalTypeRef(type);
@override
TypeRef orUndefined() => this;
}
class RecordTypeRef extends TypeRef {
final Map<Identifier, TypeRef> types;
RecordTypeRef(this.types);
T accept<T>(NodeVisitor<T> visitor) => visitor.visitRecordTypeRef(this);
void visitChildren(NodeVisitor visitor) {
types.values.forEach((p) => p.accept(visitor));
}
_clone() => new RecordTypeRef(types);
}
class FunctionTypeRef extends TypeRef {
final TypeRef returnType;
final Map<Identifier, TypeRef> paramTypes;
FunctionTypeRef(this.returnType, this.paramTypes);
T accept<T>(NodeVisitor<T> visitor) => visitor.visitFunctionTypeRef(this);
void visitChildren(NodeVisitor visitor) {
returnType.accept(visitor);
paramTypes.forEach((n, t) {
n.accept(visitor);
t.accept(visitor);
});
}
_clone() => new FunctionTypeRef(returnType, paramTypes);
}