| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 library kernel.checks; | 4 library kernel.checks; |
| 5 | 5 |
| 6 import 'ast.dart'; | 6 import 'ast.dart'; |
| 7 import 'transformations/flags.dart'; | 7 import 'transformations/flags.dart'; |
| 8 | 8 |
| 9 void verifyProgram(Program program) { | 9 void verifyProgram(Program program) { |
| 10 VerifyingVisitor.check(program); | 10 VerifyingVisitor.check(program); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 return "$file:${location.line}:${location.column}: Verification error:" | 31 return "$file:${location.line}:${location.column}: Verification error:" |
| 32 " $details"; | 32 " $details"; |
| 33 } else { | 33 } else { |
| 34 return "Verification error: $details\n" | 34 return "Verification error: $details\n" |
| 35 "Context: '$context'.\n" | 35 "Context: '$context'.\n" |
| 36 "Node: '$node'."; | 36 "Node: '$node'."; |
| 37 } | 37 } |
| 38 } | 38 } |
| 39 } | 39 } |
| 40 | 40 |
| 41 enum TypedefState { Done, BeingChecked } | |
| 42 | |
| 43 /// Checks that a kernel program is well-formed. | 41 /// Checks that a kernel program is well-formed. |
| 44 /// | 42 /// |
| 45 /// This does not include any kind of type checking. | 43 /// This does not include any kind of type checking. |
| 46 class VerifyingVisitor extends RecursiveVisitor { | 44 class VerifyingVisitor extends RecursiveVisitor { |
| 47 final Set<Class> classes = new Set<Class>(); | 45 final Set<Class> classes = new Set<Class>(); |
| 48 final Set<Typedef> typedefs = new Set<Typedef>(); | 46 final Set<TypeParameter> typeParameters = new Set<TypeParameter>(); |
| 49 Set<TypeParameter> typeParametersInScope = new Set<TypeParameter>(); | |
| 50 final List<VariableDeclaration> variableStack = <VariableDeclaration>[]; | 47 final List<VariableDeclaration> variableStack = <VariableDeclaration>[]; |
| 51 final Map<Typedef, TypedefState> typedefState = <Typedef, TypedefState>{}; | |
| 52 bool classTypeParametersAreInScope = false; | 48 bool classTypeParametersAreInScope = false; |
| 53 | 49 |
| 54 /// If true, relax certain checks for *outline* mode. For example, don't | 50 /// If true, relax certain checks for *outline* mode. For example, don't |
| 55 /// attempt to validate constructor initializers. | 51 /// attempt to validate constructor initializers. |
| 56 bool isOutline = false; | 52 bool isOutline = false; |
| 57 | 53 |
| 58 bool inCatchBlock = false; | 54 bool inCatchBlock = false; |
| 59 | 55 |
| 60 Member currentMember; | 56 Member currentMember; |
| 61 Class currentClass; | 57 Class currentClass; |
| 62 TreeNode currentParent; | 58 TreeNode currentParent; |
| 63 | 59 |
| 64 TreeNode get context => currentMember ?? currentClass; | 60 TreeNode get context => currentMember ?? currentClass; |
| 65 | 61 |
| 66 static void check(Program program) { | 62 static void check(Program program) { |
| 67 program.accept(new VerifyingVisitor()); | 63 program.accept(new VerifyingVisitor()); |
| 68 } | 64 } |
| 69 | 65 |
| 70 defaultTreeNode(TreeNode node) { | 66 defaultTreeNode(TreeNode node) { |
| 71 visitChildren(node); | 67 visitChildren(node); |
| 72 } | 68 } |
| 73 | 69 |
| 74 problem(TreeNode node, String details, {TreeNode context}) { | 70 problem(TreeNode node, String details) { |
| 75 context ??= this.context; | |
| 76 throw new VerificationError(context, node, details); | 71 throw new VerificationError(context, node, details); |
| 77 } | 72 } |
| 78 | 73 |
| 79 TreeNode enterParent(TreeNode node) { | 74 TreeNode enterParent(TreeNode node) { |
| 80 if (!identical(node.parent, currentParent)) { | 75 if (!identical(node.parent, currentParent)) { |
| 81 problem( | 76 problem( |
| 82 node, | 77 node, |
| 83 "Incorrect parent pointer on ${node.runtimeType}:" | 78 "Incorrect parent pointer: expected '${node.parent.runtimeType}'," |
| 84 " expected '${node.parent.runtimeType}'," | |
| 85 " but found: '${currentParent.runtimeType}'."); | 79 " but found: '${currentParent.runtimeType}'."); |
| 86 } | 80 } |
| 87 var oldParent = currentParent; | 81 var oldParent = currentParent; |
| 88 currentParent = node; | 82 currentParent = node; |
| 89 return oldParent; | 83 return oldParent; |
| 90 } | 84 } |
| 91 | 85 |
| 92 void exitParent(TreeNode oldParent) { | 86 void exitParent(TreeNode oldParent) { |
| 93 currentParent = oldParent; | 87 currentParent = oldParent; |
| 94 } | 88 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 variableStack.add(variable); | 128 variableStack.add(variable); |
| 135 } | 129 } |
| 136 | 130 |
| 137 void undeclareVariable(VariableDeclaration variable) { | 131 void undeclareVariable(VariableDeclaration variable) { |
| 138 variable.flags &= ~VariableDeclaration.FlagInScope; | 132 variable.flags &= ~VariableDeclaration.FlagInScope; |
| 139 } | 133 } |
| 140 | 134 |
| 141 void declareTypeParameters(List<TypeParameter> parameters) { | 135 void declareTypeParameters(List<TypeParameter> parameters) { |
| 142 for (int i = 0; i < parameters.length; ++i) { | 136 for (int i = 0; i < parameters.length; ++i) { |
| 143 var parameter = parameters[i]; | 137 var parameter = parameters[i]; |
| 144 if (!typeParametersInScope.add(parameter)) { | 138 if (!typeParameters.add(parameter)) { |
| 145 problem(parameter, "Type parameter '$parameter' redeclared."); | 139 problem(parameter, "Type parameter '$parameter' redeclared."); |
| 146 } | 140 } |
| 147 } | 141 } |
| 148 } | 142 } |
| 149 | 143 |
| 150 void undeclareTypeParameters(List<TypeParameter> parameters) { | 144 void undeclareTypeParameters(List<TypeParameter> parameters) { |
| 151 typeParametersInScope.removeAll(parameters); | 145 typeParameters.removeAll(parameters); |
| 152 } | 146 } |
| 153 | 147 |
| 154 void checkVariableInScope(VariableDeclaration variable, TreeNode where) { | 148 void checkVariableInScope(VariableDeclaration variable, TreeNode where) { |
| 155 if (variable.flags & VariableDeclaration.FlagInScope == 0) { | 149 if (variable.flags & VariableDeclaration.FlagInScope == 0) { |
| 156 problem(where, "Variable '$variable' used out of scope."); | 150 problem(where, "Variable '$variable' used out of scope."); |
| 157 } | 151 } |
| 158 } | 152 } |
| 159 | 153 |
| 160 visitProgram(Program program) { | 154 visitProgram(Program program) { |
| 161 try { | 155 try { |
| 162 for (var library in program.libraries) { | 156 for (var library in program.libraries) { |
| 163 for (var class_ in library.classes) { | 157 for (var class_ in library.classes) { |
| 164 if (!classes.add(class_)) { | 158 if (!classes.add(class_)) { |
| 165 problem(class_, "Class '$class_' declared more than once."); | 159 problem(class_, "Class '$class_' declared more than once."); |
| 166 } | 160 } |
| 167 } | 161 } |
| 168 for (var typedef_ in library.typedefs) { | |
| 169 if (!typedefs.add(typedef_)) { | |
| 170 problem(typedef_, "Typedef '$typedef_' declared more than once."); | |
| 171 } | |
| 172 } | |
| 173 library.members.forEach(declareMember); | 162 library.members.forEach(declareMember); |
| 174 for (var class_ in library.classes) { | 163 for (var class_ in library.classes) { |
| 175 class_.members.forEach(declareMember); | 164 class_.members.forEach(declareMember); |
| 176 } | 165 } |
| 177 } | 166 } |
| 178 visitChildren(program); | 167 visitChildren(program); |
| 179 } finally { | 168 } finally { |
| 180 for (var library in program.libraries) { | 169 for (var library in program.libraries) { |
| 181 library.members.forEach(undeclareMember); | 170 library.members.forEach(undeclareMember); |
| 182 for (var class_ in library.classes) { | 171 for (var class_ in library.classes) { |
| 183 class_.members.forEach(undeclareMember); | 172 class_.members.forEach(undeclareMember); |
| 184 } | 173 } |
| 185 } | 174 } |
| 186 variableStack.forEach(undeclareVariable); | 175 variableStack.forEach(undeclareVariable); |
| 187 } | 176 } |
| 188 } | 177 } |
| 189 | 178 |
| 190 void checkTypedef(Typedef node) { | |
| 191 var state = typedefState[node]; | |
| 192 if (state == TypedefState.Done) return; | |
| 193 if (state == TypedefState.BeingChecked) { | |
| 194 problem(node, "The typedef '$node' refers to itself", context: node); | |
| 195 } | |
| 196 assert(state == null); | |
| 197 typedefState[node] = TypedefState.BeingChecked; | |
| 198 var savedTypeParameters = typeParametersInScope; | |
| 199 typeParametersInScope = node.typeParameters.toSet(); | |
| 200 var savedParent = currentParent; | |
| 201 currentParent = node; | |
| 202 // Visit children without checking the parent pointer on the typedef itself | |
| 203 // since this can be called from a context other than its true parent. | |
| 204 node.visitChildren(this); | |
| 205 currentParent = savedParent; | |
| 206 typeParametersInScope = savedTypeParameters; | |
| 207 typedefState[node] = TypedefState.Done; | |
| 208 } | |
| 209 | |
| 210 visitTypedef(Typedef node) { | |
| 211 checkTypedef(node); | |
| 212 // Enter and exit the node to check the parent pointer on the typedef node. | |
| 213 exitParent(enterParent(node)); | |
| 214 } | |
| 215 | |
| 216 visitField(Field node) { | 179 visitField(Field node) { |
| 217 currentMember = node; | 180 currentMember = node; |
| 218 var oldParent = enterParent(node); | 181 var oldParent = enterParent(node); |
| 219 classTypeParametersAreInScope = !node.isStatic; | 182 classTypeParametersAreInScope = !node.isStatic; |
| 220 node.initializer?.accept(this); | 183 node.initializer?.accept(this); |
| 221 classTypeParametersAreInScope = false; | 184 classTypeParametersAreInScope = false; |
| 222 visitList(node.annotations, this); | 185 visitList(node.annotations, this); |
| 223 exitParent(oldParent); | 186 exitParent(oldParent); |
| 224 node.type.accept(this); | |
| 225 currentMember = null; | 187 currentMember = null; |
| 226 } | 188 } |
| 227 | 189 |
| 228 visitProcedure(Procedure node) { | 190 visitProcedure(Procedure node) { |
| 229 currentMember = node; | 191 currentMember = node; |
| 230 var oldParent = enterParent(node); | 192 var oldParent = enterParent(node); |
| 231 classTypeParametersAreInScope = !node.isStatic; | 193 classTypeParametersAreInScope = !node.isStatic; |
| 232 node.function.accept(this); | 194 node.function.accept(this); |
| 233 classTypeParametersAreInScope = false; | 195 classTypeParametersAreInScope = false; |
| 234 visitList(node.annotations, this); | 196 visitList(node.annotations, this); |
| (...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 523 | 485 |
| 524 @override | 486 @override |
| 525 visitClassReference(Class node) { | 487 visitClassReference(Class node) { |
| 526 if (!classes.contains(node)) { | 488 if (!classes.contains(node)) { |
| 527 problem( | 489 problem( |
| 528 node, "Dangling reference to '$node', parent is: '${node.parent}'."); | 490 node, "Dangling reference to '$node', parent is: '${node.parent}'."); |
| 529 } | 491 } |
| 530 } | 492 } |
| 531 | 493 |
| 532 @override | 494 @override |
| 533 visitTypedefReference(Typedef node) { | |
| 534 if (!typedefs.contains(node)) { | |
| 535 problem( | |
| 536 node, "Dangling reference to '$node', parent is: '${node.parent}'"); | |
| 537 } | |
| 538 } | |
| 539 | |
| 540 @override | |
| 541 visitTypeParameterType(TypeParameterType node) { | 495 visitTypeParameterType(TypeParameterType node) { |
| 542 var parameter = node.parameter; | 496 var parameter = node.parameter; |
| 543 if (!typeParametersInScope.contains(parameter)) { | 497 if (!typeParameters.contains(parameter)) { |
| 544 problem( | 498 problem( |
| 545 currentParent, | 499 currentParent, |
| 546 "Type parameter '$parameter' referenced out of" | 500 "Type parameter '$parameter' referenced out of" |
| 547 " scope, parent is: '${parameter.parent}'."); | 501 " scope, parent is: '${parameter.parent}'."); |
| 548 } | 502 } |
| 549 if (parameter.parent is Class && !classTypeParametersAreInScope) { | 503 if (parameter.parent is Class && !classTypeParametersAreInScope) { |
| 550 problem( | 504 problem( |
| 551 currentParent, | 505 currentParent, |
| 552 "Type parameter '$parameter' referenced from" | 506 "Type parameter '$parameter' referenced from" |
| 553 " static context, parent is '${parameter.parent}'."); | 507 " static context, parent is '${parameter.parent}'."); |
| 554 } | 508 } |
| 555 } | 509 } |
| 556 | 510 |
| 557 @override | 511 @override |
| 558 visitInterfaceType(InterfaceType node) { | 512 visitInterfaceType(InterfaceType node) { |
| 559 node.visitChildren(this); | 513 node.visitChildren(this); |
| 560 if (node.typeArguments.length != node.classNode.typeParameters.length) { | 514 if (node.typeArguments.length != node.classNode.typeParameters.length) { |
| 561 problem( | 515 problem( |
| 562 currentParent, | 516 currentParent, |
| 563 "Type $node provides ${node.typeArguments.length}" | 517 "Type $node provides ${node.typeArguments.length}" |
| 564 " type arguments but the class declares" | 518 " type arguments but the class declares" |
| 565 " ${node.classNode.typeParameters.length} parameters."); | 519 " ${node.classNode.typeParameters.length} parameters."); |
| 566 } | 520 } |
| 567 } | 521 } |
| 568 | |
| 569 @override | |
| 570 visitTypedefType(TypedefType node) { | |
| 571 checkTypedef(node.typedefNode); | |
| 572 node.visitChildren(this); | |
| 573 if (node.typeArguments.length != node.typedefNode.typeParameters.length) { | |
| 574 problem( | |
| 575 currentParent, | |
| 576 "The typedef type $node provides ${node.typeArguments.length}" | |
| 577 " type arguments but the typedef declares" | |
| 578 " ${node.typedefNode.typeParameters.length} parameters."); | |
| 579 } | |
| 580 } | |
| 581 } | 522 } |
| 582 | 523 |
| 583 class CheckParentPointers extends Visitor { | 524 class CheckParentPointers extends Visitor { |
| 584 static void check(TreeNode node) { | 525 static void check(TreeNode node) { |
| 585 node.accept(new CheckParentPointers(node.parent)); | 526 node.accept(new CheckParentPointers(node.parent)); |
| 586 } | 527 } |
| 587 | 528 |
| 588 TreeNode parent; | 529 TreeNode parent; |
| 589 | 530 |
| 590 CheckParentPointers([this.parent]); | 531 CheckParentPointers([this.parent]); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 601 var oldParent = parent; | 542 var oldParent = parent; |
| 602 parent = node; | 543 parent = node; |
| 603 node.visitChildren(this); | 544 node.visitChildren(this); |
| 604 parent = oldParent; | 545 parent = oldParent; |
| 605 } | 546 } |
| 606 } | 547 } |
| 607 | 548 |
| 608 void checkInitializers(Constructor constructor) { | 549 void checkInitializers(Constructor constructor) { |
| 609 // TODO(ahe): I'll add more here in other CLs. | 550 // TODO(ahe): I'll add more here in other CLs. |
| 610 } | 551 } |
| OLD | NEW |