Chromium Code Reviews| 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 #library("closureToClassMapper"); | 5 #library("closureToClassMapper"); |
| 6 | 6 |
| 7 #import("elements/elements.dart"); | 7 #import("elements/elements.dart"); |
| 8 #import("leg.dart"); | 8 #import("leg.dart"); |
| 9 #import("scanner/scannerlib.dart"); | 9 #import("scanner/scannerlib.dart"); |
| 10 #import("tree/tree.dart"); | 10 #import("tree/tree.dart"); |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 172 // List of encountered closures. | 172 // List of encountered closures. |
| 173 List<FunctionExpression> closures; | 173 List<FunctionExpression> closures; |
| 174 | 174 |
| 175 // The variables that have been declared in the current scope. | 175 // The variables that have been declared in the current scope. |
| 176 List<Element> scopeVariables; | 176 List<Element> scopeVariables; |
| 177 | 177 |
| 178 // Keep track of the mutated variables so that we don't need to box | 178 // Keep track of the mutated variables so that we don't need to box |
| 179 // non-mutated variables. | 179 // non-mutated variables. |
| 180 Set<Element> mutatedVariables; | 180 Set<Element> mutatedVariables; |
| 181 | 181 |
| 182 FunctionElement outermostFunctionElement; | |
| 182 FunctionElement currentFunctionElement; | 183 FunctionElement currentFunctionElement; |
| 184 | |
| 183 // The closureData of the currentFunctionElement. | 185 // The closureData of the currentFunctionElement. |
| 184 ClosureClassMap closureData; | 186 ClosureClassMap closureData; |
| 185 | 187 |
| 186 bool insideClosure = false; | 188 bool insideClosure = false; |
| 187 | 189 |
| 188 ClosureTranslator(this.compiler, this.elements, this.closureMappingCache) | 190 ClosureTranslator(this.compiler, this.elements, this.closureMappingCache) |
| 189 : capturedVariableMapping = new Map<Element, Element>(), | 191 : capturedVariableMapping = new Map<Element, Element>(), |
| 190 closures = <FunctionExpression>[], | 192 closures = <FunctionExpression>[], |
| 191 mutatedVariables = new Set<Element>(); | 193 mutatedVariables = new Set<Element>(); |
| 192 | 194 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 Element fieldElement = new ClosureFieldElement(name, closureElement); | 245 Element fieldElement = new ClosureFieldElement(name, closureElement); |
| 244 closureElement.backendMembers = | 246 closureElement.backendMembers = |
| 245 closureElement.backendMembers.prepend(fieldElement); | 247 closureElement.backendMembers.prepend(fieldElement); |
| 246 data.capturedFieldMapping[fieldElement] = capturedElement; | 248 data.capturedFieldMapping[fieldElement] = capturedElement; |
| 247 freeVariableMapping[capturedElement] = fieldElement; | 249 freeVariableMapping[capturedElement] = fieldElement; |
| 248 } | 250 } |
| 249 } | 251 } |
| 250 } | 252 } |
| 251 | 253 |
| 252 void useLocal(Element element) { | 254 void useLocal(Element element) { |
| 253 // TODO(floitsch): replace this with a general solution. | |
| 254 Element functionElement = currentFunctionElement; | |
| 255 if (functionElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
| 256 ConstructorBodyElement body = functionElement; | |
| 257 functionElement = body.constructor; | |
| 258 } | |
| 259 // If the element is not declared in the current function and the element | 255 // If the element is not declared in the current function and the element |
| 260 // is not the closure itself we need to mark the element as free variable. | 256 // is not the closure itself we need to mark the element as free variable. |
| 261 if (element.enclosingElement != functionElement && | 257 if (insideClosure && |
|
floitsch
2012/09/05 13:50:34
As discussed: explain that this is not just an opt
ngeoffray
2012/09/05 15:36:11
Done.
| |
| 262 element != functionElement) { | 258 element.enclosingElement != currentFunctionElement && |
| 259 element != currentFunctionElement) { | |
| 263 assert(closureData.freeVariableMapping[element] == null || | 260 assert(closureData.freeVariableMapping[element] == null || |
| 264 closureData.freeVariableMapping[element] == element); | 261 closureData.freeVariableMapping[element] == element); |
| 265 closureData.freeVariableMapping[element] = element; | 262 closureData.freeVariableMapping[element] = element; |
| 266 } else if (inTryStatement) { | 263 } else if (inTryStatement) { |
| 267 // Don't mark the this-element. This would complicate things in the | 264 // Don't mark the this-element. This would complicate things in the |
| 268 // builder. | 265 // builder. |
| 269 if (element != closureData.thisElement) { | 266 if (element != closureData.thisElement) { |
| 270 // TODO(ngeoffray): only do this if the variable is mutated. | 267 // TODO(ngeoffray): only do this if the variable is mutated. |
| 271 closureData.usedVariablesInTry.add(element); | 268 closureData.usedVariablesInTry.add(element); |
| 272 } | 269 } |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 339 | 336 |
| 340 visitSendSet(SendSet node) { | 337 visitSendSet(SendSet node) { |
| 341 Element element = elements[node]; | 338 Element element = elements[node]; |
| 342 if (Elements.isLocal(element)) { | 339 if (Elements.isLocal(element)) { |
| 343 mutatedVariables.add(element); | 340 mutatedVariables.add(element); |
| 344 } | 341 } |
| 345 super.visitSendSet(node); | 342 super.visitSendSet(node); |
| 346 } | 343 } |
| 347 | 344 |
| 348 visitNewExpression(NewExpression node) { | 345 visitNewExpression(NewExpression node) { |
| 346 TypeAnnotation annotation = node.send.getTypeAnnotation(); | |
| 347 DartType type = elements.getType(annotation); | |
| 348 | |
| 349 bool hasTypeVariable(DartType type) { | 349 bool hasTypeVariable(DartType type) { |
| 350 if (type is TypeVariableType) { | 350 if (type is TypeVariableType) { |
| 351 return true; | 351 return true; |
| 352 } else if (type is InterfaceType) { | 352 } else if (type is InterfaceType) { |
| 353 InterfaceType ifcType = type; | 353 InterfaceType ifcType = type; |
| 354 for (DartType argument in ifcType.arguments) { | 354 for (DartType argument in ifcType.arguments) { |
| 355 if (hasTypeVariable(argument)) { | 355 if (hasTypeVariable(argument)) { |
| 356 return true; | 356 return true; |
| 357 } | 357 } |
| 358 } | 358 } |
| 359 } | 359 } |
| 360 return false; | 360 return false; |
| 361 } | 361 } |
| 362 TypeAnnotation annotation = node.send.getTypeAnnotation(); | 362 |
| 363 DartType type = elements.getType(annotation); | 363 void analyzeTypeVariables(DartType type) { |
| 364 if (hasTypeVariable(type)) { | 364 if (type is TypeVariableType) { |
| 365 // Factories do not use [this] to get the type variables. | 365 useLocal(type.element); |
| 366 if (closureData.thisElement !== null) { | 366 } else if (type is InterfaceType) { |
| 367 useLocal(closureData.thisElement); | 367 InterfaceType ifcType = type; |
| 368 for (DartType argument in ifcType.arguments) { | |
| 369 analyzeTypeVariables(argument); | |
| 370 } | |
| 368 } | 371 } |
| 369 } | 372 } |
| 373 | |
| 374 if (outermostFunctionElement.isInstanceMember() | |
| 375 || outermostFunctionElement.isGenerativeConstructor()) { | |
| 376 if (hasTypeVariable(type)) useLocal(closureData.thisElement); | |
| 377 } else if (outermostFunctionElement.isFactoryConstructor()) { | |
| 378 analyzeTypeVariables(type); | |
| 379 } | |
| 380 | |
| 370 node.visitChildren(this); | 381 node.visitChildren(this); |
| 371 } | 382 } |
| 372 | 383 |
| 373 // If variables that are declared in the [node] scope are captured and need | 384 // If variables that are declared in the [node] scope are captured and need |
| 374 // to be boxed create a box-element and update the [capturingScopes] in the | 385 // to be boxed create a box-element and update the [capturingScopes] in the |
| 375 // current [closureData]. | 386 // current [closureData]. |
| 376 // The boxed variables are updated in the [capturedVariableMapping]. | 387 // The boxed variables are updated in the [capturedVariableMapping]. |
| 377 void attachCapturedScopeVariables(Node node) { | 388 void attachCapturedScopeVariables(Node node) { |
| 378 Element box = null; | 389 Element box = null; |
| 379 Map<Element, Element> scopeMapping = new Map<Element, Element>(); | 390 Map<Element, Element> scopeMapping = new Map<Element, Element>(); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 457 const EmptyLink<Element>().prepend(callElement); | 468 const EmptyLink<Element>().prepend(callElement); |
| 458 // The nested function's 'this' is the same as the one for the outer | 469 // The nested function's 'this' is the same as the one for the outer |
| 459 // function. It could be [null] if we are inside a static method. | 470 // function. It could be [null] if we are inside a static method. |
| 460 Element thisElement = closureData.thisElement; | 471 Element thisElement = closureData.thisElement; |
| 461 return new ClosureClassMap(element, globalizedElement, | 472 return new ClosureClassMap(element, globalizedElement, |
| 462 callElement, thisElement); | 473 callElement, thisElement); |
| 463 } | 474 } |
| 464 | 475 |
| 465 visitFunctionExpression(FunctionExpression node) { | 476 visitFunctionExpression(FunctionExpression node) { |
| 466 Element element = elements[node]; | 477 Element element = elements[node]; |
| 467 if (element.kind === ElementKind.PARAMETER) { | 478 if (element.isParameter()) { |
| 468 // TODO(ahe): This is a hack. This method should *not* call | 479 // TODO(ahe): This is a hack. This method should *not* call |
| 469 // visitChildren. | 480 // visitChildren. |
| 470 return node.name.accept(this); | 481 return node.name.accept(this); |
| 471 } | 482 } |
| 472 bool isClosure = (closureData !== null); | |
| 473 | |
| 474 if (isClosure) closures.add(node); | |
| 475 | 483 |
| 476 bool oldInsideClosure = insideClosure; | 484 bool oldInsideClosure = insideClosure; |
| 477 FunctionElement oldFunctionElement = currentFunctionElement; | 485 FunctionElement oldFunctionElement = currentFunctionElement; |
| 478 ClosureClassMap oldClosureData = closureData; | 486 ClosureClassMap oldClosureData = closureData; |
| 479 | 487 |
| 480 insideClosure = isClosure; | 488 insideClosure = outermostFunctionElement != null; |
| 481 currentFunctionElement = elements[node]; | 489 currentFunctionElement = element; |
| 482 if (insideClosure) { | 490 if (insideClosure) { |
| 491 closures.add(node); | |
| 483 closureData = globalizeClosure(node, element); | 492 closureData = globalizeClosure(node, element); |
| 484 } else { | 493 } else { |
| 494 outermostFunctionElement = element; | |
| 485 Element thisElement = null; | 495 Element thisElement = null; |
| 486 // TODO(floitsch): we should not need to look for generative constructors. | 496 // TODO(floitsch): we should not need to look for generative constructors. |
| 487 // At the moment we store only one ClosureData for both the factory and | 497 // At the moment we store only one ClosureData for both the factory and |
| 488 // the body. | 498 // the body. |
| 489 if (element.isInstanceMember() || | 499 if (element.isInstanceMember() || element.isGenerativeConstructor()) { |
| 490 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 491 // TODO(floitsch): currently all variables are considered to be | 500 // TODO(floitsch): currently all variables are considered to be |
|
floitsch
2012/09/05 13:50:34
Remove the TODO.
ngeoffray
2012/09/05 15:36:11
Done.
| |
| 492 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'. | 501 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'. |
| 493 Element thisEnclosingElement = element; | 502 thisElement = new ThisElement(element); |
| 494 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
| 495 ConstructorBodyElement body = element; | |
| 496 thisEnclosingElement = body.constructor; | |
| 497 } | |
| 498 thisElement = new ThisElement(thisEnclosingElement); | |
| 499 } | 503 } |
| 500 closureData = new ClosureClassMap(null, null, null, thisElement); | 504 closureData = new ClosureClassMap(null, null, null, thisElement); |
| 501 } | 505 } |
| 502 closureMappingCache[node] = closureData; | 506 closureMappingCache[node] = closureData; |
| 503 | 507 |
| 504 inNewScope(node, () { | 508 inNewScope(node, () { |
| 505 // We have to declare the implicit 'this' parameter. | 509 // We have to declare the implicit 'this' parameter. |
| 506 if (!insideClosure && closureData.thisElement !== null) { | 510 if (!insideClosure && closureData.thisElement !== null) { |
| 507 declareLocal(closureData.thisElement); | 511 declareLocal(closureData.thisElement); |
| 508 } | 512 } |
| 509 // If we are inside a named closure we have to declare ourselve. For | 513 // If we are inside a named closure we have to declare ourselve. For |
| 510 // simplicity we declare the local even if the closure does not have a | 514 // simplicity we declare the local even if the closure does not have a |
| 511 // name. | 515 // name. |
| 512 // It will simply not be used. | 516 // It will simply not be used. |
| 513 if (insideClosure) { | 517 if (insideClosure) { |
| 514 declareLocal(element); | 518 declareLocal(element); |
| 515 } | 519 } |
| 520 | |
| 521 if (currentFunctionElement.isFactoryConstructor()) { | |
| 522 // Declare the type parameters in the scope. Generative | |
| 523 // constructors just use 'this'. | |
| 524 ClassElement cls = currentFunctionElement.enclosingElement; | |
| 525 cls.typeVariables.forEach((TypeVariableType typeVariable) { | |
| 526 declareLocal(typeVariable.element); | |
| 527 }); | |
| 528 } | |
| 516 | 529 |
| 517 // TODO(ahe): This is problematic. The backend should not repeat | 530 // TODO(ahe): This is problematic. The backend should not repeat |
| 518 // the work of the resolver. It is the resolver's job to create | 531 // the work of the resolver. It is the resolver's job to create |
| 519 // parameters, etc. Other phases should only visit statements. | 532 // parameters, etc. Other phases should only visit statements. |
| 520 // TODO(floitsch): we avoid visiting the initializers on purpose so that | 533 // TODO(floitsch): we avoid visiting the initializers on purpose so that |
| 521 // we get an error-message later in the builder. | 534 // we get an error-message later in the builder. |
| 522 if (node.parameters !== null) node.parameters.accept(this); | 535 if (node.parameters !== null) node.parameters.accept(this); |
| 523 if (node.body !== null) node.body.accept(this); | 536 if (node.body !== null) node.body.accept(this); |
| 524 }); | 537 }); |
| 525 | 538 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 552 } | 565 } |
| 553 | 566 |
| 554 visitTryStatement(TryStatement node) { | 567 visitTryStatement(TryStatement node) { |
| 555 // TODO(ngeoffray): implement finer grain state. | 568 // TODO(ngeoffray): implement finer grain state. |
| 556 bool oldInTryStatement = inTryStatement; | 569 bool oldInTryStatement = inTryStatement; |
| 557 inTryStatement = true; | 570 inTryStatement = true; |
| 558 node.visitChildren(this); | 571 node.visitChildren(this); |
| 559 inTryStatement = oldInTryStatement; | 572 inTryStatement = oldInTryStatement; |
| 560 } | 573 } |
| 561 } | 574 } |
| OLD | NEW |