blob: d2cb413e9e994b17ccca167f626badcb1cb39fe5 [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.
library kernel.transformations.erasure;
import '../ast.dart';
import '../type_algebra.dart';
/// This pass is a temporary measure to run strong mode code in the VM, which
/// does not yet have the necessary runtime support.
///
/// Function type parameter lists are cleared and all uses of a function type
/// parameter are replaced by its upper bound.
///
/// All uses of type parameters in constants are replaced by 'dynamic'.
///
/// This does not preserve dynamic type safety.
class Erasure extends Transformer {
final Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
final Map<TypeParameter, DartType> constantSubstitution =
<TypeParameter, DartType>{};
int constantContexts = 0;
void transform(Program program) {
program.accept(this);
}
void pushConstantContext() {
++constantContexts;
}
void popConstantContext() {
--constantContexts;
}
bool get isInConstantContext => constantContexts > 0;
@override
visitDartType(DartType type) {
type = substitute(type, substitution);
if (isInConstantContext) {
type = substitute(type, constantSubstitution);
}
return type;
}
@override
visitClass(Class node) {
for (var parameter in node.typeParameters) {
constantSubstitution[parameter] = const DynamicType();
}
node.transformChildren(this);
constantSubstitution.clear();
return node;
}
@override
visitProcedure(Procedure node) {
if (node.kind == ProcedureKind.Factory) {
assert(node.enclosingClass != null);
assert(node.enclosingClass.typeParameters.length ==
node.function.typeParameters.length);
// Factories can have function type parameters as long as they correspond
// exactly to those on the enclosing class. However, these type parameters
// may still not be in a constant.
for (var parameter in node.function.typeParameters) {
constantSubstitution[parameter] = const DynamicType();
}
// Skip the visitFunctionNode but traverse body.
node.function.transformChildren(this);
node.function.typeParameters.forEach(constantSubstitution.remove);
} else {
node.transformChildren(this);
}
return node;
}
bool isObject(DartType type) {
return type is InterfaceType && type.classNode.supertype == null;
}
@override
visitFunctionNode(FunctionNode node) {
for (var parameter in node.typeParameters) {
substitution[parameter] = const DynamicType();
}
for (var parameter in node.typeParameters) {
if (!isObject(parameter.bound)) {
substitution[parameter] = substitute(parameter.bound, substitution);
}
}
node.transformChildren(this);
node.typeParameters.forEach(substitution.remove);
node.typeParameters.clear();
return node;
}
@override
visitStaticInvocation(StaticInvocation node) {
if (node.target.kind != ProcedureKind.Factory) {
node.arguments.types.clear();
}
if (node.isConst) pushConstantContext();
node.transformChildren(this);
if (node.isConst) popConstantContext();
return node;
}
@override
visitMethodInvocation(MethodInvocation node) {
node.arguments.types.clear();
node.transformChildren(this);
return node;
}
@override
visitDirectMethodInvocation(DirectMethodInvocation node) {
node.arguments.types.clear();
node.transformChildren(this);
return node;
}
@override
visitConstructorInvocation(ConstructorInvocation node) {
if (node.isConst) pushConstantContext();
node.transformChildren(this);
if (node.isConst) popConstantContext();
return node;
}
@override
visitListLiteral(ListLiteral node) {
if (node.isConst) pushConstantContext();
node.transformChildren(this);
if (node.isConst) popConstantContext();
return node;
}
@override
visitMapLiteral(MapLiteral node) {
if (node.isConst) pushConstantContext();
node.transformChildren(this);
if (node.isConst) popConstantContext();
return node;
}
}