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

Unified Diff: frog/leg/ssa/closure.dart

Issue 9873021: Move frog/leg to lib/compiler/implementation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 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 | « frog/leg/ssa/builder.dart ('k') | frog/leg/ssa/codegen.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
- }
-}
« no previous file with comments | « frog/leg/ssa/builder.dart ('k') | frog/leg/ssa/codegen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698