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 interface TreeElements { | 5 interface TreeElements { |
| 6 Element operator[](Node node); | 6 Element operator[](Node node); |
| 7 Selector getSelector(Send send); | 7 Selector getSelector(Send send); |
| 8 } | 8 } |
| 9 | 9 |
| 10 class TreeElementMapping implements TreeElements { | 10 class TreeElementMapping implements TreeElements { |
| (...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 415 class LabeledStatementLabelScope implements LabelScope { | 415 class LabeledStatementLabelScope implements LabelScope { |
| 416 final LabelScope outer; | 416 final LabelScope outer; |
| 417 final LabelElement label; | 417 final LabelElement label; |
| 418 LabeledStatementLabelScope(this.outer, this.label); | 418 LabeledStatementLabelScope(this.outer, this.label); |
| 419 LabelElement lookup(String labelName) { | 419 LabelElement lookup(String labelName) { |
| 420 if (this.label.labelName == labelName) return label; | 420 if (this.label.labelName == labelName) return label; |
| 421 return outer.lookup(labelName); | 421 return outer.lookup(labelName); |
| 422 } | 422 } |
| 423 } | 423 } |
| 424 | 424 |
| 425 class SwitchLabelScope implements LabelScope { | |
| 426 final LabelScope outer; | |
| 427 final Map<String, LabelElement> caseLabels; | |
|
ahe
2012/03/13 10:17:15
Add an empty line.
Lasse Reichstein Nielsen
2012/03/13 12:09:38
Done.
| |
| 428 SwitchLabelScope(this.outer, this.caseLabels); | |
|
ahe
2012/03/13 10:17:15
Add an empty line.
Lasse Reichstein Nielsen
2012/03/13 12:09:38
Done.
| |
| 429 LabelElement lookup(String labelName) { | |
| 430 LabelElement result = caseLabels[labelName]; | |
| 431 if (result !== null) return result; | |
| 432 return outer.lookup(labelName); | |
| 433 } | |
| 434 } | |
| 435 | |
| 425 class EmptyLabelScope implements LabelScope { | 436 class EmptyLabelScope implements LabelScope { |
| 426 const EmptyLabelScope(); | 437 const EmptyLabelScope(); |
| 427 LabelElement lookup(String label) => null; | 438 LabelElement lookup(String label) => null; |
| 428 LabelScope get outer() { | 439 LabelScope get outer() { |
| 429 throw 'internal error: empty label scope has no outer'; | 440 throw 'internal error: empty label scope has no outer'; |
| 430 } | 441 } |
| 431 } | 442 } |
| 432 | 443 |
| 433 class StatementScope { | 444 class StatementScope { |
| 434 LabelScope labels; | 445 LabelScope labels; |
| 435 Link<StatementElement> breakTargetStack; | 446 Link<StatementElement> breakTargetStack; |
| 436 Link<StatementElement> continueTargetStack; | 447 Link<StatementElement> continueTargetStack; |
| 448 int nestingLevel = 0; | |
| 437 | 449 |
| 438 StatementScope() | 450 StatementScope() |
| 439 : labels = const EmptyLabelScope(), | 451 : labels = const EmptyLabelScope(), |
| 440 breakTargetStack = const EmptyLink<StatementElement>(), | 452 breakTargetStack = const EmptyLink<StatementElement>(), |
| 441 continueTargetStack = const EmptyLink<StatementElement>(); | 453 continueTargetStack = const EmptyLink<StatementElement>(); |
| 442 | 454 |
| 443 LabelElement lookupLabel(String label) => labels.lookup(label); | 455 LabelElement lookupLabel(String label) => |
| 456 labels.lookup(label); | |
| 457 | |
| 444 StatementElement currentBreakTarget() => | 458 StatementElement currentBreakTarget() => |
| 445 breakTargetStack.isEmpty() ? null : breakTargetStack.head; | 459 breakTargetStack.isEmpty() ? null : breakTargetStack.head; |
| 460 | |
| 446 StatementElement currentContinueTarget() => | 461 StatementElement currentContinueTarget() => |
| 447 continueTargetStack.isEmpty() ? null : continueTargetStack.head; | 462 continueTargetStack.isEmpty() ? null : continueTargetStack.head; |
| 448 | 463 |
| 449 void enterLabelScope(LabelElement element) { | 464 void enterLabelScope(LabelElement element) { |
| 450 labels = new LabeledStatementLabelScope(labels, element); | 465 labels = new LabeledStatementLabelScope(labels, element); |
| 466 nestingLevel++; | |
| 451 } | 467 } |
| 468 | |
| 452 void exitLabelScope() { | 469 void exitLabelScope() { |
| 470 nestingLevel--; | |
| 453 labels = labels.outer; | 471 labels = labels.outer; |
| 454 } | 472 } |
| 473 | |
| 455 void enterLoop(StatementElement element) { | 474 void enterLoop(StatementElement element) { |
| 456 breakTargetStack = breakTargetStack.prepend(element); | 475 breakTargetStack = breakTargetStack.prepend(element); |
| 457 continueTargetStack = continueTargetStack.prepend(element); | 476 continueTargetStack = continueTargetStack.prepend(element); |
| 477 nestingLevel++; | |
| 458 } | 478 } |
| 479 | |
| 459 void exitLoop() { | 480 void exitLoop() { |
| 481 nestingLevel--; | |
| 460 breakTargetStack = breakTargetStack.tail; | 482 breakTargetStack = breakTargetStack.tail; |
| 461 continueTargetStack = continueTargetStack.tail; | 483 continueTargetStack = continueTargetStack.tail; |
| 462 } | 484 } |
| 485 | |
| 486 void enterSwitch(StatementElement breakElement, | |
| 487 Map<String, LabelElement> continueElements) { | |
| 488 breakTargetStack = breakTargetStack.prepend(breakElement); | |
| 489 labels = new SwitchLabelScope(labels, continueElements); | |
| 490 nestingLevel++; | |
|
ahe
2012/03/13 10:17:15
This seems wrong, but perhaps I'm misunderstanding
Lasse Reichstein Nielsen
2012/03/13 12:09:38
The switch level doesn't need to be exact, as long
| |
| 491 } | |
| 492 | |
| 493 void exitSwitch() { | |
| 494 nestingLevel--; | |
| 495 breakTargetStack = breakTargetStack.tail; | |
| 496 labels = labels.outer; | |
| 497 } | |
| 463 } | 498 } |
| 464 | 499 |
| 465 | |
| 466 class ResolverVisitor extends CommonResolverVisitor<Element> { | 500 class ResolverVisitor extends CommonResolverVisitor<Element> { |
| 467 final TreeElementMapping mapping; | 501 final TreeElementMapping mapping; |
| 468 final Element enclosingElement; | 502 final Element enclosingElement; |
| 469 bool inInstanceContext; | 503 bool inInstanceContext; |
| 470 Scope context; | 504 Scope context; |
| 471 ClassElement currentClass; | 505 ClassElement currentClass; |
| 472 bool typeRequired = false; | 506 bool typeRequired = false; |
| 473 StatementScope statementScope; | 507 StatementScope statementScope; |
| 474 int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION; | 508 int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION; |
| 475 | 509 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 489 Element result = context.lookup(name); | 523 Element result = context.lookup(name); |
| 490 if (!inInstanceContext && result != null && result.isInstanceMember()) { | 524 if (!inInstanceContext && result != null && result.isInstanceMember()) { |
| 491 error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]); | 525 error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]); |
| 492 } | 526 } |
| 493 return result; | 527 return result; |
| 494 } | 528 } |
| 495 | 529 |
| 496 // Create, or reuse an already created, statement element for a statement. | 530 // Create, or reuse an already created, statement element for a statement. |
| 497 StatementElement getOrCreateStatementElement(Node statement) { | 531 StatementElement getOrCreateStatementElement(Node statement) { |
| 498 StatementElement element = mapping[statement]; | 532 StatementElement element = mapping[statement]; |
| 499 if (element !== null) return element; | 533 if (element === null) { |
| 500 element = new StatementElement(statement, enclosingElement); | 534 element = new StatementElement(statement, |
| 501 mapping[statement] = element; | 535 statementScope.nestingLevel, |
| 536 enclosingElement); | |
| 537 mapping[statement] = element; | |
| 538 } | |
| 502 return element; | 539 return element; |
| 503 } | 540 } |
| 504 | 541 |
| 505 inStaticContext(action()) { | 542 inStaticContext(action()) { |
| 506 bool wasInstanceContext = inInstanceContext; | 543 bool wasInstanceContext = inInstanceContext; |
| 507 inInstanceContext = false; | 544 inInstanceContext = false; |
| 508 action(); | 545 action(); |
| 509 inInstanceContext = wasInstanceContext; | 546 inInstanceContext = wasInstanceContext; |
| 510 } | 547 } |
| 511 | 548 |
| (...skipping 444 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 956 } | 993 } |
| 957 target.isBreakTarget = true; | 994 target.isBreakTarget = true; |
| 958 } else { | 995 } else { |
| 959 String labelName = node.target.source.slowToString(); | 996 String labelName = node.target.source.slowToString(); |
| 960 LabelElement label = statementScope.lookupLabel(labelName); | 997 LabelElement label = statementScope.lookupLabel(labelName); |
| 961 if (label === null) { | 998 if (label === null) { |
| 962 error(node.target, MessageKind.UNBOUND_LABEL, [labelName]); | 999 error(node.target, MessageKind.UNBOUND_LABEL, [labelName]); |
| 963 return; | 1000 return; |
| 964 } | 1001 } |
| 965 target = label.target; | 1002 target = label.target; |
| 1003 if (!target.statement.isValidBreakTarget()) { | |
| 1004 error(node.target, MessageKind.INVALID_BREAK, [labelName]); | |
| 1005 return; | |
|
ahe
2012/03/13 10:17:15
Thank you :-)
| |
| 1006 } | |
| 966 label.setBreakTarget(); | 1007 label.setBreakTarget(); |
| 967 mapping[node.target] = label; | 1008 mapping[node.target] = label; |
| 968 } | 1009 } |
| 969 mapping[node] = target; | 1010 mapping[node] = target; |
| 970 } | 1011 } |
| 971 | 1012 |
| 972 visitContinueStatement(ContinueStatement node) { | 1013 visitContinueStatement(ContinueStatement node) { |
| 973 StatementElement target; | 1014 StatementElement target; |
| 974 if (node.target === null) { | 1015 if (node.target === null) { |
| 975 target = statementScope.currentContinueTarget(); | 1016 target = statementScope.currentContinueTarget(); |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1044 visitLiteralMapEntry(LiteralMapEntry node) { | 1085 visitLiteralMapEntry(LiteralMapEntry node) { |
| 1045 node.visitChildren(this); | 1086 node.visitChildren(this); |
| 1046 } | 1087 } |
| 1047 | 1088 |
| 1048 visitNamedArgument(NamedArgument node) { | 1089 visitNamedArgument(NamedArgument node) { |
| 1049 visit(node.expression); | 1090 visit(node.expression); |
| 1050 } | 1091 } |
| 1051 | 1092 |
| 1052 visitSwitchStatement(SwitchStatement node) { | 1093 visitSwitchStatement(SwitchStatement node) { |
| 1053 node.expression.accept(this); | 1094 node.expression.accept(this); |
| 1054 StatementElement element = getOrCreateStatementElement(node); | 1095 |
| 1055 statementScope.enterLoop(element); | 1096 StatementElement breakElement = getOrCreateStatementElement(node); |
|
ahe
2012/03/13 10:17:15
Nit: I'm not digging this method. I would prefer i
Lasse Reichstein Nielsen
2012/03/13 12:09:38
The problem is that we want to create a StatementE
| |
| 1097 Map<String, LabelElement> continueLabels = <LabelElement>{}; | |
| 1098 Link<SwitchCase> cases = node.cases.nodes; | |
| 1099 while (!cases.isEmpty()) { | |
| 1100 SwitchCase switchCase = cases.head; | |
| 1101 if (switchCase.label !== null) { | |
| 1102 Identifier labelIdentifier = switchCase.label; | |
| 1103 String labelName = labelIdentifier.source.slowToString(); | |
| 1104 | |
| 1105 LabelElement existingElement = continueLabels[labelName]; | |
| 1106 if (existingElement !== null) { | |
| 1107 // It's an error if the same label occurs twice in the same switch. | |
| 1108 warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]); | |
| 1109 error(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]); | |
| 1110 } else { | |
| 1111 // It's only a warning if it shadows another label. | |
| 1112 existingElement = statementScope.lookupLabel(labelName); | |
| 1113 if (existingElement !== null) { | |
| 1114 warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]); | |
| 1115 warning(existingElement.label, | |
| 1116 MessageKind.EXISTING_LABEL, [labelName]); | |
| 1117 } | |
| 1118 } | |
| 1119 | |
| 1120 StatementElement statementElement = | |
| 1121 new StatementElement(switchCase, | |
| 1122 statementScope.nestingLevel, | |
| 1123 enclosingElement); | |
| 1124 mapping[switchCase] = statementElement; | |
| 1125 | |
| 1126 LabelElement label = | |
| 1127 new LabelElement(labelIdentifier, labelName, | |
|
ahe
2012/03/13 10:17:15
I don't understand the mental model for this. The
Lasse Reichstein Nielsen
2012/03/13 12:09:38
Are you referring to enclosingElement? I have assu
| |
| 1128 statementElement, enclosingElement); | |
| 1129 mapping[labelIdentifier] = label; | |
| 1130 continueLabels[labelName] = label; | |
| 1131 } | |
| 1132 cases = cases.tail; | |
| 1133 if (switchCase.defaultKeyword !== null && !cases.isEmpty()) { | |
| 1134 error(switchCase, MessageKind.INVALID_CASE_DEFAULT); | |
| 1135 } | |
| 1136 } | |
| 1137 statementScope.enterSwitch(breakElement, continueLabels); | |
| 1056 node.cases.accept(this); | 1138 node.cases.accept(this); |
| 1057 statementScope.exitLoop(); | 1139 statementScope.exitSwitch(); |
| 1140 | |
| 1141 // Clean-up unused labels | |
| 1142 continueLabels.forEach((String key, LabelElement label) { | |
|
ahe
2012/03/13 10:17:15
Wouldn't it better to set a bit on the label?
Lasse Reichstein Nielsen
2012/03/13 12:09:38
The bit is set. This is pure cleanup. We know that
| |
| 1143 StatementElement statementElement = label.target; | |
| 1144 SwitchCase switchCase = statementElement.statement; | |
| 1145 if (!label.isContinueTarget) { | |
| 1146 mapping.remove(switchCase); | |
| 1147 mapping.remove(label.label); | |
| 1148 } | |
| 1149 }); | |
| 1058 } | 1150 } |
| 1059 | 1151 |
| 1060 visitSwitchCase(SwitchCase node) { | 1152 visitSwitchCase(SwitchCase node) { |
| 1061 // TODO(ahe): What about the label? | 1153 // The label was handled in [visitSwitchStatement(SwitchStatement)]. |
| 1062 node.expression.accept(this); | 1154 node.expressions.accept(this); |
| 1063 visitIn(node.statements, new BlockScope(context)); | 1155 visitIn(node.statements, new BlockScope(context)); |
| 1064 } | 1156 } |
| 1065 | 1157 |
| 1066 visitDefaultCase(DefaultCase node) { | |
| 1067 // TODO(ahe): What about the label? | |
| 1068 visitIn(node.statements, new BlockScope(context)); | |
| 1069 } | |
| 1070 | |
| 1071 visitTryStatement(TryStatement node) { | 1158 visitTryStatement(TryStatement node) { |
| 1072 visit(node.tryBlock); | 1159 visit(node.tryBlock); |
| 1073 if (node.catchBlocks.isEmpty() && node.finallyBlock == null) { | 1160 if (node.catchBlocks.isEmpty() && node.finallyBlock == null) { |
| 1074 // TODO(ngeoffray): The precise location is | 1161 // TODO(ngeoffray): The precise location is |
| 1075 // node.getEndtoken.next. Adjust when issue #1581 is fixed. | 1162 // node.getEndtoken.next. Adjust when issue #1581 is fixed. |
| 1076 error(node, MessageKind.NO_CATCH_NOR_FINALLY); | 1163 error(node, MessageKind.NO_CATCH_NOR_FINALLY); |
| 1077 } | 1164 } |
| 1078 visit(node.catchBlocks); | 1165 visit(node.catchBlocks); |
| 1079 visit(node.finallyBlock); | 1166 visit(node.finallyBlock); |
| 1080 } | 1167 } |
| (...skipping 477 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1558 class TopScope extends Scope { | 1645 class TopScope extends Scope { |
| 1559 LibraryElement get library() => element; | 1646 LibraryElement get library() => element; |
| 1560 | 1647 |
| 1561 TopScope(LibraryElement library) : super(null, library); | 1648 TopScope(LibraryElement library) : super(null, library); |
| 1562 Element lookup(SourceString name) => library.find(name); | 1649 Element lookup(SourceString name) => library.find(name); |
| 1563 | 1650 |
| 1564 Element add(Element element) { | 1651 Element add(Element element) { |
| 1565 throw "Cannot add an element in the top scope"; | 1652 throw "Cannot add an element in the top scope"; |
| 1566 } | 1653 } |
| 1567 } | 1654 } |
| OLD | NEW |