Index: pkg/kernel/lib/transformations/reify/transformation/builder.dart |
diff --git a/pkg/kernel/lib/transformations/reify/transformation/builder.dart b/pkg/kernel/lib/transformations/reify/transformation/builder.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fef0f198b441cc1794ca136ffa04b3289e413187 |
--- /dev/null |
+++ b/pkg/kernel/lib/transformations/reify/transformation/builder.dart |
@@ -0,0 +1,511 @@ |
+// 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 = null; |
+ if (type is InterfaceType) { |
+ interfaceType = type; |
+ } else { |
+ interfaceType = (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), |
+ 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); |
+ } |
+} |