| 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 // Note that the check on [insideClosure] is not just an |
| 262 element != functionElement) { | 258 // optimization: factories have type parameters as function |
| 259 // parameters, and type parameters are declared in the class, not |
| 260 // the factory. |
| 261 if (insideClosure && |
| 262 element.enclosingElement != currentFunctionElement && |
| 263 element != currentFunctionElement) { |
| 263 assert(closureData.freeVariableMapping[element] == null || | 264 assert(closureData.freeVariableMapping[element] == null || |
| 264 closureData.freeVariableMapping[element] == element); | 265 closureData.freeVariableMapping[element] == element); |
| 265 closureData.freeVariableMapping[element] = element; | 266 closureData.freeVariableMapping[element] = element; |
| 266 } else if (inTryStatement) { | 267 } else if (inTryStatement) { |
| 267 // Don't mark the this-element. This would complicate things in the | 268 // Don't mark the this-element. This would complicate things in the |
| 268 // builder. | 269 // builder. |
| 269 if (element != closureData.thisElement) { | 270 if (element != closureData.thisElement) { |
| 270 // TODO(ngeoffray): only do this if the variable is mutated. | 271 // TODO(ngeoffray): only do this if the variable is mutated. |
| 271 closureData.usedVariablesInTry.add(element); | 272 closureData.usedVariablesInTry.add(element); |
| 272 } | 273 } |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 | 340 |
| 340 visitSendSet(SendSet node) { | 341 visitSendSet(SendSet node) { |
| 341 Element element = elements[node]; | 342 Element element = elements[node]; |
| 342 if (Elements.isLocal(element)) { | 343 if (Elements.isLocal(element)) { |
| 343 mutatedVariables.add(element); | 344 mutatedVariables.add(element); |
| 344 } | 345 } |
| 345 super.visitSendSet(node); | 346 super.visitSendSet(node); |
| 346 } | 347 } |
| 347 | 348 |
| 348 visitNewExpression(NewExpression node) { | 349 visitNewExpression(NewExpression node) { |
| 350 TypeAnnotation annotation = node.send.getTypeAnnotation(); |
| 351 DartType type = elements.getType(annotation); |
| 352 |
| 349 bool hasTypeVariable(DartType type) { | 353 bool hasTypeVariable(DartType type) { |
| 350 if (type is TypeVariableType) { | 354 if (type is TypeVariableType) { |
| 351 return true; | 355 return true; |
| 352 } else if (type is InterfaceType) { | 356 } else if (type is InterfaceType) { |
| 353 InterfaceType ifcType = type; | 357 InterfaceType ifcType = type; |
| 354 for (DartType argument in ifcType.arguments) { | 358 for (DartType argument in ifcType.arguments) { |
| 355 if (hasTypeVariable(argument)) { | 359 if (hasTypeVariable(argument)) { |
| 356 return true; | 360 return true; |
| 357 } | 361 } |
| 358 } | 362 } |
| 359 } | 363 } |
| 360 return false; | 364 return false; |
| 361 } | 365 } |
| 362 TypeAnnotation annotation = node.send.getTypeAnnotation(); | 366 |
| 363 DartType type = elements.getType(annotation); | 367 void analyzeTypeVariables(DartType type) { |
| 364 if (hasTypeVariable(type)) { | 368 if (type is TypeVariableType) { |
| 365 // Factories do not use [this] to get the type variables. | 369 useLocal(type.element); |
| 366 if (closureData.thisElement !== null) { | 370 } else if (type is InterfaceType) { |
| 367 useLocal(closureData.thisElement); | 371 InterfaceType ifcType = type; |
| 372 for (DartType argument in ifcType.arguments) { |
| 373 analyzeTypeVariables(argument); |
| 374 } |
| 368 } | 375 } |
| 369 } | 376 } |
| 377 |
| 378 if (outermostFunctionElement.isInstanceMember() |
| 379 || outermostFunctionElement.isGenerativeConstructor()) { |
| 380 if (hasTypeVariable(type)) useLocal(closureData.thisElement); |
| 381 } else if (outermostFunctionElement.isFactoryConstructor()) { |
| 382 analyzeTypeVariables(type); |
| 383 } |
| 384 |
| 370 node.visitChildren(this); | 385 node.visitChildren(this); |
| 371 } | 386 } |
| 372 | 387 |
| 373 // If variables that are declared in the [node] scope are captured and need | 388 // 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 | 389 // to be boxed create a box-element and update the [capturingScopes] in the |
| 375 // current [closureData]. | 390 // current [closureData]. |
| 376 // The boxed variables are updated in the [capturedVariableMapping]. | 391 // The boxed variables are updated in the [capturedVariableMapping]. |
| 377 void attachCapturedScopeVariables(Node node) { | 392 void attachCapturedScopeVariables(Node node) { |
| 378 Element box = null; | 393 Element box = null; |
| 379 Map<Element, Element> scopeMapping = new Map<Element, Element>(); | 394 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); | 472 const EmptyLink<Element>().prepend(callElement); |
| 458 // The nested function's 'this' is the same as the one for the outer | 473 // 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. | 474 // function. It could be [null] if we are inside a static method. |
| 460 Element thisElement = closureData.thisElement; | 475 Element thisElement = closureData.thisElement; |
| 461 return new ClosureClassMap(element, globalizedElement, | 476 return new ClosureClassMap(element, globalizedElement, |
| 462 callElement, thisElement); | 477 callElement, thisElement); |
| 463 } | 478 } |
| 464 | 479 |
| 465 visitFunctionExpression(FunctionExpression node) { | 480 visitFunctionExpression(FunctionExpression node) { |
| 466 Element element = elements[node]; | 481 Element element = elements[node]; |
| 467 if (element.kind === ElementKind.PARAMETER) { | 482 if (element.isParameter()) { |
| 468 // TODO(ahe): This is a hack. This method should *not* call | 483 // TODO(ahe): This is a hack. This method should *not* call |
| 469 // visitChildren. | 484 // visitChildren. |
| 470 return node.name.accept(this); | 485 return node.name.accept(this); |
| 471 } | 486 } |
| 472 bool isClosure = (closureData !== null); | |
| 473 | |
| 474 if (isClosure) closures.add(node); | |
| 475 | 487 |
| 476 bool oldInsideClosure = insideClosure; | 488 bool oldInsideClosure = insideClosure; |
| 477 FunctionElement oldFunctionElement = currentFunctionElement; | 489 FunctionElement oldFunctionElement = currentFunctionElement; |
| 478 ClosureClassMap oldClosureData = closureData; | 490 ClosureClassMap oldClosureData = closureData; |
| 479 | 491 |
| 480 insideClosure = isClosure; | 492 insideClosure = outermostFunctionElement != null; |
| 481 currentFunctionElement = elements[node]; | 493 currentFunctionElement = element; |
| 482 if (insideClosure) { | 494 if (insideClosure) { |
| 495 closures.add(node); |
| 483 closureData = globalizeClosure(node, element); | 496 closureData = globalizeClosure(node, element); |
| 484 } else { | 497 } else { |
| 498 outermostFunctionElement = element; |
| 485 Element thisElement = null; | 499 Element thisElement = null; |
| 486 // TODO(floitsch): we should not need to look for generative constructors. | 500 if (element.isInstanceMember() || element.isGenerativeConstructor()) { |
| 487 // At the moment we store only one ClosureData for both the factory and | 501 thisElement = new ThisElement(element); |
| 488 // the body. | |
| 489 if (element.isInstanceMember() || | |
| 490 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
| 491 // TODO(floitsch): currently all variables are considered to be | |
| 492 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'. | |
| 493 Element thisEnclosingElement = element; | |
| 494 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
| 495 ConstructorBodyElement body = element; | |
| 496 thisEnclosingElement = body.constructor; | |
| 497 } | |
| 498 thisElement = new ThisElement(thisEnclosingElement); | |
| 499 } | 502 } |
| 500 closureData = new ClosureClassMap(null, null, null, thisElement); | 503 closureData = new ClosureClassMap(null, null, null, thisElement); |
| 501 } | 504 } |
| 502 closureMappingCache[node] = closureData; | 505 closureMappingCache[node] = closureData; |
| 503 | 506 |
| 504 inNewScope(node, () { | 507 inNewScope(node, () { |
| 505 // We have to declare the implicit 'this' parameter. | 508 // We have to declare the implicit 'this' parameter. |
| 506 if (!insideClosure && closureData.thisElement !== null) { | 509 if (!insideClosure && closureData.thisElement !== null) { |
| 507 declareLocal(closureData.thisElement); | 510 declareLocal(closureData.thisElement); |
| 508 } | 511 } |
| 509 // If we are inside a named closure we have to declare ourselve. For | 512 // 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 | 513 // simplicity we declare the local even if the closure does not have a |
| 511 // name. | 514 // name. |
| 512 // It will simply not be used. | 515 // It will simply not be used. |
| 513 if (insideClosure) { | 516 if (insideClosure) { |
| 514 declareLocal(element); | 517 declareLocal(element); |
| 515 } | 518 } |
| 519 |
| 520 if (currentFunctionElement.isFactoryConstructor()) { |
| 521 // Declare the type parameters in the scope. Generative |
| 522 // constructors just use 'this'. |
| 523 ClassElement cls = currentFunctionElement.enclosingElement; |
| 524 cls.typeVariables.forEach((TypeVariableType typeVariable) { |
| 525 declareLocal(typeVariable.element); |
| 526 }); |
| 527 } |
| 516 | 528 |
| 517 // TODO(ahe): This is problematic. The backend should not repeat | 529 // TODO(ahe): This is problematic. The backend should not repeat |
| 518 // the work of the resolver. It is the resolver's job to create | 530 // the work of the resolver. It is the resolver's job to create |
| 519 // parameters, etc. Other phases should only visit statements. | 531 // parameters, etc. Other phases should only visit statements. |
| 520 // TODO(floitsch): we avoid visiting the initializers on purpose so that | 532 // TODO(floitsch): we avoid visiting the initializers on purpose so that |
| 521 // we get an error-message later in the builder. | 533 // we get an error-message later in the builder. |
| 522 if (node.parameters !== null) node.parameters.accept(this); | 534 if (node.parameters !== null) node.parameters.accept(this); |
| 523 if (node.body !== null) node.body.accept(this); | 535 if (node.body !== null) node.body.accept(this); |
| 524 }); | 536 }); |
| 525 | 537 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 552 } | 564 } |
| 553 | 565 |
| 554 visitTryStatement(TryStatement node) { | 566 visitTryStatement(TryStatement node) { |
| 555 // TODO(ngeoffray): implement finer grain state. | 567 // TODO(ngeoffray): implement finer grain state. |
| 556 bool oldInTryStatement = inTryStatement; | 568 bool oldInTryStatement = inTryStatement; |
| 557 inTryStatement = true; | 569 inTryStatement = true; |
| 558 node.visitChildren(this); | 570 node.visitChildren(this); |
| 559 inTryStatement = oldInTryStatement; | 571 inTryStatement = oldInTryStatement; |
| 560 } | 572 } |
| 561 } | 573 } |
| OLD | NEW |