blob: 13af0edbfe825333f52e9265042a0a73b4090c9e [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.reify.transformation.builder;
import '../asts.dart';
import 'package:kernel/ast.dart';
import 'dart:collection' show LinkedHashMap;
import 'binding.dart' show RuntimeLibrary;
import 'package:kernel/core_types.dart' show CoreTypes;
class Scope {
final Map<String, TreeNode> names = <String, TreeNode>{};
bool nameAlreadyTaken(String name, TreeNode node) {
TreeNode existing = names[name];
return existing != null && existing == node;
}
void add(String name, TreeNode node) {
assert(!nameAlreadyTaken(name, node));
names[name] = node;
}
}
class Namer {
final Scope scope;
Namer([Scope scope]) : this.scope = scope ?? new Scope();
String _getProposal(TreeNode node) {
if (node is Class) {
return node.name;
}
throw 'unsupported node: $node';
}
String getNameFor(TreeNode node) {
String base = _getProposal(node);
int id = 0;
String proposal = base;
while (scope.nameAlreadyTaken(proposal, node)) {
proposal = "$base${++id}";
}
scope.add(proposal, node);
return proposal;
}
}
class RuntimeTypeSupportBuilder {
// TODO(karlklose): group this together with other information about what
// needs to be built.
final LinkedHashMap<Class, int> reifiedClassIds =
new LinkedHashMap<Class, int>();
int currentDeclarationId = 0;
final Field declarations;
final RuntimeLibrary rtiLibrary;
final CoreTypes coreTypes;
final DartType declarationType;
RuntimeTypeSupportBuilder(
RuntimeLibrary rtiLibrary, CoreTypes coreTypes, Library mainLibrary)
: declarations = new Field(new Name(r"$declarations"),
isFinal: true, isStatic: true, fileUri: mainLibrary.fileUri),
declarationType = new InterfaceType(coreTypes.listClass,
<DartType>[rtiLibrary.declarationClass.rawType]),
rtiLibrary = rtiLibrary,
coreTypes = coreTypes {
mainLibrary.addMember(declarations);
}
int addDeclaration(Class cls) {
return reifiedClassIds.putIfAbsent(cls, () {
return currentDeclarationId++;
});
}
final Name indexOperatorName = new Name("[]");
MethodInvocation createArrayAccess(Expression target, int index) {
return new MethodInvocation(target, indexOperatorName,
new Arguments(<Expression>[new IntLiteral(index)]));
}
Expression createAccessDeclaration(Class cls) {
return createArrayAccess(new StaticGet(declarations), addDeclaration(cls));
}
Name getTypeTestTagName(Class cls) {
return new Name('\$is\$${cls.name}');
}
Name typeVariableGetterName(TypeParameter parameter) {
Class cls = getEnclosingClass(parameter);
return new Name("\$${cls.name}\$${parameter.name}");
}
// A call to a constructor or factory of a class that we have not transformed
// is wrapped in a call to `attachType`.
Expression attachTypeToConstructorInvocation(
InvocationExpression invocation, Member member) {
assert(member is Procedure && member.kind == ProcedureKind.Factory ||
member is Constructor);
Class targetClass = member.parent;
assert(targetClass != null);
DartType type = new InterfaceType(targetClass, invocation.arguments.types);
return callAttachType(invocation, type);
}
Expression callAttachType(Expression expression, DartType type) {
return new StaticInvocation(rtiLibrary.attachTypeFunction,
new Arguments(<Expression>[expression, createRuntimeType(type)]));
}
Expression createGetType(Expression receiver, {needsInterceptor: true}) {
if (receiver is ThisExpression || !needsInterceptor) {
return new PropertyGet(receiver, rtiLibrary.runtimeTypeName);
}
return new StaticInvocation(
rtiLibrary.interceptorFunction, new Arguments(<Expression>[receiver]));
}
Expression createGetTypeArguments(Expression typeObject) {
return new StaticInvocation(rtiLibrary.typeArgumentsFunction,
new Arguments(<Expression>[typeObject]));
}
// TODO(karlklose): consider adding a unique identifier for each test site.
/// `receiver.[subtypeTestName]([type])`
StaticInvocation createIsSubtypeOf(
Expression receiver, Expression typeExpression,
{targetHasTypeProperty: false}) {
Expression receiverType =
createGetType(receiver, needsInterceptor: !targetHasTypeProperty);
return new StaticInvocation(rtiLibrary.isSubtypeOfFunction,
new Arguments(<Expression>[receiverType, typeExpression]));
}
int getTypeVariableIndex(TypeParameter variable) {
Class c = getEnclosingClass(variable);
List<TypeParameter> variables = c.typeParameters;
for (int i = 0; i < variables.length; ++i) {
if (variables[i].name == variable.name) {
return i;
}
}
throw new Exception(
"Type variable $variable not found in enclosing class $c");
}
Expression createNewInterface(
Expression declaration, Expression typeArgumentList) {
List<Expression> arguments = <Expression>[declaration];
if (typeArgumentList != null) {
arguments.add(typeArgumentList);
}
return new ConstructorInvocation(
rtiLibrary.interfaceTypeConstructor, new Arguments(arguments));
}
/// Returns `true` if [types] is a list of [TypeParameterType]s that exactly
/// match the [TypeParameters] of the class they are defined in, i.e.,
/// for all 0 <= i < cls.typeParameters.length.
/// types[i].parameter == cls.typeParameters[i].
bool matchesTypeParameters(List<DartType> types) {
List<TypeParameter> parameters;
for (int i = 0; i < types.length; ++i) {
var type = types[i];
if (type is TypeParameterType) {
if (parameters == null) {
Class cls = getEnclosingClass(type.parameter);
parameters = cls.typeParameters;
if (parameters.length != types.length) return false;
}
if (type.parameter != parameters[i]) {
return false;
}
} else {
return false;
}
}
return true;
}
// TODO(karlklose): Refactor into visitor.
// TODO(karlklose): split this method in different strategies.
/// Creates an expression to represent a runtime type instance of type [type].
Expression createRuntimeType(DartType type,
{reifyTypeVariable: false,
Expression createReference(Class cls),
VariableDeclaration typeContext}) {
Expression buildReifiedTypeVariable(TypeParameterType type) {
Expression typeVariables = new PropertyGet(
createReference(type.parameter.parent),
rtiLibrary.variablesFieldName);
return createArrayAccess(
typeVariables, getTypeVariableIndex(type.parameter));
}
Expression buildDirectTypeVariableAccess(TypeParameterType variable) {
Class cls = getEnclosingClass(variable.parameter);
return extractTypeVariable(
cls,
variable.parameter,
getTypeVariableIndex(variable.parameter),
new VariableGet(typeContext));
}
Expression buildGetterTypeVariableAccess(TypeParameterType type) {
return new PropertyGet(
new ThisExpression(), typeVariableGetterName(type.parameter));
}
Expression buildTypeVariable(TypeParameterType type) {
if (reifyTypeVariable) {
assert(typeContext == null);
return buildReifiedTypeVariable(type);
} else if (typeContext != null) {
return buildDirectTypeVariableAccess(type);
} else {
return buildGetterTypeVariableAccess(type);
}
}
createReference ??= createAccessDeclaration;
/// Helper to make recursive invocation more readable.
Expression createPart(DartType type) {
return createRuntimeType(type,
reifyTypeVariable: reifyTypeVariable,
createReference: createReference,
typeContext: typeContext);
}
if (type is InterfaceType || type is Supertype) {
InterfaceType interfaceType =
(type is InterfaceType) ? type : (type as Supertype).asInterfaceType;
Class cls = interfaceType.classNode;
Expression declaration = createReference(cls);
List<DartType> typeArguments = interfaceType.typeArguments;
Expression typeArgumentList;
if (typeArguments.isNotEmpty) {
if (!reifyTypeVariable && matchesTypeParameters(typeArguments)) {
// The type argument list corresponds to the list of type parameters
// and we are not in "declaration emitter" mode, we can reuse the
// type argument vector.
TypeParameterType parameterType = typeArguments[0];
Class cls = parameterType.parameter.parent;
Expression typeObject = typeContext != null
? new VariableGet(typeContext)
: createGetType(new ThisExpression());
typeArgumentList =
createGetTypeArguments(createCallAsInstanceOf(typeObject, cls));
} else {
typeArgumentList =
new ListLiteral(typeArguments.map(createPart).toList());
}
}
return createNewInterface(declaration, typeArgumentList);
} else if (type is DynamicType) {
return new ConstructorInvocation(
rtiLibrary.dynamicTypeConstructor, new Arguments([]),
isConst: true);
} else if (type is TypeParameterType) {
return buildTypeVariable(type);
} else if (type is FunctionType) {
FunctionType functionType = type;
Expression returnType = createPart(functionType.returnType);
List<Expression> encodedParameterTypes =
functionType.positionalParameters.map(createPart).toList();
List<NamedType> namedParameters = functionType.namedParameters;
int data;
if (namedParameters.isNotEmpty) {
for (NamedType param in namedParameters) {
encodedParameterTypes.add(new StringLiteral(param.name));
encodedParameterTypes.add(createPart(param.type));
}
data = functionType.namedParameters.length << 1 | 1;
} else {
data = (functionType.positionalParameters.length -
functionType.requiredParameterCount) <<
1;
}
Expression functionTypeExpression = new ConstructorInvocation(
rtiLibrary.interfaceTypeConstructor,
new Arguments(
<Expression>[createReference(coreTypes.functionClass)]));
Arguments arguments = new Arguments(<Expression>[
functionTypeExpression,
returnType,
new IntLiteral(data),
new ListLiteral(encodedParameterTypes)
]);
return new ConstructorInvocation(
rtiLibrary.functionTypeConstructor, arguments);
} else if (type is VoidType) {
return new ConstructorInvocation(
rtiLibrary.voidTypeConstructor, new Arguments(<Expression>[]));
}
return new InvalidExpression();
}
Expression createCallAsInstanceOf(Expression receiver, Class cls) {
return new StaticInvocation(rtiLibrary.asInstanceOfFunction,
new Arguments(<Expression>[receiver, createAccessDeclaration(cls)]));
}
/// `get get$<variable-name> => <get-type>.arguments[<variable-index>]`
Member createTypeVariableGetter(
Class cls, TypeParameter variable, int index) {
Expression type = createGetType(new ThisExpression());
Expression argument = extractTypeVariable(cls, variable, index, type);
return new Procedure(
typeVariableGetterName(variable),
ProcedureKind.Getter,
new FunctionNode(new ReturnStatement(argument),
returnType: rtiLibrary.typeType),
fileUri: cls.fileUri);
}
Expression extractTypeVariable(
Class cls, TypeParameter variable, int index, Expression typeObject) {
Expression type = createCallAsInstanceOf(typeObject, cls);
Expression arguments = new StaticInvocation(
rtiLibrary.typeArgumentsFunction, new Arguments(<Expression>[type]));
// TODO(karlklose): use the global index instead of the local one.
return createArrayAccess(arguments, index);
}
void insertAsFirstArgument(Arguments arguments, Expression expression) {
expression.parent = arguments;
arguments.positional.insert(0, expression);
}
/// Creates a call to the `init` function that completes the definition of a
/// class by setting its (direct) supertypes.
Expression createCallInit(
VariableDeclaration declarations,
int index,
InterfaceType supertype,
List<InterfaceType> interfaces,
FunctionType callableType) {
/// Helper to create a reference to the declaration in the declaration
/// list instead of the field to avoid cycles if that field's
/// initialization depends on the class we are currently initializing.
Expression createReference(Class declaration) {
int id = reifiedClassIds[declaration];
return createArrayAccess(new VariableGet(declarations), id);
}
bool isNotMarkerInterface(InterfaceType interface) {
return interface.classNode != rtiLibrary.markerClass;
}
Expression createLocalType(DartType type) {
if (type == null) return null;
return createRuntimeType(type,
reifyTypeVariable: true, createReference: createReference);
}
Expression supertypeExpression =
supertype == null ? new NullLiteral() : createLocalType(supertype);
List<Expression> interfaceTypes = interfaces
.where(isNotMarkerInterface)
.map(createLocalType)
.toList(growable: false);
Expression callableTypeExpression = createLocalType(callableType);
List<Expression> arguments = <Expression>[
new VariableGet(declarations),
new IntLiteral(index),
supertypeExpression,
];
if (interfaceTypes.isNotEmpty || callableTypeExpression != null) {
arguments.add(new ListLiteral(interfaceTypes));
if (callableTypeExpression != null) {
arguments.add(callableTypeExpression);
}
}
return new StaticInvocation(
rtiLibrary.initFunction, new Arguments(arguments));
}
Expression createDeclarationsInitializer() {
List<Statement> statements = <Statement>[];
// Call function to allocate the class declarations given the names and
// number of type variables of the classes.
Namer classNamer = new Namer();
List<Expression> names = <Expression>[];
List<Expression> parameterCount = <Expression>[];
reifiedClassIds.keys.forEach((Class c) {
names.add(new StringLiteral(classNamer.getNameFor(c)));
parameterCount.add(new IntLiteral(c.typeParameters.length));
});
Expression namesList = new ListLiteral(names);
Expression parameterCountList = new ListLiteral(parameterCount);
StaticInvocation callAllocate = new StaticInvocation(
rtiLibrary.allocateDeclarationsFunction,
new Arguments(<Expression>[namesList, parameterCountList]));
VariableDeclaration parameter =
new VariableDeclaration("d", type: declarationType);
reifiedClassIds.forEach((Class cls, int id) {
if (cls == rtiLibrary.markerClass) return;
// If the class declares a `call` method, translate the signature to a
// reified type.
FunctionType callableType;
Procedure call = cls.procedures.firstWhere(
(Procedure p) => p.name.name == "call",
orElse: () => null);
if (call != null) {
FunctionNode function = call.function;
final namedParameters = new List<NamedType>();
for (VariableDeclaration v in function.namedParameters) {
namedParameters.add(new NamedType(v.name, v.type));
}
List<DartType> positionalArguments = function.positionalParameters
.map((VariableDeclaration v) => v.type)
.toList();
callableType = new FunctionType(
positionalArguments, function.returnType,
namedParameters: namedParameters,
requiredParameterCount: function.requiredParameterCount);
}
statements.add(new ExpressionStatement(createCallInit(
parameter,
id,
cls.supertype?.asInterfaceType,
cls.implementedTypes
.map((Supertype type) => type?.asInterfaceType)
.toList(),
callableType)));
});
statements.add(new ReturnStatement(new VariableGet(parameter)));
Expression function = new FunctionExpression(new FunctionNode(
new Block(statements),
positionalParameters: <VariableDeclaration>[parameter],
returnType: declarationType));
return new MethodInvocation(
function, new Name("call"), new Arguments(<Expression>[callAllocate]));
}
void createDeclarations() {
/// Recursively find all referenced classes in [type].
void collectNewReferencedClasses(DartType type, Set<Class> newClasses) {
if (type is InterfaceType || type is Supertype) {
InterfaceType interfaceType = null;
if (type is InterfaceType) {
interfaceType = type;
} else {
interfaceType = (type as Supertype).asInterfaceType;
}
Class cls = interfaceType.classNode;
if (!reifiedClassIds.containsKey(cls) && !newClasses.contains(cls)) {
newClasses.add(cls);
}
interfaceType.typeArguments.forEach((DartType argument) {
collectNewReferencedClasses(argument, newClasses);
});
}
// TODO(karlklose): function types
}
Iterable<Class> classes = reifiedClassIds.keys;
while (classes.isNotEmpty) {
Set<Class> newClasses = new Set<Class>();
for (Class c in classes) {
collectNewReferencedClasses(c.supertype?.asInterfaceType, newClasses);
c.implementedTypes.forEach((Supertype supertype) {
collectNewReferencedClasses(supertype?.asInterfaceType, newClasses);
});
}
for (Class newClass in newClasses) {
// Make sure that there is a declaration field for the class and its
// library's declaration list is setup.
addDeclaration(newClass);
}
classes = newClasses;
}
Expression initializer = createDeclarationsInitializer();
initializer.parent = declarations;
declarations.initializer = initializer;
declarations.type = declarationType;
}
Procedure createGetter(
Name name, Expression expression, Class cls, DartType type) {
return new Procedure(name, ProcedureKind.Getter,
new FunctionNode(new ReturnStatement(expression), returnType: type),
fileUri: cls.fileUri);
}
}