| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 class ClosureFieldElement extends Element { | 5 class ClosureFieldElement extends Element { |
| 6 ClosureFieldElement(SourceString name, ClassElement enclosing) | 6 ClosureFieldElement(SourceString name, ClassElement enclosing) |
| 7 : super(name, ElementKind.FIELD, enclosing); | 7 : super(name, ElementKind.FIELD, enclosing); |
| 8 | 8 |
| 9 bool isInstanceMember() => true; | 9 bool isInstanceMember() => true; |
| 10 bool isAssignable() => false; | 10 bool isAssignable() => false; |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 // Map of captured variables. Initially they will map to themselves. If | 106 // Map of captured variables. Initially they will map to themselves. If |
| 107 // a variable needs to be boxed then the scope declaring the variable | 107 // a variable needs to be boxed then the scope declaring the variable |
| 108 // will update this mapping. | 108 // will update this mapping. |
| 109 Map<Element, Element> capturedVariableMapping; | 109 Map<Element, Element> capturedVariableMapping; |
| 110 // List of encountered closures. | 110 // List of encountered closures. |
| 111 List<FunctionExpression> closures; | 111 List<FunctionExpression> closures; |
| 112 | 112 |
| 113 // The variables that have been declared in the current scope. | 113 // The variables that have been declared in the current scope. |
| 114 List<Element> scopeVariables; | 114 List<Element> scopeVariables; |
| 115 | 115 |
| 116 // Keep track of the mutated variables so that we don't need to box |
| 117 // non-mutated variables. |
| 118 Set<Element> mutatedVariables; |
| 119 |
| 116 FunctionElement currentFunctionElement; | 120 FunctionElement currentFunctionElement; |
| 117 // The closureData of the currentFunctionElement. | 121 // The closureData of the currentFunctionElement. |
| 118 ClosureData closureData; | 122 ClosureData closureData; |
| 119 | 123 |
| 120 bool insideClosure = false; | 124 bool insideClosure = false; |
| 121 | 125 |
| 122 Compiler get compiler() => builder.compiler; | 126 Compiler get compiler() => builder.compiler; |
| 123 | 127 |
| 124 ClosureTranslator(SsaBuilder builder) | 128 ClosureTranslator(SsaBuilder builder) |
| 125 : this.builder = builder, | 129 : this.builder = builder, |
| 126 this.elements = builder.elements, | 130 this.elements = builder.elements, |
| 127 capturedVariableMapping = new Map<Element, Element>(), | 131 capturedVariableMapping = new Map<Element, Element>(), |
| 128 closures = <FunctionExpression>[], | 132 closures = <FunctionExpression>[], |
| 133 mutatedVariables = new Set<Element>(), |
| 129 this.closureDataCache = builder.builder.closureDataCache; | 134 this.closureDataCache = builder.builder.closureDataCache; |
| 130 | 135 |
| 131 ClosureData translate(Node node) { | 136 ClosureData translate(Node node) { |
| 132 // Closures have already been analyzed when visiting the surrounding | 137 // Closures have already been analyzed when visiting the surrounding |
| 133 // method/function. This also shortcuts for bailout functions. | 138 // method/function. This also shortcuts for bailout functions. |
| 134 ClosureData cached = closureDataCache[node]; | 139 ClosureData cached = closureDataCache[node]; |
| 135 if (cached !== null) return cached; | 140 if (cached !== null) return cached; |
| 136 | 141 |
| 137 visit(node); | 142 visit(node); |
| 138 // When variables need to be boxed their [capturedVariableMapping] is | 143 // When variables need to be boxed their [capturedVariableMapping] is |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 227 visitNode(Node node) => node.visitChildren(this); | 232 visitNode(Node node) => node.visitChildren(this); |
| 228 | 233 |
| 229 visitVariableDefinitions(VariableDefinitions node) { | 234 visitVariableDefinitions(VariableDefinitions node) { |
| 230 for (Link<Node> link = node.definitions.nodes; | 235 for (Link<Node> link = node.definitions.nodes; |
| 231 !link.isEmpty(); | 236 !link.isEmpty(); |
| 232 link = link.tail) { | 237 link = link.tail) { |
| 233 Node definition = link.head; | 238 Node definition = link.head; |
| 234 Element element = elements[definition]; | 239 Element element = elements[definition]; |
| 235 assert(element !== null); | 240 assert(element !== null); |
| 236 declareLocal(element); | 241 declareLocal(element); |
| 242 // We still need to visit the right-hand sides of the init-assignments. |
| 243 // For SendSets don't visit the left again. Otherwise it would be marked |
| 244 // as mutated. |
| 245 if (definition is SendSet) { |
| 246 SendSet assignment = definition; |
| 247 visit(assignment.argumentsNode); |
| 248 } else { |
| 249 visit(definition); |
| 250 } |
| 237 } | 251 } |
| 238 // We still need to visit the right-hand sides of the init-assignments. | |
| 239 // Simply visit all children. We will visit the locals again and make them | |
| 240 // used, but that should not be a problem. | |
| 241 node.visitChildren(this); | |
| 242 } | 252 } |
| 243 | 253 |
| 244 visitIdentifier(Identifier node) { | 254 visitIdentifier(Identifier node) { |
| 245 if (node.isThis()) { | 255 if (node.isThis()) { |
| 246 useLocal(closureData.thisElement); | 256 useLocal(closureData.thisElement); |
| 247 } | 257 } |
| 248 node.visitChildren(this); | 258 node.visitChildren(this); |
| 249 } | 259 } |
| 250 | 260 |
| 251 visitSend(Send node) { | 261 visitSend(Send node) { |
| 252 Element element = elements[node]; | 262 Element element = elements[node]; |
| 253 if (Elements.isLocal(element)) { | 263 if (Elements.isLocal(element)) { |
| 254 useLocal(element); | 264 useLocal(element); |
| 255 } else if (node.receiver === null && | 265 } else if (node.receiver === null && |
| 256 Elements.isInstanceSend(node, elements)) { | 266 Elements.isInstanceSend(node, elements)) { |
| 257 useLocal(closureData.thisElement); | 267 useLocal(closureData.thisElement); |
| 258 } else if (node.isSuperCall) { | 268 } else if (node.isSuperCall) { |
| 259 useLocal(closureData.thisElement); | 269 useLocal(closureData.thisElement); |
| 260 } | 270 } |
| 261 node.visitChildren(this); | 271 node.visitChildren(this); |
| 262 } | 272 } |
| 263 | 273 |
| 274 visitSendSet(SendSet node) { |
| 275 Element element = elements[node]; |
| 276 if (Elements.isLocal(element)) { |
| 277 mutatedVariables.add(element); |
| 278 } |
| 279 super.visitSendSet(node); |
| 280 } |
| 281 |
| 264 // If variables that are declared in the [node] scope are captured and need | 282 // If variables that are declared in the [node] scope are captured and need |
| 265 // to be boxed create a box-element and update the [capturingScopes] in the | 283 // to be boxed create a box-element and update the [capturingScopes] in the |
| 266 // current [closureData]. | 284 // current [closureData]. |
| 267 // The boxed variables are updated in the [capturedVariableMapping]. | 285 // The boxed variables are updated in the [capturedVariableMapping]. |
| 268 void attachCapturedScopeVariables(Node node) { | 286 void attachCapturedScopeVariables(Node node) { |
| 269 Element box = null; | 287 Element box = null; |
| 270 Map<Element, Element> scopeMapping = new Map<Element, Element>(); | 288 Map<Element, Element> scopeMapping = new Map<Element, Element>(); |
| 271 for (Element element in scopeVariables) { | 289 for (Element element in scopeVariables) { |
| 272 // No need to box non-assignable elements. | 290 // No need to box non-assignable elements. |
| 273 if (!element.isAssignable()) continue; | 291 if (!element.isAssignable()) continue; |
| 292 if (!mutatedVariables.contains(element)) continue; |
| 274 if (capturedVariableMapping.containsKey(element)) { | 293 if (capturedVariableMapping.containsKey(element)) { |
| 275 if (box == null) { | 294 if (box == null) { |
| 276 // TODO(floitsch): construct better box names. | 295 // TODO(floitsch): construct better box names. |
| 277 SourceString boxName = | 296 SourceString boxName = |
| 278 new SourceString("box_${closureFieldCounter++}"); | 297 new SourceString("box_${closureFieldCounter++}"); |
| 279 box = new BoxElement(boxName, currentFunctionElement); | 298 box = new BoxElement(boxName, currentFunctionElement); |
| 280 } | 299 } |
| 281 // TODO(floitsch): construct better boxed names. | 300 // TODO(floitsch): construct better boxed names. |
| 282 String elementName = element.name.slowToString(); | 301 String elementName = element.name.slowToString(); |
| 283 // We are currently using the name in an HForeign which could replace | 302 // We are currently using the name in an HForeign which could replace |
| 284 // "$X" with something else. | 303 // "$X" with something else. |
| 285 String escaped = elementName.replaceAll("\$", "_"); | 304 String escaped = elementName.replaceAll("\$", "_"); |
| 286 SourceString boxedName = | 305 SourceString boxedName = |
| 287 new SourceString("${escaped}_${closureFieldCounter++}"); | 306 new SourceString("${escaped}_${closureFieldCounter++}"); |
| 288 Element boxed = new Element(boxedName, ElementKind.FIELD, box); | 307 Element boxed = new Element(boxedName, ElementKind.FIELD, box); |
| 289 scopeMapping[element] = boxed; | 308 scopeMapping[element] = boxed; |
| 290 capturedVariableMapping[element] = boxed; | 309 capturedVariableMapping[element] = boxed; |
| 291 } | 310 } |
| 292 } | 311 } |
| 293 if (!scopeMapping.isEmpty()) { | 312 if (!scopeMapping.isEmpty()) { |
| 294 ClosureScope scope = new ClosureScope(box, scopeMapping); | 313 ClosureScope scope = new ClosureScope(box, scopeMapping); |
| 295 closureData.capturingScopes[node] = scope; | 314 closureData.capturingScopes[node] = scope; |
| 296 } | 315 } |
| 297 } | 316 } |
| 298 | 317 |
| 299 visitLoop(Loop node) { | 318 void inNewScope(Node node, Function action) { |
| 300 List<Element> oldScopeVariables = scopeVariables; | 319 List<Element> oldScopeVariables = scopeVariables; |
| 301 scopeVariables = new List<Element>(); | 320 scopeVariables = new List<Element>(); |
| 302 node.visitChildren(this); | 321 action(); |
| 303 attachCapturedScopeVariables(node); | 322 attachCapturedScopeVariables(node); |
| 323 for (Element element in scopeVariables) { |
| 324 mutatedVariables.remove(element); |
| 325 } |
| 304 scopeVariables = oldScopeVariables; | 326 scopeVariables = oldScopeVariables; |
| 305 } | 327 } |
| 306 | 328 |
| 329 visitLoop(Loop node) { |
| 330 inNewScope(node, () { |
| 331 node.visitChildren(this); |
| 332 }); |
| 333 } |
| 334 |
| 307 visitFor(For node) { | 335 visitFor(For node) { |
| 308 visitLoop(node); | 336 visitLoop(node); |
| 309 // See if we have declared loop variables that need to be boxed. | 337 // See if we have declared loop variables that need to be boxed. |
| 310 if (node.initializer === null) return; | 338 if (node.initializer === null) return; |
| 311 VariableDefinitions definitions = node.initializer.asVariableDefinitions(); | 339 VariableDefinitions definitions = node.initializer.asVariableDefinitions(); |
| 312 if (definitions == null) return; | 340 if (definitions == null) return; |
| 313 ClosureScope scopeData = closureData.capturingScopes[node]; | 341 ClosureScope scopeData = closureData.capturingScopes[node]; |
| 314 if (scopeData === null) return; | 342 if (scopeData === null) return; |
| 315 List<Element> result = <Element>[]; | 343 List<Element> result = <Element>[]; |
| 316 for (Link<Node> link = definitions.definitions.nodes; | 344 for (Link<Node> link = definitions.definitions.nodes; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 349 // visitChildren. | 377 // visitChildren. |
| 350 return node.name.accept(this); | 378 return node.name.accept(this); |
| 351 } | 379 } |
| 352 bool isClosure = (closureData !== null); | 380 bool isClosure = (closureData !== null); |
| 353 | 381 |
| 354 if (isClosure) closures.add(node); | 382 if (isClosure) closures.add(node); |
| 355 | 383 |
| 356 bool oldInsideClosure = insideClosure; | 384 bool oldInsideClosure = insideClosure; |
| 357 FunctionElement oldFunctionElement = currentFunctionElement; | 385 FunctionElement oldFunctionElement = currentFunctionElement; |
| 358 ClosureData oldClosureData = closureData; | 386 ClosureData oldClosureData = closureData; |
| 359 List<Element> oldScopeVariables = scopeVariables; | |
| 360 | |
| 361 | 387 |
| 362 insideClosure = isClosure; | 388 insideClosure = isClosure; |
| 363 currentFunctionElement = elements[node]; | 389 currentFunctionElement = elements[node]; |
| 364 if (insideClosure) { | 390 if (insideClosure) { |
| 365 closureData = globalizeClosure(node); | 391 closureData = globalizeClosure(node); |
| 366 } else { | 392 } else { |
| 367 Element thisElement = null; | 393 Element thisElement = null; |
| 368 // TODO(floitsch): we should not need to look for generative constructors. | 394 // TODO(floitsch): we should not need to look for generative constructors. |
| 369 // At the moment we store only one ClosureData for both the factory and | 395 // At the moment we store only one ClosureData for both the factory and |
| 370 // the body. | 396 // the body. |
| 371 if (element.isInstanceMember() || | 397 if (element.isInstanceMember() || |
| 372 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | 398 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { |
| 373 // TODO(floitsch): currently all variables are considered to be | 399 // TODO(floitsch): currently all variables are considered to be |
| 374 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'. | 400 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'. |
| 375 Element thisEnclosingElement = element; | 401 Element thisEnclosingElement = element; |
| 376 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | 402 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { |
| 377 ConstructorBodyElement body = element; | 403 ConstructorBodyElement body = element; |
| 378 thisEnclosingElement = body.constructor; | 404 thisEnclosingElement = body.constructor; |
| 379 } | 405 } |
| 380 thisElement = new ThisElement(thisEnclosingElement); | 406 thisElement = new ThisElement(thisEnclosingElement); |
| 381 } | 407 } |
| 382 closureData = new ClosureData(null, null, null, thisElement); | 408 closureData = new ClosureData(null, null, null, thisElement); |
| 383 } | 409 } |
| 384 scopeVariables = new List<Element>(); | |
| 385 | 410 |
| 386 // We have to declare the implicit 'this' parameter. | 411 inNewScope(node, () { |
| 387 if (!insideClosure && closureData.thisElement !== null) { | 412 // We have to declare the implicit 'this' parameter. |
| 388 declareLocal(closureData.thisElement); | 413 if (!insideClosure && closureData.thisElement !== null) { |
| 389 } | 414 declareLocal(closureData.thisElement); |
| 390 // If we are inside a named closure we have to declare ourselve. For | 415 } |
| 391 // simplicity we declare the local even if the closure does not have a name | 416 // If we are inside a named closure we have to declare ourselve. For |
| 392 // It will simply not be used. | 417 // simplicity we declare the local even if the closure does not have a |
| 393 if (insideClosure) { | 418 // name. |
| 394 declareLocal(element); | 419 // It will simply not be used. |
| 395 } | 420 if (insideClosure) { |
| 421 declareLocal(element); |
| 422 } |
| 396 | 423 |
| 397 // TODO(ahe): This is problematic. The backend should not repeat | 424 // TODO(ahe): This is problematic. The backend should not repeat |
| 398 // the work of the resolver. It is the resolver's job to create | 425 // the work of the resolver. It is the resolver's job to create |
| 399 // parameters, etc. Other phases should only visit statements. | 426 // parameters, etc. Other phases should only visit statements. |
| 400 // TODO(floitsch): we avoid visiting the initializers on purpose so that we | 427 // TODO(floitsch): we avoid visiting the initializers on purpose so that |
| 401 // get an error-message later in the builder. | 428 // we get an error-message later in the builder. |
| 402 if (node.parameters !== null) node.parameters.accept(this); | 429 if (node.parameters !== null) node.parameters.accept(this); |
| 403 if (node.body !== null) node.body.accept(this); | 430 if (node.body !== null) node.body.accept(this); |
| 404 | 431 }); |
| 405 attachCapturedScopeVariables(node); | |
| 406 | 432 |
| 407 closureDataCache[node] = closureData; | 433 closureDataCache[node] = closureData; |
| 408 | 434 |
| 409 ClosureData savedClosureData = closureData; | 435 ClosureData savedClosureData = closureData; |
| 410 bool savedInsideClosure = insideClosure; | 436 bool savedInsideClosure = insideClosure; |
| 411 | 437 |
| 412 // Restore old values. | 438 // Restore old values. |
| 413 scopeVariables = oldScopeVariables; | |
| 414 insideClosure = oldInsideClosure; | 439 insideClosure = oldInsideClosure; |
| 415 closureData = oldClosureData; | 440 closureData = oldClosureData; |
| 416 currentFunctionElement = oldFunctionElement; | 441 currentFunctionElement = oldFunctionElement; |
| 417 | 442 |
| 418 // Mark all free variables as captured and use them in the outer function. | 443 // Mark all free variables as captured and use them in the outer function. |
| 419 List<Element> freeVariables = | 444 List<Element> freeVariables = |
| 420 savedClosureData.freeVariableMapping.getKeys(); | 445 savedClosureData.freeVariableMapping.getKeys(); |
| 421 assert(freeVariables.isEmpty() || savedInsideClosure); | 446 assert(freeVariables.isEmpty() || savedInsideClosure); |
| 422 for (Element freeElement in freeVariables) { | 447 for (Element freeElement in freeVariables) { |
| 423 if (capturedVariableMapping[freeElement] != null && | 448 if (capturedVariableMapping[freeElement] != null && |
| (...skipping 11 matching lines...) Expand all Loading... |
| 435 } | 460 } |
| 436 | 461 |
| 437 visitTryStatement(TryStatement node) { | 462 visitTryStatement(TryStatement node) { |
| 438 // TODO(ngeoffray): implement finer grain state. | 463 // TODO(ngeoffray): implement finer grain state. |
| 439 bool oldInTryStatement = inTryStatement; | 464 bool oldInTryStatement = inTryStatement; |
| 440 inTryStatement = true; | 465 inTryStatement = true; |
| 441 node.visitChildren(this); | 466 node.visitChildren(this); |
| 442 inTryStatement = oldInTryStatement; | 467 inTryStatement = oldInTryStatement; |
| 443 } | 468 } |
| 444 } | 469 } |
| OLD | NEW |