Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1711)

Unified Diff: pkg/kernel/lib/transformations/closure/converter.dart

Issue 2778223002: Add primitive to create closures and use it for closure conversion (Closed)
Patch Set: Follow common pattern for AST nodes with References in ClosureCreation Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/kernel/lib/text/ast_to_text.dart ('k') | pkg/kernel/lib/type_checker.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/kernel/lib/transformations/closure/converter.dart
diff --git a/pkg/kernel/lib/transformations/closure/converter.dart b/pkg/kernel/lib/transformations/closure/converter.dart
index 9061b0217fc66ba5e4448e4190581e50a08b3a0f..a616a77b7e1e18f43d0a8cc27f712d952f4ab693 100644
--- a/pkg/kernel/lib/transformations/closure/converter.dart
+++ b/pkg/kernel/lib/transformations/closure/converter.dart
@@ -10,9 +10,10 @@ import '../../ast.dart'
Block,
Catch,
Class,
+ ClosureCreation,
Constructor,
- ConstructorInvocation,
DartType,
+ DynamicType,
EmptyStatement,
Expression,
ExpressionStatement,
@@ -23,7 +24,9 @@ import '../../ast.dart'
FunctionDeclaration,
FunctionExpression,
FunctionNode,
+ FunctionType,
Initializer,
+ InterfaceType,
InvalidExpression,
InvocationExpression,
Let,
@@ -33,6 +36,7 @@ import '../../ast.dart'
MethodInvocation,
Name,
NamedExpression,
+ NamedType,
NullLiteral,
Procedure,
ProcedureKind,
@@ -41,8 +45,6 @@ import '../../ast.dart'
Statement,
StaticGet,
StaticInvocation,
- StringLiteral,
- Supertype,
ThisExpression,
Transformer,
TreeNode,
@@ -110,6 +112,7 @@ class ClosureConverter extends Transformer {
AstRewriter rewriter;
+ /// TODO(29181): update this comment when the type variables are restored.
/// Maps original type variable (aka type parameter) to a hoisted type
/// variable type.
///
@@ -252,6 +255,10 @@ class ClosureConverter extends Transformer {
return new BlockRewriter(body);
}
+ bool isObject(DartType type) {
+ return type is InterfaceType && type.classNode.supertype == null;
+ }
+
Expression handleLocalFunction(FunctionNode function) {
FunctionNode enclosingFunction = currentFunction;
Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution;
@@ -261,10 +268,8 @@ class ClosureConverter extends Transformer {
rewriter = makeRewriterForBody(function);
- VariableDeclaration contextVariable = new VariableDeclaration(
- "#contextParameter",
- type: const VectorType(),
- isFinal: true);
+ VariableDeclaration contextVariable =
+ new VariableDeclaration("#contextParameter", type: const VectorType());
Context parent = context;
context = context.toNestedContext(
new VariableAccessor(contextVariable, null, TreeNode.noOffset));
@@ -276,8 +281,32 @@ class ClosureConverter extends Transformer {
typeSubstitution = const <TypeParameter, DartType>{};
}
+ // TODO(29181): remove replacementTypeSubstitution variable and its usages.
+ // All the type variables used in this function body are replaced with
+ // either dynamic or their bounds. This is to temporarily remove the type
+ // variables from closure conversion. They should be returned after the VM
+ // changes are done to support vectors and closure creation. See #29181.
+ Map<TypeParameter, DartType> replacementTypeSubstitution =
+ <TypeParameter, DartType>{};
+ for (TypeParameter parameter in typeSubstitution.keys) {
+ replacementTypeSubstitution[parameter] = const DynamicType();
+ }
+ for (TypeParameter parameter in typeSubstitution.keys) {
+ if (!isObject(parameter.bound)) {
+ replacementTypeSubstitution[parameter] =
+ substitute(parameter.bound, replacementTypeSubstitution);
+ }
+ }
+ typeSubstitution = replacementTypeSubstitution;
function.transformChildren(this);
+ // TODO(29181): don't replace typeSubstitution with an empty map.
+ // Information about captured type variables is deleted from the closure
+ // class, because the type variables in this function body are already
+ // replaced with either dynamic or their bounds. This change should be
+ // undone after the VM support for vectors and closure creation is
+ // implemented. See #29181.
+ typeSubstitution = <TypeParameter, DartType>{};
Expression result = addClosure(function, contextVariable, parent.expression,
typeSubstitution, enclosingTypeSubstitution);
currentFunction = enclosingFunction;
@@ -312,64 +341,50 @@ class ClosureConverter extends Transformer {
});
}
- /// Add a new class to the current library that looks like this:
+ /// Add a new procedure to the current library that looks like this:
///
- /// class Closure#0 extends core::Object implements core::Function {
- /// field Vector context;
- /// constructor •(final Vector #t1) → dynamic
- /// : self::Closure#0::context = #t1
- /// ;
- /// method call(/* The parameters of [function] */) → dynamic {
- /// /// #t2 is [contextVariable].
- /// final Vector #t2 = this.{self::Closure#0::context};
- /// /* The body of [function]. */
- /// }
- /// }
+ /// static method closure#0(Vector #c, /* Parameters of [function]. */)
+ /// → dynamic {
///
- /// Returns a constructor call to invoke the above constructor.
+ /// /* Context is represented by #c. */
///
- /// TODO(ahe): We shouldn't create a class for each closure. Instead we turn
- /// [function] into a top-level function and use the Dart VM's mechnism for
- /// closures.
+ /// /* Body of [function]. */
+ ///
+ /// }
+ ///
+ /// Returns an invocation of the closure creation primitive that binds the
+ /// above top-level function to a context represented as Vector.
Expression addClosure(
FunctionNode function,
VariableDeclaration contextVariable,
Expression accessContext,
Map<TypeParameter, DartType> substitution,
Map<TypeParameter, DartType> enclosingTypeSubstitution) {
- Field contextField = new Field(
- // TODO(ahe): Rename to #context.
- new Name("context"),
- type: const VectorType(),
+ function.positionalParameters.insert(0, contextVariable);
+ ++function.requiredParameterCount;
+ Procedure closedTopLevelFunction = new Procedure(
+ new Name(createNameForClosedTopLevelFunction(function)),
+ ProcedureKind.Method,
+ function,
+ isStatic: true,
fileUri: currentFileUri);
- Class closureClass = createClosureClass(function,
- fields: [contextField], substitution: substitution);
- closureClass.addMember(new Procedure(
- new Name("call"), ProcedureKind.Method, function,
- fileUri: currentFileUri));
- newLibraryMembers.add(closureClass);
- Statement note = new ExpressionStatement(
- new StringLiteral("This is a temporary solution. "
- "In the VM, this will become an additional parameter."));
- List<Statement> statements = <Statement>[note, contextVariable];
- Statement body = function.body;
- if (body is Block) {
- statements.addAll(body.statements);
- } else {
- statements.add(body);
- }
- function.body = new Block(statements);
- function.body.parent = function;
- contextVariable.initializer =
- new PropertyGet(new ThisExpression(), contextField.name, contextField);
- contextVariable.initializer.parent = contextVariable;
- return new ConstructorInvocation(
- closureClass.constructors.single,
- new Arguments(<Expression>[accessContext], types:
- new List<DartType>.from(substitution.keys.map((TypeParameter t) {
- return substitute(
- new TypeParameterType(t), enclosingTypeSubstitution);
- }))));
+ newLibraryMembers.add(closedTopLevelFunction);
+
+ FunctionType closureType = new FunctionType(
+ function.positionalParameters
+ .skip(1)
+ .map((VariableDeclaration decl) => decl.type)
+ .toList(),
+ function.returnType,
+ namedParameters: function.namedParameters
+ .map((VariableDeclaration decl) =>
+ new NamedType(decl.name, decl.type))
+ .toList(),
+ typeParameters: function.typeParameters,
+ requiredParameterCount: function.requiredParameterCount - 1);
+
+ return new ClosureCreation(
+ closedTopLevelFunction, accessContext, closureType);
}
TreeNode visitField(Field node) {
@@ -403,7 +418,7 @@ class ClosureConverter extends Transformer {
node.name = tearOffName;
addGetterForwarder(oldName, node);
} else if (node.kind == ProcedureKind.Method) {
- addTearOffGetter(tearOffName, node);
+ addTearOffMethod(tearOffName, node);
}
}
}
@@ -478,7 +493,13 @@ class ClosureConverter extends Transformer {
node.transformChildren(this);
if (!capturedVariables.contains(node)) return node;
- context.extend(node, node.initializer ?? new NullLiteral());
+ if (node.initializer == null && node.parent is FunctionNode) {
+ // If the variable is a function parameter and doesn't have an
+ // initializer, just use this variable name to put it into the context.
+ context.extend(node, new VariableGet(node));
+ } else {
+ context.extend(node, node.initializer ?? new NullLiteral());
+ }
if (node.parent == currentFunction) {
return node;
@@ -609,7 +630,11 @@ class ClosureConverter extends Transformer {
TreeNode visitStaticGet(StaticGet node) {
Member target = node.target;
if (target is Procedure && target.kind == ProcedureKind.Method) {
- Expression expression = getTearOffExpression(node.target);
+ VariableDeclaration contextVariable = new VariableDeclaration(
+ "#contextParameter",
+ type: const VectorType());
+ Expression expression = getTearOffExpression(
+ null, node.target, contextVariable, new NullLiteral());
expression.transformChildren(this);
return expression;
}
@@ -619,7 +644,9 @@ class ClosureConverter extends Transformer {
TreeNode visitPropertyGet(PropertyGet node) {
Name tearOffName = tearOffGetterNames[node.name];
if (tearOffName != null) {
- node.name = tearOffName;
+ MethodInvocation replacement = new MethodInvocation(
+ node.receiver, tearOffName, new Arguments(<Expression>[]));
+ return super.visitMethodInvocation(replacement);
}
return super.visitPropertyGet(node);
}
@@ -652,9 +679,13 @@ class ClosureConverter extends Transformer {
return statement is Block ? statement : new Block(<Statement>[statement]);
}
- /// Creates a closure that will invoke [procedure] and return an expression
- /// that instantiates that closure.
- Expression getTearOffExpression(Procedure procedure) {
+ /// Creates a closure that will invoke method [procedure] of [receiver] and
+ /// return an expression that instantiates that closure.
+ Expression getTearOffExpression(
+ VariableDeclaration receiver,
+ Procedure procedure,
+ VariableDeclaration contextVariable,
+ Expression accessContext) {
Map<TypeParameter, DartType> substitution = procedure.isInstanceMember
// Note: we do not attempt to avoid copying type variables that aren't
// used in the signature of [procedure]. It might be more economical to
@@ -663,49 +694,52 @@ class ClosureConverter extends Transformer {
// variables will be handled most efficiently.
? copyTypeVariables(procedure.enclosingClass.typeParameters)
: const <TypeParameter, DartType>{};
- Expression receiver = null;
- List<Field> fields = null;
- if (procedure.isInstanceMember) {
- // TODO(ahe): Rename to #self.
- Field self = new Field(new Name("self"), fileUri: currentFileUri);
- self.type = substitute(procedure.enclosingClass.thisType, substitution);
- fields = <Field>[self];
- receiver = new PropertyGet(new ThisExpression(), self.name, self);
+
+ // TODO(29181): remove variable `dynamicSubstitution` and replace its usages
+ // with `substitution`.
+
+ Map<TypeParameter, DartType> dynamicSubstitution =
+ <TypeParameter, DartType>{};
+ for (TypeParameter parameter in substitution.keys) {
+ dynamicSubstitution[parameter] = const DynamicType();
+ }
+ for (TypeParameter parameter in substitution.keys) {
+ if (!isObject(parameter.bound)) {
+ dynamicSubstitution[parameter] =
+ substitute(parameter.bound, dynamicSubstitution);
+ }
}
// Find the closure class for the function. If there isn't one, create it.
- String closureClassName = createNameForClosureClass(procedure.function);
- Class closureClass = null;
+ String closedTopLevelFunctionName =
+ createNameForClosedTopLevelFunction(procedure.function);
+ Procedure closedTopLevelFunction = null;
for (TreeNode node in newLibraryMembers) {
- if (node is Class && node.name == closureClassName) {
- closureClass = node;
+ if (node is Procedure && node.name.name == closedTopLevelFunctionName) {
+ closedTopLevelFunction = node;
}
}
- if (closureClass == null) {
- closureClass = createClosureClass(procedure.function,
- fields: fields, substitution: substitution);
- closureClass.addMember(new Procedure(
- new Name("call"),
+ if (closedTopLevelFunction == null) {
+ closedTopLevelFunction = new Procedure(
+ new Name(closedTopLevelFunctionName),
ProcedureKind.Method,
- forwardFunction(procedure, receiver, substitution),
- fileUri: currentFileUri));
- newLibraryMembers.add(closureClass);
+ forwardFunction(
+ procedure, receiver, contextVariable, dynamicSubstitution),
+ isStatic: true,
+ fileUri: currentFileUri);
+ newLibraryMembers.add(closedTopLevelFunction);
}
- Arguments constructorArguments = procedure.isInstanceMember
- ? new Arguments(<Expression>[new ThisExpression()])
- : new Arguments.empty();
- if (substitution.isNotEmpty) {
- constructorArguments.types
- .addAll(procedure.enclosingClass.thisType.typeArguments);
- }
- return new ConstructorInvocation(
- closureClass.constructors.single, constructorArguments);
+ return new ClosureCreation(
+ closedTopLevelFunction, accessContext, procedure.function.functionType);
}
/// Creates a function that has the same signature as `procedure.function`
/// and which forwards all arguments to `procedure`.
- FunctionNode forwardFunction(Procedure procedure, Expression receiver,
+ FunctionNode forwardFunction(
+ Procedure procedure,
+ VariableDeclaration receiver,
+ VariableDeclaration contextVariable,
Map<TypeParameter, DartType> substitution) {
CloneVisitor cloner = substitution.isEmpty
? this.cloner
@@ -715,6 +749,9 @@ class ClosureConverter extends Transformer {
function.typeParameters.map(cloner.clone).toList();
List<VariableDeclaration> positionalParameters =
function.positionalParameters.map(cloner.clone).toList();
+ if (contextVariable != null) {
+ positionalParameters.insert(0, contextVariable);
+ }
List<VariableDeclaration> namedParameters =
function.namedParameters.map(cloner.clone).toList();
@@ -724,6 +761,9 @@ class ClosureConverter extends Transformer {
List<Expression> positional = positionalParameters
.map((VariableDeclaration parameter) => new VariableGet(parameter))
.toList();
+ if (contextVariable != null) {
+ positional.removeAt(0);
+ }
List<NamedExpression> named =
namedParameters.map((VariableDeclaration parameter) {
return new NamedExpression(parameter.name, new VariableGet(parameter));
@@ -731,13 +771,18 @@ class ClosureConverter extends Transformer {
Arguments arguments = new Arguments(positional, types: types, named: named);
InvocationExpression invocation = procedure.isInstanceMember
- ? new MethodInvocation(receiver, procedure.name, arguments, procedure)
+ ? new MethodInvocation(
+ context.lookup(receiver), procedure.name, arguments, procedure)
: new StaticInvocation(procedure, arguments);
+ int requiredParameterCount = function.requiredParameterCount;
+ if (contextVariable != null) {
+ ++requiredParameterCount;
+ }
return new FunctionNode(new ReturnStatement(invocation),
typeParameters: typeParameters,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
- requiredParameterCount: function.requiredParameterCount,
+ requiredParameterCount: requiredParameterCount,
returnType: substitute(function.returnType, cloner.typeSubstitution));
}
@@ -759,42 +804,8 @@ class ClosureConverter extends Transformer {
return substitution;
}
- String createNameForClosureClass(FunctionNode function) {
- return 'Closure#${localNames[function]}';
- }
-
- Class createClosureClass(FunctionNode function,
- {List<Field> fields, Map<TypeParameter, DartType> substitution}) {
- List<TypeParameter> typeParameters = new List<TypeParameter>.from(
- substitution.values
- .map((DartType t) => (t as TypeParameterType).parameter));
- Class closureClass = new Class(
- name: createNameForClosureClass(function),
- supertype: new Supertype(coreTypes.objectClass, const <DartType>[]),
- typeParameters: typeParameters,
- implementedTypes: <Supertype>[
- new Supertype(coreTypes.functionClass, const <DartType>[])
- ],
- fileUri: currentFileUri);
- addClosureClassNote(closureClass);
-
- List<VariableDeclaration> parameters = <VariableDeclaration>[];
- List<Initializer> initializers = <Initializer>[];
- for (Field field in fields ?? const <Field>[]) {
- closureClass.addMember(field);
- VariableDeclaration parameter = new VariableDeclaration(field.name.name,
- type: field.type, isFinal: true);
- parameters.add(parameter);
- initializers.add(new FieldInitializer(field, new VariableGet(parameter)));
- }
-
- closureClass.addMember(new Constructor(
- new FunctionNode(new EmptyStatement(),
- positionalParameters: parameters),
- name: new Name(""),
- initializers: initializers));
-
- return closureClass;
+ String createNameForClosedTopLevelFunction(FunctionNode function) {
+ return 'closure#${localNames[function]}';
}
Statement forwardToThisProperty(Member node) {
@@ -822,19 +833,54 @@ class ClosureConverter extends Transformer {
.add(copyWithBody(getter, forwardToThisProperty(getter))..name = name);
}
- void addTearOffGetter(Name name, Procedure procedure) {
- newClassMembers.add(new Procedure(name, ProcedureKind.Getter,
- new FunctionNode(new ReturnStatement(getTearOffExpression(procedure))),
- fileUri: currentFileUri));
- }
+ void addTearOffMethod(Name name, Procedure procedure) {
+ // [addTearOffMethod] generates a method along with a context that captures
+ // `this`. The work with contexts is typically done using the data gathered
+ // by a [ClosureInfo] instance. In absence of this information, we need to
+ // create some variables, like `#self` and `#context`, and manipulate
+ // contexts directly in some cases.
+ //
+ // Also, the tear-off method is generated during a visit to the AST node
+ // of the procedure being torn off, so we need to save and restore some
+ // auxiliary variables like `currentMember` and `currentMemberFunction`
+ // and use [saveContext], so that those variables have proper values when
+ // the procedure itself is being transformed.
+ Member oldCurrentMember = currentMember;
+ FunctionNode oldCurrentMemberFunction = currentMemberFunction;
+ try {
+ saveContext(() {
+ Block body = new Block(<Statement>[]);
+ FunctionNode tearOffMethodFunction = new FunctionNode(body);
+ setupContextForFunctionBody(tearOffMethodFunction);
+
+ // We need a variable that refers to `this` to put it into the context.
+ VariableDeclaration self = new VariableDeclaration("#self",
+ type: procedure.enclosingClass.rawType);
+ context.extend(self, new ThisExpression());
- // TODO(ahe): Remove this method when we don't generate closure classes
- // anymore.
- void addClosureClassNote(Class closureClass) {
- closureClass.addMember(new Field(new Name("note"),
- type: coreTypes.stringClass.rawType,
- initializer: new StringLiteral(
- "This is temporary. The VM doesn't need closure classes."),
- fileUri: currentFileUri));
+ // The `#context` variable is used to access the context in the closed
+ // top-level function that represents the closure and is generated in
+ // [getTearOffExpression].
+ VariableDeclaration contextVariable = new VariableDeclaration(
+ "#contextParameter",
+ type: const VectorType());
+ Context parent = context;
+ context = context.toNestedContext(
+ new VariableAccessor(contextVariable, null, TreeNode.noOffset));
+
+ body.addStatement(new ReturnStatement(getTearOffExpression(
+ self, procedure, contextVariable, parent.expression)));
+
+ Procedure tearOffMethod = new Procedure(
+ name, ProcedureKind.Method, tearOffMethodFunction,
+ fileUri: currentFileUri);
+ newClassMembers.add(tearOffMethod);
+
+ resetContext();
+ });
+ } finally {
+ currentMember = oldCurrentMember;
+ currentMemberFunction = oldCurrentMemberFunction;
+ }
}
}
« no previous file with comments | « pkg/kernel/lib/text/ast_to_text.dart ('k') | pkg/kernel/lib/type_checker.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698