blob: ed1543c0289be37231d1bfd688bef88b14de9e44 [file] [log] [blame]
// Copyright (c) 2019, 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.
library dart2js.js_model.type_recipe;
import '../elements/types.dart';
/// A TypeEnvironmentStructure describes the shape or layout of a reified type
/// environment.
///
/// A type environment maps type parameter variables to type values. The type
/// variables are mostly elided in the runtime representation, replaced by
/// indexes into the reified environment.
abstract class TypeEnvironmentStructure {
/// Structural equality on [TypeEnvironmentStructure].
static bool same(TypeEnvironmentStructure a, TypeEnvironmentStructure b) {
if (a is SingletonTypeEnvironmentStructure) {
if (b is SingletonTypeEnvironmentStructure) {
return a.variable == b.variable;
}
return false;
}
return _sameFullStructure(a, b);
}
static bool _sameFullStructure(
FullTypeEnvironmentStructure a, FullTypeEnvironmentStructure b) {
if (a.classType != b.classType) return false;
List<TypeVariableType> aBindings = a.bindings;
List<TypeVariableType> bBindings = b.bindings;
if (aBindings.length != bBindings.length) return false;
for (int i = 0; i < aBindings.length; i++) {
if (aBindings[i] != bBindings[i]) return false;
}
return true;
}
}
/// A singleton type environment maps a binds a single value.
class SingletonTypeEnvironmentStructure extends TypeEnvironmentStructure {
final TypeVariableType variable;
SingletonTypeEnvironmentStructure(this.variable);
@override
String toString() => 'SingletonTypeEnvironmentStructure($variable)';
}
/// A type environment containing an interface type and/or a tuple of function
/// type parameters.
class FullTypeEnvironmentStructure extends TypeEnvironmentStructure {
final InterfaceType classType;
final List<TypeVariableType> bindings;
FullTypeEnvironmentStructure({this.classType, this.bindings = const []});
@override
String toString() => 'FullTypeEnvironmentStructure($classType, $bindings)';
}
/// A TypeRecipe is evaluated against a type environment to produce either a
/// type, or another type environment.
abstract class TypeRecipe {
/// Returns `true` is [recipeB] evaluated in an environment described by
/// [structureB] gives the same type as [recipeA] evaluated in environment
/// described by [structureA].
static bool yieldsSameType(
TypeRecipe recipeA,
TypeEnvironmentStructure structureA,
TypeRecipe recipeB,
TypeEnvironmentStructure structureB) {
if (recipeA == recipeB &&
TypeEnvironmentStructure.same(structureA, structureB)) {
return true;
}
// TODO(sra): Type recipes that are different but equal modulo naming also
// yield the same type, e.g. `List<X> @X` and `List<Y> @Y`.
return false;
}
/// Returns `true` if [recipe] evaluates in an environment with [structure] to
/// the environment.
static bool isIdentity(
TypeRecipe recipe, TypeEnvironmentStructure structure) {
if (structure is SingletonTypeEnvironmentStructure &&
recipe is TypeExpressionRecipe) {
if (structure.variable == recipe.type) return true;
}
return false;
}
}
/// A recipe that yields a reified type.
class TypeExpressionRecipe extends TypeRecipe {
final DartType type;
TypeExpressionRecipe(this.type);
@override
bool operator ==(other) {
return other is TypeExpressionRecipe && type == other.type;
}
@override
String toString() => 'TypeExpressionRecipe($type)';
}
/// A recipe that yields a reified type environment.
abstract class TypeEnvironmentRecipe extends TypeRecipe {}
/// A recipe that yields a reified type environment that binds a single generic
/// function type parameter.
class SingletonTypeEnvironmentRecipe extends TypeEnvironmentRecipe {
final DartType type;
SingletonTypeEnvironmentRecipe(this.type);
@override
bool operator ==(other) {
return other is SingletonTypeEnvironmentRecipe && type == other.type;
}
@override
String toString() => 'SingletonTypeEnvironmentRecipe($type)';
}
/// A recipe that yields a reified type environment that binds a class instance
/// type and/or a tuple of types, usually generic function type arguments.
///
/// With no class is also used as a tuple of types.
class FullTypeEnvironmentRecipe extends TypeEnvironmentRecipe {
/// Type expression for the interface type of a class scope. `null` for
/// environments outside a class scope or a class scope where no supertype is
/// generic, or where optimization has determined that no use of the
/// environment requires any of the class type variables.
final InterfaceType classType;
// Type expressions for the tuple of function type arguments.
final List<DartType> types;
FullTypeEnvironmentRecipe({this.classType, this.types = const []});
@override
bool operator ==(other) {
return other is FullTypeEnvironmentRecipe && _equal(this, other);
}
static bool _equal(FullTypeEnvironmentRecipe a, FullTypeEnvironmentRecipe b) {
if (a.classType != b.classType) return false;
List<DartType> aTypes = a.types;
List<DartType> bTypes = b.types;
if (aTypes.length != bTypes.length) return false;
for (int i = 0; i < aTypes.length; i++) {
if (aTypes[i] != bTypes[i]) return false;
}
return true;
}
@override
String toString() => 'FullTypeEnvironmentRecipe($classType, $types)';
}