| 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;
|
| + }
|
| }
|
| }
|
|
|