Index: pkg/kernel/runtime/reify/types.dart |
diff --git a/pkg/kernel/runtime/reify/types.dart b/pkg/kernel/runtime/reify/types.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..630ca7da74c9e18a63a9fc35e7934af11124cf83 |
--- /dev/null |
+++ b/pkg/kernel/runtime/reify/types.dart |
@@ -0,0 +1,678 @@ |
+// 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. |
+ |
+/// Notation: |
+/// |
+/// * `[[T]]` is the runtime representation of type `T` (that is, `T` reified). |
+library kernel.transformations.reify.runtime.types; |
+ |
+import 'declarations.dart' show Class; |
+ |
+export 'declarations.dart'; |
+ |
+export 'interceptors.dart'; |
+ |
+// The public interface of this library are static functions to access parts of |
+// reified type objects and the constructors on the ReifiedType subclasses. |
+ |
+bool isSubtypeOf(ReifiedType a, ReifiedType b) => a._isSubtypeOf(b); |
+ |
+bool isMoreSpecificThan(ReifiedType a, ReifiedType b) { |
+ return a._isMoreSpecificThan(b); |
+} |
+ |
+Kind getKind(ReifiedType type) => type._kind; |
+ |
+ReifiedType asInstanceOf(Interface type, Class declaration) { |
+ return type.asInstanceOf(declaration); |
+} |
+ |
+List<ReifiedType> getTypeArguments(Interface type) => type._typeArguments; |
+ |
+bool isDynamic(ReifiedType type) => type._isDynamic; |
+ |
+bool isFunction(ReifiedType type) => type._isFunction; |
+ |
+bool isInterface(ReifiedType type) => type._isInterface; |
+ |
+bool isIntersection(ReifiedType type) => type._isIntersection; |
+ |
+bool isVariable(ReifiedType type) => type._isVariable; |
+ |
+bool isVoid(ReifiedType type) => type._isVoid; |
+ |
+bool isObject(ReifiedType type) => false; |
+ |
+ReifiedType getSupertype(var type) => type._supertype; |
+ |
+Iterable<ReifiedType> getInterfaces(Interface type) => type._interfaces; |
+ |
+ReifiedType subst(ReifiedType type, List<ReifiedType> arguments, |
+ List<ReifiedType> parameters) { |
+ return type._subst(arguments, parameters); |
+} |
+ |
+// TODO(ahe): Do we need ReifiedNullType? |
+ |
+ReifiedType _intersection(ReifiedType a, ReifiedType b) { |
+ if (a == null) return b; |
+ if (b == null) return a; |
+ if (a == b) return a; |
+ return new Intersection(a, b); |
+} |
+ |
+enum Kind { |
+ Bottom, |
+ Dynamic, |
+ Function, |
+ Interface, |
+ Intersection, |
+ Variable, |
+ Void, |
+} |
+ |
+abstract class ReifiedType { |
+ // TODO(ahe): Should this be a getter to save memory? Which is faster? |
+ final Kind _kind; |
+ |
+ const ReifiedType(this._kind); |
+ |
+ bool get _isDynamic => _kind == Kind.Dynamic; |
+ |
+ bool get _isFunction => _kind == Kind.Function; |
+ |
+ bool get _isInterface => _kind == Kind.Interface; |
+ |
+ bool get _isIntersection => _kind == Kind.Intersection; |
+ |
+ bool get _isVariable => _kind == Kind.Variable; |
+ |
+ bool get _isVoid => _kind == Kind.Void; |
+ |
+ bool get _isObject => false; |
+ |
+ /// Returns true if [this] is more specific than [type]. |
+ bool _isMoreSpecificThan(ReifiedType type); |
+ |
+ /// Performs the substitution `[arguments[i]/parameters[i]]this`. |
+ /// |
+ /// The notation is known from this lambda calculus rule: |
+ /// |
+ /// (lambda x.e0)e1 -> [e1/x]e0. |
+ /// |
+ /// Invariant: There must be the same number of [arguments] and [parameters]. |
+ ReifiedType _subst(List<ReifiedType> arguments, List<ReifiedType> parameters); |
+ |
+ /// Returns true if [this] is a subtype of [type]. |
+ bool _isSubtypeOf(ReifiedType type) { |
+ return _subst(const <ReifiedType>[const Bottom()], |
+ const <ReifiedType>[const Dynamic()])._isMoreSpecificThan(type); |
+ } |
+ |
+ bool _isAssignableTo(ReifiedType type) { |
+ if (type._isDynamic) return true; |
+ return this._isSubtypeOf(type) || type._isSubtypeOf(this); |
+ } |
+} |
+ |
+/// Represents the type `dynamic`. |
+class Dynamic extends ReifiedType { |
+ const Dynamic() : super(Kind.Dynamic); |
+ |
+ bool _isMoreSpecificThan(ReifiedType type) => type._isDynamic; |
+ |
+ ReifiedType _subst( |
+ List<ReifiedType> arguments, List<ReifiedType> parameters) { |
+ int index = 0; |
+ for (ReifiedType parameter in parameters) { |
+ if (parameter._isDynamic) return arguments[index]; |
+ index++; |
+ } |
+ return this; |
+ } |
+ |
+ String toString() => "dynamic"; |
+} |
+ |
+/// Represents the bottom type. |
+class Bottom extends ReifiedType { |
+ const Bottom() : super(Kind.Bottom); |
+ |
+ bool _isMoreSpecificThan(ReifiedType type) => true; |
+ |
+ ReifiedType _subst( |
+ List<ReifiedType> arguments, List<ReifiedType> parameters) { |
+ return this; |
+ } |
+ |
+ String toString() => "<bottom>"; |
+} |
+ |
+/// Represents the type `void`. |
+class Void extends ReifiedType { |
+ const Void() : super(Kind.Void); |
+ |
+ bool _isMoreSpecificThan(ReifiedType type) { |
+ // `void` isn't more specific than anything but itself. |
+ return type._isVoid; |
+ } |
+ |
+ bool _isSubtypeOf(ReifiedType type) { |
+ // `void` isn't the subtype of anything besides `dynamic` and itself. |
+ return type._isVoid || type._isDynamic; |
+ } |
+ |
+ ReifiedType _subst( |
+ List<ReifiedType> arguments, List<ReifiedType> parameters) { |
+ return this; |
+ } |
+ |
+ String toString() => "void"; |
+} |
+ |
+/// Represents an interface type. That is, the type of any class. |
+/// |
+/// For example, the type |
+/// |
+/// String |
+/// |
+/// Would be represented as: |
+/// |
+/// new Interface(stringDeclaration); |
+/// |
+/// Where `stringDeclaration` is an instance of [Class] which contains |
+/// information about [String]'s supertype and implemented interfaces. |
+/// |
+/// A parameterized type, for example: |
+/// |
+/// Box<int> |
+/// |
+/// Would be represented as: |
+/// |
+/// new Interface(boxDeclaration, |
+/// [new Interface(intDeclaration)]); |
+/// |
+/// Implementation notes and considerations: |
+/// |
+/// * It's possible that we want to split this class in two to save memory: one |
+/// for non-generic classes and one for generic classes. Only the latter |
+/// would need [_typeArguments]. However, this must be weighed against the |
+/// additional polymorphism. |
+/// * Generally, we don't canonicalize types. However, simple types like `new |
+/// Interface(intDeclaration)` should be canonicalized to save |
+/// memory. Precisely how this canonicalization will happen is TBD, but it |
+/// may simply be by using compile-time constants. |
+class Interface extends ReifiedType implements Type { |
+ final Class _declaration; |
+ |
+ final List<ReifiedType> _typeArguments; |
+ |
+ const Interface(this._declaration, |
+ [this._typeArguments = const <ReifiedType>[]]) |
+ : super(Kind.Interface); |
+ |
+ bool get _isObject => _declaration.supertype == null; |
+ |
+ Interface get _supertype { |
+ return _declaration.supertype |
+ ?._subst(_typeArguments, _declaration.variables); |
+ } |
+ |
+ Iterable<Interface> get _interfaces { |
+ return _declaration.interfaces.map((Interface type) { |
+ return type._subst(_typeArguments, _declaration.variables); |
+ }); |
+ } |
+ |
+ FunctionType get _callableType { |
+ return _declaration.callableType |
+ ?._subst(_typeArguments, _declaration.variables); |
+ } |
+ |
+ bool _isMoreSpecificThan(ReifiedType type) { |
+ if (type._isDynamic) return true; |
+ // Intersection types can only occur as the result of calling |
+ // [asInstanceOf], they should never be passed in to this method. |
+ assert(!type._isIntersection); |
+ if (type._isFunction) { |
+ return _callableType?._isMoreSpecificThan(type) ?? false; |
+ } |
+ if (!type._isInterface) return false; |
+ if (this == type) return true; |
+ ReifiedType supertype = asInstanceOfType(type); |
+ if (supertype == null) { |
+ // Special case: A callable class is a subtype of [Function], regardless |
+ // if it implements [Function]. It isn't more specific than |
+ // [Function]. The type representing [Function] is the supertype of |
+ // `declaration.callableType`. |
+ return _declaration.callableType?._supertype?._isSubtypeOf(type) ?? false; |
+ } |
+ if (type == supertype) return true; |
+ switch (supertype._kind) { |
+ case Kind.Dynamic: |
+ case Kind.Variable: |
+ // Shouldn't happen. See switch in [asInstanceOf]. |
+ throw "internal error: $supertype"; |
+ |
+ case Kind.Interface: |
+ Interface s = supertype; |
+ Interface t = type; |
+ for (int i = 0; i < s._typeArguments.length; i++) { |
+ if (!s._typeArguments[i]._isMoreSpecificThan(t._typeArguments[i])) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ |
+ case Kind.Intersection: |
+ return supertype._isMoreSpecificThan(type); |
+ |
+ default: |
+ throw "Internal error: unhandled kind '${type._kind}'"; |
+ } |
+ } |
+ |
+ bool _isSubtypeOf(ReifiedType type) { |
+ if (type._isInterface) { |
+ Interface interface = type; |
+ if (interface._declaration != this._declaration) { |
+ // This addition to the specified rules allows us to handle cases like |
+ // class D extends A<dynamic> {} |
+ // new D() is A<A> |
+ // where directly going to `isMoreSpecific` would leave `dynamic` in the |
+ // result of `asInstanceOf(A)` instead of bottom. |
+ ReifiedType that = asInstanceOf(interface._declaration); |
+ if (that != null) { |
+ return that._isSubtypeOf(type); |
+ } |
+ } |
+ } |
+ return super._isSubtypeOf(type) || |
+ (_callableType?._isSubtypeOf(type) ?? false); |
+ } |
+ |
+ /// Returns [this] translated to [type] if [type] is a supertype of |
+ /// [this]. Otherwise null. |
+ /// |
+ /// For example, given: |
+ /// |
+ /// class Box<T> {} |
+ /// class BeatBox extends Box<Beat> {} |
+ /// class Beat {} |
+ /// |
+ /// We have: |
+ /// |
+ /// [[BeatBox]].asInstanceOf([[Box]]) -> [[Box<Beat>]]. |
+ ReifiedType asInstanceOf(Class other) { |
+ if (_declaration == other) return this; |
+ ReifiedType result = _declaration.supertype |
+ ?._subst(_typeArguments, _declaration.variables) |
+ ?.asInstanceOf(other); |
+ for (Interface interface in _declaration.interfaces) { |
+ result = _intersection( |
+ result, |
+ interface |
+ ._subst(_typeArguments, _declaration.variables) |
+ .asInstanceOf(other)); |
+ } |
+ return result; |
+ } |
+ |
+ ReifiedType asInstanceOfType(Interface type) { |
+ return asInstanceOf(type._declaration); |
+ } |
+ |
+ Interface _subst(List<ReifiedType> arguments, List<ReifiedType> parameters) { |
+ List<ReifiedType> copy; |
+ int index = 0; |
+ for (ReifiedType typeArgument in _typeArguments) { |
+ ReifiedType substitution = typeArgument._subst(arguments, parameters); |
+ if (substitution != typeArgument) { |
+ if (copy == null) { |
+ copy = new List<ReifiedType>.from(_typeArguments); |
+ } |
+ copy[index] = substitution; |
+ } |
+ index++; |
+ } |
+ return copy == null ? this : new Interface(_declaration, copy); |
+ } |
+ |
+ String toString() { |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write(_declaration.name); |
+ if (_typeArguments.isNotEmpty) { |
+ sb.write("<"); |
+ sb.writeAll(_typeArguments, ", "); |
+ sb.write(">"); |
+ } |
+ return "$sb"; |
+ } |
+ |
+ int get hashCode { |
+ int code = 23; |
+ code = (71 * code + _declaration.hashCode) & 0x3fffffff; |
+ for (ReifiedType typeArgument in _typeArguments) { |
+ code = (71 * code + typeArgument.hashCode) & 0x3fffffff; |
+ } |
+ return code; |
+ } |
+ |
+ bool operator ==(other) { |
+ if (other is Interface) { |
+ if (_declaration != other._declaration) return false; |
+ if (identical(_typeArguments, other._typeArguments)) return true; |
+ assert(_typeArguments.length == other._typeArguments.length); |
+ for (int i = 0; i < _typeArguments.length; i++) { |
+ if (_typeArguments[i] != other._typeArguments[i]) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ return false; |
+ } |
+} |
+ |
+/// Represents the intersection type of [typeA] and [typeB]. The intersection |
+/// type represents a type that is a subtype of both [typeA] and [typeB]. |
+/// |
+/// This type is produced when a class implements the same interface twice with |
+/// different type arguments. For example: |
+/// |
+/// abstract class MyNumberList implements List<int>, List<double> {} |
+/// |
+/// Can lead to this intersection type: |
+/// |
+/// new Intersection([[List<int>]], [[List<double>]]) |
+/// |
+/// For example, |
+/// |
+/// [[MyNumberList]].asInstanceOf([[List]]) -> |
+/// new Intersection([[List<int>]], [[List<double>]]) |
+/// |
+/// Note: sometimes, people confuse this with union types. However the union |
+/// type of `A` and `B` would be anything that is a subtype of either `A` or |
+/// `B`. |
+/// |
+/// See [Intersection types] |
+/// (https://en.wikipedia.org/wiki/Type_system#Intersection_types). |
+class Intersection extends ReifiedType { |
+ final ReifiedType typeA; |
+ final ReifiedType typeB; |
+ |
+ const Intersection(this.typeA, this.typeB) : super(Kind.Intersection); |
+ |
+ bool _isMoreSpecificThan(ReifiedType type) { |
+ // In the above example, `MyNumberList` is a subtype of List<int> *or* |
+ // List<double>. |
+ return typeA._isMoreSpecificThan(type) || typeB._isMoreSpecificThan(type); |
+ } |
+ |
+ ReifiedType _subst( |
+ List<ReifiedType> arguments, List<ReifiedType> parameters) { |
+ ReifiedType aSubstitution = typeA._subst(arguments, parameters); |
+ ReifiedType bSubstitution = typeB._subst(arguments, parameters); |
+ return (aSubstitution == typeA && bSubstitution == typeB) |
+ ? this |
+ : _intersection(aSubstitution, bSubstitution); |
+ } |
+ |
+ String toString() => "{ $typeA, $typeB }"; |
+} |
+ |
+/// Represents a type variable aka type parameter. |
+/// |
+/// For example, this class: |
+/// |
+/// class Box<T> {} |
+/// |
+/// Defines one type variable. In the type `Box<int>`, there are no type |
+/// variables. However, `int` is a type argument to the the type |
+/// parameter/variable `T`. |
+class TypeVariable extends ReifiedType { |
+ final int _id; |
+ |
+ // TODO(ahe): Do we need to reify bounds? |
+ ReifiedType bound; |
+ |
+ TypeVariable(this._id) : super(Kind.Variable); |
+ |
+ bool _isMoreSpecificThan(ReifiedType type) { |
+ if (type == this || type._isDynamic || type._isObject) return true; |
+ // The bounds of a type variable may contain a cycle, such as: |
+ // |
+ // class C<T extends S, S extends T> {} |
+ // |
+ // We use the [tortoise and hare algorithm] |
+ // (https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare) to |
+ // handle cycles. |
+ ReifiedType tortoise = bound; |
+ if (tortoise == type) return true; |
+ ReifiedType hare = getBoundOrNull(bound); |
+ while (tortoise != hare) { |
+ tortoise = getBoundOrNull(tortoise); |
+ if (tortoise == type) return true; |
+ hare = getBoundOrNull(getBoundOrNull(hare)); |
+ } |
+ // Here we know that `tortoise == hare`. Either they're both `null` or we |
+ // detected a cycle. |
+ if (tortoise != null) { |
+ // There was a cycle of type variables in the bounds, but it didn't |
+ // involve [type]. The variable [tortoise] visited all the type variables |
+ // in the cycle (at least once), and each time we compared it to [type]. |
+ return false; |
+ } |
+ // There are no cycles and it's safe to recurse on [bound]. |
+ return bound._isMoreSpecificThan(type); |
+ } |
+ |
+ ReifiedType _subst( |
+ List<ReifiedType> arguments, List<ReifiedType> parameters) { |
+ int index = 0; |
+ for (ReifiedType parameter in parameters) { |
+ if (this == parameter) return arguments[index]; |
+ index++; |
+ } |
+ return this; |
+ } |
+ |
+ String toString() => "#$_id"; |
+} |
+ |
+/// Represents a function type. |
+class FunctionType extends ReifiedType { |
+ /// Normally, the [Interface] representing [Function]. But an |
+ /// implementation-specific subtype of [Function] may also be used. |
+ final ReifiedType _supertype; |
+ |
+ final ReifiedType _returnType; |
+ |
+ /// Encodes number of optional parameters and if the optional parameters are |
+ /// named. |
+ final int _data; |
+ |
+ /// Encodes the argument types. Positional parameters use one element, the |
+ /// type; named parameters use two, the name [String] and type. Named |
+ /// parameters must be sorted by name. |
+ final List _encodedParameters; |
+ |
+ static const FunctionTypeRelation subtypeRelation = |
+ const FunctionSubtypeRelation(); |
+ |
+ static const FunctionTypeRelation moreSpecificRelation = |
+ const FunctionMoreSpecificRelation(); |
+ |
+ const FunctionType( |
+ this._supertype, this._returnType, this._data, this._encodedParameters) |
+ : super(Kind.Function); |
+ |
+ bool get hasNamedParameters => (_data & 1) == 1; |
+ |
+ int get optionalParameters => _data >> 1; |
+ |
+ int get parameters { |
+ return hasNamedParameters |
+ ? _encodedParameters.length - optionalParameters |
+ : _encodedParameters.length; |
+ } |
+ |
+ int get requiredParameters { |
+ return hasNamedParameters |
+ ? _encodedParameters.length - optionalParameters * 2 |
+ : _encodedParameters.length - optionalParameters; |
+ } |
+ |
+ bool _isSubtypeOf(ReifiedType type) => subtypeRelation.areRelated(this, type); |
+ |
+ bool _isMoreSpecificThan(ReifiedType type) { |
+ return moreSpecificRelation.areRelated(this, type); |
+ } |
+ |
+ FunctionType _subst( |
+ List<ReifiedType> arguments, List<ReifiedType> parameters) { |
+ List substitutedParameters; |
+ int positionalParameters = |
+ hasNamedParameters ? requiredParameters : this.parameters; |
+ for (int i = 0; i < _encodedParameters.length; i++) { |
+ if (i >= positionalParameters) { |
+ // Skip the name of a named parameter. |
+ i++; |
+ } |
+ ReifiedType type = _encodedParameters[i]; |
+ ReifiedType substitution = type._subst(arguments, parameters); |
+ if (substitution != type) { |
+ if (substitutedParameters == null) { |
+ substitutedParameters = new List.from(_encodedParameters); |
+ } |
+ substitutedParameters[i] = substitution; |
+ } |
+ } |
+ ReifiedType substitutedReturnType = |
+ _returnType._subst(arguments, parameters); |
+ if (substitutedParameters == null) { |
+ if (_returnType == substitutedReturnType) return this; |
+ substitutedParameters = _encodedParameters; |
+ } |
+ return new FunctionType( |
+ _supertype, substitutedReturnType, _data, substitutedParameters); |
+ } |
+ |
+ String toString() { |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write("("); |
+ bool first = true; |
+ for (int i = 0; i < requiredParameters; i++) { |
+ if (!first) { |
+ sb.write(", "); |
+ } |
+ sb.write(_encodedParameters[i]); |
+ first = false; |
+ } |
+ if (requiredParameters != parameters) { |
+ if (!first) { |
+ sb.write(", "); |
+ } |
+ if (hasNamedParameters) { |
+ sb.write("{"); |
+ first = true; |
+ for (int i = requiredParameters; |
+ i < _encodedParameters.length; |
+ i += 2) { |
+ if (!first) { |
+ sb.write(", "); |
+ } |
+ sb.write(_encodedParameters[i + 1]); |
+ sb.write(" "); |
+ sb.write(_encodedParameters[i]); |
+ first = false; |
+ } |
+ sb.write("}"); |
+ } else { |
+ sb.write("["); |
+ first = true; |
+ for (int i = requiredParameters; i < _encodedParameters.length; i++) { |
+ if (!first) { |
+ sb.write(", "); |
+ } |
+ sb.write(_encodedParameters[i]); |
+ first = false; |
+ } |
+ sb.write("]"); |
+ } |
+ } |
+ sb.write(") -> "); |
+ sb.write(_returnType); |
+ return "$sb"; |
+ } |
+} |
+ |
+abstract class FunctionTypeRelation { |
+ const FunctionTypeRelation(); |
+ |
+ bool areRelated(FunctionType self, ReifiedType type, {bool isMoreSpecific}) { |
+ if (!type._isFunction) { |
+ return arePartsRelated(self._supertype, type); |
+ } |
+ FunctionType other = type; |
+ if (!other._returnType._isVoid) { |
+ if (!arePartsRelated(self._returnType, other._returnType)) return false; |
+ } |
+ int positionalParameters = |
+ self.hasNamedParameters ? self.requiredParameters : self.parameters; |
+ int otherPositionalParameters = |
+ other.hasNamedParameters ? other.requiredParameters : other.parameters; |
+ if (self.requiredParameters > other.requiredParameters) return false; |
+ if (positionalParameters < otherPositionalParameters) return false; |
+ |
+ for (int i = 0; i < otherPositionalParameters; i++) { |
+ if (!arePartsRelated( |
+ self._encodedParameters[i], other._encodedParameters[i])) { |
+ return false; |
+ } |
+ } |
+ |
+ if (!other.hasNamedParameters) true; |
+ |
+ int j = positionalParameters; |
+ for (int i = otherPositionalParameters; |
+ i < other._encodedParameters.length; |
+ i += 2) { |
+ String name = other._encodedParameters[i]; |
+ for (; j < self._encodedParameters.length; j += 2) { |
+ if (self._encodedParameters[j] == name) break; |
+ } |
+ if (j == self._encodedParameters.length) return false; |
+ if (!arePartsRelated( |
+ self._encodedParameters[j + 1], other._encodedParameters[i + 1])) { |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+ } |
+ |
+ bool arePartsRelated(ReifiedType a, ReifiedType b); |
+} |
+ |
+class FunctionSubtypeRelation extends FunctionTypeRelation { |
+ const FunctionSubtypeRelation(); |
+ |
+ bool arePartsRelated(ReifiedType a, ReifiedType b) => a._isAssignableTo(b); |
+} |
+ |
+class FunctionMoreSpecificRelation extends FunctionTypeRelation { |
+ const FunctionMoreSpecificRelation(); |
+ |
+ bool arePartsRelated(ReifiedType a, ReifiedType b) => |
+ a._isMoreSpecificThan(b); |
+} |
+ |
+/// If [type] is a type variable, return its bound. Otherwise `null`. |
+ReifiedType getBoundOrNull(ReifiedType type) { |
+ if (type == null) return null; |
+ if (!type._isVariable) return null; |
+ TypeVariable tv = type; |
+ return tv.bound; |
+} |