| Index: frog/leg/ssa/closure.dart
|
| ===================================================================
|
| --- frog/leg/ssa/closure.dart (revision 5925)
|
| +++ frog/leg/ssa/closure.dart (working copy)
|
| @@ -1,417 +0,0 @@
|
| -// Copyright (c) 2012, 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.
|
| -
|
| -class ClosureFieldElement extends Element {
|
| - ClosureFieldElement(SourceString name, ClassElement enclosing)
|
| - : super(name, ElementKind.FIELD, enclosing);
|
| -
|
| - bool isInstanceMember() => true;
|
| - bool isAssignable() => false;
|
| -
|
| - String toString() => "ClosureFieldElement($name)";
|
| -}
|
| -
|
| -class ClosureClassElement extends ClassElement {
|
| - ClosureClassElement(Compiler compiler, Element enclosingElement)
|
| - : super(compiler.closureClass.name, enclosingElement) {
|
| - isResolved = true;
|
| - compiler.closureClass.ensureResolved(compiler);
|
| - supertype = compiler.closureClass.computeType(compiler);
|
| - }
|
| -}
|
| -
|
| -// The box-element for a scope, and the captured variables that need to be
|
| -// stored in the box.
|
| -class ClosureScope {
|
| - Element boxElement;
|
| - Map<Element, Element> capturedVariableMapping;
|
| - // If the scope is attached to a [For] contains the variables that are
|
| - // declared in the initializer of the [For] and that need to be boxed.
|
| - // Otherwise contains the empty List.
|
| - List<Element> boxedLoopVariables;
|
| -
|
| - ClosureScope(this.boxElement, this.capturedVariableMapping)
|
| - : boxedLoopVariables = const <Element>[];
|
| -
|
| - bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty();
|
| -}
|
| -
|
| -class ClosureData {
|
| - // The closure's element before any translation. Will be null for methods.
|
| - final FunctionElement closureElement;
|
| - // The closureClassElement will be null for methods that are not local
|
| - // closures.
|
| - final ClassElement closureClassElement;
|
| - // The callElement will be null for methods that are not local closures.
|
| - final FunctionElement callElement;
|
| - // The [thisElement] makes handling 'this' easier by treating it like any
|
| - // other argument. It is only set for instance-members.
|
| - final Element thisElement;
|
| -
|
| - // Maps free locals, arguments and function elements to their captured
|
| - // copies.
|
| - final Map<Element, Element> freeVariableMapping;
|
| - // Maps closure-fields to their captured elements. This is somehow the inverse
|
| - // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does
|
| - // not deal with boxes, here we map instance-fields (which might represent
|
| - // boxes) to their boxElement.
|
| - final Map<Element, Element> capturedFieldMapping;
|
| -
|
| - // Maps scopes ([Loop] and [FunctionExpression] nodes) to their
|
| - // [ClosureScope] which contains their box and the
|
| - // captured variables that are stored in the box.
|
| - // This map will be empty if the method/closure of this [ClosureData] does not
|
| - // contain any nested closure.
|
| - final Map<Node, ClosureScope> capturingScopes;
|
| -
|
| - final Set<Element> usedVariablesInTry;
|
| -
|
| - ClosureData(this.closureElement,
|
| - this.closureClassElement,
|
| - this.callElement,
|
| - this.thisElement)
|
| - : this.freeVariableMapping = new Map<Element, Element>(),
|
| - this.capturedFieldMapping = new Map<Element, Element>(),
|
| - this.capturingScopes = new Map<Node, ClosureScope>(),
|
| - this.usedVariablesInTry = new Set<Element>();
|
| -
|
| - bool isClosure() => closureElement !== null;
|
| -}
|
| -
|
| -class ClosureTranslator extends AbstractVisitor {
|
| - final Compiler compiler;
|
| - final TreeElements elements;
|
| - int boxCounter = 0;
|
| - bool inTryCatchOrFinally = false;
|
| - final Map<Node, ClosureData> closureDataCache;
|
| -
|
| - // Map of captured variables. Initially they will map to themselves. If
|
| - // a variable needs to be boxed then the scope declaring the variable
|
| - // will update this mapping.
|
| - Map<Element, Element> capturedVariableMapping;
|
| - // List of encountered closures.
|
| - List<FunctionExpression> closures;
|
| -
|
| - // The variables that have been declared in the current scope.
|
| - List<Element> scopeVariables;
|
| -
|
| - FunctionElement currentFunctionElement;
|
| - // The closureData of the currentFunctionElement.
|
| - ClosureData closureData;
|
| -
|
| - bool insideClosure = false;
|
| -
|
| - ClosureTranslator(Compiler compiler, this.elements)
|
| - : capturedVariableMapping = new Map<Element, Element>(),
|
| - closures = <FunctionExpression>[],
|
| - this.compiler = compiler,
|
| - this.closureDataCache = compiler.builder.closureDataCache;
|
| -
|
| - ClosureData translate(Node node) {
|
| - // Closures have already been analyzed when visiting the surrounding
|
| - // method/function. This also shortcuts for bailout functions.
|
| - ClosureData cached = closureDataCache[node];
|
| - if (cached !== null) return cached;
|
| -
|
| - visit(node);
|
| - // When variables need to be boxed their [capturedVariableMapping] is
|
| - // updated, but we delay updating the similar freeVariableMapping in the
|
| - // closure datas that capture these variables.
|
| - // The closures don't have their fields (in the closure class) set, either.
|
| - updateClosures();
|
| -
|
| - return closureDataCache[node];
|
| - }
|
| -
|
| - // This function runs through all of the existing closures and updates their
|
| - // free variables to the boxed value. It also adds the field-elements to the
|
| - // class representing the closure. At the same time it fills the
|
| - // [capturedFieldMapping].
|
| - void updateClosures() {
|
| - for (FunctionExpression closure in closures) {
|
| - // The captured variables that need to be stored in a field of the closure
|
| - // class.
|
| - Set<Element> fieldCaptures = new Set<Element>();
|
| - ClosureData data = closureDataCache[closure];
|
| - Map<Element, Element> freeVariableMapping = data.freeVariableMapping;
|
| - // We get a copy of the keys and iterate over it, to avoid modifications
|
| - // to the map while iterating over it.
|
| - freeVariableMapping.getKeys().forEach((Element fromElement) {
|
| - assert(fromElement == freeVariableMapping[fromElement]);
|
| - Element updatedElement = capturedVariableMapping[fromElement];
|
| - assert(updatedElement !== null);
|
| - if (fromElement == updatedElement) {
|
| - assert(freeVariableMapping[fromElement] == updatedElement);
|
| - assert(Elements.isLocal(updatedElement));
|
| - // The variable has not been boxed.
|
| - fieldCaptures.add(updatedElement);
|
| - } else {
|
| - // A boxed element.
|
| - freeVariableMapping[fromElement] = updatedElement;
|
| - Element boxElement = updatedElement.enclosingElement;
|
| - assert(boxElement.kind == ElementKind.VARIABLE);
|
| - fieldCaptures.add(boxElement);
|
| - }
|
| - });
|
| - ClassElement closureElement = data.closureClassElement;
|
| - assert(closureElement != null || fieldCaptures.isEmpty());
|
| - for (Element boxElement in fieldCaptures) {
|
| - Element fieldElement =
|
| - new ClosureFieldElement(boxElement.name, closureElement);
|
| - closureElement.backendMembers =
|
| - closureElement.backendMembers.prepend(fieldElement);
|
| - data.capturedFieldMapping[fieldElement] = boxElement;
|
| - freeVariableMapping[boxElement] = fieldElement;
|
| - }
|
| - }
|
| - }
|
| -
|
| - void useLocal(Element element) {
|
| - // TODO(floitsch): replace this with a general solution.
|
| - Element functionElement = currentFunctionElement;
|
| - if (functionElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
|
| - ConstructorBodyElement body = functionElement;
|
| - functionElement = body.constructor;
|
| - }
|
| - // If the element is not declared in the current function and the element
|
| - // is not the closure itself we need to mark the element as free variable.
|
| - if (element.enclosingElement != functionElement &&
|
| - element != functionElement) {
|
| - assert(closureData.freeVariableMapping[element] == null ||
|
| - closureData.freeVariableMapping[element] == element);
|
| - closureData.freeVariableMapping[element] = element;
|
| - } else if (inTryCatchOrFinally) {
|
| - // Don't mark the this-element. This would complicate things in the
|
| - // builder.
|
| - if (element != closureData.thisElement) {
|
| - // TODO(ngeoffray): only do this if the variable is mutated.
|
| - closureData.usedVariablesInTry.add(element);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void declareLocal(Element element) {
|
| - scopeVariables.add(element);
|
| - }
|
| -
|
| - visit(Node node) => node.accept(this);
|
| -
|
| - visitNode(Node node) => node.visitChildren(this);
|
| -
|
| - visitVariableDefinitions(VariableDefinitions node) {
|
| - for (Link<Node> link = node.definitions.nodes;
|
| - !link.isEmpty();
|
| - link = link.tail) {
|
| - Node definition = link.head;
|
| - Element element = elements[definition];
|
| - assert(element !== null);
|
| - declareLocal(element);
|
| - }
|
| - // We still need to visit the right-hand sides of the init-assignments.
|
| - // Simply visit all children. We will visit the locals again and make them
|
| - // used, but that should not be a problem.
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - visitIdentifier(Identifier node) {
|
| - if (node.isThis()) {
|
| - useLocal(closureData.thisElement);
|
| - }
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - visitSend(Send node) {
|
| - Element element = elements[node];
|
| - if (Elements.isLocal(element)) {
|
| - useLocal(element);
|
| - } else if (node.receiver === null &&
|
| - Elements.isInstanceSend(node, elements)) {
|
| - useLocal(closureData.thisElement);
|
| - }
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - // If variables that are declared in the [node] scope are captured and need
|
| - // to be boxed create a box-element and update the [capturingScopes] in the
|
| - // current [closureData].
|
| - // The boxed variables are updated in the [capturedVariableMapping].
|
| - void attachCapturedScopeVariables(Node node) {
|
| - Element box = null;
|
| - Map<Element, Element> scopeMapping = new Map<Element, Element>();
|
| - for (Element element in scopeVariables) {
|
| - if (capturedVariableMapping.containsKey(element)) {
|
| - if (box == null) {
|
| - // TODO(floitsch): construct better box names.
|
| - SourceString boxName = new SourceString("box${boxCounter++}");
|
| - box = new Element(boxName,
|
| - ElementKind.VARIABLE,
|
| - currentFunctionElement);
|
| - }
|
| - // TODO(floitsch): construct better boxed names.
|
| - String elementName = element.name.slowToString();
|
| - // We are currently using the name in an HForeign which could replace
|
| - // "$X" with something else.
|
| - String escaped = elementName.replaceAll("\$", "_");
|
| - SourceString boxedName = new SourceString("${escaped}_${boxCounter++}");
|
| - Element boxed = new Element(boxedName, ElementKind.FIELD, box);
|
| - scopeMapping[element] = boxed;
|
| - capturedVariableMapping[element] = boxed;
|
| - }
|
| - }
|
| - if (!scopeMapping.isEmpty()) {
|
| - ClosureScope scope = new ClosureScope(box, scopeMapping);
|
| - closureData.capturingScopes[node] = scope;
|
| - }
|
| - }
|
| -
|
| - visitLoop(Loop node) {
|
| - List<Element> oldScopeVariables = scopeVariables;
|
| - scopeVariables = new List<Element>();
|
| - node.visitChildren(this);
|
| - attachCapturedScopeVariables(node);
|
| - scopeVariables = oldScopeVariables;
|
| - }
|
| -
|
| - visitFor(For node) {
|
| - visitLoop(node);
|
| - // See if we have declared loop variables that need to be boxed.
|
| - if (node.initializer === null) return;
|
| - VariableDefinitions definitions = node.initializer.asVariableDefinitions();
|
| - if (definitions == null) return;
|
| - ClosureScope scopeData = closureData.capturingScopes[node];
|
| - if (scopeData === null) return;
|
| - List<Element> result = <Element>[];
|
| - for (Link<Node> link = definitions.definitions.nodes;
|
| - !link.isEmpty();
|
| - link = link.tail) {
|
| - Node definition = link.head;
|
| - Element element = elements[definition];
|
| - if (capturedVariableMapping.containsKey(element)) {
|
| - result.add(element);
|
| - };
|
| - }
|
| - scopeData.boxedLoopVariables = result;
|
| - }
|
| -
|
| - ClosureData globalizeClosure(FunctionExpression node) {
|
| - FunctionElement element = elements[node];
|
| - ClassElement globalizedElement =
|
| - new ClosureClassElement(compiler, element.getCompilationUnit());
|
| - FunctionElement callElement =
|
| - new FunctionElement.from(Namer.CLOSURE_INVOCATION_NAME,
|
| - element,
|
| - globalizedElement);
|
| - globalizedElement.backendMembers =
|
| - const EmptyLink<Element>().prepend(callElement);
|
| - // The nested function's 'this' is the same as the one for the outer
|
| - // function. It could be [null] if we are inside a static method.
|
| - Element thisElement = closureData.thisElement;
|
| - return new ClosureData(element, globalizedElement,
|
| - callElement, thisElement);
|
| - }
|
| -
|
| - visitFunctionExpression(FunctionExpression node) {
|
| - Element element = elements[node];
|
| - if (element.kind === ElementKind.PARAMETER) {
|
| - // TODO(ahe): This is a hack. This method should *not* call
|
| - // visitChildren.
|
| - return node.name.accept(this);
|
| - }
|
| - bool isClosure = (closureData !== null);
|
| -
|
| - if (isClosure) closures.add(node);
|
| -
|
| - bool oldInsideClosure = insideClosure;
|
| - FunctionElement oldFunctionElement = currentFunctionElement;
|
| - ClosureData oldClosureData = closureData;
|
| - List<Element> oldScopeVariables = scopeVariables;
|
| -
|
| -
|
| - insideClosure = isClosure;
|
| - currentFunctionElement = elements[node];
|
| - if (insideClosure) {
|
| - closureData = globalizeClosure(node);
|
| - } else {
|
| - Element thisElement = null;
|
| - // TODO(floitsch): we should not need to look for generative constructors.
|
| - // At the moment we store only one ClosureData for both the factory and
|
| - // the body.
|
| - if (element.isInstanceMember() ||
|
| - element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
|
| - // TODO(floitsch): currently all variables are considered to be
|
| - // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'.
|
| - Element thisEnclosingElement = element;
|
| - if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
|
| - ConstructorBodyElement body = element;
|
| - thisEnclosingElement = body.constructor;
|
| - }
|
| - thisElement = new Element(const SourceString("this"),
|
| - ElementKind.PARAMETER,
|
| - thisEnclosingElement);
|
| - }
|
| - closureData = new ClosureData(null, null, null, thisElement);
|
| - }
|
| - scopeVariables = new List<Element>();
|
| -
|
| - // TODO(floitsch): a named function is visible from inside itself. Add
|
| - // the element to the block.
|
| -
|
| - // We have to declare the implicit 'this' parameter.
|
| - if (!insideClosure && closureData.thisElement !== null) {
|
| - declareLocal(closureData.thisElement);
|
| - }
|
| - // If we are inside a named closure we have to declare ourselve. For
|
| - // simplicity we declare the local even if the closure does not have a name
|
| - // It will simply not be used.
|
| - if (insideClosure) {
|
| - declareLocal(element);
|
| - }
|
| -
|
| - // TODO(ahe): This is problematic. The backend should not repeat
|
| - // the work of the resolver. It is the resolver's job to create
|
| - // parameters, etc. Other phases should only visit statements.
|
| - // TODO(floitsch): we avoid visiting the initializers on purpose so that we
|
| - // get an error-message later in the builder.
|
| - if (node.parameters !== null) node.parameters.accept(this);
|
| - if (node.body !== null) node.body.accept(this);
|
| -
|
| - attachCapturedScopeVariables(node);
|
| -
|
| - closureDataCache[node] = closureData;
|
| -
|
| - ClosureData savedClosureData = closureData;
|
| - bool savedInsideClosure = insideClosure;
|
| -
|
| - // Restore old values.
|
| - scopeVariables = oldScopeVariables;
|
| - insideClosure = oldInsideClosure;
|
| - closureData = oldClosureData;
|
| - currentFunctionElement = oldFunctionElement;
|
| -
|
| - // Mark all free variables as captured and use them in the outer function.
|
| - List<Element> freeVariables =
|
| - savedClosureData.freeVariableMapping.getKeys();
|
| - assert(freeVariables.isEmpty() || savedInsideClosure);
|
| - for (Element freeElement in freeVariables) {
|
| - if (capturedVariableMapping[freeElement] != null &&
|
| - capturedVariableMapping[freeElement] != freeElement) {
|
| - compiler.internalError('In closure analyzer', node: node);
|
| - }
|
| - capturedVariableMapping[freeElement] = freeElement;
|
| - useLocal(freeElement);
|
| - }
|
| - }
|
| -
|
| - visitFunctionDeclaration(FunctionDeclaration node) {
|
| - node.visitChildren(this);
|
| - declareLocal(elements[node]);
|
| - }
|
| -
|
| - visitTryStatement(TryStatement node) {
|
| - // TODO(ngeoffray): implement finer grain state.
|
| - inTryCatchOrFinally = true;
|
| - node.visitChildren(this);
|
| - inTryCatchOrFinally = false;
|
| - }
|
| -}
|
|
|